@varlock/bumpy 1.10.1 → 1.11.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.
package/README.md CHANGED
@@ -171,8 +171,6 @@ jobs:
171
171
 
172
172
  </details>
173
173
 
174
- You can also use `bumpy ci release --auto-publish` to version + publish directly on merge without the intermediate PR.
175
-
176
174
  ### Token setup
177
175
 
178
176
  The default `github.token` works for basic functionality, but GitHub's anti-recursion guard means PRs created by the default token won't trigger other workflows - so your regular CI (tests, linting, etc.) won't run automatically on the Version Packages PR. To fix this, provide a `BUMPY_GH_TOKEN` secret using either a **fine-grained PAT** or a **GitHub App token**. See the [full token setup guide](https://github.com/dmno-dev/bumpy/blob/main/docs/github-actions.md#token-setup) for details.
@@ -143,7 +143,7 @@
143
143
  "publishArgs": {
144
144
  "type": "array",
145
145
  "items": { "type": "string" },
146
- "description": "Extra args appended to the publish command (e.g., \"--provenance\")",
146
+ "description": "Extra args appended to the publish command (e.g., \"--tag next\")",
147
147
  "default": []
148
148
  },
149
149
  "protocolResolution": {
@@ -151,6 +151,16 @@
151
151
  "enum": ["pack", "in-place", "none"],
152
152
  "description": "How to handle workspace:/catalog: protocol resolution. \"pack\" = use PM's pack to build a clean tarball, \"in-place\" = rewrite package.json before publish, \"none\" = don't resolve.",
153
153
  "default": "pack"
154
+ },
155
+ "provenance": {
156
+ "type": "boolean",
157
+ "description": "Attach provenance attestation when publishing via npm. Requires a supported CI environment with OIDC (GitHub Actions, GitLab CI, etc.). Only works with publishManager \"npm\".",
158
+ "default": false
159
+ },
160
+ "npmStaged": {
161
+ "type": "boolean",
162
+ "description": "Use npm staged publishing (`npm stage publish`). Stages the publish on npmjs.com, requiring manual 2FA approval before going live. Only works with publishManager \"npm\" and requires npm >= 11.15.0.",
163
+ "default": false
154
164
  }
155
165
  },
156
166
  "additionalProperties": false
@@ -1,6 +1,6 @@
1
1
  import { a as readJson, c as removeFile, f as writeText, i as listFiles, l as updateJsonFields, n as exists, s as readText, u as updateJsonNestedField } from "./fs-CBXKZhoU.mjs";
2
2
  import { r as getBumpyDir } from "./config-D_4GYDJi.mjs";
3
- import { a as prependToChangelog, i as loadFormatter, n as generateChangelogEntry } from "./changelog-A-EwWggW.mjs";
3
+ import { a as prependToChangelog, i as loadFormatter, n as generateChangelogEntry } from "./changelog-CbaET5V6.mjs";
4
4
  import { resolve } from "node:path";
5
5
  //#region src/core/apply-release-plan.ts
6
6
  /** Apply the release plan: bump versions, update changelogs, delete bump files */
@@ -26,26 +26,25 @@ const defaultFormatter = (ctx) => {
26
26
  for (const bf of sorted) {
27
27
  if (!bf.summary) continue;
28
28
  const type = getBumpTypeForPackage(bf, release.name);
29
- const tag = type !== release.type ? `*(${type})* ` : "";
30
29
  const summaryLines = bf.summary.split("\n");
31
- lines.push(`- ${tag}${summaryLines[0]}`);
30
+ lines.push(`- *(${type})* ${summaryLines[0]}`);
32
31
  for (let i = 1; i < summaryLines.length; i++) if (summaryLines[i].trim()) lines.push(` ${summaryLines[i]}`);
33
32
  }
34
33
  const sourceList = release.bumpSources.length > 0 ? release.bumpSources.map((s) => `\`${s.name}\` v${s.newVersion}`).join(", ") : "";
35
34
  if (release.isDependencyBump) {
36
35
  const depBumpType = release.bumpSources.reduce((max, s) => maxBump(max, s.bumpType), void 0);
37
- const tag = depBumpType && depBumpType !== release.type ? `*(${depBumpType})* ` : "";
38
- lines.push(`- ${tag}Updated dependency ${sourceList || "(internal)"}`);
36
+ const depBumpTag = depBumpType ? `*(${depBumpType})* ` : "";
37
+ lines.push(`- ${depBumpTag}Updated dependency ${sourceList || "(internal)"}`);
39
38
  }
40
- if (release.isGroupBump) lines.push(sourceList ? `- Version bump from group with ${sourceList}` : "- Version bump from group");
41
- if (release.isCascadeBump && !release.isDependencyBump && !release.isGroupBump) lines.push(sourceList ? `- Version bump from ${sourceList}` : "- Version bump via cascade rule");
39
+ if (release.isGroupBump) lines.push(sourceList ? `- *(${release.type})* Version bump from group with ${sourceList}` : `- *(${release.type})* Version bump from group`);
40
+ if (release.isCascadeBump && !release.isDependencyBump && !release.isGroupBump) lines.push(sourceList ? `- *(${release.type})* Version bump from ${sourceList}` : `- *(${release.type})* Version bump via cascade rule`);
42
41
  lines.push("");
43
42
  return lines.join("\n");
44
43
  };
45
44
  const BUILTIN_FORMATTERS = {
46
45
  default: defaultFormatter,
47
46
  github: async () => {
48
- const { createGithubFormatter } = await import("./changelog-github-CPVrJHyB.mjs");
47
+ const { createGithubFormatter } = await import("./changelog-github-DXDnWkrB.mjs");
49
48
  return createGithubFormatter();
50
49
  }
51
50
  };
@@ -56,7 +55,7 @@ const BUILTIN_FORMATTERS = {
56
55
  async function loadFormatter(changelog, rootDir) {
57
56
  const [name, options] = Array.isArray(changelog) ? changelog : [changelog, {}];
58
57
  if (name === "github") {
59
- const { createGithubFormatter } = await import("./changelog-github-CPVrJHyB.mjs");
58
+ const { createGithubFormatter } = await import("./changelog-github-DXDnWkrB.mjs");
60
59
  return createGithubFormatter(options);
61
60
  }
62
61
  if (typeof name === "string" && BUILTIN_FORMATTERS[name]) {
@@ -1,6 +1,6 @@
1
1
  import { c as maxBump } from "./types-Bkh-igOJ.mjs";
2
2
  import { s as tryRunArgs } from "./shell-C8KgKnMQ.mjs";
3
- import { o as sortBumpFilesByType, r as getBumpTypeForPackage } from "./changelog-A-EwWggW.mjs";
3
+ import { o as sortBumpFilesByType, r as getBumpTypeForPackage } from "./changelog-CbaET5V6.mjs";
4
4
  //#region src/core/changelog-github.ts
5
5
  /** Authors filtered from "Thanks" attribution by default (e.g. bots) */
6
6
  /** Authors filtered from "Thanks" attribution by default (e.g. AI/automation bots) */
@@ -41,8 +41,7 @@ function createGithubFormatter(options = {}) {
41
41
  const sorted = sortBumpFilesByType(bumpFiles.filter((bf) => release.bumpFiles.includes(bf.id)), release.name);
42
42
  for (const bf of sorted) {
43
43
  if (!bf.summary) continue;
44
- const type = getBumpTypeForPackage(bf, release.name);
45
- const tag = type !== release.type ? ` *(${type})*` : "";
44
+ const tag = ` *(${getBumpTypeForPackage(bf, release.name)})*`;
46
45
  const { cleanSummary, overrides } = extractSummaryMeta(bf.summary);
47
46
  const gitInfo = resolveBumpFileInfo(bf.id, repoSlug, serverUrl, overrides);
48
47
  const summaryLines = cleanSummary.split("\n");
@@ -60,11 +59,11 @@ function createGithubFormatter(options = {}) {
60
59
  const sourceList = release.bumpSources.length > 0 ? release.bumpSources.map((s) => `\`${s.name}\` v${s.newVersion}`).join(", ") : "";
61
60
  if (release.isDependencyBump) {
62
61
  const depBumpType = release.bumpSources.reduce((max, s) => maxBump(max, s.bumpType), void 0);
63
- const depTag = depBumpType && depBumpType !== release.type ? ` *(${depBumpType})* -` : "";
62
+ const depTag = depBumpType ? ` *(${depBumpType})*` : "";
64
63
  lines.push(`-${depTag} Updated dependency ${sourceList || "(internal)"}`);
65
64
  }
66
- if (release.isGroupBump) lines.push(sourceList ? `- Version bump from group with ${sourceList}` : "- Version bump from group");
67
- if (release.isCascadeBump && !release.isDependencyBump && !release.isGroupBump) lines.push(sourceList ? `- Version bump from ${sourceList}` : "- Version bump via cascade rule");
65
+ if (release.isGroupBump) lines.push(sourceList ? `- *(${release.type})* Version bump from group with ${sourceList}` : `- *(${release.type})* Version bump from group`);
66
+ if (release.isCascadeBump && !release.isDependencyBump && !release.isGroupBump) lines.push(sourceList ? `- *(${release.type})* Version bump from ${sourceList}` : `- *(${release.type})* Version bump via cascade rule`);
68
67
  lines.push("");
69
68
  return lines.join("\n");
70
69
  };
@@ -157,7 +157,7 @@ async function ciPlanCommand(rootDir) {
157
157
  packageNames: plan.releases.map((r) => r.name)
158
158
  };
159
159
  else {
160
- const { findUnpublishedPackages } = await import("./publish-BHHWtI1W.mjs");
160
+ const { findUnpublishedPackages } = await import("./publish-DWs1u5HU.mjs");
161
161
  const unpublished = await findUnpublishedPackages(packages, config);
162
162
  if (unpublished.length > 0) output = {
163
163
  mode: "publish",
@@ -212,8 +212,10 @@ function writeGitHubOutput(key, value) {
212
212
  appendFileSync(outputFile, `${key}<<${delimiter}\n${value}\n${delimiter}\n`);
213
213
  }
214
214
  /**
215
- * CI release: either auto-publish or create a version PR.
216
- * Designed for merge-to-main workflows.
215
+ * CI release: either create a version PR (bump files present) or publish unpublished
216
+ * packages (no bump files — i.e. a version PR was just merged). Pass `autoPublish` to
217
+ * collapse both steps into a single push-to-main, or `assertMode` to refuse running
218
+ * when the detected state doesn't match expectations (used by split-job workflows).
217
219
  */
218
220
  async function ciReleaseCommand(rootDir, opts) {
219
221
  const config = await loadConfig(rootDir);
@@ -225,33 +227,38 @@ async function ciReleaseCommand(rootDir, opts) {
225
227
  for (const err of releaseParseErrors) log.error(err);
226
228
  throw new Error("Bump file parse errors must be fixed before releasing.");
227
229
  }
228
- if (bumpFiles.length === 0) {
229
- log.info("No pending bump files checking for unpublished packages...");
230
- const recoveredBumpFiles = recoverDeletedBumpFiles(rootDir);
231
- const { publishCommand } = await import("./publish-BHHWtI1W.mjs");
232
- await publishCommand(rootDir, {
233
- tag: opts.tag,
234
- recoveredBumpFiles
235
- });
236
- return;
237
- }
238
- const plan = assembleReleasePlan(bumpFiles, packages, depGraph, config);
239
- if (plan.releases.length === 0) {
240
- log.info("Bump files found but no packages would be released — checking for unpublished packages...");
230
+ const plan = bumpFiles.length > 0 ? assembleReleasePlan(bumpFiles, packages, depGraph, config) : null;
231
+ const detectedMode = plan && plan.releases.length > 0 ? "version-pr" : "publish";
232
+ if (opts.assertMode && opts.assertMode !== detectedMode) throw new Error(`Expected mode "${opts.assertMode}" but detected "${detectedMode}". Either remove --expect-mode, or gate this step on the output of "bumpy ci plan".`);
233
+ if (detectedMode === "publish") {
234
+ 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...";
235
+ log.info(msg);
241
236
  const recoveredBumpFiles = recoverDeletedBumpFiles(rootDir);
242
- const { publishCommand } = await import("./publish-BHHWtI1W.mjs");
237
+ const { publishCommand } = await import("./publish-DWs1u5HU.mjs");
243
238
  await publishCommand(rootDir, {
244
239
  tag: opts.tag,
245
240
  recoveredBumpFiles
246
241
  });
247
242
  return;
248
243
  }
249
- if (opts.mode === "auto-publish") await autoPublish(rootDir, config, plan, opts.tag);
244
+ if (opts.autoPublish) await autoPublish(rootDir, config, plan, opts.tag);
250
245
  else await createVersionPr(rootDir, plan, config, new Map([...packages.values()].map((p) => [p.name, p.relativeDir])), opts.branch);
251
246
  }
247
+ /**
248
+ * "Auto-publish" mode: skip the Version Packages PR and ship version+publish in one run.
249
+ *
250
+ * The only thing forfeited vs. the default flow is the preview/review gate on version
251
+ * bumps. Credentials are NOT a differentiator — a single-job non-auto-publish workflow
252
+ * also carries both PR-write and publish creds, just split across two runs. The real
253
+ * credential separation comes from the split-job pattern, which is orthogonal to (and
254
+ * incompatible with) this flag, since this collapses both paths into one execution.
255
+ *
256
+ * That incompatibility is also why --auto-publish and --expect-mode are mutually exclusive:
257
+ * --expect-mode is for split-job workflows where each job runs exactly one path.
258
+ */
252
259
  async function autoPublish(rootDir, config, plan, tag) {
253
260
  log.step("Running bumpy version...");
254
- const { versionCommand } = await import("./version-88KuPbWa.mjs");
261
+ const { versionCommand } = await import("./version-Bxin6aQC.mjs");
255
262
  await versionCommand(rootDir);
256
263
  log.step("Committing version changes...");
257
264
  runArgs([
@@ -280,7 +287,7 @@ async function autoPublish(rootDir, config, plan, tag) {
280
287
  ], { cwd: rootDir });
281
288
  }
282
289
  log.step("Running bumpy publish...");
283
- const { publishCommand } = await import("./publish-BHHWtI1W.mjs");
290
+ const { publishCommand } = await import("./publish-DWs1u5HU.mjs");
284
291
  await publishCommand(rootDir, { tag });
285
292
  }
286
293
  /**
@@ -354,7 +361,7 @@ async function createVersionPr(rootDir, plan, config, packageDirs, branchName) {
354
361
  branch
355
362
  ], { cwd: rootDir });
356
363
  log.step("Running bumpy version...");
357
- const { versionCommand } = await import("./version-88KuPbWa.mjs");
364
+ const { versionCommand } = await import("./version-Bxin6aQC.mjs");
358
365
  await versionCommand(rootDir);
359
366
  runArgs([
360
367
  "git",
package/dist/cli.mjs CHANGED
@@ -55,7 +55,7 @@ async function main() {
55
55
  }
56
56
  case "version": {
57
57
  const rootDir = await findRoot();
58
- const { versionCommand } = await import("./version-88KuPbWa.mjs");
58
+ const { versionCommand } = await import("./version-Bxin6aQC.mjs");
59
59
  await versionCommand(rootDir, { commit: flags.commit === true });
60
60
  break;
61
61
  }
@@ -89,19 +89,30 @@ async function main() {
89
89
  const subcommand = args[1];
90
90
  const ciFlags = parseFlags(args.slice(2));
91
91
  if (subcommand === "check") {
92
- const { ciCheckCommand } = await import("./ci-fXCBOIqu.mjs");
92
+ const { ciCheckCommand } = await import("./ci-D73iWdkg.mjs");
93
93
  await ciCheckCommand(rootDir, {
94
94
  comment: ciFlags.comment !== void 0 ? ciFlags.comment === true : void 0,
95
95
  strict: ciFlags.strict === true,
96
96
  noFail: ciFlags["no-fail"] === true
97
97
  });
98
98
  } else if (subcommand === "plan") {
99
- const { ciPlanCommand } = await import("./ci-fXCBOIqu.mjs");
99
+ const { ciPlanCommand } = await import("./ci-D73iWdkg.mjs");
100
100
  await ciPlanCommand(rootDir);
101
101
  } else if (subcommand === "release") {
102
- const { ciReleaseCommand } = await import("./ci-fXCBOIqu.mjs");
102
+ const { ciReleaseCommand } = await import("./ci-D73iWdkg.mjs");
103
+ const expectModeFlag = ciFlags["expect-mode"];
104
+ const autoPublishFlag = ciFlags["auto-publish"] === true;
105
+ if (expectModeFlag !== void 0 && expectModeFlag !== "version-pr" && expectModeFlag !== "publish") {
106
+ log.error(`Invalid --expect-mode value: "${expectModeFlag}". Must be "version-pr" or "publish".`);
107
+ process.exit(1);
108
+ }
109
+ if (expectModeFlag !== void 0 && autoPublishFlag) {
110
+ log.error("--expect-mode and --auto-publish cannot be used together.");
111
+ process.exit(1);
112
+ }
103
113
  await ciReleaseCommand(rootDir, {
104
- mode: ciFlags["auto-publish"] === true ? "auto-publish" : "version-pr",
114
+ autoPublish: autoPublishFlag,
115
+ assertMode: expectModeFlag,
105
116
  tag: ciFlags.tag,
106
117
  branch: ciFlags.branch
107
118
  });
@@ -116,7 +127,7 @@ async function main() {
116
127
  }
117
128
  case "publish": {
118
129
  const rootDir = await findRoot();
119
- const { publishCommand } = await import("./publish-BHHWtI1W.mjs");
130
+ const { publishCommand } = await import("./publish-DWs1u5HU.mjs");
120
131
  await publishCommand(rootDir, {
121
132
  dryRun: flags["dry-run"] === true,
122
133
  tag: flags.tag,
@@ -140,7 +151,7 @@ async function main() {
140
151
  }
141
152
  case "--version":
142
153
  case "-v":
143
- console.log(`bumpy 1.10.1`);
154
+ console.log(`bumpy 1.11.0`);
144
155
  break;
145
156
  case "help":
146
157
  case "--help":
@@ -160,7 +171,7 @@ async function main() {
160
171
  }
161
172
  function printHelp() {
162
173
  console.log(`
163
- ${colorize(`🐸 bumpy v1.10.1`, "bold")} - Modern monorepo versioning
174
+ ${colorize(`🐸 bumpy v1.11.0`, "bold")} - Modern monorepo versioning
164
175
 
165
176
  Usage: bumpy <command> [options]
166
177
 
@@ -213,6 +224,7 @@ function printHelp() {
213
224
  --no-fail Warn only, never exit 1
214
225
 
215
226
  CI release options:
227
+ --expect-mode <mode> Assert detected mode: "version-pr" or "publish" (errors if mismatched)
216
228
  --auto-publish Version + publish directly (default: create version PR)
217
229
  --tag <tag> npm dist-tag for auto-publish
218
230
  --branch <name> Branch name for version PR (default: bumpy/version-packages)
package/dist/index.d.mts CHANGED
@@ -28,7 +28,7 @@ interface PublishConfig {
28
28
  packManager: 'auto' | 'npm' | 'pnpm' | 'bun' | 'yarn';
29
29
  /** Command to use for publishing. "npm" uses npm publish (supports OIDC). Default: "npm" */
30
30
  publishManager: 'npm' | 'pnpm' | 'bun' | 'yarn';
31
- /** Extra args appended to the publish command (e.g., "--provenance") */
31
+ /** Extra args appended to the publish command (e.g., "--tag next") */
32
32
  publishArgs: string[];
33
33
  /**
34
34
  * How to handle workspace:/catalog: protocol resolution.
package/dist/index.mjs CHANGED
@@ -2,7 +2,7 @@ import { a as DEP_TYPES, c as maxBump, i as DEFAULT_PUBLISH_CONFIG, l as normali
2
2
  import { a as loadConfig, n as findRoot, r as getBumpyDir, s as matchGlob } from "./config-D_4GYDJi.mjs";
3
3
  import { a as writeBumpFile, n as parseBumpFile, o as discoverPackages, r as readBumpFiles } from "./bump-file-B9DpXK5X.mjs";
4
4
  import { a as DependencyGraph, i as stripProtocol, n as bumpVersion, r as satisfies, t as assembleReleasePlan } from "./release-plan-mK7iGeGq.mjs";
5
- import { a as prependToChangelog, i as loadFormatter, n as generateChangelogEntry, t as defaultFormatter } from "./changelog-A-EwWggW.mjs";
6
- import { t as applyReleasePlan } from "./apply-release-plan-CmjqYo0t.mjs";
5
+ import { a as prependToChangelog, i as loadFormatter, n as generateChangelogEntry, t as defaultFormatter } from "./changelog-CbaET5V6.mjs";
6
+ import { t as applyReleasePlan } from "./apply-release-plan-DD2R7SL2.mjs";
7
7
  import { t as publishPackages } from "./publish-pipeline-BRnqVylg.mjs";
8
8
  export { BUMP_LEVELS, DEFAULT_BUMP_RULES, DEFAULT_CONFIG, DEFAULT_PUBLISH_CONFIG, DEP_TYPES, DependencyGraph, applyReleasePlan, assembleReleasePlan, bumpLevel, bumpVersion, defaultFormatter, discoverPackages, findRoot, generateChangelogEntry, getBumpyDir, hasCascade, loadConfig, loadFormatter, matchGlob, maxBump, normalizeCascadeConfig, parseBumpFile, prependToChangelog, publishPackages, readBumpFiles, satisfies, stripProtocol, writeBumpFile };
@@ -4,10 +4,10 @@ import { n as detectWorkspaces } from "./package-manager-BQPwXwu5.mjs";
4
4
  import { s as discoverWorkspace } from "./bump-file-B9DpXK5X.mjs";
5
5
  import { a as DependencyGraph } from "./release-plan-mK7iGeGq.mjs";
6
6
  import { r as runArgsAsync, s as tryRunArgs } from "./shell-C8KgKnMQ.mjs";
7
- import { i as loadFormatter, n as generateChangelogEntry } from "./changelog-A-EwWggW.mjs";
7
+ import { i as loadFormatter, n as generateChangelogEntry } from "./changelog-CbaET5V6.mjs";
8
8
  import { c as pushWithTags, s as hasUncommittedChanges } from "./git-JGLQtk-M.mjs";
9
9
  import { t as publishPackages } from "./publish-pipeline-BRnqVylg.mjs";
10
- import { CI_PLAN_CACHE_PATH } from "./ci-fXCBOIqu.mjs";
10
+ import { CI_PLAN_CACHE_PATH } from "./ci-D73iWdkg.mjs";
11
11
  //#region src/core/github-release.ts
12
12
  /** Get the current HEAD commit SHA */
13
13
  function getHeadSha(rootDir) {
@@ -4,7 +4,7 @@ import { n as detectWorkspaces } from "./package-manager-BQPwXwu5.mjs";
4
4
  import { o as discoverPackages, r as readBumpFiles } from "./bump-file-B9DpXK5X.mjs";
5
5
  import { a as DependencyGraph, t as assembleReleasePlan } from "./release-plan-mK7iGeGq.mjs";
6
6
  import { n as runArgs, s as tryRunArgs } from "./shell-C8KgKnMQ.mjs";
7
- import { t as applyReleasePlan } from "./apply-release-plan-CmjqYo0t.mjs";
7
+ import { t as applyReleasePlan } from "./apply-release-plan-DD2R7SL2.mjs";
8
8
  import { t as resolveCommitMessage } from "./commit-message-CSWVKPJ-.mjs";
9
9
  //#region src/commands/version.ts
10
10
  async function versionCommand(rootDir, opts = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@varlock/bumpy",
3
- "version": "1.10.1",
3
+ "version": "1.11.0",
4
4
  "description": "Modern monorepo versioning and changelog tool",
5
5
  "keywords": [
6
6
  "bump",