From b0ad6e2ae49dd0deab6b4f49f157b44a3c07cbff Mon Sep 17 00:00:00 2001 From: Theo Ephraim Date: Tue, 21 Apr 2026 00:38:23 -0700 Subject: [PATCH 1/3] Fix force-push guard: compare against config.baseBranch instead of HEAD The guard was comparing the push target against HEAD, but pushWithToken is called after checking out the version PR branch, so HEAD === branch always triggered the error. Use config.baseBranch instead. Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/bumpy/src/commands/ci.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/bumpy/src/commands/ci.ts b/packages/bumpy/src/commands/ci.ts index 6b8006c..bd07b2d 100644 --- a/packages/bumpy/src/commands/ci.ts +++ b/packages/bumpy/src/commands/ci.ts @@ -228,10 +228,9 @@ async function autoPublish(rootDir: string, config: BumpyConfig, tag?: string): * When only the default `GITHUB_TOKEN` is available the push still succeeds, * but PR workflows won't be triggered automatically. */ -function pushWithToken(rootDir: string, branch: string): void { +function pushWithToken(rootDir: string, branch: string, config: BumpyConfig): void { // Guard against misconfigured versionPr.branch pointing at the base branch - const baseBranch = tryRunArgs(['git', 'rev-parse', '--abbrev-ref', 'HEAD'], { cwd: rootDir }); - if (branch === baseBranch || branch === 'main' || branch === 'master') { + if (branch === config.baseBranch || branch === 'main' || branch === 'master') { throw new Error(`Refusing to force-push to "${branch}" — this looks like a base branch, not a version PR branch`); } @@ -347,7 +346,7 @@ async function createVersionPr( const commitMsg = ['Version packages', '', ...plan.releases.map((r) => `${r.name}@${r.newVersion}`)].join('\n'); runArgs(['git', 'commit', '-F', '-'], { cwd: rootDir, input: commitMsg }); - pushWithToken(rootDir, branch); + pushWithToken(rootDir, branch, config); // Create or update PR const prBody = formatVersionPrBody(plan, config.versionPr.preamble, packageDirs); @@ -377,7 +376,7 @@ async function createVersionPr( // `pull_request: synchronize` event is generated and CI workflows trigger. // (The initial push happened before the PR existed, and the PR creation // event from GITHUB_TOKEN doesn't trigger workflows.) - pushWithToken(rootDir, branch); + pushWithToken(rootDir, branch, config); } } From 74f61d0216f438fc4c9c0ba41276c028f3eb3cb9 Mon Sep 17 00:00:00 2001 From: Theo Ephraim Date: Tue, 21 Apr 2026 00:40:19 -0700 Subject: [PATCH 2/3] Add bump file and pre-push hook for bumpy check Co-Authored-By: Claude Opus 4.6 (1M context) --- .bumpy/fix-force-push-guard.md | 5 +++++ lefthook.yml | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 .bumpy/fix-force-push-guard.md diff --git a/.bumpy/fix-force-push-guard.md b/.bumpy/fix-force-push-guard.md new file mode 100644 index 0000000..ba477f1 --- /dev/null +++ b/.bumpy/fix-force-push-guard.md @@ -0,0 +1,5 @@ +--- +'@varlock/bumpy': patch +--- + +Fix force-push guard that was incorrectly comparing against HEAD instead of the configured base branch diff --git a/lefthook.yml b/lefthook.yml index 5185cd5..2572488 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -8,3 +8,8 @@ pre-commit: glob: '*.{js,ts,tsx,jsx,json,css,md,yml,yaml}' run: bunx oxfmt {staged_files} stage_fixed: true + +pre-push: + jobs: + - name: bumpy-check + run: bunx @varlock/bumpy check From 4d4ec5e817944f8d492636349c88a244057406a5 Mon Sep 17 00:00:00 2001 From: Theo Ephraim Date: Tue, 21 Apr 2026 00:42:10 -0700 Subject: [PATCH 3/3] Suppress no-control-regex lint warning in package name validation Split the regex and add an inline disable comment for the intentional control character check. Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/bumpy/src/core/bump-file.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/bumpy/src/core/bump-file.ts b/packages/bumpy/src/core/bump-file.ts index 782ba7f..f37ec5d 100644 --- a/packages/bumpy/src/core/bump-file.ts +++ b/packages/bumpy/src/core/bump-file.ts @@ -16,8 +16,11 @@ const VALID_BUMP_TYPES = new Set(['major', 'minor', 'patch', 'none']); */ function validatePackageName(name: string): boolean { if (!name || name.length > 214) return false; - // disallow control chars, HTML/shell metacharacters, whitespace - if (/[\u0000-\u001f\u007f<>"'`&;|$(){}[\]\\!#%\s]/.test(name)) return false; + // disallow control chars + // eslint-disable-next-line no-control-regex + if (/[\u0000-\u001f\u007f]/.test(name)) return false; + // disallow HTML/shell metacharacters and whitespace + if (/[<>"'`&;|$(){}[\]\\!#%\s]/.test(name)) return false; // must not start with - (could be interpreted as a CLI flag) if (name.startsWith('-')) return false; return true;