#!/usr/bin/env bash # Refuse to publish a release whose artifacts are not all from this release. # # TRACES: | DR-220 # # ./scripts/check-release-artifacts.sh [...] # # e.g. # ./scripts/check-release-artifacts.sh v0.9.2 artifacts/linux artifacts/windows # # ## The defect this exists for # # Every JellyTau release from v0.1.0 to v0.8.2 shipped every Windows installer # ever built. `src-tauri/target/*/release/bundle/` is not versioned, cargo never # cleans it, and the CI runner reuses the target directory between builds — so # the copy step's `bundle/**/*-setup.exe` glob collected the whole history. By # v0.8.2 that was sixteen installers, thirteen of them stale. v0.5.0 offered # users a download list going back to 0.1.0. # # Nobody noticed for eight months. There was nothing to notice with: the upload # loop reported success, the assets were real files, and the release page looked # busy rather than wrong. # # The builds now clear the bundle directory first, which removes the cause. This # is the backstop for the next thing that reintroduces a stale file by a route # nobody predicted — a cached directory, a restored artifact, a hand-copied fix. # # ## What it checks # # Every file whose name embeds a semantic version must embed *this* version. # Files with no version in the name (jellytau-release.apk, jellytau.exe, # SHA256SUMS, latest.json) are accepted: they are produced fresh each build and # have no version to disagree with. set -euo pipefail if [ "$#" -lt 2 ]; then echo "usage: $0 [...]" >&2 exit 2 fi VERSION_RAW="$1" shift # Accept the tag form (v0.9.2) or the bare form (0.9.2). VERSION="${VERSION_RAW#v}" echo "🔎 Checking release artifacts are all version ${VERSION}…" FOUND=0 STALE=0 UNVERSIONED=0 for dir in "$@"; do if [ ! -d "$dir" ]; then echo " (no $dir — skipping)" continue fi # -print0/read -d '' so a filename with a space cannot split into two. while IFS= read -r -d '' file; do name="$(basename "$file")" FOUND=$((FOUND + 1)) # First x.y.z in the filename, if any. embedded="$(printf '%s' "$name" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true)" if [ -z "$embedded" ]; then UNVERSIONED=$((UNVERSIONED + 1)) continue fi if [ "$embedded" != "$VERSION" ]; then echo " ❌ $name carries version $embedded" STALE=$((STALE + 1)) fi done < <(find "$dir" -type f -print0) done echo "" echo " $FOUND file(s) checked; $UNVERSIONED carry no version in the name." if [ "$FOUND" -eq 0 ]; then echo "❌ No artifacts found at all. A release with no files is a failed build," >&2 echo " not an empty one." >&2 exit 1 fi if [ "$STALE" -gt 0 ]; then echo "" echo "❌ $STALE artifact(s) belong to a different version than ${VERSION}." >&2 echo "" >&2 echo " This is how every release from v0.1.0 to v0.8.2 came to ship its" >&2 echo " predecessors' Windows installers: src-tauri/target/*/release/bundle/" >&2 echo " is never cleaned and the runner reuses it, so a glob picks up" >&2 echo " whatever was left behind." >&2 echo "" >&2 echo " The builds clear that directory first, so seeing this means a stale" >&2 echo " file arrived by some other route. Find it before publishing — do not" >&2 echo " delete the file and re-run." >&2 exit 1 fi echo "✅ Every versioned artifact is ${VERSION}."