diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index ba2bc9b..b1c14d8 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -37,6 +37,16 @@ env: jobs: build: name: Build ${{ matrix.image }} + # release.yml pushes the release commit and its tag in a single `git push`, + # so Gitea creates two runs for the same commit: one for master, one for the + # tag. Only the tag run matters — it is the one that emits the X.Y.Z / X.Y + # image tags, and it publishes `latest` and `sha-` too, since it is + # the same commit. Skip the branch run rather than racing or cancelling it. + # Ordinary pushes to master (any message but `chore(release):`) still build. + if: >- + github.event_name != 'push' || + startsWith(github.ref, 'refs/tags/') || + !startsWith(github.event.head_commit.message, 'chore(release):') runs-on: docker container: image: docker:27-dind diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 5d8e2e6..2d6a25b 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -191,12 +191,26 @@ jobs: const tag = `v${process.env.VERSION}`; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); + // The master push and the tag push carry the SAME commit, so a sha + // match alone is not enough: build.yml skips the master run by + // design, and that skipped run would satisfy a sha-only check even + // if the tag run were never created. When the API reports a ref for + // the run, require it to be the tag; when it reports none, fall back + // to the sha match rather than failing a release over a field name. + const isTagRun = (r) => { + const ref = r.head_branch || r.ref || ""; + return !ref || ref === tag || ref === `refs/tags/${tag}`; + }; + const started = async () => { const res = await fetch(`${base}/actions/runs?limit=30`, { headers }); if (!res.ok) throw new Error(`runs query failed: HTTP ${res.status}`); const body = await res.json(); return (body.workflow_runs || []).some( - (r) => r.head_sha === sha && String(r.path || "").includes("build.yml"), + (r) => + r.head_sha === sha && + String(r.path || "").includes("build.yml") && + isTagRun(r), ); };