diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c14108141d6ed6163cad8593a6df454bd0d288a..46eee202f1a0df4a30929acc8725bee151176c97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 @@ via `os.UserConfigDir`). cacher is a CI tool; dev-macs and Linux runners 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 diff --git a/VERSION b/VERSION index 0ea3a944b399d25f7e1b8fe684d754eb8da9fe7f..0c62199f16ac1e2d7f7ae75b420c1231325dff4e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2.0 +0.2.1 diff --git a/install.sh b/install.sh index 71b8a49ed2dfc990b3c7062767b535603f003a0e..a73631e6e41cb9173f442b935b475503a2a8de7b 100755 --- a/install.sh +++ b/install.sh @@ -48,32 +48,46 @@ 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.