nandi/frqpublic Fork 0
590779a45cad598faf137f0ec8ef1064066025bf
Commits
Clone
git clone https://git.rickub.com/nandi/frq.git
git clone ssh://git@rickub.com/nandi/frq.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

bump-jolt-native.bb · 196 lines · 9.4 KBBlitzBasic Blame HistoryRaw
Take every jolt-native half from the release, and only from there a008d3b nandi 18d ago1#!/bin/sh
2#_(
3exec "$(dirname "$0")/bb" "$0" "$@"
4)
5
6;; Move every jolt-native pin in this tree to a release of it.
7;;
8;; scripts/bump-jolt-native.bb # the latest release
9;; scripts/bump-jolt-native.bb v0.1.3 # a named one
10;;
11;; A jolt-native bump is four facts in three places: the tag, the URL, the size
Build the APK as derivations only, and retire the buck2 graph 3bc3aaa nandi 16d ago12;; and the digest of each archive in scripts/*.dotslash, the same URL and digest
13;; for the Android archives in nix/android.nix, and the release's commit in
14;; deps.edn. Done by hand it is a lot of copying between a browser and a
15;; sha256sum, and the failure mode is a pin that still says v0.1.2 while its
16;; digest is v0.1.3's — which DotSlash catches, but only on the machine that
17;; next fetches it, and which fetchurl catches at build time and no sooner.
18;;
19;; The two places exist because the two builds fetch differently and neither can
20;; read the other's pin: `just lib` and a released-halves run resolve DotSlash
21;; manifests, and the APK is a nix derivation, whose inputs have to be fetchurl.
22;; The digests are the same bytes either way — DotSlash's `digest` is over the
23;; archive, which is what fetchurl hashes too.
Take every jolt-native half from the release, and only from there a008d3b nandi 18d ago24;;
25;; So: ask GitLab what the release holds, fetch each archive once, weigh it,
Build the APK as derivations only, and retire the buck2 graph 3bc3aaa nandi 16d ago26;; write the manifests and nix/android.nix back, and put the release's commit in
27;; deps.edn — which is what jolt resolves glimmer-vidya from, and so what the
28;; boot image compiles against.
Take every jolt-native half from the release, and only from there a008d3b nandi 18d ago29;;
30;; Nothing here decides whether the new release is a good idea. It only makes
31;; the tree say one version instead of two.
32(require '[babashka.classpath :as cp])
33(cp/add-classpath (str (babashka.fs/parent *file*)))
34(require '[frq.paths :as paths]
35 '[babashka.fs :as fs]
36 '[babashka.process :as p]
37 '[cheshire.core :as json]
38 '[clojure.string :as str])
39
40(def here (fs/parent (fs/canonicalize *file*)))
41(def root (str (fs/parent here)))
42(def project "nandithebull%2Fjolt-native")
43(def repo "https://gitlab.com/nandithebull/jolt-native")
44
45;; The release, as GitLab has it. `?per_page=1` when no tag is asked for: the
46;; list is newest first, and the newest is what "latest" means here — the
47;; /releases/permalink/latest endpoint sorts by release date, which is not the
48;; same thing once a release is edited.
49(defn release [tag]
50 (let [url (if tag
51 (str "https://gitlab.com/api/v4/projects/" project "/releases/" tag)
52 (str "https://gitlab.com/api/v4/projects/" project "/releases?per_page=1"))
53 body (json/parse-string (paths/out "curl" "-fsSL" url))]
54 (if tag
55 body
56 (or (first body) (paths/die (str "no releases at all under " repo))))))
57
58;; The manifests this tree pins out of jolt-native. Every DotSlash file beside
59;; this script whose providers point at the repo — rather than a list written
60;; here, which would be a sixth place to forget.
61(defn manifests []
62 (->> (fs/glob here "*.dotslash")
63 sort
64 (keep (fn [file]
65 (let [text (slurp (str file))
66 json (json/parse-string (subs text (str/index-of text "{")))]
67 (when (->> (get json "platforms")
68 vals
69 (some #(str/starts-with? (get-in % ["providers" 0 "url"]) repo)))
70 {:file file :text text :json json}))))))
71
72;; The archive an entry names, at the new tag. The URL a manifest carries is the
73;; release permalink — /-/releases/<tag>/downloads/<asset> — so the asset's name
74;; is its last segment, and the name carries the version too. Both move.
75(defn retag [url old new]
76 (-> url
77 (str/replace (str "/releases/" old "/") (str "/releases/" new "/"))
78 (str/replace (str "-" old ".") (str "-" new "."))))
79
80(defn tag-of [url]
81 (second (re-find #"/-/releases/([^/]+)/downloads/" url)))
82
83;; Fetch once per distinct URL, then weigh it: DotSlash wants the size and the
84;; sha256 of the archive as downloaded, not of anything inside it.
85(def weigh
86 (memoize
87 (fn [dir url]
88 (let [file (fs/path dir (last (str/split url #"/")))]
89 (println (str " fetching " (fs/file-name file)))
90 (p/shell "curl" "-fsSL" "-o" (str file) url)
91 {:size (fs/size file)
92 :digest (first (str/split (paths/out "sha256sum" (str file)) #"\s+"))
93 :file file}))))
94
95;; What the shim will reach for inside the archive. A release that moved a file
96;; leaves a manifest that fetches and verifies and then cannot resolve, which is
97;; a worse thing to find out from than this.
98(defn check-path! [{:keys [file]} path]
99 (let [names (set (str/split-lines (paths/out "tar" "-tzf" (str file))))]
100 (when-not (some #(= path (str/replace % #"^\./" "")) names)
101 (paths/die (str "no " path " in " (fs/file-name file))
102 "The release moved it; the manifest's `path` needs a hand."))))
103
104;; The JSON back out, in the shape it went in: two-space indentation, a value
105;; after each key, and the `//` comment block above it kept — that block is why
106;; anyone reading the manifest knows what the object is for.
107(def pretty
108 (json/create-pretty-printer
109 (assoc json/default-pretty-print-options
110 :indentation " "
111 :indent-arrays? true
112 :object-field-value-separator ": ")))
113
114(defn rewrite [{:keys [file text json]} tmp tag]
115 (let [platforms (get json "platforms")
116 updated
117 (into (array-map)
118 (for [[platform entry] platforms
119 :let [url (retag (get-in entry ["providers" 0 "url"]) (tag-of (get-in entry ["providers" 0 "url"])) tag)
120 {:keys [size digest] :as got} (weigh tmp url)]]
121 (do
122 (check-path! got (get entry "path"))
123 [platform (-> entry
124 (assoc "size" size "digest" digest)
125 (assoc-in ["providers" 0 "url"] url))])))
126 head (subs text 0 (str/index-of text "{"))
127 ;; The prose above the JSON names the release too — "from jolt-native's
128 ;; v0.1.3 release". Left saying the old one it would be a lie the moment
129 ;; this script succeeds.
130 head (reduce (fn [h old] (str/replace h old tag))
131 head
132 (distinct (keep #(tag-of (get-in % ["providers" 0 "url"])) (vals platforms))))]
133 (spit (str file) (str head (json/generate-string (assoc json "platforms" updated) {:pretty pretty}) "\n"))
134 (println (str " " (fs/file-name file)))))
135
Build the APK as derivations only, and retire the buck2 graph 3bc3aaa nandi 16d ago136;; nix/android.nix fetches two of the same archives as `pkgs.fetchurl`, because
137;; a derivation cannot resolve a DotSlash manifest: the fetch is the input, and
138;; an input has to be a fixed-output derivation with the digest written down.
139;; Rewritten as text for the same reason deps.edn is — the file is mostly the
140;; comments explaining each step, and there is no round-tripping Nix as data
141;; here anyway.
142;;
143;; Every fetchurl whose url points at the repo, whatever it is called: a third
144;; archive appearing in android.nix should move with the other two rather than
145;; be remembered about.
146(defn bump-nix! [tmp tag]
147 (let [file (fs/path root "nix" "android.nix")
148 text (slurp (str file))
149 ;; url and sha256 as one match, so the pair moves together. Anything
150 ;; between them — a comment, another attribute — would not match, and
151 ;; not matching is the safe direction: it leaves the pin alone and says
152 ;; nothing was written.
153 pattern (re-pattern (str "(url\\s*=\\s*\")(" (java.util.regex.Pattern/quote repo)
154 "[^\"]*)(\";\\s*\n\\s*sha256\\s*=\\s*\")([^\"]*)(\")"))
155 seen (atom [])
156 updated (str/replace text pattern
157 (fn [[_ head url mid _ tail]]
158 (let [new-url (retag url (tag-of url) tag)
159 {:keys [digest]} (weigh tmp new-url)]
160 (swap! seen conj (last (str/split new-url #"/")))
161 (str head new-url mid digest tail))))]
162 (when (empty? @seen)
163 (paths/die (str "no fetchurl in " file " points at " repo)))
164 (spit (str file) updated)
165 (doseq [name @seen] (println (str " nix/android.nix " name)))))
166
Take every jolt-native half from the release, and only from there a008d3b nandi 18d ago167;; deps.edn takes glimmer-vidya as a git dependency, and the boot image is
168;; compiled from the source root jolt resolves that to. Replaced as text rather
169;; than round-tripped as EDN: the file is mostly comments explaining why each
170;; pin is where it is, and rewriting it as data would throw all of them away.
171(defn bump-deps! [commit]
172 (let [file (fs/path root "deps.edn")
173 text (slurp (str file))
174 old (paths/dep-sha root repo)]
175 (cond
176 (nil? old) (paths/die (str "no dependency on " repo " in deps.edn"))
177 (= old commit) (println " deps.edn already at this commit")
178 :else (do (spit (str file) (str/replace text old commit))
179 (println (str " deps.edn " (subs old 0 8) " -> " (subs commit 0 8)))))))
180
181(let [tag (first *command-line-args*)
182 rel (release tag)
183 tag (get rel "tag_name")
184 commit (get-in rel ["commit" "id"])
185 files (manifests)
186 tmp (fs/create-temp-dir {:prefix "jolt-native-"})]
187 (when (empty? files)
188 (paths/die (str "no DotSlash manifest in " here " points at " repo)))
189 (println (str "jolt-native " tag " (" (subs commit 0 8) ")"))
190 (try
191 (doseq [m files] (rewrite m tmp tag))
192 (finally (fs/delete-tree tmp)))
Build the APK as derivations only, and retire the buck2 graph 3bc3aaa nandi 16d ago193 (bump-nix! tmp tag)
Take every jolt-native half from the release, and only from there a008d3b nandi 18d ago194 (bump-deps! commit)
195 (println (str "\nNow: git diff, then `just lib` and `just apk` — the pins are "
196 "written, nothing is built.")))