Compare commits

..

No commits in common. "master" and "fecd09c65624afef5565d7120421891a948a8643" have entirely different histories.

9 changed files with 51 additions and 182 deletions

View File

@ -5,24 +5,8 @@
`matchbot` uses [version 2.0.0 of the Semantic Versioning](https://semver.org/spec/v2.0.0.html) scheme.
## [1.1.0] - 2022-05-14
* Add build system for generating POM, JAR and uberjar (standalone JAR) files
* Improve code quality
## [1.0.1] - 2022-04-01
* Fix vulnerability that causes serialised data to be deleted when it contains certain user input
* Change internal representation of game names, fixing a potential memory leak for long-running instances
* Improve reliability and code quality of the startup and shutdown sequences
## [1.0.0] - 2022-03-03
* Initial release
[1.1.0]: https://git.libregaming.org/LibreGaming/matchbot/releases/tag/1.1.0
[1.0.1]: https://git.libregaming.org/LibreGaming/matchbot/releases/tag/1.0.1
[1.0.0]: https://git.libregaming.org/LibreGaming/matchbot/releases/tag/1.0.0

View File

@ -40,37 +40,3 @@ For example:
> `;; SPDX-FileCopyrightText: 2022 Joe Bloggs <joe@example.com>`
If the copyright to your contributions is held by your employer, put your employer's name in brackets after your own name.
## Build system
### Building a POM file
The POM file lists the dependencies needed to run `matchbot` as well as some additional information that can help people learn more about `matchbot`.
To generate a POM file, run `clojure -T:build pom`; you should find the generated `POM.xml` file in the `target/` directory.
### Building a JAR file
A JAR file contains all the source code of `matchbot` in a form that the JVM can load and pass to the Clojure compiler to run.
To generate a JAR file, run `clojure -T:build jar`; again, you should find the JAR file called something like `matchbot-x.x.x.jar` in the `target/` directory.
### Building an uberjar
An uberjar is much like a normal JAR file, but comes with all the dependencies of `matchbot` bundled in it.
This means that it can run directly on the JVM without Clojure being installed (it contains a copy of the Clojure compiler itself).
To generate an uberjar, run `clojure -T:build uber`; you should find the uberjar called something like `matchbot-x.x.x-standalone.jar` in the `target/` directory.
Please note that if you distribute an uberjar, you must not only comply with the licence of `matchbot`, but also the licences of all `matchbot`'s dependencies, both transitive and intransitive.
## Versioning and release process
### Semantic Versioning
`matchbot` uses [version 2.0.0 of the Semantic Versioning](https://semver.org/spec/v2.0.0.html) scheme, but there is still ambiguity in what exactly comprises the 'Public API' (used for determining the right part of the version to increment) for something like `matchbot`.
This 'Public API' is defined for `matchbot` as everything that is accessible by the end user or administrator of a `matchbot` instance.
For example, a change that requires the configuration file to be updated warrants a major version increment because it effects the administrator; however, a change to the structure of the internal namespaces would only require a patch level version increment because it doesn't affect either the administrator or the end user.
### Release process
At some point after a new feature has been added to `matchbot` or a bug has been fixed, a release will be made.
Once a suitable version increment for the type of changes has been determined, the `CHANGELOG.md` file at the root of the repository will be updated with release notes documenting the changes made in that version.
Then, a JAR file and a POM file will be produced using the build system (see above for more information), signed using GPG and finally uploaded to [Maven Central](https://central.sonatype.org/).
Currently this process is done by [Sebastian Crane](https://git.libregaming.org/seabass); if you would like to help with making releases, please familiarise yourself with the process (you can try everything locally except upload to Maven Central) and get in contact! 😀

View File

@ -55,7 +55,7 @@ clojure -M -m system
Running the tests is a similar process to running the main application:
```bash
clojure -M:test
clojure -M:test -m kaocha.runner
```
### Starting a development REPL

View File

@ -1,77 +0,0 @@
;; SPDX-License-Identifier: Apache-2.0
;; SPDX-FileCopyrightText: 2022 Sebastian Crane <seabass-labrax@gmx.com>
(ns build
(:require [clojure.tools.build.api :as b]
[tools-pom.tasks :as pom]))
(def application 'org.libregaming/matchbot)
(def version "1.1.1-SNAPSHOT")
(def src-dirs ["src"])
(def target-dir "target")
(def class-dir (format "%s/%s" target-dir "classes"))
(def basis (b/create-basis {:project "deps.edn"}))
(def pom-file (format "%s/pom.xml" target-dir))
(def jar-file (format "%s/%s-%s.jar" target-dir (name application) version))
(def uber-file (format "%s/%s-%s-standalone.jar" target-dir (name application) version))
(defn clean [_]
(b/delete {:path target-dir}))
(defn uber [_]
(b/delete {:path class-dir})
(b/copy-dir {:src-dirs src-dirs
:target-dir class-dir})
(b/compile-clj {:basis basis
:src-dirs src-dirs
:class-dir class-dir})
(b/uber {:class-dir class-dir
:uber-file uber-file
:basis basis
:main 'system}))
(defn jar [_]
(b/delete {:path class-dir})
(b/copy-dir {:src-dirs src-dirs
:target-dir class-dir})
(b/jar {:class-dir class-dir
:jar-file jar-file}))
(defn pom [_]
(pom/pom
{:lib application
:version version
:write-pom true
:validate-pom true
:pom
{:description
"A chatbot for announcing upcoming matches and finding fellow players, written for the LibreGaming community"
:url
"https://git.libregaming.org/LibreGaming/matchbot"
:licenses
[:license
{:name "Apache-2.0"
:url "https://www.apache.org/licenses/LICENSE-2.0.html"}]
:developers
[:developer
{:id "seabass"
:name "Sebastian Crane"
:email "seabass-labrax@gmx.com"
:organization "LibreGaming"
:organization-url "https://libregaming.org"
:roles [:role "Maintainer"]
:timezone "Europe/London"}]
:scm
{:url "https://git.libregaming.org/LibreGaming/matchbot"
:connection "scm:git:https://git.libregaming.org/LibreGaming/matchbot.git"
:developer-connection "scm:git:ssh://git@git.libregaming.org/LibreGaming/matchbot.git"}
:issue-management
{:system "Gitea"
:url "https://git.libregaming.org/LibreGaming/matchbot/issues"}}})
(b/copy-file {:src "pom.xml" :target pom-file})
(b/delete {:path "pom.xml"}))
(defn all [_]
(jar nil)
(uber nil)
(pom nil))

View File

@ -6,8 +6,4 @@
clj-commons/clj-yaml {:mvn/version "0.7.107"}
irclj/irclj {:mvn/version "0.5.0-alpha4"}}
:aliases {:test {:extra-paths ["test"]
:extra-deps {lambdaisland/kaocha {:mvn/version "1.60.972"}}
:main-opts ["-m" "kaocha.runner"]}
:build {:deps {io.github.clojure/tools.build {:git/tag "v0.8.1" :git/sha "7d40500"}
com.github.pmonks/tools-pom {:mvn/version "1.0.74"}}
:ns-default build}}}
:extra-deps {lambdaisland/kaocha {:mvn/version "1.60.972"}}}}}

View File

@ -6,15 +6,15 @@
[game]
[irclj.core]))
(defn lower-case-game [game]
(defn keywordise-game [game]
(when (string? game)
(str/lower-case game)))
(keyword (str/lower-case game))))
(defn sort-case-insensitive [coll]
(sort #(apply compare (map str/lower-case %&)) coll))
(defn match-string [& {:keys [state game player]}]
(as-> (lower-case-game game) x
(as-> (keywordise-game game) x
(game/get-players-of-game state x)
(disj x player)
(sort-case-insensitive x)
@ -22,7 +22,7 @@
(str "Anyone ready for " game "? " x)))
(defn list-players-string [& {:keys [state game]}]
(as-> (lower-case-game game) x
(as-> (keywordise-game game) x
(game/get-players-of-game state x)
(sort-case-insensitive x)
(map #(str " _" % "_") x)
@ -46,7 +46,7 @@
(str "Games with a list of players: "
(str/join
", "
(sort-case-insensitive (game/get-games state)))))
(sort-case-insensitive (map name (game/get-games state))))))
(defn help-string [& {:keys []}]
" !list - show all the games that have a list of players
@ -59,7 +59,7 @@
(let [message-parts (str/split message #"\s")
command (if-let [x (first message-parts)] (str/lower-case x) "")
game (second message-parts)
game-keyword (lower-case-game game)]
game-keyword (keywordise-game game)]
{:command command
:game game
:game-keyword game-keyword}))

View File

@ -4,26 +4,23 @@
(ns system
(:require [irc]
[clojure.data.json :as json]
[clojure.set :as set]
[clj-yaml.core :as yaml])
(:gen-class))
[clj-yaml.core :as yaml]))
(defn setify-vals [x]
(reduce #(assoc %1
(first %2)
(set (second %2)))
{} x))
(defn process-json [x]
(-> (set/rename-keys x {"games" :games})
(update :games setify-vals)))
(defn json-data-reader [key value]
(if (= key :games)
(into (empty value)
(map #(hash-map (first %)
(set (second %)))
value))
value))
(defn load-state [f]
(process-json
(try
(with-open [datafile (clojure.java.io/reader f)]
(json/read datafile))
(catch Exception e nil))))
(try
(with-open [datafile (clojure.java.io/reader f)]
(json/read datafile
:value-fn json-data-reader
:key-fn keyword))
(catch Exception e nil)))
(defn save-state [f data]
(try
@ -37,7 +34,7 @@
(yaml/parse-stream datafile))
(catch Exception e nil)))
(defn start [_]
(defn start [system]
(let [config (load-config "config.yaml")
state (atom (load-state (:data-file config)))
irc (irc/new-irc-connection state config)]
@ -47,14 +44,17 @@
:irc irc}))
(defn stop [system]
(save-state
(get-in system [:config :data-file])
(deref (:state system)))
(irclj.core/quit (system :irc)))
(do
(save-state
(get-in system [:config :data-file])
(deref (:state system)))
(irclj.core/quit (system :irc))))
(defn restart [system-var]
(stop (deref system-var))
(alter-var-root system-var start))
(do
(alter-var-root system-var stop)
(alter-var-root system-var (constantly nil))
(alter-var-root system-var start)))
(defn -main [& args]
(let [main-system (system/start nil)]

View File

@ -5,13 +5,13 @@
(:require [clojure.test :refer :all]
[bot :refer :all]))
(def test-state '{:games {"hypothetical-shooter" #{"abc" "xyz" "123"}
"quasi-rts" #{"abc" "123"}
"imaginary-rpg" #{"xyz" "abc"}}})
(def test-state '{:games {:hypothetical-shooter #{"abc" "xyz" "123"}
:quasi-rts #{"abc" "123"}
:imaginary-rpg #{"xyz" "abc"}}})
(deftest lower-case-game-test
(is (= "quasi-rts"
(lower-case-game "Quasi-RTS"))))
(deftest keywordise-game-test
(is (= :quasi-rts
(keywordise-game "Quasi-RTS"))))
(deftest sort-case-insensitive-test
(is (= ["A" "b" "C"]
@ -38,7 +38,7 @@
(list-games-string :state test-state))))
(deftest split-message-test
(is (= {:command "!match" :game "Quasi-Rts" :game-keyword "quasi-rts"}
(is (= {:command "!match" :game "Quasi-Rts" :game-keyword :quasi-rts}
(split-message "!match Quasi-Rts "))))
(deftest dispatch-command-test
@ -46,7 +46,7 @@
(is (and
(= "Added 123 to the list of players for Imaginary-RPG!"
(dispatch-command state-atom "123" "!add Imaginary-RPG"))
(= {:games {"hypothetical-shooter" #{"abc" "xyz" "123"}
"quasi-rts" #{"abc" "123"}
"imaginary-rpg" #{"xyz" "abc" "123"}}}
(= {:games {:hypothetical-shooter #{"abc" "xyz" "123"}
:quasi-rts #{"abc" "123"}
:imaginary-rpg #{"xyz" "abc" "123"}}}
@state-atom)))))

View File

@ -5,31 +5,31 @@
(:require [clojure.test :refer :all]
[game :refer :all]))
(def test-state '{:games {"hypothetical-shooter" #{"player-one" "player-two" "player-three"}
"quasi-rts" #{"player-two" "player-four"}
"imaginary-rpg" #{"player-one" "player-three" "player-four"}}})
(def test-state '{:games {:hypothetical-shooter #{"player-one" "player-two" "player-three"}
:quasi-rts #{"player-two" "player-four"}
:imaginary-rpg #{"player-one" "player-three" "player-four"}}})
(deftest get-players-of-game-test
(is (=
'#{"player-two" "player-four"}
(get-players-of-game test-state "quasi-rts"))))
(get-players-of-game test-state :quasi-rts))))
(deftest add-player-of-game-test
(is (=
'#{"player-one" "player-two" "player-four"}
(get-in (add-player-of-game test-state "quasi-rts" "player-one") [:games "quasi-rts"]))))
(get-in (add-player-of-game test-state :quasi-rts "player-one") [:games :quasi-rts]))))
(deftest remove-player-of-game-test
(is (=
'#{"player-one" "player-three"}
(get-in (remove-player-of-game test-state "imaginary-rpg" "player-four") [:games "imaginary-rpg"]))))
(get-in (remove-player-of-game test-state :imaginary-rpg "player-four") [:games :imaginary-rpg]))))
(deftest get-games-test
(is (=
'#{"hypothetical-shooter" "quasi-rts" "imaginary-rpg"}
'#{:hypothetical-shooter :quasi-rts :imaginary-rpg}
(set (get-games test-state)))))
(deftest remove-game-test
(is (=
'#{"hypothetical-shooter" "imaginary-rpg"}
(set (keys (:games (remove-game test-state "quasi-rts")))))))
'#{:hypothetical-shooter :imaginary-rpg}
(set (keys (:games (remove-game test-state :quasi-rts)))))))