From 7c58ba2617ef652bc83eba2b5e0eb9fa67430d45 Mon Sep 17 00:00:00 2001 From: "Chelsea Kelly-Reif (she/her)" Date: Thu, 10 Sep 2026 13:38:30 -0700 Subject: [PATCH] release-authorize: fetch the tag with the checkout, not after it The authorize job checks out with `persist-credentials: false`, which is right: this workflow should not leave a credential behind for later steps. The consequence was unnoticed because every repository that had ever released through it is public. `git fetch --force origin refs/tags/` runs against an origin with no credentials. A public caller is served anonymously and it works. A private caller is asked for a username, and with no terminal the job dies: fatal: could not read Username for 'https://github.com': No such device or address Measured across all 17 callers of this workflow: the six repositories that have published a release through it are public (ctdl-validate 3, fhir-scorecard 2, outcome-receipts 2, transit-delivery-atlas 2, exitdrill 1, oscal-validate 1). Both private callers -- self-osint-monitor and govchat-eval -- have published none. The path has never once run against a private repository. `actions/checkout` authenticates its own fetch regardless of `persist-credentials`, which controls only whether the credential is written into `.git/config` afterwards. So `fetch-tags: true` brings the tag down over the authenticated fetch, and the unauthenticated network call goes away rather than being papered over with a token. Every assertion after it is unchanged: annotated-tag object, ancestor of main, allowed_signers present, `git verify-tag`. A tag that is genuinely absent now fails with a message saying to push it, instead of a git credential prompt. --- .github/workflows/release-authorize.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-authorize.yml b/.github/workflows/release-authorize.yml index 50eeaa8..6d9a12f 100644 --- a/.github/workflows/release-authorize.yml +++ b/.github/workflows/release-authorize.yml @@ -45,6 +45,11 @@ jobs: with: ref: main fetch-depth: 0 + # The tag has to arrive with this checkout, because the checkout is the + # only step here that is authenticated. A later `git fetch` runs against + # an origin with no credentials: a public caller is served anonymously, + # a private one is asked for a username and the job dies. + fetch-tags: true persist-credentials: false - name: Resolve and verify the reviewed signed tag @@ -64,7 +69,11 @@ jobs: exit 1 fi - git fetch --force origin "refs/tags/${TAG}:refs/tags/${TAG}" + if ! git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then + echo "::error::Release tag '${TAG}' was not fetched with the checkout." \ + "Push the tag before dispatching the release." + exit 1 + fi if [ "$(git cat-file -t "refs/tags/${TAG}")" != tag ]; then echo "::error::Release tag '${TAG}' must be an annotated tag object." exit 1