ref:2e84796deca82499236916fbf1b00566f95a8b68

fix(ci): delegate build-runner to bash script — dash doesn't support pipefail

The Anvil CI runner invokes steps via /bin/sh which is dash on Debian. Dash rejects 'set -o pipefail' with 'Illegal option' exit 2, killing the step before any build runs. Move the build/publish logic to ci/build-runner.sh and exec bash from the yaml step. Also install bash in the container (rust:1.86-bookworm has it already via dependencies but make it explicit). Verified end-to-end on the actual runner host: both arm64 native and amd64 cross-compiled binaries produced successfully. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SHA: 2e84796deca82499236916fbf1b00566f95a8b68
Author: Cole Christensen <cole.christensen@macmillan.com>
Date: 2026-04-10 20:32
Parents: 37173d0
2 files changed +84 -79
Type
.anvil.yml +4 −79
@@ -59,86 +59,11 @@
- name: build-runner
timeout_seconds: 1800
# Delegated to a bash script because the runner invokes /bin/sh (dash)
# and we need bash features (pipefail, trap ERR) for publish rollback.
run: |
set -euo pipefail
# Fail fast if we're about to publish a release but the secret is missing —
# don't waste 10+ minutes on a build that will error at the end.
PUBLISH=0
if [ "${ANVIL_BRANCH:-}" = "main" ] || [ "${ANVIL_BRANCH:-}" = "refs/heads/main" ]; then
if [ -z "${ANVIL_TOKEN:-}" ]; then
echo "ERROR: ANVIL_TOKEN secret not set. Run:" >&2
echo " anvil ci set-secret --name ANVIL_TOKEN --value <pat> --repo fangorn/anvil-cli" >&2
exit 1
fi
PUBLISH=1
fi
apt-get update && apt-get install -y gcc-x86-64-linux-gnu jq 2>&1
# Build arm64 natively (CI runner is aarch64)
echo "==> Building arm64 binary (native)..."
cargo build --release 2>&1
# Cross-compile amd64
echo "==> Cross-compiling amd64 binary..."
rustup target add x86_64-unknown-linux-gnu 2>&1
CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=x86_64-linux-gnu-gcc \
cargo build --release --target x86_64-unknown-linux-gnu 2>&1
# Stage for CI artifact upload (unversioned names)
mkdir -p runner-dist
cp target/release/anvil runner-dist/anvil_runner_linux_arm64
cp target/x86_64-unknown-linux-gnu/release/anvil runner-dist/anvil_runner_linux_amd64
if [ "$PUBLISH" = "1" ]; then
export ANVIL_CLI="$PWD/target/release/anvil"
VERSION=$(bash ci/release.sh)
if [ -z "$VERSION" ]; then
echo "ERROR: ci/release.sh returned empty version" >&2
exit 1
fi
apt-get update && apt-get install -y bash gcc-x86-64-linux-gnu jq 2>&1
exec bash ci/build-runner.sh
echo "==> Publishing release $VERSION"
cp target/release/anvil "runner-dist/anvil_runner_linux_arm64_${VERSION}"
cp target/x86_64-unknown-linux-gnu/release/anvil "runner-dist/anvil_runner_linux_amd64_${VERSION}"
# Generate changelog body from commits since the previous CalVer tag.
PREV_TAG=$("$ANVIL_CLI" release list --format json fangorn/anvil-cli \
| jq -r '[.[] | select(.tag_name | test("^[0-9]{4}\\.[0-9]{2}\\.[0-9]+$"))]
| sort_by(.tag_name | split(".") | map(tonumber))
| .[-1].tag_name // empty')
if [ -n "$PREV_TAG" ] && git rev-parse --verify "$PREV_TAG" >/dev/null 2>&1; then
CHANGELOG=$(git log --oneline "${PREV_TAG}..HEAD" || echo "(no commits since $PREV_TAG)")
else
CHANGELOG=$(git log --oneline -n 20)
fi
BODY=$(printf 'Runner binaries for linux/amd64 and linux/arm64.\n\n## Changes\n\n%s\n' "$CHANGELOG")
"$ANVIL_CLI" release create \
--tag "$VERSION" \
--title "anvil-cli $VERSION" \
--body "$BODY" \
--repo fangorn/anvil-cli
# Roll back the release if any subsequent step fails — don't leave
# orphaned empty/partial releases lying around.
cleanup_release() {
echo "==> Publish failed — rolling back release $VERSION" >&2
"$ANVIL_CLI" release delete "$VERSION" --repo fangorn/anvil-cli >&2 || true
}
trap cleanup_release ERR
"$ANVIL_CLI" release upload "$VERSION" \
"runner-dist/anvil_runner_linux_arm64_${VERSION}" \
--repo fangorn/anvil-cli
"$ANVIL_CLI" release upload "$VERSION" \
"runner-dist/anvil_runner_linux_amd64_${VERSION}" \
--repo fangorn/anvil-cli
trap - ERR
echo "==> Published release $VERSION"
fi
depends_on: [test, clippy, fmt]
cache:
key: Cargo.lock