M CHANGELOG.md => CHANGELOG.md +13 -0
@@ 6,6 6,18 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
+## [0.2.1] — 2026-08-07
+
+### Fixed
+- `install.sh` verified the download with `sha256sum -c --ignore-missing`,
+ a coreutils extension busybox rejects — so on Alpine, which is what
+ several manifests using this run on, the bootstrap aborted with a bogus
+ "checksum mismatch". The hash is now compared as a plain string, which
+ busybox, coreutils and macOS `shasum` all handle.
+- `install.sh` stages the binary inside the target directory and renames
+ it into place, so installing over a `cacher` that is currently running
+ can't fail with ETXTBSY.
+
## [0.2.0] — 2026-08-07
### Added
@@ 100,6 112,7 @@ with a single static Go binary.
must look in the same place.
[Unreleased]: https://git.srht.bigb.es/~bigbes/ci-cacher/log/master
+[0.2.1]: https://git.srht.bigb.es/~bigbes/ci-cacher/refs/v0.2.1
[0.2.0]: https://git.srht.bigb.es/~bigbes/ci-cacher/refs/v0.2.0
[0.1.2]: https://git.srht.bigb.es/~bigbes/ci-cacher/refs/v0.1.2
[0.1.1]: https://git.srht.bigb.es/~bigbes/ci-cacher/refs/v0.1.1
M VERSION => VERSION +1 -1
@@ 1,1 1,1 @@
-0.2.0
+0.2.1
M install.sh => install.sh +32 -18
@@ 48,32 48,46 @@ trap 'rm -rf "$tmp"' EXIT
echo "install.sh: fetching ${BASE_URL}/${asset}"
fetch "${BASE_URL}/${asset}" "${tmp}/cacher"
-# checksums.txt lists every published asset; --ignore-missing lets us
-# verify just the one we downloaded. Skipped, with a warning, when the
-# host has no sha256 tool rather than failing the install.
+# checksums.txt lists every published asset; pull out the line for the
+# one we downloaded and compare hashes as plain strings. Deliberately not
+# `sha256sum -c --ignore-missing`: --ignore-missing is a coreutils
+# extension, and Alpine ships busybox sha256sum, which rejects it.
+# A host with no sha256 tool at all warns and installs anyway.
+if command -v sha256sum >/dev/null 2>&1; then
+ hash_of() { sha256sum "$1" | cut -d' ' -f1; }
+elif command -v shasum >/dev/null 2>&1; then
+ hash_of() { shasum -a 256 "$1" | cut -d' ' -f1; }
+else
+ hash_of() { echo ""; }
+ echo "install.sh: no sha256sum/shasum — skipping checksum verification" >&2
+fi
+
if fetch "${BASE_URL}/checksums.txt" "${tmp}/checksums.txt"; then
- if command -v sha256sum >/dev/null 2>&1; then
- sum_cmd="sha256sum --ignore-missing -c"
- elif command -v shasum >/dev/null 2>&1; then
- sum_cmd="shasum -a 256 --ignore-missing -c"
- else
- sum_cmd=""
- echo "install.sh: no sha256sum/shasum — skipping checksum verification" >&2
- fi
- if [ -n "$sum_cmd" ]; then
- # The manifest names files without a path, so verify from inside $tmp
- # with the asset under its published name.
- cp "${tmp}/cacher" "${tmp}/${asset}"
- ( cd "$tmp" && $sum_cmd checksums.txt >/dev/null ) \
- || { echo "install.sh: checksum mismatch for ${asset}" >&2; exit 1; }
+ want=$(awk -v a="$asset" '$2 == a { print $1 }' "${tmp}/checksums.txt")
+ got=$(hash_of "${tmp}/cacher")
+ if [ -z "$want" ]; then
+ echo "install.sh: ${asset} is not listed in checksums.txt — skipping verification" >&2
+ elif [ -n "$got" ] && [ "$want" != "$got" ]; then
+ echo "install.sh: checksum mismatch for ${asset}" >&2
+ echo " want ${want}" >&2
+ echo " got ${got}" >&2
+ exit 1
+ elif [ -n "$got" ]; then
echo "install.sh: checksum ok"
fi
else
echo "install.sh: checksums.txt unavailable — skipping verification" >&2
fi
+# Land it via a staging file inside $BINDIR, so the final step is a
+# rename on one filesystem: writing over a binary in place fails with
+# ETXTBSY while something is running it.
mkdir -p "$BINDIR"
-install -m 0755 "${tmp}/cacher" "${BINDIR}/cacher"
+staged="${BINDIR}/.cacher.new.$$"
+trap 'rm -rf "$tmp" "$staged"' EXIT
+cp "${tmp}/cacher" "$staged"
+chmod 0755 "$staged"
+mv -f "$staged" "${BINDIR}/cacher"
# builds.sr.ht sources ~/.buildenv before every task; adding the export
# here saves each manifest a PATH line of its own.