@varlock/bumpy 1.14.0-rc.1 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -77,9 +77,10 @@ async function ciCheckCommand(rootDir, opts) {
77
77
  const config = await loadConfig(rootDir);
78
78
  const { packages } = await discoverWorkspace(rootDir, config);
79
79
  const depGraph = new DependencyGraph(packages);
80
- const { bumpFiles: allBumpFiles, errors: parseErrors } = await readBumpFiles(rootDir);
80
+ const { bumpFiles: allBumpFiles, errors: parseErrors } = await readBumpFiles(rootDir, { channels: channelNames(config) });
81
81
  const prBranchName = detectPrBranch(rootDir);
82
- const releasePrBranches = new Set([config.versionPr.branch, ...[...resolveChannels(config).values()].map((c) => c.versionPr.branch)]);
82
+ const channels = resolveChannels(config);
83
+ const releasePrBranches = new Set([config.versionPr.branch, ...[...channels.values()].map((c) => c.versionPr.branch)]);
83
84
  if (prBranchName && releasePrBranches.has(prBranchName)) {
84
85
  log.dim(" Skipping — this is a release PR branch.");
85
86
  return;
@@ -122,7 +123,7 @@ async function ciCheckCommand(rootDir, opts) {
122
123
  console.log(` ${r.name}: ${r.oldVersion} → ${colorize(`${r.newVersion}${releaseSuffix}`, "cyan")}${tag}`);
123
124
  }
124
125
  if (plan.warnings.length > 0) for (const w of plan.warnings) log.warn(w);
125
- if (shouldComment && prNumber) await postOrUpdatePrComment(prNumber, formatReleasePlanComment(plan, prBumpFiles, prNumber, detectPrBranch(rootDir), pm, plan.warnings, parseErrors, emptyBumpFileIds, prChannel), rootDir);
126
+ if (shouldComment && prNumber) await postOrUpdatePrComment(prNumber, formatReleasePlanComment(plan, prBumpFiles, prNumber, detectPrBranch(rootDir), pm, plan.warnings, parseErrors, emptyBumpFileIds, prChannel, channels), rootDir);
126
127
  if (parseErrors.length > 0 && !opts.noFail) process.exit(1);
127
128
  const coveredPackages = new Set(plan.releases.map((r) => r.name));
128
129
  for (const bf of prBumpFiles) for (const release of bf.releases) coveredPackages.add(release.name);
@@ -169,7 +170,7 @@ async function ciPlanCommand(rootDir) {
169
170
  packageNames: plan.releases.map((r) => r.name)
170
171
  };
171
172
  else {
172
- const { findUnpublishedPackages } = await import("./publish-Ak6jmwi_.mjs");
173
+ const { findUnpublishedPackages } = await import("./publish-DWxi552d.mjs");
173
174
  const unpublished = await findUnpublishedPackages(packages, config);
174
175
  if (unpublished.length > 0) output = {
175
176
  mode: "publish",
@@ -253,7 +254,7 @@ async function ciReleaseCommand(rootDir, opts) {
253
254
  const msg = bumpFiles.length === 0 ? "No pending bump files — checking for unpublished packages..." : "Bump files found but no packages would be released — checking for unpublished packages...";
254
255
  log.info(msg);
255
256
  const recoveredBumpFiles = recoverDeletedBumpFiles(rootDir);
256
- const { publishCommand } = await import("./publish-Ak6jmwi_.mjs");
257
+ const { publishCommand } = await import("./publish-DWxi552d.mjs");
257
258
  await publishCommand(rootDir, {
258
259
  tag: opts.tag,
259
260
  recoveredBumpFiles
@@ -306,7 +307,7 @@ async function autoPublish(rootDir, config, plan, tag) {
306
307
  ], { cwd: rootDir });
307
308
  }
308
309
  log.step("Running bumpy publish...");
309
- const { publishCommand } = await import("./publish-Ak6jmwi_.mjs");
310
+ const { publishCommand } = await import("./publish-DWxi552d.mjs");
310
311
  await publishCommand(rootDir, { tag });
311
312
  }
312
313
  /**
@@ -475,6 +476,93 @@ async function createVersionPr(rootDir, plan, config, packageDirs, branchName) {
475
476
  "checkout",
476
477
  baseBranch
477
478
  ], { cwd: rootDir });
479
+ await closePromotedChannelReleasePrs(rootDir, config, plan.bumpFiles);
480
+ }
481
+ /**
482
+ * Channels whose dirs gained bump files in the triggering push — i.e. this push is
483
+ * the promotion/graduation merge that delivered them. (Same range detection as the
484
+ * channel publish trigger.)
485
+ */
486
+ function detectArrivedChannelFiles(rootDir, config) {
487
+ const range = getPushEventRange();
488
+ let diffRange;
489
+ if (range) diffRange = `${range.before}..${range.after}`;
490
+ else {
491
+ if (!tryRunArgs([
492
+ "git",
493
+ "rev-parse",
494
+ "--verify",
495
+ "HEAD^"
496
+ ], { cwd: rootDir })) return /* @__PURE__ */ new Set();
497
+ diffRange = "HEAD^..HEAD";
498
+ }
499
+ const out = tryRunArgs([
500
+ "git",
501
+ "diff",
502
+ "--name-only",
503
+ "--diff-filter=A",
504
+ "--no-renames",
505
+ diffRange,
506
+ "--",
507
+ ".bumpy/"
508
+ ], { cwd: rootDir });
509
+ if (!out) return /* @__PURE__ */ new Set();
510
+ const knownChannels = new Set(channelNames(config));
511
+ const arrived = /* @__PURE__ */ new Set();
512
+ for (const f of out.split("\n")) {
513
+ if (!f.endsWith(".md") || f.endsWith("README.md")) continue;
514
+ const parts = f.split("/");
515
+ if (parts.length === 3 && knownChannels.has(parts[1])) arrived.add(parts[1]);
516
+ }
517
+ return arrived;
518
+ }
519
+ /**
520
+ * Close lingering channel release PRs whose cycles were promoted: once a channel's
521
+ * bump files are pending on this branch (via a promotion or graduation merge), the
522
+ * source channel's own release PR is obsolete — merging it would re-publish a cycle
523
+ * that's already moving to its next stage. A fresh release PR is created automatically
524
+ * if new work lands on the channel branch.
525
+ *
526
+ * Only channels whose files arrived in the TRIGGERING push are considered: the files
527
+ * stay pending here until our version/release PR merges, and re-closing on every
528
+ * later push in that window would kill the release PR of a newly restarted cycle.
529
+ */
530
+ async function closePromotedChannelReleasePrs(rootDir, config, bumpFiles, currentChannel) {
531
+ const arrived = detectArrivedChannelFiles(rootDir, config);
532
+ const promoted = [...new Set(bumpFiles.map((bf) => bf.channel))].filter((name) => name != null && name !== currentChannel?.name && arrived.has(name));
533
+ if (promoted.length === 0) return;
534
+ const channels = resolveChannels(config);
535
+ for (const name of promoted) {
536
+ const channel = channels.get(name);
537
+ if (!channel) continue;
538
+ const pr = tryRunArgs([
539
+ "gh",
540
+ "pr",
541
+ "list",
542
+ "--head",
543
+ channel.versionPr.branch,
544
+ "--json",
545
+ "number",
546
+ "--jq",
547
+ ".[0].number"
548
+ ], { cwd: rootDir });
549
+ if (!pr) continue;
550
+ const validPr = validatePrNumber(pr);
551
+ log.step(`Closing release PR #${validPr} — the "${name}" cycle's changes are pending here now...`);
552
+ try {
553
+ await withPatToken(() => runArgsAsync([
554
+ "gh",
555
+ "pr",
556
+ "close",
557
+ validPr,
558
+ "--comment",
559
+ `Closing — the \`${name}\` cycle's bump files were promoted and are now pending a release here. A new release PR will be created automatically if more changes land on \`${channel.branch}\`.`
560
+ ], { cwd: rootDir }));
561
+ log.success(`🐸 Closed obsolete release PR #${validPr}`);
562
+ } catch (e) {
563
+ log.warn(` Failed to close release PR #${validPr}: ${e}`);
564
+ }
565
+ }
478
566
  }
479
567
  /** Read the push event's before/after range, if running on a GitHub Actions push event */
480
568
  function getPushEventRange() {
@@ -555,7 +643,7 @@ async function ciChannelRelease(rootDir, config, channel, opts) {
555
643
  "--no-verify"
556
644
  ], { cwd: rootDir });
557
645
  }
558
- const { publishCommand } = await import("./publish-Ak6jmwi_.mjs");
646
+ const { publishCommand } = await import("./publish-DWxi552d.mjs");
559
647
  await publishCommand(rootDir, {
560
648
  channel: channel.name,
561
649
  tag: opts.tag
@@ -566,7 +654,7 @@ async function ciChannelRelease(rootDir, config, channel, opts) {
566
654
  const shouldPublish = movedIds.length > 0 && opts.assertMode !== "version-pr";
567
655
  if (shouldPublish) {
568
656
  log.step(`Release PR merge detected (${movedIds.map((id) => `${id}.md`).join(", ")}) — publishing prereleases...`);
569
- const { publishCommand } = await import("./publish-Ak6jmwi_.mjs");
657
+ const { publishCommand } = await import("./publish-DWxi552d.mjs");
570
658
  await publishCommand(rootDir, {
571
659
  channel: channel.name,
572
660
  tag: opts.tag
@@ -732,6 +820,7 @@ async function createChannelReleasePr(rootDir, config, channel, packages, branch
732
820
  "checkout",
733
821
  baseBranch
734
822
  ], { cwd: rootDir });
823
+ await closePromotedChannelReleasePrs(rootDir, config, result.movedFiles, channel);
735
824
  }
736
825
  function buildChannelPrPreamble(config, channel) {
737
826
  return [
@@ -824,11 +913,13 @@ function pmRunCommand(pm) {
824
913
  if (pm === "yarn") return "yarn bumpy";
825
914
  return "npx bumpy";
826
915
  }
827
- function formatReleasePlanComment(plan, bumpFiles, prNumber, prBranch, pm, warnings = [], parseErrors = [], emptyBumpFileIds = [], channel = null) {
916
+ function formatReleasePlanComment(plan, bumpFiles, prNumber, prBranch, pm, warnings = [], parseErrors = [], emptyBumpFileIds = [], channel = null, allChannels = null) {
828
917
  const repo = process.env.GITHUB_REPOSITORY;
829
918
  const lines = [];
830
919
  const versionSuffix = channel ? `-${channel.preid}.x` : "";
831
- const headline = channel ? `**This PR targets the \`${channel.name}\` prerelease channel** — merging it ships these packages as a **prerelease** to the \`@${channel.tag}\` dist-tag, not a stable release.` : "**The changes in this PR will be included in the next version bump.**";
920
+ const promotedChannels = channel ? [] : [...new Set(bumpFiles.map((bf) => bf.channel))].filter((c) => c != null);
921
+ const channelTag = (name) => `\`@${allChannels?.get(name)?.tag ?? name}\``;
922
+ const headline = channel ? `**This PR targets the \`${channel.name}\` prerelease channel** — merging it ships these packages as a **prerelease** to the \`@${channel.tag}\` dist-tag, not a stable release.` : promotedChannels.length > 0 ? `**This PR promotes the ${promotedChannels.map((c) => `\`${c}\``).join(", ")} prerelease cycle${promotedChannels.length > 1 ? "s" : ""} to a stable release.** The changes below that already shipped to the ${promotedChannels.map(channelTag).join(", ")} dist-tag${promotedChannels.length > 1 ? "s" : ""} will be consolidated into the next stable version bump.` : "**The changes in this PR will be included in the next version bump.**";
832
923
  const preamble = [
833
924
  `<a href="https://bumpy.varlock.dev"><img src="${FROG_IMG_BASE}/frog-clipboard.png" alt="bumpy-frog" width="60" align="left" style="image-rendering: pixelated;" title="Hi! I'm bumpy!" /></a>`,
834
925
  "",
@@ -867,8 +958,9 @@ function formatReleasePlanComment(plan, bumpFiles, prNumber, prBranch, pm, warni
867
958
  lines.push(`#### Bump files in this PR`);
868
959
  lines.push("");
869
960
  for (const bf of bumpFiles) {
870
- const filename = `${bf.id}.md`;
961
+ const filename = bf.channel ? `${bf.channel}/${bf.id}.md` : `${bf.id}.md`;
871
962
  const parts = [`\`${filename}\``];
963
+ if (bf.channel) parts.push(`_(shipped on ${channelTag(bf.channel)})_`);
872
964
  if (repo) {
873
965
  parts.push(`([view diff](https://github.com/${repo}/pull/${prNumber}/changes#diff-${sha256Hex(`.bumpy/${filename}`)}))`);
874
966
  if (prBranch) parts.push(`([edit](https://github.com/${repo}/edit/${prBranch}/.bumpy/${filename}))`);
package/dist/cli.mjs CHANGED
@@ -94,17 +94,17 @@ async function main() {
94
94
  const subcommand = args[1];
95
95
  const ciFlags = parseFlags(args.slice(2));
96
96
  if (subcommand === "check") {
97
- const { ciCheckCommand } = await import("./ci-ChYmDuwy.mjs");
97
+ const { ciCheckCommand } = await import("./ci-BRJNl-VN.mjs");
98
98
  await ciCheckCommand(rootDir, {
99
99
  comment: ciFlags.comment !== void 0 ? ciFlags.comment === true : void 0,
100
100
  strict: ciFlags.strict === true,
101
101
  noFail: ciFlags["no-fail"] === true
102
102
  });
103
103
  } else if (subcommand === "plan") {
104
- const { ciPlanCommand } = await import("./ci-ChYmDuwy.mjs");
104
+ const { ciPlanCommand } = await import("./ci-BRJNl-VN.mjs");
105
105
  await ciPlanCommand(rootDir);
106
106
  } else if (subcommand === "release") {
107
- const { ciReleaseCommand } = await import("./ci-ChYmDuwy.mjs");
107
+ const { ciReleaseCommand } = await import("./ci-BRJNl-VN.mjs");
108
108
  const expectModeFlag = ciFlags["expect-mode"];
109
109
  const autoPublishFlag = ciFlags["auto-publish"] === true;
110
110
  if (expectModeFlag !== void 0 && expectModeFlag !== "version-pr" && expectModeFlag !== "publish") {
@@ -132,7 +132,7 @@ async function main() {
132
132
  }
133
133
  case "publish": {
134
134
  const rootDir = await findRoot();
135
- const { publishCommand } = await import("./publish-Ak6jmwi_.mjs");
135
+ const { publishCommand } = await import("./publish-DWxi552d.mjs");
136
136
  await publishCommand(rootDir, {
137
137
  dryRun: flags["dry-run"] === true,
138
138
  tag: flags.tag,
@@ -157,7 +157,7 @@ async function main() {
157
157
  }
158
158
  case "--version":
159
159
  case "-v":
160
- console.log(`bumpy 1.13.2`);
160
+ console.log(`bumpy 1.14.0`);
161
161
  break;
162
162
  case "help":
163
163
  case "--help":
@@ -177,7 +177,7 @@ async function main() {
177
177
  }
178
178
  function printHelp() {
179
179
  console.log(`
180
- ${colorize(`🐸 bumpy v1.13.2`, "bold")} - Modern monorepo versioning
180
+ ${colorize(`🐸 bumpy v1.14.0`, "bold")} - Modern monorepo versioning
181
181
 
182
182
  Usage: bumpy <command> [options]
183
183
 
@@ -9,7 +9,7 @@ import { f as tagExists, l as hasUncommittedChanges, n as forcePushTag } from ".
9
9
  import { n as willUseOidcExclusively, t as publishPackages } from "./publish-pipeline-BD8mLbL9.mjs";
10
10
  import { channelNames, resolveActiveChannel } from "./channels-CFXZkyGd.mjs";
11
11
  import { i as writeChannelVersionsInPlace, t as buildChannelReleasePlan } from "./prerelease-B2PVfXkm.mjs";
12
- import { CI_PLAN_CACHE_PATH } from "./ci-ChYmDuwy.mjs";
12
+ import { CI_PLAN_CACHE_PATH } from "./ci-BRJNl-VN.mjs";
13
13
  //#region src/core/github-release.ts
14
14
  var import_semver = /* @__PURE__ */ __toESM(require_semver(), 1);
15
15
  /** Get the current HEAD commit SHA */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@varlock/bumpy",
3
- "version": "1.14.0-rc.1",
3
+ "version": "1.14.0",
4
4
  "description": "Modern monorepo versioning and changelog tool",
5
5
  "keywords": [
6
6
  "bump",