From 77a5befd53efd4d94d3d1ef01b0fa7d425cfdf50 Mon Sep 17 00:00:00 2001 From: roboomp Date: Sun, 14 Jun 2026 23:43:17 +0000 Subject: [PATCH] fix(ci): authenticate gh release lookup and fail loud on lookup error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .github/workflows/ci.yml | 7 +++++++ scripts/ci-release-notes.ts | 34 ++++++++++++++++++++-------------- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1a7f56b1a..f556c9a78 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/scripts/ci-release-notes.ts b/scripts/ci-release-notes.ts index 8e69ab781..7a663e9b8 100755 --- a/scripts/ci-release-notes.ts +++ b/scripts/ci-release-notes.ts @@ -188,13 +188,18 @@ async function loadPackageName(pkgDir: string): Promise { /** * 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 { const override = process.env.OMP_RELEASE_NOTES_FLOOR; @@ -207,21 +212,22 @@ async function resolvePublishedFloorTag(targetVersion: string): Promise) .filter(t => t.isDraft !== true && t.isPrerelease !== true) .map(t => (typeof t.tagName === "string" ? t.tagName : ""))