diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc
index 44e909a9..2e95cdd0 100644
--- a/CHANGELOG.adoc
+++ b/CHANGELOG.adoc
@@ -2,6 +2,22 @@ All notable changes to this project will be documented in this file. This change
== Unreleased (dev)
+== 0.11.0 (2021-02-06)
+
+// {{{
+=== Added
+
+* Added support to display diff URLs for outdated dependencies.
+
+=== Changed
+
+* Changed default error message for `format` reporter to add diff URLs.
+
+=== Fixed
+
+* Fixed to return actual tag name for outdated GitHub Actions.
+// }}}
+
== 0.10.3 (2021-02-01)
// {{{
=== Changed
diff --git a/README.adoc b/README.adoc
index 9f13af3c..4cedee62 100644
--- a/README.adoc
+++ b/README.adoc
@@ -140,6 +140,9 @@ You can use following variables:
| `{{latest-version}}`
| The latest version.
+| `{{diff-url}}`
+| The diff URL for Version Control System. (Nullable)
+
| `{{message}}`
| Default error message.
diff --git a/pom.xml b/pom.xml
index db06d1a4..8c4cabe4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
4.0.0
antq
antq
- 0.10.3
+ 0.11.0
antq
Point out your outdated dependencies
https://github.com/liquidz/antq
diff --git a/src/antq/core.clj b/src/antq/core.clj
index 9621b8be..d9c547c9 100644
--- a/src/antq/core.clj
+++ b/src/antq/core.clj
@@ -14,6 +14,11 @@
[antq.dep.leiningen :as dep.lein]
[antq.dep.pom :as dep.pom]
[antq.dep.shadow :as dep.shadow]
+ [antq.diff :as diff]
+ [antq.diff.git-sha]
+ [antq.diff.github-tag]
+ [antq.diff.java]
+ [antq.log :as log]
[antq.record :as r]
[antq.report :as report]
[antq.report.edn]
@@ -100,7 +105,7 @@
:name dep-name})
(ver/get-sorted-versions)
(first)
- (println))))
+ (log/info))))
(defn- assoc-latest-version
[dep]
@@ -146,6 +151,12 @@
assoc-latest-version))
(remove ver/latest?))))
+(defn assoc-diff-url
+ [version-checked-dep]
+ (if-let [url (diff/get-diff-url version-checked-dep)]
+ (assoc version-checked-dep :diff-url url)
+ version-checked-dep))
+
(defn exit
[outdated-deps]
(System/exit (if (seq outdated-deps) 1 0)))
@@ -184,7 +195,8 @@
deps (fetch-deps options)
deps (unify-org-clojure-deps deps)]
(if (seq deps)
- (let [outdated (outdated-deps deps options)]
+ (let [outdated (outdated-deps deps options)
+ outdated (map assoc-diff-url outdated)]
(report/reporter outdated options)
(cond-> outdated
@@ -193,5 +205,5 @@
true
(exit)))
- (do (println "No project file")
+ (do (log/info "No project file")
(System/exit 1)))))
diff --git a/src/antq/dep/github_action.clj b/src/antq/dep/github_action.clj
index c1b21f54..04516524 100644
--- a/src/antq/dep/github_action.clj
+++ b/src/antq/dep/github_action.clj
@@ -2,7 +2,6 @@
(:require
[antq.record :as r]
[antq.util.dep :as u.dep]
- [antq.util.ver :as u.ver]
[clj-yaml.core :as yaml]
[clojure.java.io :as io]
[clojure.string :as str]
@@ -28,7 +27,7 @@
:version version
:extra {:url (name->url name)}}
{:type :github-tag
- :version (u.ver/normalize-version version)}))
+ :version version}))
(defn extract-deps
[file-path workflow-content-str]
diff --git a/src/antq/diff.clj b/src/antq/diff.clj
new file mode 100644
index 00000000..a3a82ce5
--- /dev/null
+++ b/src/antq/diff.clj
@@ -0,0 +1,9 @@
+(ns antq.diff)
+
+(defmulti get-diff-url
+ (fn [version-checked-dep]
+ (:type version-checked-dep)))
+
+(defmethod get-diff-url :default
+ [_dep]
+ nil)
diff --git a/src/antq/diff/git_sha.clj b/src/antq/diff/git_sha.clj
new file mode 100644
index 00000000..89b7f6bd
--- /dev/null
+++ b/src/antq/diff/git_sha.clj
@@ -0,0 +1,19 @@
+(ns antq.diff.git-sha
+ (:require
+ [antq.diff :as diff]
+ [antq.log :as log]
+ [antq.util.url :as u.url]
+ [clojure.string :as str]))
+
+(defmethod diff/get-diff-url :git-sha
+ [dep]
+ (when-let [url (get-in dep [:extra :url])]
+ (cond
+ (str/starts-with? url "https://github.com/")
+ (format "%scompare/%s...%s"
+ (u.url/ensure-tail-slash url)
+ (:version dep)
+ (:latest-version dep))
+
+ :else
+ (log/error (str "Diff is not supported for " url)))))
diff --git a/src/antq/diff/github_tag.clj b/src/antq/diff/github_tag.clj
new file mode 100644
index 00000000..4e5cf230
--- /dev/null
+++ b/src/antq/diff/github_tag.clj
@@ -0,0 +1,11 @@
+(ns antq.diff.github-tag
+ (:require
+ [antq.diff :as diff]
+ [clojure.string :as str]))
+
+(defmethod diff/get-diff-url :github-tag
+ [dep]
+ (format "https://github.com/%s/compare/%s...%s"
+ (str/join "/" (take 2 (str/split (:name dep) #"/")))
+ (:version dep)
+ (:latest-version dep)))
diff --git a/src/antq/diff/java.clj b/src/antq/diff/java.clj
new file mode 100644
index 00000000..c95ad500
--- /dev/null
+++ b/src/antq/diff/java.clj
@@ -0,0 +1,93 @@
+(ns antq.diff.java
+ (:require
+ [antq.diff :as diff]
+ [antq.log :as log]
+ [antq.util.git :as u.git]
+ [antq.util.maven :as u.mvn]
+ [antq.util.url :as u.url]
+ [clojure.string :as str])
+ (:import
+ (org.eclipse.aether.resolution
+ ArtifactRequest)))
+
+(defn memoize-by
+ [f key-fn]
+ (let [mem (atom {})]
+ (fn [m & args]
+ (if-let [res (get @mem (get m key-fn))]
+ res
+ (let [ret (apply f m args)]
+ (swap! mem assoc (get m key-fn) ret)
+ ret)))))
+
+(defn- get-repository-url*
+ [{:keys [name version] :as dep}]
+ (try
+ (let [opts (u.mvn/dep->opts dep)
+ {:keys [system session artifact remote-repos]} (u.mvn/repository-system name version opts)
+ req (doto (ArtifactRequest.)
+ (.setArtifact artifact)
+ (.setRepositories remote-repos))]
+ (some-> (.resolveArtifact system session req)
+ (.getRepository)
+ (.getUrl)))
+ ;; Skip showing diff URL when fetching repository URL is failed
+ (catch Exception _ nil)))
+(def get-repository-url (memoize-by get-repository-url* :name))
+
+(defn- dep->pom-url
+ [dep]
+ (let [{:keys [version]} dep
+ [group-id artifact-id] (str/split (:name dep) #"/" 2)
+ repo-url (get-repository-url dep)]
+ (when repo-url
+ (format "%s%s/%s/%s/%s-%s.pom"
+ (u.url/ensure-tail-slash repo-url)
+ (str/replace group-id "." "/")
+ artifact-id
+ version
+ artifact-id
+ version))))
+
+(defn- get-scm-url*
+ [dep]
+ (try
+ (when-let [model (some-> dep
+ (dep->pom-url)
+ (u.mvn/read-pom))]
+ (-> model
+ (u.mvn/get-scm)
+ (u.mvn/get-scm-url)
+ ;; fallback
+ (or (u.mvn/get-url model))
+ ;; normalize
+ (u.url/ensure-https)
+ (u.url/ensure-git-https-url)))
+
+ ;; Skip showing diff URL when POM file is not found
+ (catch java.io.FileNotFoundException _ nil)))
+(def get-scm-url (memoize-by get-scm-url* :name))
+
+(defmethod diff/get-diff-url :java
+ [dep]
+ (when-let [url (get-scm-url dep)]
+ (cond
+ (str/starts-with? url "https://github.com/")
+ (let [tags (u.git/tags-by-ls-remote url)
+ current (first (filter #(str/includes? % (:version dep)) tags))
+ latest (or (first (filter #(str/includes? % (:latest-version dep)) tags))
+ ;; If there isn't a tag for latest version
+ "head")]
+ (if current
+ (format "%scompare/%s...%s"
+ (u.url/ensure-tail-slash url)
+ current
+ latest)
+ (do (log/error (str "The tag for current version is not found: " url))
+ ;; not diff, but URL is useful for finding the differences.
+ nil)))
+
+ :else
+ (do (log/error (str "Diff is not supported for " url))
+ ;; not diff, but URL is useful for finding the differences.
+ nil))))
diff --git a/src/antq/log.clj b/src/antq/log.clj
new file mode 100644
index 00000000..3327da31
--- /dev/null
+++ b/src/antq/log.clj
@@ -0,0 +1,10 @@
+(ns antq.log)
+
+(defn info
+ [s]
+ (println s))
+
+(defn error
+ [s]
+ (binding [*out* *err*]
+ (println s)))
diff --git a/src/antq/record.clj b/src/antq/record.clj
index 559b73e5..7f76c073 100644
--- a/src/antq/record.clj
+++ b/src/antq/record.clj
@@ -1,5 +1,23 @@
(ns antq.record)
(defrecord Dependency
- [type file name version latest-version
- repositories project])
+ [;; Dependency type keyword
+ ;; e.g. :java, :git-sha or :github-tag
+ type
+ ;; File path for project configuration file
+ file
+ ;; Dependency name
+ ;; e.g. "org.clojure/clojure", "medley/medley"
+ name
+ ;; Current version string
+ version
+ ;; Latest version string (Nullable)
+ latest-version
+ ;; Additional Maven repositories (Nullable)
+ ;; e.g. {"nexus-snapshots" {:url "http://localhost:8081/repository/maven-snapshots/"}}
+ repositories
+ ;; Project type keyword
+ ;; e.g. :clojure, :leiningen, :shadow-cljs and so on.
+ project
+ ;; Diff URL for Version Control System (Nullable)
+ diff-url])
diff --git a/src/antq/report.clj b/src/antq/report.clj
index 894963d8..d70d0fb1 100644
--- a/src/antq/report.clj
+++ b/src/antq/report.clj
@@ -1,4 +1,6 @@
-(ns antq.report)
+(ns antq.report
+ (:require
+ [antq.log :as log]))
(defmulti reporter
(fn [_deps options]
@@ -6,4 +8,4 @@
(defmethod reporter :default
[_ options]
- (println "Unknown reporter:" (:reporter options)))
+ (log/error (str "Unknown reporter: " (:reporter options))))
diff --git a/src/antq/report/format.clj b/src/antq/report/format.clj
index c65998d0..b682dfaf 100644
--- a/src/antq/report/format.clj
+++ b/src/antq/report/format.clj
@@ -6,7 +6,7 @@
[clojure.string :as str]))
(def ^:private default-outdated-message-format
- "{{name}} {{version}} is outdated. Latest version is {{latest-version}}.")
+ "{{name}} {{version}} is outdated. Latest version is {{latest-version}}. {{diff-url}}")
(def ^:private default-failed-message-format
"Failed to fetch the latest version of {{name}} {{version}}.")
@@ -15,7 +15,7 @@
[dep format-string]
(let [dep (-> dep
(assoc :latest-version (u.ver/normalize-latest-version dep))
- (select-keys [:file :name :version :latest-version :message]))]
+ (select-keys [:file :name :version :latest-version :message :diff-url]))]
(reduce-kv (fn [s k v]
(str/replace s (str "{{" (name k) "}}") (or v "")))
format-string
diff --git a/src/antq/report/table.clj b/src/antq/report/table.clj
index 92687439..1f19c9c0 100644
--- a/src/antq/report/table.clj
+++ b/src/antq/report/table.clj
@@ -3,7 +3,8 @@
[antq.report :as report]
[antq.util.dep :as u.dep]
[antq.util.ver :as u.ver]
- [clojure.pprint :as pprint]))
+ [clojure.pprint :as pprint]
+ [clojure.set :as set]))
(defn skip-duplicated-file-name
[sorted-deps]
@@ -18,10 +19,23 @@
(defmethod report/reporter "table"
[deps _options]
+ ;; Show table
(if (seq deps)
(->> deps
(sort u.dep/compare-deps)
skip-duplicated-file-name
(map #(assoc % :latest-version (u.ver/normalize-latest-version %)))
- (pprint/print-table [:file :name :version :latest-version]))
- (println "All dependencies are up-to-date.")))
+ (map #(set/rename-keys % {:version :current
+ :latest-version :latest}))
+ (pprint/print-table [:file :name :current :latest]))
+ (println "All dependencies are up-to-date."))
+
+ ;; Show diff URLs
+ (let [urls (->> deps
+ (sort u.dep/compare-deps)
+ (keep :diff-url)
+ (distinct))]
+ (when (seq urls)
+ (println "\nAvailable diffs:")
+ (doseq [u urls]
+ (println "-" u)))))
diff --git a/src/antq/upgrade.clj b/src/antq/upgrade.clj
index 374491eb..0fa94758 100644
--- a/src/antq/upgrade.clj
+++ b/src/antq/upgrade.clj
@@ -1,5 +1,6 @@
(ns antq.upgrade
(:require
+ [antq.log :as log]
[antq.util.zip :as u.zip]))
(defmulti upgrader
@@ -8,7 +9,7 @@
(defmethod upgrader :default
[dep]
- (println
+ (log/error
(format "%s: Not supported yet."
(name (:project dep)))))
@@ -16,7 +17,7 @@
[dep force?]
(cond
(not u.zip/rewrite-cljc-supported?)
- (do (println "Upgrading is only supported Clojure 1.9 or later.")
+ (do (log/error "Upgrading is only supported Clojure 1.9 or later.")
false)
(and (:latest-version dep)
@@ -42,16 +43,20 @@
(defn upgrade!
"Return only non-upgraded deps"
[version-checked-deps force?]
+ (when (and (seq version-checked-deps)
+ (not force?))
+ (log/info ""))
+
(doall
(remove
(fn [dep]
(if (confirm dep force?)
(if-let [upgraded-content (upgrader dep)]
- (do (println (format "Upgraded %s '%s' to '%s' in %s."
- (:name dep)
- (:version dep)
- (:latest-version dep)
- (:file dep)))
+ (do (log/info (format "Upgraded %s '%s' to '%s' in %s."
+ (:name dep)
+ (:version dep)
+ (:latest-version dep)
+ (:file dep)))
(spit (:file dep) upgraded-content)
true)
false)
diff --git a/src/antq/util/git.clj b/src/antq/util/git.clj
new file mode 100644
index 00000000..56114428
--- /dev/null
+++ b/src/antq/util/git.clj
@@ -0,0 +1,19 @@
+(ns antq.util.git
+ (:require
+ [clojure.java.shell :as sh]
+ [clojure.string :as str]))
+
+(defn- extract-tags
+ [ls-remote-resp]
+ (->> (:out ls-remote-resp)
+ (str/split-lines)
+ (keep #(second (str/split % #"\t" 2)))
+ (filter #(= 0 (.indexOf ^String % "refs/tags")))
+ (map #(str/replace % #"^refs/tags/" ""))))
+
+(defn tags-by-ls-remote*
+ [url]
+ (-> (sh/sh "git" "ls-remote" url)
+ (extract-tags)))
+(def tags-by-ls-remote
+ (memoize tags-by-ls-remote*))
diff --git a/src/antq/util/maven.clj b/src/antq/util/maven.clj
new file mode 100644
index 00000000..0b80befe
--- /dev/null
+++ b/src/antq/util/maven.clj
@@ -0,0 +1,97 @@
+(ns antq.util.maven
+ (:require
+ [antq.log :as log]
+ [clojure.java.io :as io]
+ [clojure.string :as str]
+ [clojure.tools.deps.alpha.util.maven :as deps.util.maven]
+ [clojure.tools.deps.alpha.util.session :as deps.util.session])
+ (:import
+ (org.apache.maven.model
+ Model
+ Scm)
+ org.apache.maven.model.io.xpp3.MavenXpp3Reader
+ (org.eclipse.aether
+ DefaultRepositorySystemSession
+ RepositorySystem)
+ (org.eclipse.aether.transfer
+ TransferEvent
+ TransferListener)))
+
+(def default-repos
+ {"central" {:url "https://repo1.maven.org/maven2/"}
+ "clojars" {:url "https://repo.clojars.org/"}})
+
+(defn normalize-repo-url
+ "c.f. https://clojure.org/reference/deps_and_cli#_maven_s3_repos"
+ [url]
+ (str/replace url #"^s3p://" "s3://"))
+
+(defn normalize-repos
+ [repos]
+ (reduce-kv
+ (fn [acc k v]
+ (assoc acc k (if (contains? v :url)
+ (update v :url normalize-repo-url)
+ v)))
+ {} repos))
+
+(defn snapshot?
+ [s]
+ (if s
+ (str/includes? (str/lower-case s) "snapshot")
+ false))
+
+(defn dep->opts
+ [dep]
+ {:repositories (-> default-repos
+ (merge (:repositories dep))
+ (normalize-repos))
+ :snapshots? (snapshot? (:version dep))})
+
+(def ^TransferListener custom-transfer-listener
+ "Copy from clojure.tools.deps.alpha.util.maven/console-listener
+ But no outputs for `transferStarted`"
+ (reify TransferListener
+ (transferStarted [_ event])
+ (transferCorrupted [_ event]
+ (log/info "Download corrupted:" (.. ^TransferEvent event getException getMessage)))
+ (transferFailed [_ event]
+ ;; This happens when Maven can't find an artifact in a particular repo
+ ;; (but still may find it in a different repo), ie this is a common event
+ )
+ (transferInitiated [_ _event])
+ (transferProgressed [_ _event])
+ (transferSucceeded [_ _event])))
+
+(defn repository-system
+ [name version opts]
+ (let [lib (cond-> name (string? name) symbol)
+ local-repo deps.util.maven/default-local-repo
+ system ^RepositorySystem (deps.util.session/retrieve :mvn/system #(deps.util.maven/make-system))
+ session ^DefaultRepositorySystemSession (deps.util.maven/make-session system local-repo)
+ ;; Overwrite TransferListener not to show "Downloading" messages
+ _ (.setTransferListener session custom-transfer-listener)
+ ; c.f. https://stackoverflow.com/questions/35488167/how-can-you-find-the-latest-version-of-a-maven-artifact-from-java-using-aether
+ artifact (deps.util.maven/coord->artifact lib {:mvn/version version})
+ remote-repos (deps.util.maven/remote-repos (:repositories opts))]
+ {:system system
+ :session session
+ :artifact artifact
+ :remote-repos remote-repos}))
+
+(defn ^Model read-pom
+ [^String url]
+ (with-open [reader (io/reader url)]
+ (.read (MavenXpp3Reader.) reader)))
+
+(defn ^String get-url
+ [^Model model]
+ (.getUrl model))
+
+(defn ^Scm get-scm
+ [^Model model]
+ (.getScm model))
+
+(defn ^String get-scm-url
+ [^Scm scm]
+ (.getUrl scm))
diff --git a/src/antq/util/url.clj b/src/antq/util/url.clj
new file mode 100644
index 00000000..16ee4cb6
--- /dev/null
+++ b/src/antq/util/url.clj
@@ -0,0 +1,25 @@
+(ns antq.util.url
+ (:require
+ [clojure.string :as str]))
+
+(defn ensure-tail-slash
+ [s]
+ (cond-> s
+ (not (str/ends-with? s "/")) (str "/")))
+
+(defn ensure-git-https-url
+ [url]
+ (if-not (str/starts-with? url "git@")
+ (-> url
+ (str/replace #"\.git$" "")
+ (ensure-tail-slash))
+ (let [[_ s] (str/split url #"@" 2)
+ [domain s] (str/split s #":" 2)
+ path (str/replace s #"\.git$" "")]
+ (-> (format "https://%s/%s" domain path)
+ (ensure-tail-slash)))))
+
+(defn ensure-https
+ [url]
+ (cond-> url
+ (str/starts-with? url "http://") (str/replace #"^http://" "https://")))
diff --git a/src/antq/ver/github_tag.clj b/src/antq/ver/github_tag.clj
index 0ce81e93..ea810ec7 100644
--- a/src/antq/ver/github_tag.clj
+++ b/src/antq/ver/github_tag.clj
@@ -1,13 +1,12 @@
(ns antq.ver.github-tag
(:require
+ [antq.log :as log]
+ [antq.util.git :as u.git]
[antq.util.ver :as u.ver]
[antq.ver :as ver]
[cheshire.core :as json]
- [clojure.java.shell :as sh]
[clojure.string :as str]
- [version-clj.core :as version])
- (:import
- java.io.PrintWriter))
+ [version-clj.core :as version]))
(defonce ^:private failed-to-fetch-from-api
(atom false))
@@ -17,28 +16,16 @@
(format "https://api.github.com/repos/%s/tags"
(str/join "/" (take 2 (str/split (:name dep) #"/")))))
-(defn- github-ls-remote
+(defn get-sorted-versions-by-ls-remote*
[dep]
(let [url (format "https://github.com/%s"
(str/join "/" (take 2 (str/split (:name dep) #"/"))))]
- (sh/sh "git" "ls-remote" url)))
-
-(defn- extract-tags
- [ls-remote-resp]
- (->> (:out ls-remote-resp)
- (str/split-lines)
- (keep #(second (str/split % #"\t" 2)))
- (filter #(= 0 (.indexOf ^String % "refs/tags")))
- (map #(u.ver/normalize-version (str/replace % #"^refs/tags/" "")))
- (filter u.ver/sem-ver?)
- (sort version/version-compare)
- (reverse)))
-
-(defn get-sorted-versions-by-ls-remote*
- [dep]
- (-> dep
- (github-ls-remote)
- (extract-tags)))
+ (->> (u.git/tags-by-ls-remote url)
+ (filter (comp u.ver/sem-ver? u.ver/normalize-version))
+ (sort (fn [& args]
+ (apply version/version-compare
+ (map u.ver/normalize-version args))))
+ (reverse))))
(def get-sorted-versions-by-ls-remote
(memoize get-sorted-versions-by-ls-remote*))
@@ -48,9 +35,11 @@
(-> url
(slurp)
(json/parse-string true)
- (->> (map (comp u.ver/normalize-version :name))
- (filter u.ver/sem-ver?)
- (sort version/version-compare)
+ (->> (map :name)
+ (filter (comp u.ver/sem-ver? u.ver/normalize-version))
+ (sort (fn [& args]
+ (apply version/version-compare
+ (map u.ver/normalize-version args))))
(reverse))))
(def get-sorted-versions-by-url
@@ -61,8 +50,8 @@
(try
(get-sorted-versions-by-ls-remote dep)
(catch Exception ex
- (.println ^PrintWriter *err* (str "Failed to fetch versions from GitHub: "
- (.getMessage ex))))))
+ (log/error (str "Failed to fetch versions from GitHub: "
+ (.getMessage ex))))))
(defmethod ver/get-sorted-versions :github-tag
[dep]
@@ -74,7 +63,8 @@
(get-sorted-versions-by-url))
(catch Exception ex
(reset! failed-to-fetch-from-api true)
- (.println ^PrintWriter *err* (str "Failed to fetch versions from GitHub, so fallback to `git ls-remote`: " (.getMessage ex)))
+ (log/error (str "Failed to fetch versions from GitHub, so fallback to `git ls-remote`: "
+ (.getMessage ex)))
(fallback-to-ls-remote dep)))))
(defn- nth-newer?
@@ -86,8 +76,8 @@
(defmethod ver/latest? :github-tag
[dep]
- (let [current (some-> dep :version version/version->seq)
- latest (some-> dep :latest-version version/version->seq)]
+ (let [current (some-> dep :version u.ver/normalize-version version/version->seq)
+ latest (some-> dep :latest-version u.ver/normalize-version version/version->seq)]
(when (and current latest)
(case (count (first current))
1 (nth-newer? current latest 0)
diff --git a/src/antq/ver/java.clj b/src/antq/ver/java.clj
index bd780b1c..525dd132 100644
--- a/src/antq/ver/java.clj
+++ b/src/antq/ver/java.clj
@@ -1,64 +1,15 @@
(ns antq.ver.java
(:require
+ [antq.util.maven :as u.mvn]
[antq.ver :as ver]
- [clojure.string :as str]
- [clojure.tools.deps.alpha.util.maven :as deps.util.maven]
- [clojure.tools.deps.alpha.util.session :as deps.util.session]
[version-clj.core :as version])
(:import
- (org.eclipse.aether
- DefaultRepositorySystemSession
- RepositorySystem)
(org.eclipse.aether.resolution
- VersionRangeRequest)
- (org.eclipse.aether.transfer
- TransferEvent
- TransferListener)))
-
-(def default-repos
- {"central" {:url "https://repo1.maven.org/maven2/"}
- "clojars" {:url "https://repo.clojars.org/"}})
-
-(defn- normalize-repo-url
- [url]
- (-> url
- (str/replace #"^s3p://" "s3://")))
-
-(defn normalize-repos
- [repos]
- (reduce-kv
- (fn [acc k v]
- (assoc acc k (if (contains? v :url)
- (update v :url normalize-repo-url)
- v)))
- {} repos))
-
-(def ^TransferListener custom-transfer-listener
- "Copy from clojure.tools.deps.alpha.util.maven/console-listener
- But no outputs for `transferStarted`"
- (reify TransferListener
- (transferStarted [_ event])
- (transferCorrupted [_ event]
- (println "Download corrupted:" (.. ^TransferEvent event getException getMessage)))
- (transferFailed [_ event]
- ;; This happens when Maven can't find an artifact in a particular repo
- ;; (but still may find it in a different repo), ie this is a common event
- )
- (transferInitiated [_ _event])
- (transferProgressed [_ _event])
- (transferSucceeded [_ _event])))
+ VersionRangeRequest)))
(defn get-versions
[name opts]
- (let [lib (cond-> name (string? name) symbol)
- local-repo deps.util.maven/default-local-repo
- system ^RepositorySystem (deps.util.session/retrieve :mvn/system #(deps.util.maven/make-system))
- session ^DefaultRepositorySystemSession (deps.util.maven/make-session system local-repo)
- ;; Overwrite TransferListener not to show "Downloading" messages
- _ (.setTransferListener session custom-transfer-listener)
- ; c.f. https://stackoverflow.com/questions/35488167/how-can-you-find-the-latest-version-of-a-maven-artifact-from-java-using-aether
- artifact (deps.util.maven/coord->artifact lib {:mvn/version "[0,)"})
- remote-repos (deps.util.maven/remote-repos (:repositories opts))
+ (let [{:keys [system session artifact remote-repos]} (u.mvn/repository-system name "[0,)" opts)
req (doto (VersionRangeRequest.)
(.setArtifact artifact)
(.setRepositories remote-repos))]
@@ -81,7 +32,4 @@
[dep]
(get-sorted-versions-by-name
(:name dep)
- {:repositories (-> default-repos
- (merge (:repositories dep))
- (normalize-repos))
- :snapshots? (ver/snapshot? (:version dep))}))
+ (u.mvn/dep->opts dep)))
diff --git a/test/antq/dep/github_action_test.clj b/test/antq/dep/github_action_test.clj
index fc4e9625..01747d58 100644
--- a/test/antq/dep/github_action_test.clj
+++ b/test/antq/dep/github_action_test.clj
@@ -23,7 +23,7 @@
(slurp (io/resource "dep/github_action.yml")))]
(t/is (sequential? deps))
(t/is (every? #(instance? antq.record.Dependency %) deps))
- (t/is (= #{(git-tag-dependency {:name "foo/bar" :version "1.0.0"})
+ (t/is (= #{(git-tag-dependency {:name "foo/bar" :version "v1.0.0"})
(git-tag-dependency {:name "bar/baz" :version "master"})
(git-sha-dependency {:name "git/sha" :version "8be09192b01d78912b03852f5d6141e8c48f4179"
:extra {:url "https://github.com/git/sha.git"}})
diff --git a/test/antq/diff/git_sha_test.clj b/test/antq/diff/git_sha_test.clj
new file mode 100644
index 00000000..85fb2da0
--- /dev/null
+++ b/test/antq/diff/git_sha_test.clj
@@ -0,0 +1,20 @@
+(ns antq.diff.git-sha-test
+ (:require
+ [antq.diff :as diff]
+ [antq.diff.git-sha]
+ [antq.record :as r]
+ [clojure.test :as t]))
+
+(t/deftest get-diff-url-test
+ (let [dep (r/map->Dependency {:type :git-sha
+ :extra {:url "https://github.com/foo/bar"}
+ :version "1.0"
+ :latest-version "2.0"})]
+ (t/is (= "https://github.com/foo/bar/compare/1.0...2.0"
+ (diff/get-diff-url dep)))
+
+ (t/testing "missing extra"
+ (t/is (nil? (diff/get-diff-url (dissoc dep :extra)))))
+
+ (t/testing "not supported extra URL"
+ (t/is (nil? (diff/get-diff-url (assoc-in dep [:extra :url] "INVALID")))))))
diff --git a/test/antq/diff/github_tag_test.clj b/test/antq/diff/github_tag_test.clj
new file mode 100644
index 00000000..3cdc1e71
--- /dev/null
+++ b/test/antq/diff/github_tag_test.clj
@@ -0,0 +1,14 @@
+(ns antq.diff.github-tag-test
+ (:require
+ [antq.diff :as diff]
+ [antq.diff.github-tag]
+ [antq.record :as r]
+ [clojure.test :as t]))
+
+(t/deftest get-diff-url-test
+ (let [dep (r/map->Dependency {:type :github-tag
+ :name "foo/bar"
+ :version "1.0"
+ :latest-version "2.0"})]
+ (t/is (= "https://github.com/foo/bar/compare/1.0...2.0"
+ (diff/get-diff-url dep)))))
diff --git a/test/antq/diff/java_test.clj b/test/antq/diff/java_test.clj
new file mode 100644
index 00000000..000ade2b
--- /dev/null
+++ b/test/antq/diff/java_test.clj
@@ -0,0 +1,63 @@
+(ns antq.diff.java-test
+ (:require
+ [antq.diff :as diff]
+ [antq.diff.java :as sut]
+ [antq.record :as r]
+ [antq.util.git :as u.git]
+ [antq.util.maven :as u.mvn]
+ [clojure.test :as t])
+ (:import
+ (org.apache.maven.model
+ Model
+ Scm)))
+
+(defn- gen-dummy-model
+ [^String scm-url]
+ (let [scm (doto (Scm.)
+ (.setUrl scm-url))]
+ (doto (Model.)
+ (.setScm scm))))
+
+(t/deftest get-diff-url-test
+ (let [dep (r/map->Dependency {:type :java
+ :name "foo/bar"
+ :version "1.0"
+ :latest-version "2.0"})]
+ (t/testing "https://github.com"
+ (with-redefs [sut/get-repository-url (constantly "https://example.com")
+ u.mvn/read-pom (fn [url]
+ (when (= "https://example.com/foo/bar/1.0/bar-1.0.pom" url)
+ (gen-dummy-model "https://github.com/bar/baz")))
+ u.git/tags-by-ls-remote (fn [url]
+ (when (= "https://github.com/bar/baz/" url)
+ ["v0.0" "v1.0" "v2.0" "v3.0"]))]
+ (t/is (= "https://github.com/bar/baz/compare/v1.0...v2.0"
+ (diff/get-diff-url dep)))))
+
+ (t/testing "git@github.com"
+ (with-redefs [sut/get-repository-url (constantly "https://example.com")
+ u.mvn/read-pom (fn [url]
+ (when (= "https://example.com/git/at/1.0/at-1.0.pom" url)
+ (gen-dummy-model "git@github.com:git/at")))
+ u.git/tags-by-ls-remote (fn [url]
+ (when (= "https://github.com/git/at/" url)
+ ["v0.0" "v1.0" "v2.0" "v3.0"]))]
+ (t/is (= "https://github.com/git/at/compare/v1.0...v2.0"
+ (diff/get-diff-url (assoc dep :name "git/at"))))))
+
+ (t/testing "Failed to fetch repository URL"
+ (with-redefs [sut/get-repository-url (constantly nil)]
+ (t/is (nil? (diff/get-diff-url (assoc dep :name "fetch/repo-url"))))))
+
+ (t/testing "POM not found"
+ (with-redefs [sut/get-repository-url (constantly "https://example2.com")
+ u.mvn/read-pom (fn [_] (throw (java.io.FileNotFoundException. "test exception")))]
+ (t/is (nil? (diff/get-diff-url (assoc dep :name "pom/not-found"))))))
+
+
+ (t/testing "not supported URL"
+ (with-redefs [sut/get-repository-url (constantly "https://example.com")
+ u.mvn/read-pom (fn [url]
+ (when (= "https://example.com/not/supported/1.0/supported-1.0.pom" url)
+ (gen-dummy-model "https://not-supported.com")))]
+ (t/is (nil? (diff/get-diff-url (assoc dep :name "not/supported"))))))))
diff --git a/test/antq/log_test.clj b/test/antq/log_test.clj
new file mode 100644
index 00000000..225cc496
--- /dev/null
+++ b/test/antq/log_test.clj
@@ -0,0 +1,15 @@
+(ns antq.log-test
+ (:require
+ [antq.log :as sut]
+ [clojure.test :as t]))
+
+(t/deftest info-test
+ (t/is (= "INFO\n"
+ (with-out-str (sut/info "INFO")))))
+
+(t/deftest error-test
+ (let [sw (java.io.StringWriter.)
+ err-str (binding [*err* sw]
+ (sut/error "ERROR")
+ (str sw))]
+ (t/is (= "ERROR\n" err-str))))
diff --git a/test/antq/report/format_test.clj b/test/antq/report/format_test.clj
index 7091465f..67a1007a 100644
--- a/test/antq/report/format_test.clj
+++ b/test/antq/report/format_test.clj
@@ -13,16 +13,19 @@
(t/deftest reporter-test
(let [dummy-deps [(h/test-dep {:file "a" :name "foo" :version "1" :latest-version "2"})
- (h/test-dep {:file "b" :name "bar" :version "1" :latest-version nil})]]
+ (h/test-dep {:file "b" :name "bar" :version "1" :latest-version nil})
+ (h/test-dep {:file "c" :name "baz" :version "1" :latest-version "3"
+ :diff-url "https://example.com"})]]
(t/is (seq (with-out-str (reporter
dummy-deps
"::error file={{file}}::{{message}}"))))
- (t/is (= ["::error file=a::foo,1,2"
- "::error file=b::bar,1,Failed to fetch"]
+ (t/is (= ["::error file=a::foo,1,2. "
+ "::error file=b::bar,1,Failed to fetch. "
+ "::error file=c::baz,1,3. https://example.com"]
(str/split-lines
(with-out-str
(reporter
dummy-deps
- "::error file={{file}}::{{name}},{{version}},{{latest-version}}")))))))
+ "::error file={{file}}::{{name}},{{version}},{{latest-version}}. {{diff-url}}")))))))
diff --git a/test/antq/upgrade_test.clj b/test/antq/upgrade_test.clj
index 88ca9f98..e6f7662c 100644
--- a/test/antq/upgrade_test.clj
+++ b/test/antq/upgrade_test.clj
@@ -55,8 +55,11 @@
:file temp1})]
(t/is (= "before0" (slurp temp1)))
- (let [out-str (with-out-str (sut/upgrade! [dep] true))]
- (t/is (not= -1 (.indexOf out-str "Not supported"))))
+ (let [sw (java.io.StringWriter.)
+ err-str (binding [*err* sw]
+ (sut/upgrade! [dep] true)
+ (str sw))]
+ (t/is (not= -1 (.indexOf err-str "Not supported"))))
(t/is (= "before0" (slurp temp1)))))
diff --git a/test/antq/util/git_test.clj b/test/antq/util/git_test.clj
new file mode 100644
index 00000000..2bb611f0
--- /dev/null
+++ b/test/antq/util/git_test.clj
@@ -0,0 +1,23 @@
+(ns antq.util.git-test
+ (:require
+ [antq.util.git :as sut]
+ [clojure.java.shell :as sh]
+ [clojure.string :as str]
+ [clojure.test :as t]))
+
+(def ^:private dummy-ls-remote-out
+ (->> [["dummy-sha" "HEAD"]
+ ["dummy-sha" "refs/heads/foo"]
+ ["dummy-sha" "refs/pull/1/head"]
+ ["dummy-sha" "refs/tags/v1"]
+ ["dummy-sha" "refs/tags/v2"]]
+ (map #(str/join "\t" %))
+ (str/join "\n")))
+
+(t/deftest tags-by-ls-remote*-test
+ (with-redefs [sh/sh (constantly {:exit 0
+ :out dummy-ls-remote-out
+ :err ""})]
+ (t/is (= ["v1" "v2"]
+ (sut/tags-by-ls-remote* "dummy url")))))
+
diff --git a/test/antq/util/maven_test.clj b/test/antq/util/maven_test.clj
new file mode 100644
index 00000000..6874b6c5
--- /dev/null
+++ b/test/antq/util/maven_test.clj
@@ -0,0 +1,54 @@
+(ns antq.util.maven-test
+ (:require
+ [antq.record :as r]
+ [antq.util.maven :as sut]
+ [clojure.test :as t]))
+
+(t/deftest normalize-repo-url-test
+ (t/are [expected in] (= expected (sut/normalize-repo-url in))
+ "" ""
+ "foo" "foo"
+ "s3://foo/bar" "s3p://foo/bar"))
+
+(t/deftest normalize-repos-test
+ (t/is (= sut/default-repos
+ (sut/normalize-repos sut/default-repos)))
+ (t/is (= {"foo" {:url "s3://bar"}}
+ (sut/normalize-repos {"foo" {:url "s3://bar"}})))
+ (t/is (= {"foo" {:invalid "invalid"}}
+ (sut/normalize-repos {"foo" {:invalid "invalid"}})))
+
+ (t/testing "replace s3p:// to s3://"
+ (t/is (= {"foo" {:url "s3://bar"}}
+ (sut/normalize-repos {"foo" {:url "s3p://bar"}})))
+ (t/is (= {"foo" {:url "s3://bar" :no-auth true}}
+ (sut/normalize-repos {"foo" {:url "s3p://bar" :no-auth true}})))))
+
+(t/deftest snapshot?-test
+ (t/are [expected in] (= expected (sut/snapshot? in))
+ false ""
+ false "foo"
+ true "foo-snapshot"
+ true "foo-SnapShot"
+ true "foo-SNAPSHOT"))
+
+(t/deftest dep->opts-test
+ (t/is (= {:repositories sut/default-repos
+ :snapshots? false}
+ (sut/dep->opts (r/map->Dependency {:version "1.0.0"}))))
+ (t/is (= {:repositories (assoc sut/default-repos
+ "foo" {:url "s3://foo"})
+ :snapshots? true}
+ (sut/dep->opts (r/map->Dependency {:repositories {"foo" {:url "s3p://foo"}}
+ :version "1.0.0-SNAPSHOT"})))))
+
+(t/deftest get-url-test
+ (let [model (sut/read-pom "pom.xml")]
+ (t/is (= "https://github.com/liquidz/antq"
+ (sut/get-url model)))))
+
+(t/deftest get-scm-url-test
+ (let [model (sut/read-pom "pom.xml")
+ scm (sut/get-scm model)]
+ (t/is (= "https://github.com/liquidz/antq"
+ (sut/get-scm-url scm)))))
diff --git a/test/antq/util/url_test.clj b/test/antq/util/url_test.clj
new file mode 100644
index 00000000..5058696d
--- /dev/null
+++ b/test/antq/util/url_test.clj
@@ -0,0 +1,26 @@
+(ns antq.util.url-test
+ (:require
+ [antq.util.url :as sut]
+ [clojure.test :as t]))
+
+(t/deftest ensure-tail-slash-test
+ (t/is (= "foo/" (sut/ensure-tail-slash "foo")))
+ (t/is (= "foo/" (sut/ensure-tail-slash "foo/"))))
+
+(t/deftest ensure-git-https-url-test
+ (t/is (= "https://github.com/foo/bar/"
+ (sut/ensure-git-https-url "https://github.com/foo/bar")))
+ (t/is (= "https://github.com/foo/bar/"
+ (sut/ensure-git-https-url "https://github.com/foo/bar.git")))
+ (t/is (= "https://github.com/foo/bar/"
+ (sut/ensure-git-https-url "git@github.com:foo/bar"))))
+
+(t/deftest ensure-https
+ (t/is (= "https://github.com"
+ (sut/ensure-https "https://github.com")))
+ (t/is (= "https://github.com"
+ (sut/ensure-https "http://github.com")))
+ (t/is (= "git@github.com"
+ (sut/ensure-https "git@github.com")))
+ (t/is (= ""
+ (sut/ensure-https ""))))
diff --git a/test/antq/ver/github_tag_test.clj b/test/antq/ver/github_tag_test.clj
index 614cdf47..ec52e5d5 100644
--- a/test/antq/ver/github_tag_test.clj
+++ b/test/antq/ver/github_tag_test.clj
@@ -4,6 +4,7 @@
[antq.ver :as ver]
[antq.ver.github-tag :as sut]
[cheshire.core :as json]
+ [clojure.java.shell :as sh]
[clojure.string :as str]
[clojure.test :as t]))
@@ -35,11 +36,11 @@
(t/deftest get-sorted-versions-test
(with-redefs [slurp (constantly dummy-json)]
- (t/is (= ["3.0.0" "2.0.0" "1.0.0"]
+ (t/is (= ["v3.0.0" "v2.0.0" "v1.0.0"]
(get-sorted-versions {:name "foo/bar"}))))
(t/testing "response should be cached"
- (t/is (= ["3.0.0" "2.0.0" "1.0.0"]
+ (t/is (= ["v3.0.0" "v2.0.0" "v1.0.0"]
(get-sorted-versions {:name "foo/bar"})))))
(t/deftest get-sorted-versions-fallback-test
@@ -53,9 +54,9 @@
(with-redefs [slurp (fn [& _]
(reset! api-errored true)
(throw (Exception. "test exception")))
- sut/github-ls-remote (fn [dep]
- (when (= "bar/baz" (:name dep))
- {:out dummy-out}))]
+ sh/sh (fn [& args]
+ (when (= ["git" "ls-remote" "https://github.com/bar/baz"] args)
+ {:out dummy-out}))]
(t/testing "pre"
(t/is (false? @api-errored))
(t/is (false? @(deref #'sut/failed-to-fetch-from-api))))
diff --git a/test/antq/ver/java_test.clj b/test/antq/ver/java_test.clj
index 11af835f..425e5e81 100644
--- a/test/antq/ver/java_test.clj
+++ b/test/antq/ver/java_test.clj
@@ -1,6 +1,7 @@
(ns antq.ver.java-test
(:require
[antq.record :as r]
+ [antq.util.maven :as u.mvn]
[antq.ver :as ver]
[antq.ver.java :as sut]
[clojure.edn :as edn]
@@ -11,24 +12,9 @@
(get-in (edn/read-string (slurp "deps.edn"))
[:deps 'org.clojure/clojure :mvn/version]))
-(t/deftest normalize-repos-test
- (t/is (= sut/default-repos
- (sut/normalize-repos sut/default-repos)))
- (t/is (= {"foo" {:url "s3://bar"}}
- (sut/normalize-repos {"foo" {:url "s3://bar"}})))
-
- (t/is (= {"foo" {:invalid "invalid"}}
- (sut/normalize-repos {"foo" {:invalid "invalid"}})))
-
- (t/testing "replace s3p:// to s3://"
- (t/is (= {"foo" {:url "s3://bar"}}
- (sut/normalize-repos {"foo" {:url "s3p://bar"}})))
- (t/is (= {"foo" {:url "s3://bar" :no-auth true}}
- (sut/normalize-repos {"foo" {:url "s3p://bar" :no-auth true}})))))
-
(t/deftest get-versions-test
(let [vers (sut/get-versions 'org.clojure/clojure
- {:repositories sut/default-repos})]
+ {:repositories u.mvn/default-repos})]
(t/is (seq vers))
(t/is (contains? (set (map str vers)) current-clojure-version))))
@@ -57,6 +43,6 @@
(with-redefs [sut/get-sorted-versions-by-name (fn [_ opts] opts)]
(let [res (get-sorted-versions {:repositories {"foo" {:url "s3p://bar"}}})
diff (set/difference (set (:repositories res))
- (set sut/default-repos))]
+ (set u.mvn/default-repos))]
(t/is (= #{["foo" {:url "s3://bar"}]}
diff))))))