fix(ci): preflight release credentials and recover from an orphan tag
All checks were successful
PR Checks / rust-gates (pull_request) Successful in 5s

servuo-plugins hit both of these on its first real release run; this repo
runs the same engine, so it has the same two defects latent.

REGISTRY_USER / REGISTRY_TOKEN were empty there, yet the tag push
SUCCEEDED: actions/checkout leaves an `http.<host>.extraheader` credential
in the local git config, so `git remote set-url` to a URL with empty
credentials still authenticated through that leftover header. The release
API call had no such fallback and returned 401. Net result was the worst
available outcome — the repo tagged, no release, and a failed job.

Two fixes:

  A credential preflight, before anything is built or pushed, gated on the
  run actually intending to publish so a docs:/chore:-only merge (or this
  repo's pre-crate no-op) still passes on a repo with no secrets. It names
  the missing secrets and the scope they need instead of failing wherever
  they happen to be used first.

  Orphan-tag recovery. A tag with no release behind it means an earlier run
  died after tagging, and the old code treated any existing tag as "nothing
  to release" — so that state could never clear itself: every later run
  would see the tag and stand down, forever. The plan step now asks the API
  whether a release exists for the tag, and on 404 reuses the tag and
  publishes the release it is missing. This deliberately overrides the
  RELEASE=false the bump logic just decided, which is the whole point —
  with the tag in place there are no releasable commits after it.

  Anything other than 200/404 (network failure, bad token) is refused
  rather than guessed, since assuming "no release" would republish over a
  good one.

  The tag step now reuses an existing tag instead of failing on `git tag`,
  and the changelog for a recovery run summarizes what the tag contains
  (previous-tag..this-tag) rather than the empty range after it.

sync-project-tree gets the same preflight: its first run on main failed
with an opaque `git clone` error against `https://:@host/...` that said
nothing about a missing secret.

Verified by extracting every run block and exercising the paths: empty
secrets fail the preflight with a legible message and populated ones pass;
the no-Cargo.toml guard still short-circuits to release=false; a crate with
no tag still takes the seed path; and against real repo state, a tag with a
release stands down while an orphan tag recovers.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-04 10:14:47 -05:00
parent 71a9210503
commit 60ae6f1f75
2 changed files with 110 additions and 12 deletions

View File

@@ -58,6 +58,24 @@ jobs:
echo "----- generated ${DOCS_PATH} -----"
cat _sync/PROJECT_TREE.md
# Checked explicitly because the failure mode otherwise is a `git clone`
# against `https://:@host/...`, whose error says nothing about a missing
# secret. This workflow's first run on `main` failed exactly that way.
- name: Verify docs-repo credentials are configured
env:
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
run: |
set -euo pipefail
MISSING=""
[ -n "$(printf '%s' "${REGISTRY_USER:-}" | tr -d '\r\n')" ] || MISSING="${MISSING} REGISTRY_USER"
[ -n "$(printf '%s' "${REGISTRY_TOKEN:-}" | tr -d '\r\n')" ] || MISSING="${MISSING} REGISTRY_TOKEN"
if [ -n "$MISSING" ]; then
echo "::error::Missing Actions secret(s):${MISSING}. Set them under Settings → Actions → Secrets on ${SELF_REPO}. The token needs read/write on ${DOCS_REPO} to push the branch and open the PR."
exit 1
fi
echo "Docs-repo credentials present."
- name: Open or update the docs PR if the tree changed
env:
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}