diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml index 800746e..5a973bf 100644 --- a/.forgejo/workflows/ci.yml +++ b/.forgejo/workflows/ci.yml @@ -14,6 +14,17 @@ on: # pull_request intentionally absent — push on [dev, main] already fires CI for # every dev commit and dev→main PRs. Single-operator repo, no fork PRs. +# Serialize CI runs per ref so the publish lane never shares the runner's docker +# daemon with another run. Two publishes racing on one daemon evict each other's +# freshly-built image mid-push, breaking the post-build tag/push (issue #1093). +# cancel-in-progress:false is deliberate — rule 46 requires EVERY dev/main push to +# publish its own immutable : image, so a superseded run must still run to +# completion (never cancelled) to emit its SHA. (Forgejo honors `concurrency:` — +# CI-runner's build workflows use it.) +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: false + jobs: # Fast-fail lint lane. ruff is pre-installed in the ci-python image, so this # runs with NO dependency install and surfaces lint bounces in seconds. @@ -115,14 +126,25 @@ jobs: run: | set -euxo pipefail IMAGE=git.fabledsword.com/bvandeusen/steward - docker build -t "$IMAGE:${{ github.sha }}" . - docker push "$IMAGE:${{ github.sha }}" + # The moving tag for this ref: dev→:dev, main→:latest (rule 46). main IS + # the production line, so :latest tracks main's tip; there is no :main. + MOVING="" if [ "${{ github.ref }}" = "refs/heads/dev" ]; then - docker tag "$IMAGE:${{ github.sha }}" "$IMAGE:dev" - docker push "$IMAGE:dev" + MOVING="dev" elif [ "${{ github.ref }}" = "refs/heads/main" ]; then - docker tag "$IMAGE:${{ github.sha }}" "$IMAGE:latest" - docker push "$IMAGE:latest" + MOVING="latest" + fi + # Tag BOTH targets at build time (mirrors CI-runner's build workflows): + # one `docker build -t : -t :` points both tags at the image + # atomically, so we never run a post-push `docker tag` of an image a + # concurrent run could have evicted from the shared daemon (issue #1093). + if [ -n "$MOVING" ]; then + docker build -t "$IMAGE:${{ github.sha }}" -t "$IMAGE:$MOVING" . + docker push "$IMAGE:${{ github.sha }}" + docker push "$IMAGE:$MOVING" + else + docker build -t "$IMAGE:${{ github.sha }}" . + docker push "$IMAGE:${{ github.sha }}" fi - name: Prune dangling layers if: always()