From 7dc5445de1fcfef053203416b580e04e36e40d3d Mon Sep 17 00:00:00 2001 From: Theo Ephraim Date: Tue, 26 May 2026 23:52:04 -0700 Subject: [PATCH 1/2] fix: use BUMPY_GH_TOKEN for GitHub release creation GitHub releases created with the default GITHUB_TOKEN don't trigger downstream workflows. Now gh release create calls use BUMPY_GH_TOKEN when available, matching the existing pattern for PR and push operations. Also adds token redaction to error messages in both withReleaseToken and withPatToken to prevent leakage in CI logs. --- .github/workflows/on-release.yaml | 15 +++++++++ packages/bumpy/src/commands/ci.ts | 4 +++ packages/bumpy/src/core/github-release.ts | 37 +++++++++++++++++++---- 3 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/on-release.yaml diff --git a/.github/workflows/on-release.yaml b/.github/workflows/on-release.yaml new file mode 100644 index 0000000..cb82e4f --- /dev/null +++ b/.github/workflows/on-release.yaml @@ -0,0 +1,15 @@ +name: On Release + +on: + release: + types: [published] + +jobs: + verify: + runs-on: ubuntu-latest + steps: + - run: echo "Release event triggered successfully!" + - run: | + echo "Release tag: ${{ github.event.release.tag_name }}" + echo "Release name: ${{ github.event.release.name }}" + echo "Created by: ${{ github.event.release.author.login }}" diff --git a/packages/bumpy/src/commands/ci.ts b/packages/bumpy/src/commands/ci.ts index 13ee394..c2cf9c7 100644 --- a/packages/bumpy/src/commands/ci.ts +++ b/packages/bumpy/src/commands/ci.ts @@ -31,6 +31,10 @@ async function withPatToken(fn: () => Promise): Promise { process.env.GH_TOKEN = token; try { return await fn(); + } catch (err) { + // Redact token from error messages to prevent leakage in CI logs + const msg = err instanceof Error ? err.message : String(err); + throw new Error(msg.replaceAll(token, '***')); } finally { if (originalGhToken !== undefined) { process.env.GH_TOKEN = originalGhToken; diff --git a/packages/bumpy/src/core/github-release.ts b/packages/bumpy/src/core/github-release.ts index 18c7add..99b0b34 100644 --- a/packages/bumpy/src/core/github-release.ts +++ b/packages/bumpy/src/core/github-release.ts @@ -10,6 +10,35 @@ function getHeadSha(rootDir: string): string | null { return tryRunArgs(['git', 'rev-parse', 'HEAD'], { cwd: rootDir }); } +/** + * Run an async function with BUMPY_GH_TOKEN as GH_TOKEN if available. + * + * GitHub releases created with the default GITHUB_TOKEN won't trigger + * downstream workflows. Using BUMPY_GH_TOKEN (a PAT or App token) + * allows `release` events to fire follow-up workflows. + * + * Any errors are scrubbed so the token never appears in CI logs. + */ +async function withReleaseToken(fn: () => Promise): Promise { + const token = process.env.BUMPY_GH_TOKEN; + if (!token) return fn(); + const original = process.env.GH_TOKEN; + process.env.GH_TOKEN = token; + try { + return await fn(); + } catch (err) { + // Redact token from error messages to prevent leakage in CI logs + const msg = err instanceof Error ? err.message : String(err); + throw new Error(msg.replaceAll(token, '***')); + } finally { + if (original !== undefined) { + process.env.GH_TOKEN = original; + } else { + delete process.env.GH_TOKEN; + } + } +} + export interface GitHubReleaseOptions { dryRun?: boolean; title?: string; @@ -46,9 +75,7 @@ export async function createIndividualReleases( // Use --target so gh can create the tag on the remote if it wasn't pushed yet const args = ['gh', 'release', 'create', tag, '--title', title, '--notes', body]; if (headSha) args.push('--target', headSha); - await runArgsAsync(args, { - cwd: rootDir, - }); + await withReleaseToken(() => runArgsAsync(args, { cwd: rootDir })); log.dim(` Created GitHub release: ${title}`); } catch (err) { log.warn(` Failed to create GitHub release for ${tag}: ${err instanceof Error ? err.message : err}`); @@ -91,9 +118,7 @@ export async function createAggregateRelease( const headSha = getHeadSha(rootDir); const args = ['gh', 'release', 'create', tag, '--title', title, '--notes', body]; if (headSha) args.push('--target', headSha); - await runArgsAsync(args, { - cwd: rootDir, - }); + await withReleaseToken(() => runArgsAsync(args, { cwd: rootDir })); log.success(`Created aggregate GitHub release: ${title}`); } catch (err) { log.warn(`Failed to create aggregate GitHub release: ${err instanceof Error ? err.message : err}`); From 4c43aef77fa9b2c3e5b67dfabb01710fd6492055 Mon Sep 17 00:00:00 2001 From: Theo Ephraim Date: Tue, 26 May 2026 23:52:41 -0700 Subject: [PATCH 2/2] chore: add bump file --- .bumpy/use-pat-for-releases.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .bumpy/use-pat-for-releases.md diff --git a/.bumpy/use-pat-for-releases.md b/.bumpy/use-pat-for-releases.md new file mode 100644 index 0000000..ce54174 --- /dev/null +++ b/.bumpy/use-pat-for-releases.md @@ -0,0 +1,5 @@ +--- +'@varlock/bumpy': patch +--- + +Use `BUMPY_GH_TOKEN` for GitHub release creation so releases trigger downstream workflows. Also adds token redaction to error messages in `withPatToken` and the new `withReleaseToken` helper to prevent leakage in CI logs.