From 18b9c9c549dc6783f8dbb52f5bbdca4e7ea7c7ea Mon Sep 17 00:00:00 2001 From: Joakim Persson Date: Mon, 18 May 2026 21:45:10 +0200 Subject: [PATCH] CI: harden promote-base-latest (pinned crane + skip on cache-hit) Two workflow-only changes for promote-base-latest, no image-side impact: T14 \u2014 replace imjasonh/setup-crane@v0.4 with direct pinned crane install. The action's bootstrap script calls api.github.com/.../releases/latest at every run to discover the crane version. That call periodically rate-limits and returns JSON without .tag_name, jq emits 'null', the action then downloads .../releases/download/null/... \u2192 404 \u2192 'gzip: unexpected end of file' \u2192 exit 2. We hit this on the v1.15.3 release (2026-05-16) where it was cosmetic only \u2014 base-latest was already correct from cache hit \u2014 but the red-X is annoying. Replaced with curl + tar pinned to crane v0.21.6 (latest at time of change). Same pattern as other GitHub-sourced binaries in the Dockerfile layer (gosu, fzf, eza etc.); operator bumps CRANE_VERSION deliberately when wanting updates. T15 \u2014 gate promote-base-latest on need_build == 'true'. When the base layer's content hash hasn't changed (cache hit on existing base- from a prior run), base-latest already points at the correct digest. The retag is a tautology, and any transient failure of it produces a red-X for an operation that didn't need to happen. Skipping the job entirely on cache-hit is correct and removes a whole class of cosmetic failure. Manual workflow_dispatch with promote_latest=true still bypasses the gate as an escape hatch (e.g., if base-latest got hand-deleted and needs regeneration without rebuilding the base). This will not trigger a CI publish run (main-branch commit, no tag). --- .gitea/workflows/docker-publish-split.yml | 25 +++++++++++++++++++++-- CHANGELOG.md | 5 +++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/docker-publish-split.yml b/.gitea/workflows/docker-publish-split.yml index a0d6295..017dc3f 100644 --- a/.gitea/workflows/docker-publish-split.yml +++ b/.gitea/workflows/docker-publish-split.yml @@ -526,12 +526,33 @@ jobs: - build-variant-omos - build-variant-with-pi - build-variant-omos-with-pi - if: ${{ github.ref_type == 'tag' || inputs.promote_latest == 'true' }} + # Skip on cache-hit base builds: when need_build=false, base-latest + # already points at the same digest as base-, so the retag is + # a tautology and any transient failure of it is purely cosmetic. + # Manual workflow_dispatch with promote_latest=true overrides this + # gate as an escape hatch (e.g., if base-latest got hand-deleted). + if: | + inputs.promote_latest == 'true' || + (github.ref_type == 'tag' && needs.base-decide.outputs.need_build == 'true') runs-on: ubuntu-latest container: image: catthehacker/ubuntu:act-latest steps: - - uses: imjasonh/setup-crane@v0.4 + # Direct pinned install instead of imjasonh/setup-crane@v0.4. The + # action's bootstrap script calls api.github.com/.../releases/latest + # to discover the crane version, which periodically rate-limits and + # produces tag=null → download from .../download/null/... → 404 → + # 'gzip: unexpected end of file' → exit 2. Pinning removes the + # runtime dependency on GitHub API entirely. Bump CRANE_VERSION + # deliberately when you want updates. + - name: Install crane (pinned) + env: + CRANE_VERSION: v0.21.6 + run: | + set -eux + curl -fsSL "https://github.com/google/go-containerregistry/releases/download/${CRANE_VERSION}/go-containerregistry_Linux_x86_64.tar.gz" \ + | tar -xz -C /usr/local/bin crane + crane version - name: Login (crane) run: | crane auth login docker.io \ diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c955fc..d6bb3d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,11 @@ Tags follow `v{opencode_version}[letter]` — bare tag for the first build on a ## Unreleased +CI hardening for the `promote-base-latest` job. No image-side changes — these are workflow-only edits and won't trigger a tag. + +- **Replaced `imjasonh/setup-crane@v0.4` with a direct pinned crane install.** The action's bootstrap script calls `api.github.com/.../releases/latest` to discover what crane version to install. That call periodically rate-limits and produces `tag=null` → the action downloads `releases/download/null/...` → 404 → `gzip: unexpected end of file` → exit 2. Hit this on the v1.15.3 release run (cosmetic failure — base-latest was already correct). Now we curl + extract crane v0.21.6 directly into `/usr/local/bin/`. Bump `CRANE_VERSION` deliberately when wanting updates, same pattern as the other GitHub-sourced binaries in the Dockerfile layer. +- **Gated `promote-base-latest` on `need_build == 'true'`.** When the base layer hash hasn't changed (cache-hit on the existing `base-` from a previous run), `base-latest` already points at the correct digest, so the retag is a tautology. Skipping in that case removes a class of cosmetic failure (no-op-that-fails). Manual `workflow_dispatch` with `promote_latest: true` overrides the gate as an escape hatch for hand-recovery scenarios. Tagged releases that genuinely rebuild the base still promote as before. + ## v1.15.3 — 2026-05-16 opencode 1.15.0 → 1.15.3 bump (three upstream patch releases).