Skip to content

Commit 01e1e71

Browse files
ci(release): stop finalize persisting credentials so the tag push triggers tags.yaml
pull-requests-release.yaml's finalize job checked out the repo with the default persist-credentials, leaving GITHUB_TOKEN embedded as http.extraheader. Every later tag/branch push re-injects the CI app token via `git remote set-url`, but a persisted extraheader silently wins over the URL credential — so those pushes authenticated as GITHUB_TOKEN. A GITHUB_TOKEN-authenticated push creates no workflow run (GitHub anti-recursion), which is exactly why v1.6.0's stable tag never triggered tags.yaml: the generate-changelog and update-website-docs backstops never ran. Set persist-credentials: false on the finalize checkout, so the set-url app token actually authenticates the pushes — the same handling promote-rc.yaml already uses. Audited every git/gh step in the job: both tag steps set-url the app token before pushing, and Ensure-maintenance-branch / Publish-draft-release / the retag+chart steps use the app token via github-script or a separate registry login — none depends on the persisted GITHUB_TOKEN extraheader. Pin it in hack/promote-gate-contract.bats. Assisted-By: Claude <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
1 parent 936190d commit 01e1e71

3 files changed

Lines changed: 29 additions & 1 deletion

File tree

‎.github/workflows/pull-requests-release.yaml‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,15 @@ jobs:
8181
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
8282
with:
8383
fetch-depth: 0
84+
# Do NOT persist GITHUB_TOKEN as http.extraheader. Every tag/branch push
85+
# below re-injects the app token via `git remote set-url`, but a persisted
86+
# extraheader silently wins over the URL credential — so those pushes
87+
# would authenticate as GITHUB_TOKEN, and a GITHUB_TOKEN push creates no
88+
# workflow run (GitHub anti-recursion). That is exactly why v1.6.0's
89+
# stable tag triggered no tags.yaml run (the generate-changelog and
90+
# update-website-docs backstops never fired). With this false, the set-url
91+
# app token authenticates, matching how promote-rc.yaml already pushes.
92+
persist-credentials: false
8493

8594
# Create the release tag at the merge commit — write-once.
8695
#

‎docs/release.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ Reviewer checklist:
259259

260260
Fires on merge of a PR that is merged, carries the `release` label, and is authored by `cozystack-ci[bot]` (the last guard closes the "name a branch `release-X.Y.Z`, get it labelled, merge it" hole). Head branch must match `release-X.Y.Z[-suffix]`. This is where **every irreversible side effect of a promotion happens** — after the rc's mandatory E2E was checked at dispatch and the PR merged, making promotion transactional. Steps:
261261

262-
1. **Create the tag at the merge commit** (write-once). The merge commit of `Prepare release vX.Y.Z` did not exist before the PR opened, so there is nothing to move — the tag is created here for the first time. A pre-existing tag at a different commit fails the step loudly rather than being force-moved (see [Tag immutability](#tag-immutability)).
262+
1. **Create the tag at the merge commit** (write-once). The merge commit of `Prepare release vX.Y.Z` did not exist before the PR opened, so there is nothing to move — the tag is created here for the first time. A pre-existing tag at a different commit fails the step loudly rather than being force-moved (see [Tag immutability](#tag-immutability)). The checkout uses `persist-credentials: false` so this push authenticates as the **CI app** (via `git remote set-url`), not the default `GITHUB_TOKEN` — a `GITHUB_TOKEN` push creates no workflow run, so a persisted-credential push would silently skip `tags.yaml` entirely and its stable-tag backstops (`generate-changelog`, `update-website-docs`) would never fire.
263263
2. **Cut the `api/apps/v1alpha1/vX.Y.Z` Go-module tag** (write-once, stable only) at the same commit, so Go consumers of `api/apps/v1alpha1` get the release. Moved here from `tags.yaml`, whose `prepare-release` body is skipped for a promoted stable (the draft already exists).
264264
3. **Ensure the maintenance branch `release-X.Y` exists** at the tag commit. Created if missing; updated fast-forward-only — a non-fast-forward update warns and is left for a maintainer rather than being force-updated.
265265
4. **Publish the draft release**, with `docs/changelogs/vX.Y.Z.md` from the merge commit as its body (an absent or whitespace-only file warns and leaves the draft's body in place, rather than publishing blank notes). `make_latest` is computed against published-non-prerelease tags: prereleases stay `false`; tags older than the current max stay `false` (and the current max is force-restored to `latest` if necessary, so an older patch tag cut after a newer minor won't downgrade `latest`).

‎hack/promote-gate-contract.bats‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ REPO_ROOT="$(cd "$(dirname "${BATS_TEST_FILENAME:-$0}")/.." && pwd)"
88
PROMOTE="$REPO_ROOT/.github/workflows/promote-rc.yaml"
99
PULL_REQUESTS="$REPO_ROOT/.github/workflows/pull-requests.yaml"
1010
TAGS="$REPO_ROOT/.github/workflows/tags.yaml"
11+
FINALIZE="$REPO_ROOT/.github/workflows/pull-requests-release.yaml"
1112

1213
job_block() {
1314
awk -v job=" $1:" '
@@ -217,3 +218,21 @@ code_lines() {
217218
grep -qF 'promote-rc.yaml::website-docs' "$TAGS"
218219
grep -qiF 'backstop' "$TAGS"
219220
}
221+
222+
@test "finalize checkout does not persist credentials so the app-token tag push triggers tags.yaml" {
223+
block="$(job_block finalize "$FINALIZE")"
224+
[ -n "$block" ]
225+
checkout="$(printf '%s\n' "$block" | awk '
226+
/^ - name: Checkout repo$/ { inside = 1; next }
227+
/^ - name: / { inside = 0 }
228+
inside')"
229+
[ -n "$checkout" ]
230+
231+
# The one-line root-cause fix. Without persist-credentials:false the checkout
232+
# persists GITHUB_TOKEN as http.extraheader, which silently defeats the app token
233+
# each later `git remote set-url` injects onto the tag pushes — and a
234+
# GITHUB_TOKEN-authenticated push creates no workflow run (anti-recursion), so
235+
# tags.yaml's stable-tag backstops never fire (v1.6.0's tag never triggered it).
236+
count="$(printf '%s\n' "$checkout" | code_lines | rg -cF 'persist-credentials: false' || true)"
237+
[ "${count:-0}" -eq 1 ]
238+
}

0 commit comments

Comments
 (0)