(rootDir: string, opts: ReleaseOptions)
| 545 | if (pkgConfig.publishCommand) { |
| 546 | targets.push({ type: 'custom' }); |
| 547 | } |
| 548 | if (!pkgConfig.publishCommand && !pkgConfig.skipNpmPublish) { |
| 549 | targets.push({ type: 'npm' }); |
| 550 | } |
| 551 | return targets; |
| 552 | } |
| 553 | |
| 554 | /** Write a key=value pair to $GITHUB_OUTPUT if available */ |
| 555 | function writeGitHubOutput(key: string, value: string): void { |
| 556 | const outputFile = process.env.GITHUB_OUTPUT; |
| 557 | if (!outputFile) return; |
| 558 | // Use delimiter protocol for multiline values |
| 559 | const delimiter = `ghadelimiter_${Date.now()}`; |
| 560 | appendFileSync(outputFile, `${key}<<${delimiter}\n${value}\n${delimiter}\n`); |
| 561 | } |
| 562 | |
| 563 | // ---- ci release ---- |
| 564 | |
| 565 | interface ReleaseOptions { |
| 566 | autoPublish?: boolean; // skip the version-PR step and version+publish in one shot |
| 567 | assertMode?: 'version-pr' | 'publish'; // refuse to run if detected mode doesn't match — see CiPlanMode |
| 568 | tag?: string; // npm dist-tag for auto-publish (or the snapshot dist-tag) |
| 569 | branch?: string; // branch name for version PR (default: "bumpy/version-packages") |
| 570 | snapshot?: string; // publish a transient snapshot under this name + comment install instructions on the PR |
| 571 | } |
| 572 | |
| 573 | /** |
| 574 | * CI release: either create a version PR (bump files present) or publish unpublished |
| 575 | * packages (no bump files — i.e. a version PR was just merged). Pass `autoPublish` to |
| 576 | * collapse both steps into a single push-to-main, or `assertMode` to refuse running |
| 577 | * when the detected state doesn't match expectations (used by split-job workflows). |
| 578 | */ |
| 579 | export async function ciReleaseCommand(rootDir: string, opts: ReleaseOptions): Promise<void> { |
| 580 | const config = await loadConfig(rootDir); |
| 581 | ensureGitIdentity(rootDir, config); |
| 582 | |
| 583 | // Snapshots are a one-shot transient release that can run from any branch (typically a |
| 584 | // feature PR), so they bypass the base/channel branch routing entirely. |
| 585 | if (opts.snapshot !== undefined) { |
| 586 | await ciSnapshotRelease(rootDir, opts); |
| 587 | return; |
| 588 | } |
| 589 | |
| 590 | // Channel branches get the channel flow; unknown branches are refused (when channels |
| 591 | // are configured) so a misconfigured workflow can't publish from a feature branch. |
| 592 | const releaseBranch = detectReleaseBranch(rootDir); |
| 593 | const channel = matchChannelByBranch(config, releaseBranch); |
| 594 | if (channel) { |
| 595 | await ciChannelRelease(rootDir, config, channel, opts); |
| 596 | return; |
| 597 | } |
| 598 | if (Object.keys(config.channels || {}).length > 0 && releaseBranch && releaseBranch !== config.baseBranch) { |
| 599 | throw new Error( |
| 600 | `"bumpy ci release" ran on branch "${releaseBranch}", which is neither the base branch ` + |
| 601 | `("${config.baseBranch}") nor a configured channel branch. Refusing to release — ` + |
| 602 | 'add the branch to "channels" in .bumpy/_config.json or fix the workflow trigger.', |
| 603 | ); |
| 604 | } |
no test coverage detected
searching dependent graphs…