From 96eb3e818534cd962b2ec4be8aed5f468b7abefe Mon Sep 17 00:00:00 2001 From: Tyler Koenig Date: Fri, 12 Jun 2026 17:20:48 -0400 Subject: [PATCH] fix(release): scan gates docker push, rc tags spare :latest, mirror waits for stable assets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rc.2 proved the grype gate was decorative — buildx pushed before the scan ran, so a red run still shipped the image (and rc tags moved :latest). Build amd64 locally, scan that, then run the multi-arch push from the warm builder cache. :latest now only moves on non-rc tags. mirror-release: poll until the Gitea asset count is stable across two polls (GoReleaser uploads sequentially — assets>0 could mirror a partial set) and stretch the timeout to 20 min since the release run can queue behind the Docker job on the single runner. --- .gitea/workflows/release-docker.yml | 31 ++++++++++++++++++++++------ .github/workflows/mirror-release.yml | 19 +++++++++++------ 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/.gitea/workflows/release-docker.yml b/.gitea/workflows/release-docker.yml index 98d8062..d10cea1 100644 --- a/.gitea/workflows/release-docker.yml +++ b/.gitea/workflows/release-docker.yml @@ -35,8 +35,12 @@ jobs: TAGS="lerkolabs/uptop:${TAG}" TAGS="${TAGS},lerkolabs/uptop:sha-${SHORT_SHA}" + # :latest only for real releases — rc rehearsal tags must not move it if [ "${{ github.ref_type }}" = "tag" ]; then - TAGS="${TAGS},lerkolabs/uptop:latest" + case "$TAG" in + *-*) ;; + *) TAGS="${TAGS},lerkolabs/uptop:latest" ;; + esac fi echo "tags=$TAGS" >> "$GITHUB_OUTPUT" @@ -52,6 +56,26 @@ jobs: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} + # Scan must gate the push: build amd64 locally, scan it, and only then run + # the multi-arch push (amd64 layers come from the builder cache, so the + # second build only adds the arm64 work). + - name: Build for scan (amd64, local) + uses: docker/build-push-action@v5 + with: + context: . + load: true + platforms: linux/amd64 + tags: uptop-scan:${{ steps.meta.outputs.tag }} + build-args: | + VERSION=${{ steps.meta.outputs.tag }} + COMMIT=${{ github.sha }} + BUILD_DATE=${{ github.event.head_commit.timestamp }} + + - name: Scan image for CVEs + run: | + curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin v0.114.0 + grype uptop-scan:${{ steps.meta.outputs.tag }} --fail-on critical --output table + - name: Build and push uses: docker/build-push-action@v5 with: @@ -66,11 +90,6 @@ jobs: COMMIT=${{ github.sha }} BUILD_DATE=${{ github.event.head_commit.timestamp }} - - name: Scan image for CVEs - run: | - curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin v0.114.0 - grype lerkolabs/uptop:${{ steps.meta.outputs.tag }} --fail-on critical --output table - - name: Update Docker Hub description uses: peter-evans/dockerhub-description@v4 with: diff --git a/.github/workflows/mirror-release.yml b/.github/workflows/mirror-release.yml index d25c723..8c085db 100644 --- a/.github/workflows/mirror-release.yml +++ b/.github/workflows/mirror-release.yml @@ -19,22 +19,29 @@ jobs: run: | API="https://gitea.lerkolabs.com/api/v1/repos/lerkolabs/uptop/releases/tags/${TAG}" - for i in $(seq 1 20); do + # 40 x 30s = 20 min: the Gitea release can queue behind the ~18-min + # Docker job on the single runner. Asset count must hold steady for + # two consecutive polls — GoReleaser uploads one file at a time, and + # mirroring mid-upload would publish a partial asset set. + PREV_COUNT=0 + ASSET_COUNT=0 + for i in $(seq 1 40); do if RESPONSE=$(curl -sf "$API" 2>/dev/null); then ASSET_COUNT=$(echo "$RESPONSE" | jq '.assets | length') - if [ "$ASSET_COUNT" -gt 0 ]; then - echo "Found release with $ASSET_COUNT assets" + if [ "$ASSET_COUNT" -gt 0 ] && [ "$ASSET_COUNT" -eq "$PREV_COUNT" ]; then + echo "Found release with $ASSET_COUNT assets (stable)" break fi - echo "Release exists but no assets yet... attempt $i/20" + echo "Release has $ASSET_COUNT assets (was $PREV_COUNT)... attempt $i/40" + PREV_COUNT="$ASSET_COUNT" else - echo "Waiting for Gitea release... attempt $i/20" + echo "Waiting for Gitea release... attempt $i/40" fi sleep 30 done if [ -z "$RESPONSE" ] || [ "$ASSET_COUNT" -eq 0 ]; then - echo "::error::Gitea release for ${TAG} not found or has no assets after 10 minutes" + echo "::error::Gitea release for ${TAG} not found or has no assets after 20 minutes" exit 1 fi