fix(ci): authenticate gh release lookup and fail loud on lookup error
Two issues caught in review on #2597: 1. `gh release list` in GitHub Actions requires GH_TOKEN. The release notes step in `.github/workflows/ci.yml` had no env block, so gh would exit non-zero and the script's silent fallback would re-strand the silent-tag entries this change is meant to recover. Pass `GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}` to the step. 2. Silently degrading to legacy single-version output on gh failure is itself the regression vector — a future token misconfig or gh outage would lose data with no signal. `resolvePublishedFloorTag` now throws on gh failure with an actionable hint ("pass GH_TOKEN in Actions; set OMP_RELEASE_NOTES_FLOOR= locally to opt into legacy mode"). The thrown error propagates out of `main` and exits non-zero, failing the CI step loudly so the release is rebuilt with the fix. The legitimate null path is preserved: `OMP_RELEASE_NOTES_FLOOR=` (empty) still forces single-version mode, and a successful gh call with no candidate < target still returns null (first-ever publish case). Verified locally: hiding gh from PATH now exits 1 with the hint; `OMP_RELEASE_NOTES_FLOOR=` with hidden gh still produces the legacy 84-bullet single-version output. Refs #2596
This commit is contained in:
@@ -649,6 +649,13 @@ jobs:
|
||||
with:
|
||||
bun-version: "1.3"
|
||||
- name: Generate release notes from CHANGELOGs
|
||||
env:
|
||||
# `gh release list` (used to find the latest published GitHub
|
||||
# Release tag below the target so silent-tag changelog sections
|
||||
# roll forward — #2596) requires GH_TOKEN in Actions. Without
|
||||
# it gh exits non-zero and the script would degrade to legacy
|
||||
# single-version notes, defeating the recovery.
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: bun scripts/ci-release-notes.ts ${{ needs.release_metadata.outputs.release-tag }}
|
||||
- name: Download release binaries
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
|
||||
+20
-14
@@ -188,13 +188,18 @@ async function loadPackageName(pkgDir: string): Promise<string> {
|
||||
|
||||
/**
|
||||
* Resolve the highest published, non-prerelease, non-draft semver tag strictly
|
||||
* below `targetVersion`. Falls back to `null` on error or no candidate, which
|
||||
* downgrades the script to legacy single-version extraction.
|
||||
* below `targetVersion` via `gh release list`.
|
||||
*
|
||||
* `OMP_RELEASE_NOTES_FLOOR` env override:
|
||||
* - unset → query `gh` (CI default)
|
||||
* - `vX.Y.Z` → use as-is (manual rerun: "I know the floor")
|
||||
* - empty string → force single-version mode (legacy escape hatch)
|
||||
* Failure semantics:
|
||||
* - `OMP_RELEASE_NOTES_FLOOR` set → honored verbatim (`""` forces null).
|
||||
* - `gh` succeeded, no candidate < target → `null` (legitimate first-ever
|
||||
* publish; legacy single-version output is correct).
|
||||
* - `gh` itself failed (missing binary, missing `GH_TOKEN` in Actions,
|
||||
* network/auth error) → throws. Letting this degrade to single-version
|
||||
* output silently re-strands silent-tag entries (#2596 review); the CI
|
||||
* step must die loudly so the release is rebuilt with the token wired.
|
||||
* Local runs without `gh` should set `OMP_RELEASE_NOTES_FLOOR=` to opt
|
||||
* into legacy mode explicitly.
|
||||
*/
|
||||
async function resolvePublishedFloorTag(targetVersion: string): Promise<string | null> {
|
||||
const override = process.env.OMP_RELEASE_NOTES_FLOOR;
|
||||
@@ -207,21 +212,22 @@ async function resolvePublishedFloorTag(targetVersion: string): Promise<string |
|
||||
.quiet()
|
||||
.nothrow();
|
||||
if (res.exitCode !== 0) {
|
||||
console.warn(
|
||||
`Warning: gh release list failed (exit ${res.exitCode}); falling back to single-version notes. stderr: ${res.stderr.toString().trim()}`,
|
||||
const stderr = res.stderr.toString().trim();
|
||||
throw new Error(
|
||||
`gh release list exited ${res.exitCode}.\nstderr: ${stderr || "(empty)"}\n` +
|
||||
`Hint: in GitHub Actions, pass GH_TOKEN: \${{ secrets.GITHUB_TOKEN }} to this step. ` +
|
||||
`Locally without gh, set OMP_RELEASE_NOTES_FLOOR= to fall back to single-version notes.`,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
let raw: unknown;
|
||||
try {
|
||||
raw = JSON.parse(res.stdout.toString());
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
`Warning: failed to parse gh release list output: ${(err as Error).message}; falling back to single-version notes.`,
|
||||
);
|
||||
return null;
|
||||
throw new Error(`gh release list returned non-JSON output: ${(err as Error).message}`);
|
||||
}
|
||||
if (!Array.isArray(raw)) {
|
||||
throw new Error(`gh release list returned a non-array payload: ${typeof raw}`);
|
||||
}
|
||||
if (!Array.isArray(raw)) return null;
|
||||
const candidates = (raw as Array<{ tagName?: unknown; isDraft?: unknown; isPrerelease?: unknown }>)
|
||||
.filter(t => t.isDraft !== true && t.isPrerelease !== true)
|
||||
.map(t => (typeof t.tagName === "string" ? t.tagName : ""))
|
||||
|
||||
Reference in New Issue
Block a user