( rootDir: string, plan: ReleasePlan, config: BumpyConfig, packageDirs: Map<string, string>, branchName?: string, )
| 721 | * Push a branch to origin, optionally using a custom GitHub token. |
| 722 | * |
| 723 | * When `BUMPY_GH_TOKEN` is set, the remote URL is temporarily rewritten to |
| 724 | * include the token. Pushes made with a PAT or GitHub App token bypass |
| 725 | * GitHub's anti-recursion guard, allowing `pull_request` workflows to fire |
| 726 | * on the version PR — no extra CI configuration required. |
| 727 | * |
| 728 | * When only the default `GITHUB_TOKEN` is available the push still succeeds, |
| 729 | * but PR workflows won't be triggered automatically. |
| 730 | */ |
| 731 | function pushWithToken(rootDir: string, branch: string, config: BumpyConfig): void { |
| 732 | // Guard against misconfigured versionPr.branch pointing at the base branch |
| 733 | if (branch === config.baseBranch || branch === 'main' || branch === 'master') { |
| 734 | throw new Error(`Refusing to force-push to "${branch}" — this looks like a base branch, not a version PR branch`); |
| 735 | } |
| 736 | |
| 737 | withGitToken(rootDir, () => { |
| 738 | // --no-verify skips pre-push hooks (e.g. bumpy check) which would fail |
| 739 | // on the version branch since bump files are consumed during versioning |
| 740 | runArgs(['git', 'push', '-u', 'origin', branch, '--force', '--no-verify'], { cwd: rootDir }); |
| 741 | }); |
| 742 | |
| 743 | if (process.env.BUMPY_GH_TOKEN && process.env.GITHUB_REPOSITORY) { |
| 744 | log.dim(' Pushed with custom token — PR workflows will be triggered'); |
| 745 | } else if (!process.env.BUMPY_GH_TOKEN && process.env.GITHUB_REPOSITORY) { |
| 746 | // Only warn on GitHub Actions — other CI providers don't have this limitation |
| 747 | log.warn( |
| 748 | 'BUMPY_GH_TOKEN is not set — PR checks will not trigger automatically.\n' + ' Run `bumpy ci setup` for help.', |
| 749 | ); |
| 750 | } |
| 751 | } |
| 752 | |
| 753 | // ---- version-pr mode ---- |
| 754 | |
| 755 | async function createVersionPr( |
| 756 | rootDir: string, |
| 757 | plan: ReleasePlan, |
| 758 | config: BumpyConfig, |
| 759 | packageDirs: Map<string, string>, |
| 760 | branchName?: string, |
| 761 | ): Promise<void> { |
| 762 | const branch = validateBranchName(branchName || config.versionPr.branch); |
| 763 | const baseBranch = validateBranchName( |
| 764 | tryRunArgs(['git', 'rev-parse', '--abbrev-ref', 'HEAD'], { cwd: rootDir }) || 'main', |
| 765 | ); |
| 766 | |
| 767 | // Check if a version PR already exists |
| 768 | const existingPr = tryRunArgs(['gh', 'pr', 'list', '--head', branch, '--json', 'number', '--jq', '.[0].number'], { |
| 769 | cwd: rootDir, |
| 770 | }); |
| 771 | |
| 772 | // Create or update the branch |
| 773 | log.step(`Creating branch ${branch}...`); |
| 774 | const branchExists = tryRunArgs(['git', 'rev-parse', '--verify', branch], { cwd: rootDir }) !== null; |
| 775 | |
| 776 | if (branchExists) { |
| 777 | runArgs(['git', 'checkout', branch], { cwd: rootDir }); |
| 778 | runArgs(['git', 'reset', '--hard', baseBranch], { cwd: rootDir }); |
| 779 | } else { |
| 780 | runArgs(['git', 'checkout', '-b', branch], { cwd: rootDir }); |
no test coverage detected
searching dependent graphs…