206 lines
8.6 KiB
Clojure
Executable file
206 lines
8.6 KiB
Clojure
Executable file
#!/usr/bin/env bb
|
|
;; Adds a library to bb-tested-libs.edn and libraries.csv and optionally run its
|
|
;; tests. There are two modes to this script - automatic (default) and manual.
|
|
;; The script defaults to automatically copying tests as this normally works.
|
|
;; There are several options to specify where the library is including
|
|
;; --git-url, --dir and --test-directories. See --help for more. In manual mode,
|
|
;; tests are manually added outside of the script and the script is run to add
|
|
;; the library to library lists.
|
|
|
|
(ns add-libtest
|
|
(:require [babashka.deps :as deps]
|
|
[babashka.fs :as fs]
|
|
[babashka.tasks :refer [shell]]
|
|
[clojure.edn :as edn]
|
|
[clojure.java.io :as io]
|
|
[clojure.string :as str]
|
|
[clojure.tools.cli :as cli]
|
|
[org.httpkit.client :as http]))
|
|
|
|
(deps/add-deps '{:deps {org.clojure/tools.gitlibs {:mvn/version "2.4.172"}
|
|
borkdude/rewrite-edn {:mvn/version "0.1.0"}
|
|
org.clojure/data.csv {:mvn/version "1.0.0"}}})
|
|
|
|
(require '[clojure.tools.gitlibs :as gl])
|
|
(require '[borkdude.rewrite-edn :as r])
|
|
(require '[clojure.data.csv :as csv])
|
|
|
|
;; CLI Utils
|
|
;; =========
|
|
(defn- error
|
|
"Print error message(s) and exit"
|
|
[& msgs]
|
|
(apply println "Error:" msgs)
|
|
(System/exit 1))
|
|
|
|
(defn- print-summary
|
|
"Print help summary given args and opts strings"
|
|
[args-string options-summary]
|
|
(println (format "Usage: %s [OPTIONS]%s\nOptions:\n%s"
|
|
(.getName (io/file *file*))
|
|
args-string
|
|
options-summary)))
|
|
|
|
(defn- run-command
|
|
"Processes a command's functionality given a cli options definition, arguments
|
|
and primary command fn. This handles option parsing, handles any errors with
|
|
parsing and then passes parsed input to command fn"
|
|
[command-fn args cli-opts & parse-opts-options]
|
|
(let [{:keys [errors] :as parsed-input}
|
|
(apply cli/parse-opts args cli-opts parse-opts-options)]
|
|
(if (seq errors)
|
|
(do
|
|
(error (str/join "\n" (into ["Options failed to parse:"] errors)))
|
|
(System/exit 1))
|
|
(command-fn parsed-input))))
|
|
|
|
;; Add libtest
|
|
;; ===========
|
|
(defn- add-lib-to-deps
|
|
[lib-name lib-coordinate]
|
|
(let [nodes (-> "deps.edn" slurp r/parse-string)]
|
|
(spit "deps.edn"
|
|
(str (r/assoc-in nodes
|
|
[:aliases :lib-tests :extra-deps lib-name]
|
|
lib-coordinate)))))
|
|
|
|
(defn- default-test-dir
|
|
[lib-root-dir]
|
|
(some #(when (fs/exists? (fs/file lib-root-dir %))
|
|
(str (fs/file lib-root-dir %)))
|
|
;; Most common test dir
|
|
["test"
|
|
;; official clojure repos like https://github.com/clojure/tools.gitlibs
|
|
"src/test/clojure"]))
|
|
|
|
(defn- copy-tests
|
|
[git-url lib-name {:keys [directory branch test-directories]}]
|
|
(let [lib-dir (if branch
|
|
(gl/procure git-url lib-name branch)
|
|
(or (gl/procure git-url lib-name "master")
|
|
(gl/procure git-url lib-name "main")
|
|
(throw (ex-info "Unable to clone git-url" {}))))
|
|
_ (println "Git clone is at" lib-dir)
|
|
lib-root-dir (if directory (fs/file lib-dir directory) lib-dir)
|
|
test-dirs (if test-directories
|
|
(map #(when (fs/exists? (fs/file lib-root-dir %))
|
|
(str (fs/file lib-root-dir %)))
|
|
test-directories)
|
|
(some-> (default-test-dir lib-root-dir) vector))]
|
|
(when (empty? test-dirs)
|
|
(error "No test directories found"))
|
|
(doseq [test-dir test-dirs]
|
|
(shell "cp -R" (str test-dir fs/file-separator) "test-resources/lib_tests/"))
|
|
{:lib-dir lib-dir
|
|
:test-dirs test-dirs}))
|
|
|
|
(defn- default-test-namespaces
|
|
[test-dir]
|
|
(let [relative-test-files (map #(str (fs/relativize test-dir %))
|
|
(fs/glob test-dir "**/*.{clj,cljc}"))]
|
|
(when (empty? relative-test-files)
|
|
(error (str "No test files found in " test-dir)))
|
|
(map #(-> %
|
|
(str/replace fs/file-separator ".")
|
|
(str/replace "_" "-")
|
|
(str/replace-first #"\.clj(c?)$" "")
|
|
symbol)
|
|
relative-test-files)))
|
|
|
|
(defn- add-lib-to-tested-libs
|
|
[lib-name git-url {:keys [lib-dir test-dirs]} options]
|
|
(let [namespaces (or (get-in options [:manually-added :test-namespaces])
|
|
(mapcat default-test-namespaces test-dirs))
|
|
default-lib (merge
|
|
{:git-url git-url
|
|
:test-namespaces namespaces}
|
|
;; Options needed to update libs
|
|
(select-keys options [:branch :directory :test-directories]))
|
|
lib (if (:manually-added options)
|
|
(-> default-lib
|
|
(merge (:manually-added options))
|
|
(assoc :manually-added true))
|
|
(assoc default-lib
|
|
:git-sha (fs/file-name lib-dir)))
|
|
nodes (-> "test-resources/lib_tests/bb-tested-libs.edn" slurp r/parse-string)]
|
|
(spit "test-resources/lib_tests/bb-tested-libs.edn"
|
|
(str (r/assoc-in nodes
|
|
[(symbol lib-name)]
|
|
lib)))
|
|
namespaces))
|
|
|
|
(defn- fetch-artifact
|
|
"Using the clojars api to get a library's git url doesn't always work. A
|
|
possibly more reliable data source could be the scm urls in this POM feed -
|
|
https://github.com/clojars/clojars-web/wiki/Data#useful-extracts-from-the-poms"
|
|
[artifact]
|
|
(let [url (str "https://clojars.org/api/artifacts/" artifact)
|
|
_ (println "GET" url "...")
|
|
resp @(http/get url {:headers {"Accept" "application/edn"}})]
|
|
(if (= 200 (:status resp))
|
|
(-> resp :body slurp edn/read-string)
|
|
(error (str "Response failed and returned " (pr-str resp))))))
|
|
|
|
(defn- get-lib-map
|
|
[deps-string options]
|
|
;; if deps-string is artifact name
|
|
(if (re-matches #"\S+/\S+" deps-string)
|
|
(let [artifact-edn (fetch-artifact deps-string)]
|
|
{:lib-name (symbol deps-string)
|
|
:lib-coordinate {:mvn/version (:latest_version artifact-edn)}
|
|
:git-url (or (:git-url options) (:homepage artifact-edn))})
|
|
(let [deps-map (edn/read-string deps-string)
|
|
_ (when (or (not (map? deps-map)) (not= 1 (count deps-map)))
|
|
(error "Deps map must have one key"))
|
|
lib-coordinate (-> deps-map vals first)]
|
|
{:lib-name (ffirst deps-map)
|
|
:lib-coordinate lib-coordinate
|
|
:git-url (or (:git/url lib-coordinate) (:git-url options))})))
|
|
|
|
(defn- write-lib-to-csv
|
|
"Updates libraries.csv with latest bb-tested-libs.edn"
|
|
[]
|
|
(let [libs (-> "test-resources/lib_tests/bb-tested-libs.edn" slurp edn/read-string)
|
|
rows (sort-by first
|
|
(map (fn [[name {:keys [git-url]}]]
|
|
[name git-url]) libs))]
|
|
(with-open [w (io/writer "doc/libraries.csv")]
|
|
(csv/write-csv w (into [["maven-name" "git-url"]] rows)))))
|
|
|
|
(defn- add-libtest*
|
|
[args options]
|
|
(let [[artifact-or-deps-string] args
|
|
{:keys [lib-name lib-coordinate git-url]}
|
|
(get-lib-map artifact-or-deps-string options)
|
|
_ (when (nil? git-url)
|
|
(error "git-url is required. Please specify with --git-url"))
|
|
_ (when-not (:manually-added options) (add-lib-to-deps lib-name lib-coordinate))
|
|
dirs (when-not (:manually-added options) (copy-tests git-url lib-name options))
|
|
namespaces (add-lib-to-tested-libs lib-name git-url dirs options)]
|
|
(println "Added lib" lib-name "which tests the following namespaces:" namespaces)
|
|
(write-lib-to-csv)
|
|
(when (:test options)
|
|
(apply shell "script/lib_tests/run_all_libtests" namespaces))))
|
|
|
|
(defn add-libtest
|
|
[{:keys [arguments options summary]}]
|
|
(if (or (< (count arguments) 1) (:help options))
|
|
(print-summary "ARTIFACT-OR-DEPS-MAP " summary)
|
|
(add-libtest* arguments options)))
|
|
|
|
(def cli-options
|
|
[["-h" "--help"]
|
|
["-t" "--test" "Run tests"]
|
|
;; https://github.com/weavejester/environ/tree/master/environ used this option
|
|
["-d" "--directory DIRECTORY" "Directory where library is located"]
|
|
;; https://github.com/reifyhealth/specmonstah used this option
|
|
["-b" "--branch BRANCH" "Default branch for git url"]
|
|
["-g" "--git-url GITURL" "Git url for artifact. Defaults to homepage on clojars"]
|
|
["-m" "--manually-added LIB-MAP" "Only add library to edn file with LIB-MAP merged into library entry"
|
|
:parse-fn edn/read-string :validate-fn map?]
|
|
;; https://github.com/jeaye/orchestra used this option
|
|
["-T" "--test-directories TEST-DIRECTORY" "Directories where library tests are located"
|
|
:multi true :update-fn conj]])
|
|
|
|
(when (= *file* (System/getProperty "babashka.file"))
|
|
(run-command add-libtest *command-line-args* cli-options))
|