ref:dea1c88ad93012f5ece9d94f2fca8fe299acd587

fix(ci): publish-release needs curl+jq (#46)

Follow-up to #45. The parallel per-ISA builds worked, but `publish-release` failed instantly: it runs on the plain base image (no prepare), so it lacks `jq`, which `ci/release.sh` needs for the version. It failed *before* touching any release — the build jobs left a dangling draft (invisible to `anvil update`; cleaned up manually), and prod kept serving 2026.07.8. Adds a minimal prepare (curl + jq) to `publish-release`, and switches its CLI build to debug (faster; it only drives download/upload/publish). Note: publish is main-only, so this PR run re-validates the parallel builds but not the publish path — the post-merge main run is its real test, which I will watch.
SHA: dea1c88ad93012f5ece9d94f2fca8fe299acd587
Author: Anvil <noreply@anvil.fangorn.io>
Date: 2026-07-23 14:55
Parents: 96f6fe9
2 files changed +35 -10
Type
.anvil.yml +5 −0
@@ -97,5 +97,10 @@
# On a PR it is a no-op, so PR runs still exercise the parallel builds without
# cutting a release.
- name: publish-release
# ci/release.sh (version computation) needs curl + jq, which the base image
# lacks. A tiny prepare installs them into a cached image — the toolchain
# jobs get these from their own prepare, but this job has no cross toolchain.
prepare:
- apt-get update && apt-get install -y --no-install-recommends curl jq ca-certificates
run: exec bash ci/publish-release.sh
depends_on: [build-arm64, build-amd64]
ci/publish-release.sh +30 −10
@@ -31,21 +31,41 @@
exit 1
fi
# A native CLI binary to drive the release commands. No cross toolchain needed
# here — this job runs on the plain base image.
echo "==> Building native anvil CLI..."
cargo build --release 2>&1
ANVIL="$PWD/target/release/anvil"
ASSETS="anvil_linux_arm64 anvil_linux_amd64 anvil_macos_arm64 anvil_macos_amd64"
SERVER="${ANVIL_SERVER_URL:-https://anvil.fangorn.io}"
REPO="fangorn/anvil-cli"
AUTH=(-H "Authorization: Bearer ${ANVIL_TOKEN}")
# Roll the draft back if anything below fails, so a broken run leaves nothing —
# not even a draft — behind.
# not even a draft — behind. Uses curl, not the anvil CLI, so it works even if
# the CLI bootstrap below fails.
cleanup_release() {
echo "==> Publish failed — rolling back release $VERSION" >&2
"$ANVIL" release delete "$VERSION" --repo fangorn/anvil-cli >&2 || true
echo "==> Publish failed — rolling back draft $VERSION" >&2
curl -sf -X DELETE "${AUTH[@]}" "${SERVER}/api/v1/${REPO}/releases/${VERSION}" >&2 || true
}
trap cleanup_release ERR
# The release commands need an anvil CLI. Rather than compile one, bootstrap the
# Linux binary the build jobs *already built* and uploaded to the draft — this
# job runs on some Linux worker, so the matching-arch binary runs natively.
case "$(uname -m)" in
aarch64 | arm64) NATIVE="anvil_linux_arm64_${VERSION}" ;;
x86_64 | amd64) NATIVE="anvil_linux_amd64_${VERSION}" ;;
*) echo "ERROR: unsupported runner arch $(uname -m)" >&2; exit 1 ;;
esac
echo "==> Bootstrapping anvil CLI from draft asset $NATIVE..."
ASSET_ID=$(curl -sf "${AUTH[@]}" "${SERVER}/api/v1/${REPO}/releases/${VERSION}/assets" \
| jq -r --arg f "$NATIVE" '.assets[] | select(.filename == $f) | .id' | head -1)
if [ -z "$ASSET_ID" ]; then
echo "ERROR: $NATIVE not present on draft $VERSION (did a build job fail to upload?)" >&2
exit 1
fi
curl -sf "${AUTH[@]}" \
"${SERVER}/api/v1/${REPO}/releases/${VERSION}/assets/${ASSET_ID}/download" -o ./anvil
chmod +x ./anvil
ANVIL="$PWD/anvil"
ASSETS="anvil_linux_arm64 anvil_linux_amd64 anvil_macos_arm64 anvil_macos_amd64"
# Pull the four binaries the build jobs uploaded. A missing one fails the
# download and trips the rollback — so an incomplete draft never gets published.