From e368b82f0a45ce1921362f58afc7b11fb7020348 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Fri, 7 Aug 2026 08:23:11 -0400 Subject: [PATCH] ci(release): fail loudly when a tag release ends up without its APK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v2026.08.07 had to be re-cut, and the tag build's android-release job never started — no job log was written at all, so all eight steps reported `failure` with none executed and image-release showed `skipped`. The run was red, but the release PAGE rendered fine and main's own push build had already moved :latest, so the code was deployable and nothing looked obviously wrong. What was actually missing — the attached APK and the immutable :vYYYY.MM.DD image — is easy to skim past, and I nearly did. This cannot prevent that. The cause was a runner failing to launch a container, not anything in this file, and it did not reproduce on an unchanged re-run. What this does is make the CONSEQUENCE legible: an incomplete release now fails with a named error instead of eight mystery step failures, and the message says to re-run the run rather than delete and re-create the tag. `if: always()` is load-bearing — the job has to report precisely when the jobs above did not succeed. Correcting the record while here: I first blamed this on the workflow's `cancel-in-progress` concurrency block. That was wrong. Cancellation needs a NEWER run in the same group, and there was exactly one run on the tag ref (total_count 632 -> 633 on release creation); the main-push runs sit in a different group. Plausible mechanism, unchecked precondition. Validated locally: YAML parses, `bash -n` clean, and the asset-parsing logic unit-checked against a release with an APK, one with no assets, and one with a non-APK asset. --- .gitea/workflows/release.yml | 55 ++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 577b5b45..8d1dc317 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -322,3 +322,58 @@ jobs: docker buildx build \ --build-arg MINSTREL_VERSION="${{ steps.tags.outputs.version }}" \ --push ${{ steps.tags.outputs.args }} . + + # Verifies a tag release actually ended up complete, and names the specific + # thing that's missing if not. + # + # Added 2026-08-07 after v2026.08.07 was re-cut. The android-release job never + # started — no log was written at all — so all eight of its steps reported + # `failure` with none executed and image-release showed `skipped`. The run was + # red, but the *release page rendered fine*, and `main`'s own push build had + # already moved `:latest`, so the code was deployable and nothing looked + # obviously wrong. The release was simply missing its APK and its immutable + # `:vYYYY.MM.DD` image, which is easy to skim past. + # + # This job cannot prevent that (the cause was a runner failing to launch, not + # anything in this file). What it does is turn an incomplete release into an + # explicit, named error instead of eight mystery step failures — so the + # consequence is legible without having to infer it. + # + # `if: always()` is the whole point: it has to report precisely when the jobs + # above did NOT succeed. + verify-release: + name: Verify release artifacts (tag releases only) + needs: [android-release, image-release] + if: ${{ always() && startsWith(github.ref, 'refs/tags/v') }} + runs-on: go-ci + container: + image: git.fabledsword.com/bvandeusen/ci-go:1.26 + + steps: + - name: Release must have an APK attached + shell: bash + env: + CI_TOKEN: ${{ secrets.CI_TOKEN }} + run: | + set -euo pipefail + TAG="${GITHUB_REF#refs/tags/}" + REPO="${GITHUB_REPOSITORY}" + + REL_JSON="$(curl -fsSL \ + -H "Authorization: token ${CI_TOKEN}" \ + "https://git.fabledsword.com/api/v1/repos/${REPO}/releases/tags/${TAG}" || true)" + if [ -z "${REL_JSON}" ]; then + echo "::error::no release found for ${TAG} — the tag exists but nothing was published" + exit 1 + fi + + APK="$(printf '%s' "${REL_JSON}" \ + | grep -oP '"browser_download_url":\s*"\K[^"]+' \ + | grep -E '\.apk$' | head -1 || true)" + if [ -z "${APK}" ]; then + echo "::error::release ${TAG} has NO APK attached — in-app update will offer nothing, and the bundled-APK path on future :latest builds has no source." + echo "::error::Fix by RE-RUNNING this workflow run. Do NOT delete and re-create the tag; if it fails again the runner never started the container, and the evidence is in act_runner on the host (Gitea will hold no job log)." + exit 1 + fi + + echo "::notice::${TAG} verified — APK attached: ${APK}"