From 5a5aaa2c8ff18cfdf49d2f47828c8a4d8f5ce4a8 Mon Sep 17 00:00:00 2001 From: vvillait88 Date: Mon, 21 Sep 2026 12:20:38 -0400 Subject: [PATCH] Checksum the published tarball for Homebrew instead of waiting on npm's CDN The tap step polled the tarball URL for five minutes right after npm publish and failed on four of the last seven tag pushes, each fixed by a later dispatch. The job now packs once, publishes that file, and pins the formula to its sha256 once its sha512 matches the registry's recorded integrity. A resumed run, which has no local copy, downloads the tarball and checks it against the same integrity. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/publish.yml | 55 +++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ddd5b71..ceb1413 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -26,7 +26,7 @@ concurrency: jobs: publish: runs-on: ubuntu-latest - timeout-minutes: 20 + timeout-minutes: 30 env: RELEASE_TAG: ${{ inputs.tag || github.ref_name }} steps: @@ -47,6 +47,10 @@ jobs: - run: bun install --frozen-lockfile - run: bun run build + # Pack once and publish that exact file, so the Homebrew step can checksum + # the bytes npm received without fetching them back through npm's CDN. + # A local pack does not reproduce an earlier run's bytes, so the file is + # only trusted when THIS run published it. - name: Publish to npm (with provenance) run: | VERSION="${RELEASE_TAG#v}" @@ -54,14 +58,17 @@ jobs: echo "@agent-score/pay@${VERSION} is already on npm; skipping publish (resumed run)" exit 0 fi + TARBALL="$RUNNER_TEMP/$(npm pack --ignore-scripts --pack-destination "$RUNNER_TEMP" | tail -1)" + test -s "$TARBALL" if [[ "$VERSION" == *-* ]]; then DIST_TAG="${VERSION#*-}" DIST_TAG="${DIST_TAG%%.*}" echo "prerelease detected — publishing under dist-tag: $DIST_TAG" - npm publish --access public --provenance --tag "$DIST_TAG" + npm publish "$TARBALL" --access public --provenance --tag "$DIST_TAG" else - npm publish --access public --provenance + npm publish "$TARBALL" --access public --provenance fi + echo "PUBLISHED_TARBALL=$TARBALL" >> "$GITHUB_ENV" - name: Build native binaries run: bun run build:binary:all @@ -107,22 +114,38 @@ jobs: set -euo pipefail VERSION_NO_V="${VERSION#v}" TARBALL_URL="https://registry.npmjs.org/@agent-score/pay/-/pay-${VERSION_NO_V}.tgz" - # npm CDN can take several minutes to propagate a new tarball; poll up to 5 min. + # The registry's recorded integrity is the authority on which bytes the + # formula must pin. Package metadata appears within seconds of a + # publish; the tarball URL can serve a cached 404 for far longer, which + # is why this step never waits on it when it has the file already. + INTEGRITY="" for i in $(seq 1 30); do - if curl -fsSI "$TARBALL_URL" >/dev/null 2>&1; then - echo "tarball reachable after ${i}0s" - break - fi - if [ "$i" = "30" ]; then - echo "tarball never became reachable in 5 minutes — aborting" >&2 - exit 1 - fi + INTEGRITY="$(npm view "@agent-score/pay@${VERSION_NO_V}" dist.integrity 2>/dev/null || true)" + [ -n "$INTEGRITY" ] && break sleep 10 done - # Download once + checksum the file (separate steps so a partial download fails) - curl -fsSL "$TARBALL_URL" -o /tmp/pay.tgz - test -s /tmp/pay.tgz || { echo "downloaded tarball is empty" >&2; exit 1; } - SHA256=$(shasum -a 256 /tmp/pay.tgz | awk '{print $1}') + [ -n "$INTEGRITY" ] || { echo "registry has no integrity for ${VERSION_NO_V} after 5 minutes" >&2; exit 1; } + integrity_of() { echo "sha512-$(openssl dgst -sha512 -binary "$1" | base64 -w0)"; } + + if [ -n "${PUBLISHED_TARBALL:-}" ] && [ "$(integrity_of "$PUBLISHED_TARBALL")" = "$INTEGRITY" ]; then + TARBALL_FILE="$PUBLISHED_TARBALL" + echo "using the tarball this run published" + else + # Resumed run: the version was published earlier, so fetch it. + TARBALL_FILE=/tmp/pay.tgz + for i in $(seq 1 40); do + if curl -fsSL "$TARBALL_URL" -o "$TARBALL_FILE" && [ "$(integrity_of "$TARBALL_FILE")" = "$INTEGRITY" ]; then + echo "downloaded tarball matches the registry integrity" + break + fi + if [ "$i" = "40" ]; then + echo "could not fetch a tarball matching ${INTEGRITY} in 10 minutes" >&2 + exit 1 + fi + sleep 15 + done + fi + SHA256=$(shasum -a 256 "$TARBALL_FILE" | awk '{print $1}') echo "tarball: $TARBALL_URL" echo "sha256: $SHA256"