Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 35 additions & 27 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,17 @@ jobs:
- name: Compare the committed version against the published one
id: decide
run: |
VERSION=$(python3 -c "import tomllib;print(tomllib.load(open('Cargo.toml','rb'))['workspace']['package']['version'])")
# `cargo pkgid` rather than a TOML parser. The version is resolved by
# the tool that owns it, so this cannot disagree with what the build
# actually produces, and it needs no interpreter on the runner.
VERSION=$(cargo pkgid -p chuzz-gui | sed 's/.*@//')
echo "version=$VERSION" >> "$GITHUB_OUTPUT"

# `latest.json` is written by this workflow further down, so its shape
# is known: one "version" field. `sed` reads it rather than a JSON
# parser, which keeps the runner requirement at coreutils.
LIVE=$(curl -fsS --max-time 30 "$CDN_BASE/latest.json" 2>/dev/null \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["version"])' 2>/dev/null || echo "")
| sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1)

if [ "${{ inputs.force }}" = "true" ]; then
echo "publish=true" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -237,31 +243,32 @@ jobs:
run: |
TARBALL="$BUNDLE_DIR/Chuzz.app.tar.gz"
test -f "$TARBALL.sig" || { echo "::error::missing $TARBALL.sig"; exit 1; }
python3 - "$VERSION" "$TARBALL.sig" "$CDN_BASE" <<'PY'
import json, sys, datetime
version, sig_path, base = sys.argv[1], sys.argv[2], sys.argv[3]
with open(sig_path) as f:
signature = f.read().strip()
manifest = {
"version": version,
"pub_date": datetime.datetime.now(datetime.timezone.utc)
.strftime("%Y-%m-%dT%H:%M:%SZ"),
"platforms": {
"darwin-aarch64": {
"signature": signature,
# The query string is a cache-buster, and it works because
# this pull zone keys its cache on the full URL including
# the query, so each release advertises a URL no edge has
# ever held. Brew fetches the bare URL and relies on the
# purge.
"url": f"{base}/Chuzz.app.tar.gz?v={version}",
}
},
# A minisign signature is base64 and the version is a semver string,
# so neither can hold a character JSON would need escaped, and this
# manifest is interpolation rather than serialisation. Guard that
# assumption instead of taking on a parser.
SIGNATURE=$(cat "$TARBALL.sig")
case $SIGNATURE in *[!A-Za-z0-9+/=]*)
echo "::error::signature is not plain base64; refusing to interpolate"; exit 1;; esac
case $VERSION in *[!0-9A-Za-z.+-]*)
echo "::error::version has unexpected characters; refusing to interpolate"; exit 1;; esac

# The url query string is a cache-buster, and it works because this
# pull zone keys its cache on the full URL including the query, so
# each release advertises a URL no edge has ever held. Brew fetches
# the bare URL and relies on the purge.
cat > latest.json <<JSON
{
"version": "$VERSION",
"pub_date": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"platforms": {
"darwin-aarch64": {
"signature": "$SIGNATURE",
"url": "$CDN_BASE/Chuzz.app.tar.gz?v=$VERSION"
}
}
}
with open("latest.json", "w") as f:
json.dump(manifest, f, indent=2)
f.write("\n")
PY
JSON
cat latest.json

# Ordering matters and is the whole reason these are separate steps. The
Expand Down Expand Up @@ -352,7 +359,8 @@ jobs:
# nobody sees the release. It is also what the next run compares
# against, so a stale copy would republish this version forever.
for attempt in 1 2 3 4 5 6; do
LIVE=$(curl -sS -L "$CDN_BASE/latest.json" | python3 -c 'import json,sys; print(json.load(sys.stdin)["version"])' 2>/dev/null || echo "")
LIVE=$(curl -sS -L "$CDN_BASE/latest.json" 2>/dev/null \
| sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1)
if [ "$LIVE" = "$VERSION" ]; then
echo "edge serves version $LIVE"
exit 0
Expand Down
Loading
Loading