fix(ci): make release preflight accept the Actions installation token#121
fix(ci): make release preflight accept the Actions installation token#121
Conversation
Adding .gitkeep for PR creation (default mode). This file will be removed when the task is complete. Issue: #120
…oken `scripts/preflight-credentials.mjs` validated the token with `gh api user --jq .login`. That endpoint requires a *user* identity, so the auto-issued `secrets.GITHUB_TOKEN` (an installation token) returned HTTP 403 "Resource not accessible by integration", and both release pipelines failed at preflight on every push to `main` after PR #119 (runs 25282945820 and 25282945817). Switch the probe to `gh api repos/${GITHUB_REPOSITORY}` which works for both the installation token and personal access tokens (it only needs `metadata: read`, already granted by every workflow). Fall back to `gh auth status` when running locally without `GITHUB_REPOSITORY`. Also annotate `crates-io` HTTP 404 as "package not yet published" so a real lookup miss is not silently reported as `ok`. Switch from `execSync` to `execFileSync` so the repo slug is passed as a literal argv element rather than interpolated into a shell string. See `docs/case-studies/issue-120/root-cause.md`.
The Rust run cited in issue #120 was littered with the GitHub deprecation annotation: "Node.js 20 actions are deprecated...". GitHub's escape hatch (and the path `link-assistant/hive-mind` already adopted) is the runner-level env `FORCE_JAVASCRIPT_ACTIONS_TO_NODE24`, which makes JavaScript-based actions execute on the Node 24 runtime that the runner ships with anyway. Add the env at workflow scope in `.github/workflows/js.yml` and append it to the existing top-level env block in `.github/workflows/rust.yml`.
Captures the full investigation behind the preflight-credentials fix: - `README.md` — index + TL;DR. - `timeline.md` — when each piece of evidence appeared. - `requirements.md` — every explicit and implicit ask in #120 mapped to the change (or the upstream report) that addresses it. - `root-cause.md` — three root causes (RC-1 token kind, RC-2 crates.io 404 mislabel, RC-3 release short-circuit hides credential issues), each with quoted log lines. - `solutions.md` — chosen endpoint, alternatives ruled out, prior art. - `comparison-with-templates.md` — diff against the four `link-foundation/*-pipeline-template` repos and `link-assistant/hive-mind`. - `ci-logs/` — preserved `gh run view --log-failed` dumps for the two failing runs cited in the issue. - `issue-data.json`, `recent-runs.json` — gh API snapshots.
`experiments/preflight-token-validation.mjs` writes a fake `gh` to a
temp directory that mimics the Actions installation token: `gh api user`
exits with HTTP 403, `gh api repos/<slug>` exits 0, `gh auth status`
exits 0. It then runs `scripts/preflight-credentials.mjs --require
gh-token` against that fake `gh` and asserts the script now passes.
Run with: `node experiments/preflight-token-validation.mjs`.
`experiments/test-token-checks.sh` is a tiny manual probe — point it
at any token / runner and it prints which of `gh api user`, `gh api
meta`, and `gh api repos/{owner}/{repo}` succeed. Useful when
diagnosing a future "the token doesn't have what I expected" report.
Working session summaryPR #121 is updated, ready for review, and mergeable: #121 What changed:
Verified locally: This summary was automatically extracted from the AI working session output. |
🤖 Solution Draft LogThis log file contains the complete execution trace of the AI solution draft process. 💰 Cost: $3.751069📊 Context and tokens usage:Claude Opus 4.7: (2 sub-sessions)
Total: (1.9K new + 144.9K cache writes + 3.9M cache reads) input tokens, 36.4K output tokens, $3.751069 cost 🤖 Models used:
📎 Log file uploaded as Gist (1784KB)Now working session is ended, feel free to review and add any feedback on the solution draft. |
✅ Ready to mergeThis pull request is now ready to be merged:
Monitored by hive-mind with --auto-restart-until-mergeable flag |
This reverts commit 5e9ac2f.
Fixes #120.
Summary
The release jobs in both pipelines failed at the Preflight credential checks step on every push to
mainafter PR #119:(Failing runs: Rust 25282945820, JS 25282945817.)
scripts/preflight-credentials.mjsvalidated the token by callinggh api user. That endpoint requires a user identity. The auto-issuedsecrets.GITHUB_TOKENis an installation token without a user identity — calling/userreturns HTTP 403. Personal access tokens happen to work, which is why the script passed every sanity-test the author ran locally and on PR runs (which used a different code path; release jobs only run on push tomain).The fix swaps the probe to
gh api repos/${GITHUB_REPOSITORY}which works for both kinds of token (it only needsmetadata: read, already granted by every workflow). WhenGITHUB_REPOSITORYis missing (running locally) the script falls back togh auth status.Changes
scripts/preflight-credentials.mjsto validate the GH token with an endpoint that works for both installation tokens and PATs. Switched fromexecSynctoexecFileSyncso the repo slug is passed as a literal arg. Annotatedcrates.ioHTTP 404 as "package not yet published" so a real lookup miss is not silently reported asok..github/workflows/{js,rust}.yml: setFORCE_JAVASCRIPT_ACTIONS_TO_NODE24: trueto silence the "Node.js 20 actions are deprecated" annotation that fired on every step in run 25282945820 (the same env var is already used inlink-assistant/hive-mind).docs/case-studies/issue-120/capturing timeline, requirements mapping, three root causes, alternatives considered, comparison against the fourlink-foundation/*-pipeline-templaterepos andlink-assistant/hive-mind, and the rawgh run view --log-faileddumps for the two cited runs.experiments/preflight-token-validation.mjsthat writes a fakeghmimicking the installation token (gh api user→ HTTP 403;gh api repos/<slug>→ 200) and runs the preflight script against it. Passes after the fix; would have failed before.Trusted publishing & token preservation
npm-oidccheck still validatesACTIONS_ID_TOKEN_REQUEST_URL/TOKEN, which is the OIDC contractnpm publish --provenancerelies on. No npm tokens are introduced.secrets.GITHUB_TOKENonly. The fix actually makes the token check correct for that token; the previous code rejected the very token the workflow was supposed to use.How to reproduce / verify
node experiments/preflight-token-validation.mjs # → ✓ preflight succeeded against installation-token-style ghWithout the fix the same script reports
Preflight check failed (gh-token)because the fakegh api userreturns HTTP 403, exactly mirroring the runner.Test plan
node experiments/preflight-token-validation.mjspasses after the fix and would have failed before.node -c scripts/preflight-credentials.mjs..github/workflows/{js,rust}.ymlandscripts/preflight-credentials.mjs.mainruns bothRelease on npmandRelease on crates.iopast the preflight step (will be confirmed when this PR merges; the change is gated byif: github.ref == 'refs/heads/main').Follow-up reported separately
Two template-shaped follow-ups are tracked in
docs/case-studies/issue-120/comparison-with-templates.md(preflight pattern not yet present in the fourlink-foundation/*-pipeline-templaterepos; Node 24 force-flag worth backporting).