Skip to content

fix(ci): make release preflight accept the Actions installation token#121

Merged
konard merged 6 commits intomainfrom
issue-120-22538e16da80
May 3, 2026
Merged

fix(ci): make release preflight accept the Actions installation token#121
konard merged 6 commits intomainfrom
issue-120-22538e16da80

Conversation

@konard
Copy link
Copy Markdown
Member

@konard konard commented May 3, 2026

Fixes #120.

Summary

The release jobs in both pipelines failed at the Preflight credential checks step on every push to main after PR #119:

::error::Preflight check failed (gh-token): `gh api user` rejected the token
(HTTP error or expired). Underlying: Command failed: gh api user --jq .login

(Failing runs: Rust 25282945820, JS 25282945817.)

scripts/preflight-credentials.mjs validated the token by calling gh api user. That endpoint requires a user identity. The auto-issued secrets.GITHUB_TOKEN is an installation token without a user identity — calling /user returns 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 to main).

The fix swaps the probe to gh api repos/${GITHUB_REPOSITORY} which works for both kinds of token (it only needs metadata: read, already granted by every workflow). When GITHUB_REPOSITORY is missing (running locally) the script falls back to gh auth status.

Changes

  • Fix scripts/preflight-credentials.mjs to validate the GH token with an endpoint that works for both installation tokens and PATs. Switched from execSync to execFileSync so the repo slug is passed as a literal arg. Annotated crates.io HTTP 404 as "package not yet published" so a real lookup miss is not silently reported as ok.
  • Workflow polish in .github/workflows/{js,rust}.yml: set FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true to silence the "Node.js 20 actions are deprecated" annotation that fired on every step in run 25282945820 (the same env var is already used in link-assistant/hive-mind).
  • Case study under docs/case-studies/issue-120/ capturing timeline, requirements mapping, three root causes, alternatives considered, comparison against the four link-foundation/*-pipeline-template repos and link-assistant/hive-mind, and the raw gh run view --log-failed dumps for the two cited runs.
  • Reproduction experiment experiments/preflight-token-validation.mjs that writes a fake gh mimicking 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 trusted publishing is unchanged: the npm-oidc check still validates ACTIONS_ID_TOKEN_REQUEST_URL/TOKEN, which is the OIDC contract npm publish --provenance relies on. No npm tokens are introduced.
  • Version bump still uses secrets.GITHUB_TOKEN only. 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 gh

Without the fix the same script reports Preflight check failed (gh-token) because the fake gh api user returns HTTP 403, exactly mirroring the runner.

Test plan

  • Reproduction: node experiments/preflight-token-validation.mjs passes after the fix and would have failed before.
  • Syntax: node -c scripts/preflight-credentials.mjs.
  • Local diff review for .github/workflows/{js,rust}.yml and scripts/preflight-credentials.mjs.
  • CI: push to main runs both Release on npm and Release on crates.io past the preflight step (will be confirmed when this PR merges; the change is gated by if: 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 four link-foundation/*-pipeline-template repos; Node 24 force-flag worth backporting).

Adding .gitkeep for PR creation (default mode).
This file will be removed when the task is complete.

Issue: #120
@konard konard self-assigned this May 3, 2026
konard added 4 commits May 3, 2026 17:30
…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.
@konard konard changed the title [WIP] We need to fix CI/CD fully fix(ci): make release preflight accept the Actions installation token May 3, 2026
@konard konard marked this pull request as ready for review May 3, 2026 17:32
@konard
Copy link
Copy Markdown
Member Author

konard commented May 3, 2026

Working session summary

PR #121 is updated, ready for review, and mergeable: #121

What changed:

  • scripts/preflight-credentials.mjs:81-103 — probe repos/{owner}/{repo} instead of /user so the Actions installation GITHUB_TOKEN is accepted.
  • .github/workflows/js.yml + rust.ymlFORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true to silence the Node 20 deprecation annotation.
  • docs/case-studies/issue-120/ — full case study (timeline, requirements, root causes, solutions, template comparison, raw CI logs).
  • experiments/preflight-token-validation.mjs — reproduction with a fake gh that mimics the installation token; passes after the fix.

Verified locally: node experiments/preflight-token-validation.mjs✓ preflight succeeded against installation-token-style gh. Real-CI confirmation will only come once this lands on main (the failing release jobs are gated on push to main).


This summary was automatically extracted from the AI working session output.

@konard
Copy link
Copy Markdown
Member Author

konard commented May 3, 2026

🤖 Solution Draft Log

This 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)

  1. 115.4K / 1M (12%) input tokens, 23.4K / 128K (18%) output tokens
  2. 48.7K / 1M (5%) input tokens, 6.2K / 128K (5%) output tokens

Total: (1.9K new + 144.9K cache writes + 3.9M cache reads) input tokens, 36.4K output tokens, $3.751069 cost

🤖 Models used:

  • Tool: Anthropic Claude Code
  • Requested: opus
  • Model: Claude Opus 4.7 (claude-opus-4-7)

📎 Log file uploaded as Gist (1784KB)


Now working session is ended, feel free to review and add any feedback on the solution draft.

@konard
Copy link
Copy Markdown
Member Author

konard commented May 3, 2026

✅ Ready to merge

This pull request is now ready to be merged:

  • All CI checks have passed
  • No merge conflicts
  • No pending changes

Monitored by hive-mind with --auto-restart-until-mergeable flag

@konard konard merged commit ea5d0db into main May 3, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

We need to fix CI/CD fully

1 participant