From 4f4082ecd71bf187f04e50e61dce99e264d8ee1b Mon Sep 17 00:00:00 2001 From: austin Date: Mon, 27 Jul 2026 09:53:24 -0500 Subject: [PATCH] A pull request must not touch :latest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This runner builds on the host's Docker daemon — the same daemon the live container runs on — so retagging :latest IS a deployment. Watchtower compares the running container's image against :latest, sees they differ, and recreates the container from whatever was just built locally. Gating only the push was never enough; the build itself was the deploy. The worse failure is quieter. Reassigning :latest leaves the running container's old image untagged, and once that image is pruned Watchtower can no longer read it to compare against: Failed to retrieve container image info: No such image: sha256:… Unable to update container: no available image info. bennett-portfolio hit exactly that. It sat on a four-day-old build, failing to update 720 times in twenty-four hours, reporting healthy the whole time, and had to be recreated by hand. This repo has the same workflow and the same exposure — it simply has not been unlucky yet. A PR now builds pr-, which nothing watches. Trivy scans whatever was built either way, so a bad Dockerfile or a new CVE still blocks the merge, and the push step is unchanged — still main-only. Co-Authored-By: Claude Opus 5 (1M context) --- .gitea/workflows/build.yml | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 7f1c0a3..311aea9 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -40,7 +40,35 @@ jobs: mvn -B -ntp -s .gitea/ci-settings.xml -DskipFrontend=true verify # Build + Trivy on every run (PRs included), so a broken Dockerfile or a new HIGH/CRITICAL CVE - # blocks the merge. Only the push is main-only. + # blocks the merge. + # + # A PULL REQUEST MUST NOT TOUCH THE :latest TAG. This runner builds on the host's Docker daemon — + # the same daemon the live container runs on — so retagging :latest IS a deployment: Watchtower + # compares the running container's image against :latest, finds they differ, and recreates the + # container from the locally built image. Gating only the push is not enough; the build is the + # deploy. + # + # The damage is not only that unmerged code ships. Reassigning :latest leaves the running + # container's old image untagged, and once that image is pruned Watchtower can no longer read it + # to compare against, so it gives up every cycle: + # + # Failed to retrieve container image info: No such image: sha256:… + # Unable to update container "/": no available image info. + # + # That happened to bennett-portfolio on 2026-07-27: it sat on a four-day-old build, failing to + # update 720 times in twenty-four hours, reporting healthy throughout. It had to be recreated by + # hand. This repo has the same workflow and the same exposure. + # + # So a PR builds pr-, which nothing watches. Trivy scans whatever was built, and the push + # step below still only runs off a PR. + - name: Choose the image tag + run: | + if [ "${{ github.event_name }}" = "pull_request" ]; then + echo "IMAGE_TAG=pr-${{ github.event.number }}" >> "$GITHUB_ENV" + else + echo "IMAGE_TAG=latest" >> "$GITHUB_ENV" + fi + - name: Build image env: DOCKER_BUILDKIT: "1" @@ -51,17 +79,17 @@ jobs: --secret id=maven_user,env=MAVEN_USER \ --secret id=maven_token,env=MAVEN_TOKEN \ --build-arg GIT_SHA=${{ github.sha }} \ - -t git.thebennett.net/reformedwitness/confessions-of-grace:latest -t git.thebennett.net/reformedwitness/confessions-of-grace:${{ github.sha }} . + -t "git.thebennett.net/reformedwitness/confessions-of-grace:$IMAGE_TAG" -t git.thebennett.net/reformedwitness/confessions-of-grace:${{ github.sha }} . - name: Scan image (Trivy) run: | docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \ aquasec/trivy:latest image --scanners vuln --severity HIGH,CRITICAL --ignore-unfixed --no-progress \ - git.thebennett.net/reformedwitness/confessions-of-grace:latest || true + "git.thebennett.net/reformedwitness/confessions-of-grace:$IMAGE_TAG" || true docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \ aquasec/trivy:latest image --scanners vuln --severity HIGH,CRITICAL --ignore-unfixed \ --pkg-types library --exit-code 1 --no-progress \ - git.thebennett.net/reformedwitness/confessions-of-grace:latest + "git.thebennett.net/reformedwitness/confessions-of-grace:$IMAGE_TAG" # Publish only on a real push to main (or manual dispatch) — never from a pull request. - name: Push image