diff --git a/.github/workflows/cli-pin-bump.yml b/.github/workflows/cli-pin-bump.yml index 0f65f35c9a..ccd98e1053 100644 --- a/.github/workflows/cli-pin-bump.yml +++ b/.github/workflows/cli-pin-bump.yml @@ -68,6 +68,7 @@ on: - 'src/scripts/test-check-cli-model-lists.sh' - 'src/scripts/cli-model-requirements.txt' - 'src/Dockerfile' + - 'src/deploy/ci-runners/Dockerfile' - 'src/pkg/dashboard/cli_models.go' permissions: @@ -250,7 +251,7 @@ jobs: git config user.name "hive-release[bot]" git config user.email "hive-release@hive.kubestellar.io" git checkout -B "$HEAD_BRANCH" - git add src/Dockerfile src/Dockerfile.contributor src/pkg/config/dockerfile_contributor_test.go + git add src/Dockerfile src/Dockerfile.contributor src/deploy/ci-runners/Dockerfile src/pkg/config/dockerfile_contributor_test.go { echo "🌱 images: bump ${CLI} ${OLD} -> ${NEW}" echo diff --git a/changelog.d/fixed-ci-runner-gh-pin-gate.md b/changelog.d/fixed-ci-runner-gh-pin-gate.md new file mode 100644 index 0000000000..63229483f9 --- /dev/null +++ b/changelog.d/fixed-ci-runner-gh-pin-gate.md @@ -0,0 +1 @@ +- CI runner image: bumped the pinned `gh` to 2.103.3 (the apt repo only serves the latest), and the daily CLI pin bump now keeps that pin current, including `gh` patch releases. diff --git a/src/deploy/ci-runners/Dockerfile b/src/deploy/ci-runners/Dockerfile index 19cbf0f300..73c205c0a8 100644 --- a/src/deploy/ci-runners/Dockerfile +++ b/src/deploy/ci-runners/Dockerfile @@ -48,7 +48,7 @@ ARG RUNNER_BASE_IMAGE=summerwind/actions-runner:v2.337.0-ubuntu-24.04 FROM ${RUNNER_BASE_IMAGE} -ARG GITHUB_CLI_VERSION=2.103.2 +ARG GITHUB_CLI_VERSION=2.103.3 ARG GITHUB_CLI_KEYRING_SHA256=6084d5d7bd8e288441e0e94fc6275570895da18e6751f70f057485dc2d1a811b # The base image runs as uid 1001 (`runner`), which does not own /etc/apt. diff --git a/src/docs/cli-pins.md b/src/docs/cli-pins.md index b650980769..e53751befa 100644 --- a/src/docs/cli-pins.md +++ b/src/docs/cli-pins.md @@ -13,7 +13,7 @@ Every agent CLI hive drives is baked into the images at a fixed version: | Oh My Pi (omp) | `OMP_VERSION`, `OMP_SHA256_*` | GitHub releases `can1357/oh-my-pi` | SHA-256 per arch, cross-checked with `SHA256SUMS.txt` | | Muse Code | `MUSE_VERSION`, `MUSE_SHA256_*` | `api.meta.ai/muse-code/channels/muse-stable` and its release manifest | SHA-256 per arch, cross-checked with the manifest | | bobshell (bob) | `BOBSHELL_VERSION`, `BOBSHELL_SHA256_*` | `bobshell2-version.txt` on IBM COS | SHA-256, cross-checked with the vendor `.tgz.sha256` | -| gh | `GH_VERSION`, `GH_SHA256_*` (hub only) | GitHub releases `cli/cli` | SHA-256 per arch, cross-checked with `gh__checksums.txt` | +| gh | `GH_VERSION`, `GH_SHA256_*` (hub only); also `GITHUB_CLI_VERSION` in `src/deploy/ci-runners/Dockerfile` | GitHub releases `cli/cli` | SHA-256 per arch, cross-checked with `gh__checksums.txt` | The pins live in `src/Dockerfile` (hub and spoke image) and `src/Dockerfile.contributor` (containerized contributors), and the two must @@ -88,7 +88,7 @@ Most CLIs ship features on major/minor releases, so patch-only bumps are skipped. **Claude Code is patch-eligible**: it ships new models as 2.1.x patches (for example Sonnet 5.5 needs 2.1.284), so skipping patches left the image unable to launch models Hive lists. `cli-pin-bump.sh` bumps patch -releases of the CLIs in `HIVE_PIN_PATCH_ELIGIBLE` (default `claude`; set it to +releases of the CLIs in `HIVE_PIN_PATCH_ELIGIBLE` (default `claude gh`; set it to empty to restore the strict rule). Add a CLI there when it starts shipping models in patches. diff --git a/src/scripts/cli-pin-bump.sh b/src/scripts/cli-pin-bump.sh index 60efc5fefd..706477aa09 100755 --- a/src/scripts/cli-pin-bump.sh +++ b/src/scripts/cli-pin-bump.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # cli-pin-bump.sh — keep the agent CLI pins in src/Dockerfile and -# src/Dockerfile.contributor current without giving up the pin (#8419). +# src/Dockerfile.contributor (and gh in the runner image) current without giving up the pin (#8419). # # Every CLI hive drives is installed at a fixed version: npm packages through # `ARG X_VERSION`, release downloads through `ARG X_VERSION` plus a per-arch @@ -38,6 +38,7 @@ # HIVE_PIN_ROOT repo root (default: two levels above this script) # HIVE_PIN_DOCKERFILE override src/Dockerfile # HIVE_PIN_CONTRIB_DOCKERFILE override src/Dockerfile.contributor +# HIVE_PIN_RUNNER_DOCKERFILE override src/deploy/ci-runners/Dockerfile (gh's GITHUB_CLI_VERSION) # HIVE_PIN_PI_TEST override src/pkg/config/dockerfile_contributor_test.go # HIVE_PIN_WORKDIR where downloads land (default: mktemp -d, removed on exit) # GH_TOKEN / GITHUB_TOKEN optional; raises the GitHub API rate limit @@ -45,13 +46,14 @@ # HIVE_PIN_INCLUDE_PATCH=1 include patch-only bumps (default: skip, except # for the patch-eligible CLIs below) # HIVE_PIN_PATCH_ELIGIBLE CLIs whose patch releases are bumped anyway -# (default: "claude"; set empty to disable) +# (default: "claude gh"; set empty to disable) set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT="${HIVE_PIN_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}" DOCKERFILE="${HIVE_PIN_DOCKERFILE:-$ROOT/src/Dockerfile}" CONTRIB="${HIVE_PIN_CONTRIB_DOCKERFILE:-$ROOT/src/Dockerfile.contributor}" +RUNNER="${HIVE_PIN_RUNNER_DOCKERFILE:-$ROOT/src/deploy/ci-runners/Dockerfile}" PI_TEST="${HIVE_PIN_PI_TEST:-$ROOT/src/pkg/config/dockerfile_contributor_test.go}" # Every CLI this script knows. Order is the order the workflow's matrix uses. @@ -60,7 +62,7 @@ CLIS="claude codex copilot pi goose agy omp muse bob gh" # CLIs that ship features (new models) in patch releases: Claude Code's 2.1.x # line adds models as patches, so skipping patches leaves the image unable to # launch models Hive lists (#9805). See src/docs/cli-pins.md. -PATCH_ELIGIBLE="${HIVE_PIN_PATCH_ELIGIBLE-claude}" +PATCH_ELIGIBLE="${HIVE_PIN_PATCH_ELIGIBLE-claude gh}" # Digest lengths in hex characters — a value of any other length is never # written into a Dockerfile (the "never guess a hash" guard). @@ -454,7 +456,10 @@ apply_cli() { bob) for f in "$DOCKERFILE" "$CONTRIB"; do set_arg "$f" BOBSHELL_VERSION "$v"; apply_sha_pair "$f" BOBSHELL "$kv" 256; done ;; gh) - set_arg "$DOCKERFILE" GH_VERSION "$v"; apply_sha_pair "$DOCKERFILE" GH "$kv" 256 ;; + set_arg "$DOCKERFILE" GH_VERSION "$v"; apply_sha_pair "$DOCKERFILE" GH "$kv" 256 + # The runner image installs gh from the apt repo, which only serves the + # latest release, so its exact pin goes stale unless it moves with this one. + set_arg "$RUNNER" GITHUB_CLI_VERSION "$v" ;; agy) # The two images spell the same release differently: the hub image # keeps version and build id in separate ARGs and verifies SHA-512, the diff --git a/src/scripts/test-cli-pin-bump.sh b/src/scripts/test-cli-pin-bump.sh index bd14accf5b..a21da52dfb 100755 --- a/src/scripts/test-cli-pin-bump.sh +++ b/src/scripts/test-cli-pin-bump.sh @@ -72,6 +72,11 @@ ARG OMP_VERSION=18.2.6 ARG OMP_SHA256_AMD64=6666666666666666666666666666666666666666666666666666666666666666 ARG OMP_SHA256_ARM64=7777777777777777777777777777777777777777777777777777777777777777 ARG PI_CODING_AGENT_VERSION=0.84.1 +DF + cat > "$TMP/runner.Dockerfile" <<'DF' +FROM scratch +ARG GITHUB_CLI_VERSION=2.101.0 +ARG GITHUB_CLI_KEYRING_SHA256=6084d5d7bd8e288441e0e94fc6275570895da18e6751f70f057485dc2d1a811b DF cat > "$TMP/pi_test.go" <<'GO' for _, want := range []string{ @@ -83,7 +88,7 @@ GO run() { # run : the script against the fixtures; stdout to $OUT, rc in $RC OUT="$(HIVE_PIN_DOCKERFILE="$TMP/Dockerfile" HIVE_PIN_CONTRIB_DOCKERFILE="$TMP/Dockerfile.contributor" \ - HIVE_PIN_PI_TEST="$TMP/pi_test.go" HIVE_PIN_WORKDIR="$TMP/work" \ + HIVE_PIN_PI_TEST="$TMP/pi_test.go" HIVE_PIN_RUNNER_DOCKERFILE="$TMP/runner.Dockerfile" HIVE_PIN_WORKDIR="$TMP/work" \ HIVE_PIN_INCLUDE_PATCH="${HIVE_PIN_INCLUDE_PATCH:-}" \ STUB_CLAUDE_VERSION="${STUB_CLAUDE_VERSION:-}" \ STUB_AGY_VERSION="${STUB_AGY_VERSION:-}" \ @@ -125,6 +130,9 @@ run apply gh "$TMP/gh.kv" [ $RC -eq 0 ] && [ "$(arg Dockerfile GH_VERSION)" = "2.102.0" ] && [ "$(arg Dockerfile GH_SHA256_ARM64)" = "$SHA256_B" ] \ && ! grep -q GH_VERSION "$TMP/Dockerfile.contributor" \ && pass "gh is hub-only" || fail "gh apply" "$ERR" +[ "$(arg runner.Dockerfile GITHUB_CLI_VERSION)" = "2.102.0" ] \ + && grep -q '^ARG GITHUB_CLI_KEYRING_SHA256=6084d5d7' "$TMP/runner.Dockerfile" \ + && pass "gh also bumps the runner image GITHUB_CLI_VERSION and leaves the keyring sha" || fail "gh runner apply" "$ERR" printf 'VERSION=1.2.9\nBUILD=5905287731871744\nSHA256_AMD64=%s\nSHA256_ARM64=%s\nSHA512_AMD64=%s\nSHA512_ARM64=%s\n' \ "$SHA256_A" "$SHA256_B" "$SHA512_C" "$SHA512_D" > "$TMP/agy.kv" run apply agy "$TMP/agy.kv" @@ -179,6 +187,14 @@ case "$url" in "https://api.github.com/repos/aaif-goose/goose/releases/latest") printf '{"tag_name":"v1.53.0"}' > "$out" ;; "https://github.com/aaif-goose/goose/releases/download/v1.53.0/goose-x86_64-unknown-linux-gnu.tar.gz") printf 'goose-amd64' > "$out" ;; "https://github.com/aaif-goose/goose/releases/download/v1.53.0/goose-aarch64-unknown-linux-gnu.tar.gz") printf 'goose-arm64' > "$out" ;; + "https://api.github.com/repos/cli/cli/releases/latest") printf '{"tag_name":"v%s"}' "${STUB_GH_VERSION:-2.101.1}" > "$out" ;; + *"/github.com/cli/cli/releases/download/v"*"/gh_"*"_checksums.txt") + v="${url##*/download/v}"; v="${v%%/*}" + amd64="$(printf 'gh-amd64' | shasum -a 256 | cut -d' ' -f1)" + arm64="$(printf 'gh-arm64' | shasum -a 256 | cut -d' ' -f1)" + printf '%s gh_%s_linux_amd64.tar.gz\n%s gh_%s_linux_arm64.tar.gz\n' "$amd64" "$v" "$arm64" "$v" > "$out" ;; + *"/github.com/cli/cli/releases/download/"*"/gh_"*"_linux_amd64.tar.gz") printf 'gh-amd64' > "$out" ;; + *"/github.com/cli/cli/releases/download/"*"/gh_"*"_linux_arm64.tar.gz") printf 'gh-arm64' > "$out" ;; *"/api/cask/antigravity-cli.json") amd64="$(printf 'agy-amd64' | shasum -a 256 | cut -d' ' -f1)" arm64="$(printf 'agy-arm64' | shasum -a 256 | cut -d' ' -f1)" @@ -237,6 +253,24 @@ HIVE_PIN_INCLUDE_PATCH=1 STUB_AGY_VERSION=1.1.19,5905287731871744 run bump agy [ $RC -eq 0 ] && [ "$OUT" = "$(printf 'OLD=1.1.19,4894004681244672\nNEW=1.1.19,5905287731871744\nMAJOR=false\nCHANGED=true')" ] \ && [ "$(arg Dockerfile AGY_BUILD)" = "5905287731871744" ] \ && pass "HIVE_PIN_INCLUDE_PATCH=1 applies an agy build-only bump" || fail "agy patch override" "$OUT $ERR" + +echo "-- gh patch eligibility --" +make_fixtures +STUB_GH_VERSION=2.101.1 run bump gh +GH_AMD64="$(printf 'gh-amd64' | shasum -a 256 | cut -d' ' -f1)" +GH_ARM64="$(printf 'gh-arm64' | shasum -a 256 | cut -d' ' -f1)" +[ $RC -eq 0 ] && [ "$OUT" = "$(printf 'OLD=2.101.0\nNEW=2.101.1\nMAJOR=false\nCHANGED=true')" ] \ + && [ "$(arg Dockerfile GH_VERSION)" = "2.101.1" ] \ + && [ "$(arg Dockerfile GH_SHA256_AMD64)" = "$GH_AMD64" ] && [ "$(arg Dockerfile GH_SHA256_ARM64)" = "$GH_ARM64" ] \ + && [ "$(arg runner.Dockerfile GITHUB_CLI_VERSION)" = "2.101.1" ] \ + && pass "gh patch-only bump (2.101.0 -> 2.101.1) applies by default because gh is now patch-eligible" || fail "gh patch-eligible" "$OUT $ERR" + +make_fixtures +HIVE_PIN_PATCH_ELIGIBLE= STUB_GH_VERSION=2.101.1 run bump gh +[ $RC -eq 0 ] && [ "$OUT" = "$(printf 'OLD=2.101.0\nNEW=2.101.1\nMAJOR=false\nCHANGED=false\nSKIPPED=patch')" ] \ + && [ "$(arg Dockerfile GH_VERSION)" = "2.101.0" ] \ + && pass "gh patch bump is skipped when patch eligibility is disabled (HIVE_PIN_PATCH_ELIGIBLE=empty)" || fail "gh patch skip" "$OUT $ERR" + unset HIVE_PIN_HTTP echo