@varlock/bumpy 1.9.0 → 1.10.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 +5 -4
- package/config-schema.json +0 -22
- package/dist/{add-D3HlRjOy.mjs → add-B_1I_sen.mjs} +4 -4
- package/dist/{apply-release-plan-CCoCcpdv.mjs → apply-release-plan-CmjqYo0t.mjs} +2 -2
- package/dist/{bump-file-BOqrdkYP.mjs → bump-file-B9DpXK5X.mjs} +1 -1
- package/dist/{changelog-DrAf-rL6.mjs → changelog-A-EwWggW.mjs} +3 -3
- package/dist/{changelog-github-BYqd2jw1.mjs → changelog-github-CPVrJHyB.mjs} +2 -2
- package/dist/{check-Dc0ts_q3.mjs → check-pbyTB4KC.mjs} +3 -3
- package/dist/{ci-9tmC3oNF.mjs → ci-DeZ4zHmn.mjs} +35 -27
- package/dist/cli.mjs +12 -12
- package/dist/{config-CDC1Y_ye.mjs → config-D_4GYDJi.mjs} +1 -1
- package/dist/{generate-BgKOXV47.mjs → generate-BlZIe3aC.mjs} +3 -3
- package/dist/{git-CpJqzpp-.mjs → git-JGLQtk-M.mjs} +1 -12
- package/dist/index.d.mts +8 -11
- package/dist/index.mjs +7 -7
- package/dist/publish-B3D-70sJ.mjs +622 -0
- package/dist/{publish-pipeline-BGyQsVyw.mjs → publish-pipeline-BRnqVylg.mjs} +33 -16
- package/dist/{release-plan-Ck_DlgcK.mjs → release-plan-mK7iGeGq.mjs} +2 -2
- package/dist/{status-CV8wBBiI.mjs → status-DeMcLxiM.mjs} +5 -5
- package/dist/{types-CL2pqN9N.mjs → types-Bkh-igOJ.mjs} +1 -1
- package/dist/{version-fTSOVlym.mjs → version-88KuPbWa.mjs} +4 -4
- package/package.json +1 -1
- package/dist/publish-BplMRp4-.mjs +0 -371
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { n as log, t as colorize } from "./logger-BgksGFuf.mjs";
|
|
2
2
|
import { a as readJson, u as updateJsonNestedField } from "./fs-CBXKZhoU.mjs";
|
|
3
3
|
import { r as resolveCatalogDep } from "./package-manager-BQPwXwu5.mjs";
|
|
4
|
-
import { i as stripProtocol } from "./release-plan-
|
|
4
|
+
import { i as stripProtocol } from "./release-plan-mK7iGeGq.mjs";
|
|
5
5
|
import { i as runAsync, o as sq, r as runArgsAsync, s as tryRunArgs } from "./shell-C8KgKnMQ.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { l as tagExists, t as createTag } from "./git-JGLQtk-M.mjs";
|
|
7
7
|
import { resolve } from "node:path";
|
|
8
8
|
import { unlink } from "node:fs/promises";
|
|
9
9
|
import { appendFileSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
@@ -28,6 +28,26 @@ const OIDC_NPM_UPGRADE_HINTS = {
|
|
|
28
28
|
gitlab: "Use a Node.js image with npm >= 11.5.1 or run `npm install -g npm@latest`",
|
|
29
29
|
circleci: "Use a Node.js image with npm >= 11.5.1 or run `sudo npm install -g npm@latest`"
|
|
30
30
|
};
|
|
31
|
+
/** Compare semver triples: returns true if version >= minimum */
|
|
32
|
+
function npmVersionAtLeast(version, minimum) {
|
|
33
|
+
const [major, minor, patch] = version.split(".").map(Number);
|
|
34
|
+
const [minMajor, minMinor, minPatch] = minimum;
|
|
35
|
+
if (major > minMajor) return true;
|
|
36
|
+
if (major < minMajor) return false;
|
|
37
|
+
if (minor > minMinor) return true;
|
|
38
|
+
if (minor < minMinor) return false;
|
|
39
|
+
return patch >= minPatch;
|
|
40
|
+
}
|
|
41
|
+
const MIN_NPM_OIDC = [
|
|
42
|
+
11,
|
|
43
|
+
5,
|
|
44
|
+
1
|
|
45
|
+
];
|
|
46
|
+
const MIN_NPM_STAGED = [
|
|
47
|
+
11,
|
|
48
|
+
15,
|
|
49
|
+
0
|
|
50
|
+
];
|
|
31
51
|
/**
|
|
32
52
|
* Set up npm authentication for publishing.
|
|
33
53
|
*
|
|
@@ -50,13 +70,10 @@ function setupNpmAuth(rootDir, publishManager) {
|
|
|
50
70
|
const oidcProvider = detectOidcProvider();
|
|
51
71
|
if (oidcProvider) {
|
|
52
72
|
const npmVersion = tryRunArgs(["npm", "--version"]);
|
|
53
|
-
if (npmVersion) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
log.warn(` ${OIDC_NPM_UPGRADE_HINTS[oidcProvider]}`);
|
|
58
|
-
} else log.dim(` OIDC detected (${oidcProvider}) — npm ${npmVersion} will authenticate via trusted publishing`);
|
|
59
|
-
}
|
|
73
|
+
if (npmVersion) if (!npmVersionAtLeast(npmVersion, MIN_NPM_OIDC)) {
|
|
74
|
+
log.warn(` npm ${npmVersion} detected — trusted publishing (OIDC) requires npm >= ${MIN_NPM_OIDC.join(".")}`);
|
|
75
|
+
log.warn(` ${OIDC_NPM_UPGRADE_HINTS[oidcProvider]}`);
|
|
76
|
+
} else log.dim(` OIDC detected (${oidcProvider}) — npm ${npmVersion} will authenticate via trusted publishing`);
|
|
60
77
|
return;
|
|
61
78
|
}
|
|
62
79
|
const token = process.env.NODE_AUTH_TOKEN || process.env.NPM_TOKEN;
|
|
@@ -88,14 +105,13 @@ async function publishPackages(releasePlan, packages, depGraph, config, rootDir,
|
|
|
88
105
|
};
|
|
89
106
|
const publishConfig = config.publish;
|
|
90
107
|
setupNpmAuth(rootDir, publishConfig.publishManager);
|
|
91
|
-
if (publishConfig.
|
|
92
|
-
|
|
108
|
+
if (publishConfig.provenance && publishConfig.publishManager !== "npm") throw new Error("provenance requires publishManager \"npm\" — provenance attestation is an npm-specific feature");
|
|
109
|
+
if (publishConfig.npmStaged) {
|
|
110
|
+
if (publishConfig.publishManager !== "npm") throw new Error("npmStaged requires publishManager \"npm\" — staged publishing is an npm-specific feature");
|
|
93
111
|
const npmVersion = tryRunArgs(["npm", "--version"]);
|
|
94
|
-
if (npmVersion) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
else log.dim(`Staged publishing enabled — packages will require 2FA approval on npmjs.com`);
|
|
98
|
-
}
|
|
112
|
+
if (!npmVersion) throw new Error(`npmStaged is enabled but npm was not found — install npm >= ${MIN_NPM_STAGED.join(".")}`);
|
|
113
|
+
if (!npmVersionAtLeast(npmVersion, MIN_NPM_STAGED)) throw new Error(`npmStaged requires npm >= ${MIN_NPM_STAGED.join(".")} (found ${npmVersion})\n Upgrade npm: npm install -g npm@latest`);
|
|
114
|
+
log.dim(`Staged publishing enabled — packages will require 2FA approval on npmjs.com`);
|
|
99
115
|
}
|
|
100
116
|
const packManager = publishConfig.packManager === "auto" ? detectedPm : publishConfig.packManager;
|
|
101
117
|
const topoOrder = depGraph.topologicalSort(packages);
|
|
@@ -217,6 +233,7 @@ function buildPublishArgs(pkg, pkgConfig, config, opts, tarball) {
|
|
|
217
233
|
args.push("--access", access);
|
|
218
234
|
if (pkgConfig?.registry) args.push("--registry", pkgConfig.registry);
|
|
219
235
|
if (opts.tag) args.push("--tag", opts.tag);
|
|
236
|
+
if (config.publish.provenance && publishManager === "npm") args.push("--provenance");
|
|
220
237
|
if (config.publish.publishArgs.length > 0) args.push(...config.publish.publishArgs);
|
|
221
238
|
return args;
|
|
222
239
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { i as __commonJSMin, s as __toESM } from "./logger-BgksGFuf.mjs";
|
|
2
|
-
import { c as maxBump, l as normalizeCascadeConfig, n as DEFAULT_BUMP_RULES, o as bumpLevel, s as hasCascade } from "./types-
|
|
3
|
-
import { s as matchGlob } from "./config-
|
|
2
|
+
import { c as maxBump, l as normalizeCascadeConfig, n as DEFAULT_BUMP_RULES, o as bumpLevel, s as hasCascade } from "./types-Bkh-igOJ.mjs";
|
|
3
|
+
import { s as matchGlob } from "./config-D_4GYDJi.mjs";
|
|
4
4
|
//#region src/core/dep-graph.ts
|
|
5
5
|
var DependencyGraph = class {
|
|
6
6
|
/** Map from package name → packages that depend on it */
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { n as log, t as colorize } from "./logger-BgksGFuf.mjs";
|
|
2
|
-
import { a as loadConfig } from "./config-
|
|
3
|
-
import { o as discoverPackages, r as readBumpFiles, t as filterBranchBumpFiles } from "./bump-file-
|
|
4
|
-
import { a as DependencyGraph, t as assembleReleasePlan } from "./release-plan-
|
|
5
|
-
import { i as getCurrentBranch, r as getChangedFiles } from "./git-
|
|
2
|
+
import { a as loadConfig } from "./config-D_4GYDJi.mjs";
|
|
3
|
+
import { o as discoverPackages, r as readBumpFiles, t as filterBranchBumpFiles } from "./bump-file-B9DpXK5X.mjs";
|
|
4
|
+
import { a as DependencyGraph, t as assembleReleasePlan } from "./release-plan-mK7iGeGq.mjs";
|
|
5
|
+
import { i as getCurrentBranch, r as getChangedFiles } from "./git-JGLQtk-M.mjs";
|
|
6
6
|
//#region src/commands/status.ts
|
|
7
7
|
async function statusCommand(rootDir, opts) {
|
|
8
8
|
const config = await loadConfig(rootDir);
|
|
@@ -29,7 +29,7 @@ async function statusCommand(rootDir, opts) {
|
|
|
29
29
|
releases = releases.filter((r) => types.includes(r.type));
|
|
30
30
|
}
|
|
31
31
|
if (opts.filter) {
|
|
32
|
-
const { matchGlob } = await import("./config-
|
|
32
|
+
const { matchGlob } = await import("./config-D_4GYDJi.mjs").then((n) => n.t);
|
|
33
33
|
const patterns = opts.filter.split(",").map((p) => p.trim());
|
|
34
34
|
releases = releases.filter((r) => patterns.some((p) => matchGlob(r.name, p)));
|
|
35
35
|
}
|
|
@@ -49,6 +49,7 @@ const DEFAULT_PUBLISH_CONFIG = {
|
|
|
49
49
|
packManager: "auto",
|
|
50
50
|
publishManager: "npm",
|
|
51
51
|
publishArgs: [],
|
|
52
|
+
provenance: false,
|
|
52
53
|
npmStaged: false,
|
|
53
54
|
protocolResolution: "pack"
|
|
54
55
|
};
|
|
@@ -71,7 +72,6 @@ const DEFAULT_CONFIG = {
|
|
|
71
72
|
allowCustomCommands: false,
|
|
72
73
|
packages: {},
|
|
73
74
|
publish: { ...DEFAULT_PUBLISH_CONFIG },
|
|
74
|
-
aggregateRelease: false,
|
|
75
75
|
gitUser: {
|
|
76
76
|
name: "bumpy-bot",
|
|
77
77
|
email: "276066384+bumpy-bot@users.noreply.github.com"
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { n as log, t as colorize } from "./logger-BgksGFuf.mjs";
|
|
2
|
-
import { a as loadConfig } from "./config-
|
|
2
|
+
import { a as loadConfig } from "./config-D_4GYDJi.mjs";
|
|
3
3
|
import { n as detectWorkspaces } from "./package-manager-BQPwXwu5.mjs";
|
|
4
|
-
import { o as discoverPackages, r as readBumpFiles } from "./bump-file-
|
|
5
|
-
import { a as DependencyGraph, t as assembleReleasePlan } from "./release-plan-
|
|
4
|
+
import { o as discoverPackages, r as readBumpFiles } from "./bump-file-B9DpXK5X.mjs";
|
|
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-
|
|
7
|
+
import { t as applyReleasePlan } from "./apply-release-plan-CmjqYo0t.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,371 +0,0 @@
|
|
|
1
|
-
import { n as log, o as __require, t as colorize } from "./logger-BgksGFuf.mjs";
|
|
2
|
-
import { a as loadConfig } from "./config-CDC1Y_ye.mjs";
|
|
3
|
-
import { n as detectWorkspaces } from "./package-manager-BQPwXwu5.mjs";
|
|
4
|
-
import { s as discoverWorkspace } from "./bump-file-BOqrdkYP.mjs";
|
|
5
|
-
import { a as DependencyGraph } from "./release-plan-Ck_DlgcK.mjs";
|
|
6
|
-
import { r as runArgsAsync, s as tryRunArgs } from "./shell-C8KgKnMQ.mjs";
|
|
7
|
-
import { i as loadFormatter, n as generateChangelogEntry } from "./changelog-DrAf-rL6.mjs";
|
|
8
|
-
import { c as listTags, l as pushWithTags, s as hasUncommittedChanges } from "./git-CpJqzpp-.mjs";
|
|
9
|
-
import { t as publishPackages } from "./publish-pipeline-BGyQsVyw.mjs";
|
|
10
|
-
import { CI_PLAN_CACHE_PATH } from "./ci-9tmC3oNF.mjs";
|
|
11
|
-
//#region src/core/github-release.ts
|
|
12
|
-
/** Get the current HEAD commit SHA */
|
|
13
|
-
function getHeadSha(rootDir) {
|
|
14
|
-
return tryRunArgs([
|
|
15
|
-
"git",
|
|
16
|
-
"rev-parse",
|
|
17
|
-
"HEAD"
|
|
18
|
-
], { cwd: rootDir });
|
|
19
|
-
}
|
|
20
|
-
/** Create individual GitHub releases for each published package */
|
|
21
|
-
async function createIndividualReleases(releases, bumpFiles, rootDir, opts = {}) {
|
|
22
|
-
if (!isGhAvailable()) {
|
|
23
|
-
log.dim(" gh CLI not found — skipping GitHub releases");
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
const headSha = getHeadSha(rootDir);
|
|
27
|
-
for (const release of releases) {
|
|
28
|
-
const tag = `${release.name}@${release.newVersion}`;
|
|
29
|
-
const body = opts.formatter ? await generateReleaseBody(release, bumpFiles, opts.formatter) : buildReleaseBody(release, bumpFiles);
|
|
30
|
-
const title = `${release.name} v${release.newVersion}`;
|
|
31
|
-
if (opts.dryRun) {
|
|
32
|
-
log.dim(` Would create GitHub release: ${title}`);
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
try {
|
|
36
|
-
const args = [
|
|
37
|
-
"gh",
|
|
38
|
-
"release",
|
|
39
|
-
"create",
|
|
40
|
-
tag,
|
|
41
|
-
"--title",
|
|
42
|
-
title,
|
|
43
|
-
"--notes",
|
|
44
|
-
body
|
|
45
|
-
];
|
|
46
|
-
if (headSha) args.push("--target", headSha);
|
|
47
|
-
await runArgsAsync(args, { cwd: rootDir });
|
|
48
|
-
log.dim(` Created GitHub release: ${title}`);
|
|
49
|
-
} catch (err) {
|
|
50
|
-
log.warn(` Failed to create GitHub release for ${tag}: ${err instanceof Error ? err.message : err}`);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
/** Create a single aggregated GitHub release for all published packages */
|
|
55
|
-
async function createAggregateRelease(releases, bumpFiles, rootDir, opts = {}) {
|
|
56
|
-
if (!isGhAvailable()) {
|
|
57
|
-
log.dim(" gh CLI not found — skipping GitHub release");
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
if (releases.length === 0) return;
|
|
61
|
-
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
62
|
-
const { tag, title } = resolveAggregateTagAndTitle(date, listTags(`release-${date}*`, { cwd: rootDir }), opts.title);
|
|
63
|
-
const body = opts.formatter ? await generateAggregateBody(releases, bumpFiles, opts.formatter) : buildAggregateBody(releases, bumpFiles);
|
|
64
|
-
if (opts.dryRun) {
|
|
65
|
-
log.dim(` Would create aggregate GitHub release: ${title}`);
|
|
66
|
-
log.dim(` Tag: ${tag}`);
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
try {
|
|
70
|
-
tryRunArgs([
|
|
71
|
-
"git",
|
|
72
|
-
"tag",
|
|
73
|
-
tag
|
|
74
|
-
], { cwd: rootDir });
|
|
75
|
-
const headSha = getHeadSha(rootDir);
|
|
76
|
-
const args = [
|
|
77
|
-
"gh",
|
|
78
|
-
"release",
|
|
79
|
-
"create",
|
|
80
|
-
tag,
|
|
81
|
-
"--title",
|
|
82
|
-
title,
|
|
83
|
-
"--notes",
|
|
84
|
-
body
|
|
85
|
-
];
|
|
86
|
-
if (headSha) args.push("--target", headSha);
|
|
87
|
-
await runArgsAsync(args, { cwd: rootDir });
|
|
88
|
-
log.success(`Created aggregate GitHub release: ${title}`);
|
|
89
|
-
} catch (err) {
|
|
90
|
-
log.warn(`Failed to create aggregate GitHub release: ${err instanceof Error ? err.message : err}`);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
/** Generate a release body for a single package using the changelog formatter */
|
|
94
|
-
async function generateReleaseBody(release, bumpFiles, formatter) {
|
|
95
|
-
return stripVersionHeading(await generateChangelogEntry(release, bumpFiles, formatter, void 0, "github-release")).trim() || "No changelog entries.";
|
|
96
|
-
}
|
|
97
|
-
/** Generate an aggregate release body using the changelog formatter */
|
|
98
|
-
async function generateAggregateBody(releases, bumpFiles, formatter) {
|
|
99
|
-
const lines = [];
|
|
100
|
-
const groups = [
|
|
101
|
-
["Major Changes", releases.filter((r) => r.type === "major")],
|
|
102
|
-
["Minor Changes", releases.filter((r) => r.type === "minor")],
|
|
103
|
-
["Patch Changes", releases.filter((r) => r.type === "patch")]
|
|
104
|
-
];
|
|
105
|
-
for (const [heading, group] of groups) {
|
|
106
|
-
if (group.length === 0) continue;
|
|
107
|
-
lines.push(`## ${heading}\n`);
|
|
108
|
-
for (const release of group) {
|
|
109
|
-
lines.push(`### ${release.name} v${release.newVersion}\n`);
|
|
110
|
-
const body = stripVersionHeading(await generateChangelogEntry(release, bumpFiles, formatter, void 0, "github-release")).trim();
|
|
111
|
-
if (body) lines.push(body);
|
|
112
|
-
else if (release.isDependencyBump) {
|
|
113
|
-
const sourceList = release.bumpSources.map((s) => `\`${s.name}\` v${s.newVersion}`).join(", ");
|
|
114
|
-
lines.push(sourceList ? `- Updated dependency ${sourceList}` : "- Updated dependencies");
|
|
115
|
-
} else if (release.isGroupBump) {
|
|
116
|
-
const sourceList = release.bumpSources.map((s) => `\`${s.name}\` v${s.newVersion}`).join(", ");
|
|
117
|
-
lines.push(sourceList ? `- Version bump from group with ${sourceList}` : "- Version bump from group");
|
|
118
|
-
} else if (release.isCascadeBump) {
|
|
119
|
-
const sourceList = release.bumpSources.map((s) => `\`${s.name}\` v${s.newVersion}`).join(", ");
|
|
120
|
-
lines.push(sourceList ? `- Version bump from ${sourceList}` : "- Version bump via cascade rule");
|
|
121
|
-
}
|
|
122
|
-
lines.push("");
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
return lines.join("\n").trim() || "No changelog entries.";
|
|
126
|
-
}
|
|
127
|
-
/** Strip the leading ## version heading and date sub-heading from a changelog entry */
|
|
128
|
-
function stripVersionHeading(entry) {
|
|
129
|
-
return entry.replace(/^## .+\n/, "").replace(/^<sub>.+<\/sub>\n/, "").replace(/^_.+_\n/, "");
|
|
130
|
-
}
|
|
131
|
-
function buildReleaseBody(release, bumpFiles) {
|
|
132
|
-
const lines = [];
|
|
133
|
-
const relevant = bumpFiles.filter((bf) => release.bumpFiles.includes(bf.id));
|
|
134
|
-
if (relevant.length > 0) {
|
|
135
|
-
for (const bf of relevant) if (bf.summary) lines.push(`- ${bf.summary.split("\n")[0]}`);
|
|
136
|
-
}
|
|
137
|
-
if (relevant.length === 0) {
|
|
138
|
-
const sourceList = release.bumpSources.map((s) => `\`${s.name}\` v${s.newVersion}`).join(", ");
|
|
139
|
-
if (release.isDependencyBump) lines.push(sourceList ? `- Updated dependency ${sourceList}` : "- Updated dependencies");
|
|
140
|
-
else if (release.isGroupBump) lines.push(sourceList ? `- Version bump from group with ${sourceList}` : "- Version bump from group");
|
|
141
|
-
else if (release.isCascadeBump) lines.push(sourceList ? `- Version bump from ${sourceList}` : "- Version bump via cascade rule");
|
|
142
|
-
}
|
|
143
|
-
return lines.join("\n") || "No changelog entries.";
|
|
144
|
-
}
|
|
145
|
-
function buildAggregateBody(releases, bumpFiles) {
|
|
146
|
-
const lines = [];
|
|
147
|
-
const groups = [
|
|
148
|
-
["Major Changes", releases.filter((r) => r.type === "major")],
|
|
149
|
-
["Minor Changes", releases.filter((r) => r.type === "minor")],
|
|
150
|
-
["Patch Changes", releases.filter((r) => r.type === "patch")]
|
|
151
|
-
];
|
|
152
|
-
for (const [heading, group] of groups) {
|
|
153
|
-
if (group.length === 0) continue;
|
|
154
|
-
lines.push(`## ${heading}\n`);
|
|
155
|
-
for (const release of group) {
|
|
156
|
-
lines.push(`### ${release.name} v${release.newVersion}\n`);
|
|
157
|
-
const relevant = bumpFiles.filter((bf) => release.bumpFiles.includes(bf.id));
|
|
158
|
-
if (relevant.length > 0) {
|
|
159
|
-
for (const bf of relevant) if (bf.summary) lines.push(`- ${bf.summary.split("\n")[0]}`);
|
|
160
|
-
} else {
|
|
161
|
-
const sourceList = release.bumpSources.map((s) => `\`${s.name}\` v${s.newVersion}`).join(", ");
|
|
162
|
-
if (release.isDependencyBump) lines.push(sourceList ? `- Updated dependency ${sourceList}` : "- Updated dependencies");
|
|
163
|
-
else if (release.isGroupBump) lines.push(sourceList ? `- Version bump from group with ${sourceList}` : "- Version bump from group");
|
|
164
|
-
else if (release.isCascadeBump) lines.push(sourceList ? `- Version bump from ${sourceList}` : "- Version bump via cascade rule");
|
|
165
|
-
}
|
|
166
|
-
lines.push("");
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return lines.join("\n").trim() || "No changelog entries.";
|
|
170
|
-
}
|
|
171
|
-
/** Compute the aggregate release tag and title, appending -n suffix if a tag for the same date already exists */
|
|
172
|
-
function resolveAggregateTagAndTitle(date, existingTags, titleTemplate) {
|
|
173
|
-
const baseTag = `release-${date}`;
|
|
174
|
-
const suffix = existingTags.length === 0 ? "" : `-${existingTags.length + 1}`;
|
|
175
|
-
return {
|
|
176
|
-
tag: `${baseTag}${suffix}`,
|
|
177
|
-
title: (titleTemplate || "Release {{date}}").replace("{{date}}", `${date}${suffix}`)
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
function isGhAvailable() {
|
|
181
|
-
return tryRunArgs(["gh", "--version"]) !== null;
|
|
182
|
-
}
|
|
183
|
-
//#endregion
|
|
184
|
-
//#region src/commands/publish.ts
|
|
185
|
-
/**
|
|
186
|
-
* Publish packages that have been versioned but not yet published.
|
|
187
|
-
* Detects unpublished versions by comparing package.json versions against npm registry.
|
|
188
|
-
*/
|
|
189
|
-
async function publishCommand(rootDir, opts) {
|
|
190
|
-
const config = await loadConfig(rootDir);
|
|
191
|
-
const { packages, catalogs } = await discoverWorkspace(rootDir, config);
|
|
192
|
-
const { packageManager: detectedPm } = await detectWorkspaces(rootDir);
|
|
193
|
-
const depGraph = new DependencyGraph(packages);
|
|
194
|
-
if (!opts.dryRun && hasUncommittedChanges({ cwd: rootDir })) {
|
|
195
|
-
log.warn("You have uncommitted changes. Commit or stash them before publishing.");
|
|
196
|
-
process.exit(1);
|
|
197
|
-
}
|
|
198
|
-
let toPublish = await findUnpublishedWithCache(rootDir, packages, config);
|
|
199
|
-
if (opts.filter) {
|
|
200
|
-
const { matchGlob } = await import("./config-CDC1Y_ye.mjs").then((n) => n.t);
|
|
201
|
-
const patterns = opts.filter.split(",").map((p) => p.trim());
|
|
202
|
-
toPublish = toPublish.filter((r) => patterns.some((p) => matchGlob(r.name, p)));
|
|
203
|
-
}
|
|
204
|
-
if (toPublish.length === 0) {
|
|
205
|
-
log.info("No unpublished packages found.");
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
const recoveredBumpFiles = opts.recoveredBumpFiles || [];
|
|
209
|
-
if (recoveredBumpFiles.length > 0) for (const release of toPublish) release.bumpFiles = recoveredBumpFiles.filter((bf) => bf.releases.some((r) => r.name === release.name)).map((bf) => bf.id);
|
|
210
|
-
const releasePlan = {
|
|
211
|
-
bumpFiles: recoveredBumpFiles,
|
|
212
|
-
releases: toPublish,
|
|
213
|
-
warnings: []
|
|
214
|
-
};
|
|
215
|
-
if (opts.dryRun) log.bold("Dry run — would publish:");
|
|
216
|
-
else log.bold("Publishing:");
|
|
217
|
-
for (const r of toPublish) console.log(` ${r.name}@${colorize(r.newVersion, "cyan")}`);
|
|
218
|
-
console.log();
|
|
219
|
-
const result = await publishPackages(releasePlan, packages, depGraph, config, rootDir, {
|
|
220
|
-
dryRun: opts.dryRun,
|
|
221
|
-
tag: opts.tag
|
|
222
|
-
}, catalogs, detectedPm);
|
|
223
|
-
if (result.published.length > 0) log.success(`🐸 Published ${result.published.length} package(s)`);
|
|
224
|
-
if (result.skipped.length > 0) log.dim(`Skipped ${result.skipped.length}: ${result.skipped.map((s) => s.name).join(", ")}`);
|
|
225
|
-
if (result.failed.length > 0) {
|
|
226
|
-
log.error(`Failed ${result.failed.length}: ${result.failed.map((f) => `${f.name} (${f.error})`).join(", ")}`);
|
|
227
|
-
process.exit(1);
|
|
228
|
-
}
|
|
229
|
-
if (!opts.dryRun && !opts.noPush && result.published.length > 0) try {
|
|
230
|
-
log.step("Pushing tags...");
|
|
231
|
-
pushWithTags({ cwd: rootDir });
|
|
232
|
-
log.success("Pushed tags to remote");
|
|
233
|
-
} catch (err) {
|
|
234
|
-
log.warn(`Failed to push tags: ${err instanceof Error ? err.message : err}`);
|
|
235
|
-
}
|
|
236
|
-
if (result.published.length > 0) {
|
|
237
|
-
const publishedReleases = releasePlan.releases.filter((r) => result.published.some((p) => p.name === r.name));
|
|
238
|
-
const aggConfig = config.aggregateRelease;
|
|
239
|
-
const isAggregate = aggConfig === true || typeof aggConfig === "object" && aggConfig.enabled;
|
|
240
|
-
const aggTitle = typeof aggConfig === "object" ? aggConfig.title : void 0;
|
|
241
|
-
const formatter = config.changelog !== false ? await loadFormatter(config.changelog, rootDir) : void 0;
|
|
242
|
-
if (isAggregate) await createAggregateRelease(publishedReleases, releasePlan.bumpFiles, rootDir, {
|
|
243
|
-
dryRun: opts.dryRun,
|
|
244
|
-
title: aggTitle,
|
|
245
|
-
formatter
|
|
246
|
-
});
|
|
247
|
-
else await createIndividualReleases(publishedReleases, releasePlan.bumpFiles, rootDir, {
|
|
248
|
-
dryRun: opts.dryRun,
|
|
249
|
-
formatter
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* Try to load cached plan from `ci plan`. Returns the unpublished package names
|
|
255
|
-
* if the cache is valid, or null to fall back to registry lookups.
|
|
256
|
-
*
|
|
257
|
-
* Validates that every cached package exists in the workspace with the same version,
|
|
258
|
-
* so the cache can only filter — never fabricate — the set of packages.
|
|
259
|
-
*/
|
|
260
|
-
function loadCachedPlan(rootDir, packages) {
|
|
261
|
-
const cachePath = `${rootDir}/${CI_PLAN_CACHE_PATH}`;
|
|
262
|
-
let raw;
|
|
263
|
-
try {
|
|
264
|
-
raw = __require("node:fs").readFileSync(cachePath, "utf-8");
|
|
265
|
-
__require("node:fs").unlinkSync(cachePath);
|
|
266
|
-
} catch {
|
|
267
|
-
return null;
|
|
268
|
-
}
|
|
269
|
-
try {
|
|
270
|
-
const cached = JSON.parse(raw);
|
|
271
|
-
if (cached?.mode !== "publish" || !Array.isArray(cached.releases)) return null;
|
|
272
|
-
const names = /* @__PURE__ */ new Set();
|
|
273
|
-
for (const r of cached.releases) {
|
|
274
|
-
if (typeof r.name !== "string" || typeof r.newVersion !== "string") return null;
|
|
275
|
-
const pkg = packages.get(r.name);
|
|
276
|
-
if (!pkg || pkg.version !== r.newVersion) {
|
|
277
|
-
log.dim(" ci plan cache is stale — falling back to registry lookups");
|
|
278
|
-
return null;
|
|
279
|
-
}
|
|
280
|
-
names.add(r.name);
|
|
281
|
-
}
|
|
282
|
-
log.dim(" Using cached plan from ci plan");
|
|
283
|
-
return names;
|
|
284
|
-
} catch {
|
|
285
|
-
return null;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
/**
|
|
289
|
-
* Find unpublished packages, using the ci plan cache if available.
|
|
290
|
-
* Falls back to registry lookups if no cache or cache is invalid.
|
|
291
|
-
*/
|
|
292
|
-
async function findUnpublishedWithCache(rootDir, packages, config) {
|
|
293
|
-
const cachedNames = loadCachedPlan(rootDir, packages);
|
|
294
|
-
if (cachedNames) {
|
|
295
|
-
const unpublished = [];
|
|
296
|
-
for (const name of cachedNames) {
|
|
297
|
-
const pkg = packages.get(name);
|
|
298
|
-
unpublished.push({
|
|
299
|
-
name,
|
|
300
|
-
type: "patch",
|
|
301
|
-
oldVersion: pkg.version,
|
|
302
|
-
newVersion: pkg.version,
|
|
303
|
-
bumpFiles: [],
|
|
304
|
-
isDependencyBump: false,
|
|
305
|
-
isCascadeBump: false,
|
|
306
|
-
isGroupBump: false,
|
|
307
|
-
bumpSources: []
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
return unpublished;
|
|
311
|
-
}
|
|
312
|
-
return findUnpublishedPackages(packages, config);
|
|
313
|
-
}
|
|
314
|
-
/**
|
|
315
|
-
* Find packages whose current version is not yet published.
|
|
316
|
-
*
|
|
317
|
-
* Detection strategy (per package):
|
|
318
|
-
* 1. Custom `checkPublished` command → run it, compare output to current version
|
|
319
|
-
* 2. `skipNpmPublish` or custom `publishCommand` → check git tags
|
|
320
|
-
* 3. Default → check npm registry via `npm info`
|
|
321
|
-
*/
|
|
322
|
-
async function findUnpublishedPackages(packages, _config) {
|
|
323
|
-
const unpublished = [];
|
|
324
|
-
for (const [name, pkg] of packages) {
|
|
325
|
-
if (pkg.private && !pkg.bumpy?.publishCommand) continue;
|
|
326
|
-
if (pkg.version === "0.0.0") continue;
|
|
327
|
-
if (!await checkIfPublished(name, pkg.version, pkg.bumpy)) unpublished.push({
|
|
328
|
-
name,
|
|
329
|
-
type: "patch",
|
|
330
|
-
oldVersion: pkg.version,
|
|
331
|
-
newVersion: pkg.version,
|
|
332
|
-
bumpFiles: [],
|
|
333
|
-
isDependencyBump: false,
|
|
334
|
-
isCascadeBump: false,
|
|
335
|
-
isGroupBump: false,
|
|
336
|
-
bumpSources: []
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
return unpublished;
|
|
340
|
-
}
|
|
341
|
-
async function checkIfPublished(name, version, pkgConfig) {
|
|
342
|
-
const { runAsync, runArgsAsync, tryRunArgs } = await import("./shell-C8KgKnMQ.mjs").then((n) => n.a);
|
|
343
|
-
if (pkgConfig?.checkPublished) try {
|
|
344
|
-
return (await runAsync(pkgConfig.checkPublished)).trim() === version;
|
|
345
|
-
} catch {
|
|
346
|
-
return false;
|
|
347
|
-
}
|
|
348
|
-
if (pkgConfig?.skipNpmPublish || pkgConfig?.publishCommand) {
|
|
349
|
-
const tag = `${name}@${version}`;
|
|
350
|
-
return tryRunArgs([
|
|
351
|
-
"git",
|
|
352
|
-
"tag",
|
|
353
|
-
"-l",
|
|
354
|
-
tag
|
|
355
|
-
]) === tag;
|
|
356
|
-
}
|
|
357
|
-
try {
|
|
358
|
-
const args = [
|
|
359
|
-
"npm",
|
|
360
|
-
"info",
|
|
361
|
-
`${name}@${version}`,
|
|
362
|
-
"version"
|
|
363
|
-
];
|
|
364
|
-
if (pkgConfig?.registry) args.push("--registry", pkgConfig.registry);
|
|
365
|
-
return await runArgsAsync(args) === version;
|
|
366
|
-
} catch {
|
|
367
|
-
return false;
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
//#endregion
|
|
371
|
-
export { findUnpublishedPackages, publishCommand };
|