~bigbes/ci-cacher

46be89f3e30bff634a7699be89964749a16611d9 — Eugene Blikh 11 days ago 8851cb5 v0.2.1
install.sh: verify checksums without coreutils extensions; bump to 0.2.1

sha256sum -c --ignore-missing is a coreutils extension. Alpine ships
busybox sha256sum, which prints its usage and exits non-zero, so the
bootstrap died with a 'checksum mismatch' that was nothing of the sort —
on the exact distro the sr-ht-cover manifest builds on. Pull the expected
hash out of checksums.txt with awk and compare strings instead; busybox,
coreutils and macOS shasum all agree on 'sha256sum FILE'.

Also stage the binary inside the target directory and rename it into
place, so an install over a running cacher can't hit ETXTBSY.

Verified against the published release on alpine:edge (busybox) and
ubuntu:noble (coreutils), and across all four published assets by faking
uname: Linux/x86_64, Linux/aarch64, Darwin/x86_64, Darwin/arm64 each
fetch the matching binary and verify.
3 files changed, 46 insertions(+), 19 deletions(-)

M CHANGELOG.md
M VERSION
M install.sh
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.