-
Notifications
You must be signed in to change notification settings - Fork 4
/
release.clj
209 lines (187 loc) · 8.05 KB
/
release.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
(ns release
(:require [clojure.tools.build.api :as b]
[org.corfield.build :as bb]
[clojure.string :as string]))
(def ^:private lib 'com.github.mainej/schema-voyager)
(def ^:private rev-count (Integer/parseInt (b/git-count-revs nil)))
(def ^:private semantic-version "2.0")
(defn- format-version [revision] (format "%s.%s" semantic-version revision))
(def ^:private version (format-version rev-count))
(def ^:private next-version (format-version (inc rev-count)))
(def ^:private tag (str "v" version))
(def ^:private basis (b/create-basis {:aliases [:release-deps]}))
(defn- die
([message & args]
(die (apply format message args)))
([message]
(binding [*out* *err*]
(println message))
(System/exit 1)))
(defn- git [command args]
(b/process (assoc args :command-args (into ["git"] command))))
(defn- git-rev []
(let [{:keys [exit out]} (git ["rev-parse" "HEAD"] {:out :capture})]
(when (zero? exit)
(string/trim out))))
(defn- git-push [params]
(println "\nSyncing with github...")
(when-not (and (zero? (:exit (git ["push" "origin" tag] {})))
(zero? (:exit (git ["push" "origin"] {})))
(zero? (:exit (git ["push" "origin" "gh-pages"] {}))))
(die "\nCouldn't sync with github."))
params)
(defn- assert-clojars-creds [params]
(when-not (System/getenv "CLOJARS_USERNAME")
(die "\nMissing required CLOJARS_* credentials."))
params)
(defn- assert-changelog-updated [params]
(println "\nChecking that CHANGELOG references tag...")
(when-not (string/includes? (slurp "CHANGELOG.md") tag)
(die (string/join "\n"
["CHANGELOG.md must include tag."
" * If you will amend the current commit, use version %s"
" * If you intend to create a new commit, use version %s"])
version next-version))
params)
(defn- scm-clean?
([] (scm-clean? {}))
([opts]
(string/blank? (:out (git ["status" "--porcelain"] (assoc opts :out :capture))))))
(defn- assert-scm-clean [params]
(println "\nChecking that working directory is clean...")
(when-not (scm-clean?)
(die "\nGit working directory must be clean. Run `git commit`"))
params)
(defn- assert-scm-tagged [params]
(println "\nChecking that tag exists and is on HEAD...")
(when-not (zero? (:exit (git ["rev-list" tag] {:out :ignore})))
(die "\nGit tag %s must exist. Run `bin/release/tag`" tag))
(let [{:keys [exit out]} (git ["describe" "--tags" "--abbrev=0" "--exact-match"] {:out :capture})]
(when-not (and (zero? exit)
(= (string/trim out) tag))
(die (string/join "\n"
[""
"Git tag %s must be on HEAD."
""
"Proceed with caution, because this tag may have already been released. If you've determined it's safe, run `git tag -d %s` before re-running `bin/tag-release`."])
tag tag)))
params)
(defn- build-template "Create the template html file" [params]
(println "\nBuilding template html...")
(when-not (zero? (:exit (b/process {:command-args ["clojure" "-X:build-template"]})))
(die "\nCould not build template html."))
params)
(defn- copy-template-to-jar-resources [params]
;; The template file is Git-ignored. During a release we put it in the classes
;; directory so that it is available when Schema Voyager is used as a
;; dependency. (It's used by `standalone`, the primary interface into Schema
;; Voyager). This lets us keep the large template file out of version control.
;; IMPORTANT: keep this in sync with
;; `schema-voyager.template.config/template-file-name`. We intentionally do
;; not depend on `schema-voyager.template.config`, so that the release can be
;; run as `clojure -T:release`, i.e. as a tool without other dependencies.
(b/copy-file {:src "resources/standalone-template.html"
:target "target/classes/standalone-template.html"})
params)
(defn- build-standalone-example [file sources]
;; expects _site to have been initialized:
;; git fetch origin gh-pages
;; git worktree add _site gh-pages
(let [params {:sources sources
:output-path (str "_site/" file)}]
(when-not (-> (b/process {:command-args ["clojure" "-X:cli" "standalone" (str params)]})
:exit
zero?)
(die "\nCouldn't create %s" file))))
(defn- build-standalone-examples [params]
(println "\nBuilding sample projects for GitHub Pages...")
(build-standalone-example "mbrainz-schema.html"
[{:file/name "resources/mbrainz-schema/schema.edn"}
{:file/name "resources/mbrainz-schema/enums.edn"}
{:file/name "resources/mbrainz-schema/supplemental.edn"}])
;; A meta view of Datomic's schema
(build-standalone-example "datomic-schema.html"
[{:file/name "resources/datomic-schema/schema.edn"}
{:file/name "resources/datomic-schema/fixes.edn"}
{:file/name "resources/datomic-schema/supplemental.edn"}])
;; A meta view of Schema Voyager. Shows Datomic properties and supplemental
;; Schema Voyager properties and their relationships.
(build-standalone-example "schema-voyager-schema.html"
[{:file/name "resources/datomic-schema/schema.edn"}
{:file/name "resources/datomic-schema/fixes.edn"}
{:file/name "resources/datomic-schema/supplemental.edn"}
{:file/name "resources/schema-voyager-schema/schema.edn"}
{:file/name "resources/schema-voyager-schema/supplemental.edn"}])
(when-not (or (scm-clean? {:dir "./_site"})
(zero? (:exit (git ["commit" "-a" "--no-gpg-sign" "-m" "Deploy updates"]
{:dir "./_site"}))))
(die "\nCouldn't commit GitHub Pages"))
params)
#_{:clj-kondo/ignore #{:clojure-lsp/unused-public-var}}
(defn tag-release
"Tag the HEAD commit for the current release."
[params]
(when-not (zero? (:exit (git ["tag" "-a" tag "-m" tag] {})))
(die "\nCouldn't create tag %s." tag))
params)
(defn run-tests [params]
(-> params bb/run-tests))
(defn check-release
"Check that the library is ready to be released.
* Tests pass
* No outstanding commits
* Git tag for current release exists in local repo
* CHANGELOG.md references new tag"
[params]
(-> params
(run-tests)
(assert-changelog-updated)
;; after assertions about content, so any change can be committed/amended
(assert-scm-clean)
;; last, so that correct commit is tagged
(assert-scm-tagged)))
(defn jar
"Build the JAR, along with the template HTML resource."
[params]
(-> params
(assoc :lib lib
:version version
:src-dirs ["src/core"] ;; src/build and src/web are only needed in dev
:resource-dirs [] ;; ignore resources, which are only needed in dev
:basis basis
:tag (git-rev))
(build-template)
(copy-template-to-jar-resources)
(bb/jar)))
#_{:clj-kondo/ignore #{:clojure-lsp/unused-public-var}}
(defn install
"Build the JAR, and install it to the local maven repository."
[params]
(-> params
(assoc :lib lib :version version)
(bb/clean)
(jar)
(bb/install)))
#_{:clj-kondo/ignore #{:clojure-lsp/unused-public-var}}
(defn release
"Release the library.
* Confirm that we are ready to release
* Build template file
* Build JAR, including the template
* Build the content for GitHub Pages from the template
* Deploy the JAR to Clojars
* Ensure everything is available on Github"
[params]
(let [params (assoc params :lib lib :version version)]
(-> params
(assert-clojars-creds)
(bb/clean)
(check-release)
(jar)
;; After jar (and more importantly build-template), so that examples agree
;; with released template.
(build-standalone-examples)
(bb/deploy)
(git-push))
(println "\nDone")
params))