@varlock/bumpy 1.13.2 → 1.14.0-rc.1
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 +3 -1
- package/config-schema.json +43 -0
- package/dist/{add-Dt1hddMt.mjs → add-C9rU_89s.mjs} +4 -4
- package/dist/{apply-release-plan-DD2R7SL2.mjs → apply-release-plan-DxTsUSqa.mjs} +11 -2
- package/dist/{bump-file-B7hmXZlB.mjs → bump-file-mRJeReRJ.mjs} +43 -8
- package/dist/{changelog-CbaET5V6.mjs → changelog-DuFhnJRO.mjs} +3 -3
- package/dist/{changelog-github-DXDnWkrB.mjs → changelog-github-jLOtwuWj.mjs} +2 -2
- package/dist/channels-CFXZkyGd.mjs +75 -0
- package/dist/{check-Dvi0DIqC.mjs → check-DIl9Dz68.mjs} +18 -6
- package/dist/{ci-B7gF6CFP.mjs → ci-ChYmDuwy.mjs} +376 -23
- package/dist/cli.mjs +30 -15
- package/dist/{config-D_4GYDJi.mjs → config-0we4ISZX.mjs} +5 -1
- package/dist/{generate-DohUlhu3.mjs → generate-B2OMt_64.mjs} +3 -3
- package/dist/{git-BWPimLgc.mjs → git-DAWj8LyV.mjs} +12 -1
- package/dist/index.d.mts +46 -4
- package/dist/index.mjs +7 -7
- package/dist/prerelease-B2PVfXkm.mjs +205 -0
- package/dist/{publish-VYBhDYFM.mjs → publish-Ak6jmwi_.mjs} +105 -14
- package/dist/{publish-pipeline-BPedWvKS.mjs → publish-pipeline-BD8mLbL9.mjs} +2 -2
- package/dist/{release-plan-mK7iGeGq.mjs → release-plan-C84pcBi-.mjs} +12 -17
- package/dist/status-DIzi-Iai.mjs +232 -0
- package/dist/{types-Bkh-igOJ.mjs → types-lpiG-Zxh.mjs} +1 -0
- package/dist/version-CMJUopVj.mjs +192 -0
- package/package.json +1 -1
- package/dist/status-DxzKPM8d.mjs +0 -129
- package/dist/version-Cm0nRAFF.mjs +0 -123
- /package/dist/{commit-message-CSWVKPJ-.mjs → commit-message-BwsowSds.mjs} +0 -0
- /package/dist/{init-BCkm6Nfa.mjs → init-DkAY5hjc.mjs} +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { n as log, t as colorize } from "./logger-BgksGFuf.mjs";
|
|
2
2
|
import { t as ensureDir } from "./fs-CBXKZhoU.mjs";
|
|
3
|
-
import { a as loadConfig, r as getBumpyDir } from "./config-
|
|
4
|
-
import {
|
|
3
|
+
import { a as loadConfig, r as getBumpyDir } from "./config-0we4ISZX.mjs";
|
|
4
|
+
import { o as writeBumpFile, s as discoverPackages } from "./bump-file-mRJeReRJ.mjs";
|
|
5
5
|
import { s as tryRunArgs } from "./shell-C8KgKnMQ.mjs";
|
|
6
|
-
import { c as getFilesChangedInCommit, i as getBranchCommits } from "./git-
|
|
6
|
+
import { c as getFilesChangedInCommit, i as getBranchCommits } from "./git-DAWj8LyV.mjs";
|
|
7
7
|
import { n as slugify, t as randomName } from "./names-COooXAFg.mjs";
|
|
8
8
|
import { relative } from "node:path";
|
|
9
9
|
//#region src/commands/generate.ts
|
|
@@ -269,5 +269,16 @@ function getFileStatuses(dir, opts) {
|
|
|
269
269
|
}
|
|
270
270
|
return statuses;
|
|
271
271
|
}
|
|
272
|
+
/** Get all tags matching a pattern */
|
|
273
|
+
function listTags(pattern, opts) {
|
|
274
|
+
const result = tryRunArgs([
|
|
275
|
+
"git",
|
|
276
|
+
"tag",
|
|
277
|
+
"-l",
|
|
278
|
+
pattern
|
|
279
|
+
], opts);
|
|
280
|
+
if (!result) return [];
|
|
281
|
+
return result.split("\n").filter(Boolean);
|
|
282
|
+
}
|
|
272
283
|
//#endregion
|
|
273
|
-
export { getChangedFiles as a, getFilesChangedInCommit as c,
|
|
284
|
+
export { getChangedFiles as a, getFilesChangedInCommit as c, readFileAtRef as d, tagExists as f, getBranchCommits as i, hasUncommittedChanges as l, forcePushTag as n, getCurrentBranch as o, withGitToken as p, getBaseCompareRef as r, getFileStatuses as s, createTag as t, listTags as u };
|
package/dist/index.d.mts
CHANGED
|
@@ -53,9 +53,30 @@ interface PublishConfig {
|
|
|
53
53
|
*/
|
|
54
54
|
npmStaged: boolean;
|
|
55
55
|
}
|
|
56
|
+
interface ChannelConfig {
|
|
57
|
+
/** Branch that triggers this channel (required) */
|
|
58
|
+
branch: string;
|
|
59
|
+
/** Version suffix (preid), e.g. "rc" → 1.2.0-rc.0. Defaults to the channel name. */
|
|
60
|
+
preid?: string;
|
|
61
|
+
/** npm dist-tag for publishes. Defaults to the channel name. */
|
|
62
|
+
tag?: string;
|
|
63
|
+
/** Release PR overrides for this channel */
|
|
64
|
+
versionPr?: {
|
|
65
|
+
title?: string;
|
|
66
|
+
branch?: string; /** Enable auto-merge on the release PR */
|
|
67
|
+
automerge?: boolean;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
56
70
|
interface BumpyConfig {
|
|
57
71
|
baseBranch: string;
|
|
58
72
|
access: 'public' | 'restricted';
|
|
73
|
+
/**
|
|
74
|
+
* Prerelease channels, keyed by channel name. Each maps a long-lived branch
|
|
75
|
+
* to a prerelease line (version suffix + npm dist-tag). Shipped bump files
|
|
76
|
+
* are tracked in `.bumpy/<name>/`. Prerelease versions are never committed —
|
|
77
|
+
* they are derived from bump files, the registry, and git tags at publish time.
|
|
78
|
+
*/
|
|
79
|
+
channels: Record<string, ChannelConfig>;
|
|
59
80
|
/**
|
|
60
81
|
* Customize the commit message used when versioning.
|
|
61
82
|
* A string starting with "./" is treated as a path to a module that exports
|
|
@@ -136,6 +157,8 @@ interface BumpFile {
|
|
|
136
157
|
id: string;
|
|
137
158
|
releases: BumpFileRelease[];
|
|
138
159
|
summary: string;
|
|
160
|
+
/** Channel directory this file lives in (`.bumpy/<channel>/`), if any. Undefined = `.bumpy/` root. */
|
|
161
|
+
channel?: string;
|
|
139
162
|
}
|
|
140
163
|
interface WorkspacePackage {
|
|
141
164
|
name: string;
|
|
@@ -219,8 +242,15 @@ interface ReadBumpFilesResult {
|
|
|
219
242
|
bumpFiles: BumpFile[];
|
|
220
243
|
errors: string[];
|
|
221
244
|
}
|
|
222
|
-
|
|
223
|
-
|
|
245
|
+
interface ReadBumpFilesOptions {
|
|
246
|
+
/**
|
|
247
|
+
* Channel names whose `.bumpy/<name>/` subdirectories should also be read.
|
|
248
|
+
* Files from those dirs get their `channel` field set; root files don't.
|
|
249
|
+
*/
|
|
250
|
+
channels?: string[];
|
|
251
|
+
}
|
|
252
|
+
/** Read all bump files from .bumpy/ (and optionally channel subdirs), sorted by git creation order */
|
|
253
|
+
declare function readBumpFiles(rootDir: string, opts?: ReadBumpFilesOptions): Promise<ReadBumpFilesResult>;
|
|
224
254
|
interface BumpFileParseResult {
|
|
225
255
|
bumpFile: BumpFile | null;
|
|
226
256
|
errors: string[];
|
|
@@ -240,7 +270,19 @@ declare function writeBumpFile(rootDir: string, filename: string, releases: Bump
|
|
|
240
270
|
* Phase B — enforce fixed/linked group constraints
|
|
241
271
|
* Phase C — apply cascades and proactive propagation rules
|
|
242
272
|
*/
|
|
243
|
-
|
|
273
|
+
interface AssemblePlanOptions {
|
|
274
|
+
/**
|
|
275
|
+
* Prerelease preid for channel plans (e.g. "rc"). When set, Phase A checks range
|
|
276
|
+
* satisfaction against `<target>-<preid>.0` instead of the stable target. Since a
|
|
277
|
+
* prerelease never satisfies a normal range (`^1.0.0` doesn't match `1.5.0-rc.0`),
|
|
278
|
+
* every dependent of a bumped package goes out of range and joins the cycle —
|
|
279
|
+
* required so consumers can actually install the cycle together from the dist-tag.
|
|
280
|
+
* Planned versions remain stable targets; the prerelease suffix is applied later
|
|
281
|
+
* from the registry floor.
|
|
282
|
+
*/
|
|
283
|
+
prereleasePreid?: string;
|
|
284
|
+
}
|
|
285
|
+
declare function assembleReleasePlan(bumpFiles: BumpFile[], packages: Map<string, WorkspacePackage>, depGraph: DependencyGraph, config: BumpyConfig, opts?: AssemblePlanOptions): ReleasePlan;
|
|
244
286
|
//#endregion
|
|
245
287
|
//#region src/core/apply-release-plan.d.ts
|
|
246
288
|
/** Apply the release plan: bump versions, update changelogs, delete bump files */
|
|
@@ -322,4 +364,4 @@ interface PublishResult {
|
|
|
322
364
|
*/
|
|
323
365
|
declare function publishPackages(releasePlan: ReleasePlan, packages: Map<string, WorkspacePackage>, depGraph: DependencyGraph, config: BumpyConfig, rootDir: string, opts?: PublishOptions, catalogs?: CatalogMap, detectedPm?: PackageManager): Promise<PublishResult>;
|
|
324
366
|
//#endregion
|
|
325
|
-
export { BUMP_LEVELS, BumpFile, type BumpFileParseResult, BumpFileRelease, BumpFileReleaseCascade, BumpFileReleaseSimple, BumpType, BumpTypeWithNone, BumpyConfig, CascadeConfig, CascadeRule, type ChangelogContext, type ChangelogFormatter, DEFAULT_BUMP_RULES, DEFAULT_CONFIG, DEFAULT_PUBLISH_CONFIG, DEP_TYPES, DepType, DependencyBumpRule, DependencyGraph, DependentInfo, type GithubChangelogOptions, PackageConfig, PackageManager, PlannedRelease, PublishConfig, type ReadBumpFilesResult, ReleasePlan, WorkspacePackage, applyReleasePlan, assembleReleasePlan, bumpLevel, bumpVersion, defaultFormatter, discoverPackages, findRoot, generateChangelogEntry, getBumpyDir, hasCascade, loadConfig, loadFormatter, matchGlob, maxBump, normalizeCascadeConfig, parseBumpFile, prependToChangelog, publishPackages, readBumpFiles, satisfies, stripProtocol, writeBumpFile };
|
|
367
|
+
export { BUMP_LEVELS, BumpFile, type BumpFileParseResult, BumpFileRelease, BumpFileReleaseCascade, BumpFileReleaseSimple, BumpType, BumpTypeWithNone, BumpyConfig, CascadeConfig, CascadeRule, type ChangelogContext, type ChangelogFormatter, ChannelConfig, DEFAULT_BUMP_RULES, DEFAULT_CONFIG, DEFAULT_PUBLISH_CONFIG, DEP_TYPES, DepType, DependencyBumpRule, DependencyGraph, DependentInfo, type GithubChangelogOptions, PackageConfig, PackageManager, PlannedRelease, PublishConfig, type ReadBumpFilesResult, ReleasePlan, WorkspacePackage, applyReleasePlan, assembleReleasePlan, bumpLevel, bumpVersion, defaultFormatter, discoverPackages, findRoot, generateChangelogEntry, getBumpyDir, hasCascade, loadConfig, loadFormatter, matchGlob, maxBump, normalizeCascadeConfig, parseBumpFile, prependToChangelog, publishPackages, readBumpFiles, satisfies, stripProtocol, writeBumpFile };
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { a as DEP_TYPES, c as maxBump, i as DEFAULT_PUBLISH_CONFIG, l as normalizeCascadeConfig, n as DEFAULT_BUMP_RULES, o as bumpLevel, r as DEFAULT_CONFIG, s as hasCascade, t as BUMP_LEVELS } from "./types-
|
|
2
|
-
import { a as loadConfig, n as findRoot, r as getBumpyDir, s as matchGlob } from "./config-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { a as prependToChangelog, i as loadFormatter, n as generateChangelogEntry, t as defaultFormatter } from "./changelog-
|
|
6
|
-
import { t as applyReleasePlan } from "./apply-release-plan-
|
|
7
|
-
import { t as publishPackages } from "./publish-pipeline-
|
|
1
|
+
import { a as DEP_TYPES, c as maxBump, i as DEFAULT_PUBLISH_CONFIG, l as normalizeCascadeConfig, n as DEFAULT_BUMP_RULES, o as bumpLevel, r as DEFAULT_CONFIG, s as hasCascade, t as BUMP_LEVELS } from "./types-lpiG-Zxh.mjs";
|
|
2
|
+
import { a as loadConfig, n as findRoot, r as getBumpyDir, s as matchGlob } from "./config-0we4ISZX.mjs";
|
|
3
|
+
import { i as readBumpFiles, o as writeBumpFile, r as parseBumpFile, s as discoverPackages } from "./bump-file-mRJeReRJ.mjs";
|
|
4
|
+
import { i as stripProtocol, n as bumpVersion, o as DependencyGraph, r as satisfies, t as assembleReleasePlan } from "./release-plan-C84pcBi-.mjs";
|
|
5
|
+
import { a as prependToChangelog, i as loadFormatter, n as generateChangelogEntry, t as defaultFormatter } from "./changelog-DuFhnJRO.mjs";
|
|
6
|
+
import { t as applyReleasePlan } from "./apply-release-plan-DxTsUSqa.mjs";
|
|
7
|
+
import { t as publishPackages } from "./publish-pipeline-BD8mLbL9.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 };
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { s as __toESM } from "./logger-BgksGFuf.mjs";
|
|
2
|
+
import { f as writeText, l as updateJsonFields, s as readText, u as updateJsonNestedField } from "./fs-CBXKZhoU.mjs";
|
|
3
|
+
import { a as require_semver } from "./release-plan-C84pcBi-.mjs";
|
|
4
|
+
import { r as runArgsAsync, s as tryRunArgs } from "./shell-C8KgKnMQ.mjs";
|
|
5
|
+
import { u as listTags } from "./git-DAWj8LyV.mjs";
|
|
6
|
+
import { resolve } from "node:path";
|
|
7
|
+
//#region src/core/prerelease.ts
|
|
8
|
+
var import_semver = /* @__PURE__ */ __toESM(require_semver(), 1);
|
|
9
|
+
/**
|
|
10
|
+
* Extract existing prerelease counters for a target+preid from a list of published versions.
|
|
11
|
+
* Only exact `<target>-<preid>.<N>` versions count — other preids and targets are ignored.
|
|
12
|
+
*/
|
|
13
|
+
function extractPrereleaseCounters(versions, target, preid) {
|
|
14
|
+
const counters = [];
|
|
15
|
+
for (const v of versions) {
|
|
16
|
+
const parsed = import_semver.default.parse(v);
|
|
17
|
+
if (!parsed) continue;
|
|
18
|
+
if (`${parsed.major}.${parsed.minor}.${parsed.patch}` !== target) continue;
|
|
19
|
+
if (parsed.prerelease.length !== 2) continue;
|
|
20
|
+
if (parsed.prerelease[0] !== preid) continue;
|
|
21
|
+
const n = parsed.prerelease[1];
|
|
22
|
+
if (typeof n === "number") counters.push(n);
|
|
23
|
+
}
|
|
24
|
+
return counters;
|
|
25
|
+
}
|
|
26
|
+
/** Compute the next prerelease version: `<target>-<preid>.<max existing counter + 1>` */
|
|
27
|
+
function nextPrereleaseVersion(target, preid, existingCounters) {
|
|
28
|
+
return `${target}-${preid}.${existingCounters.length > 0 ? Math.max(...existingCounters) + 1 : 0}`;
|
|
29
|
+
}
|
|
30
|
+
/** Fetch all published versions of a package from the registry */
|
|
31
|
+
async function fetchPublishedVersions(name, registry) {
|
|
32
|
+
const args = [
|
|
33
|
+
"npm",
|
|
34
|
+
"info",
|
|
35
|
+
name,
|
|
36
|
+
"versions",
|
|
37
|
+
"--json"
|
|
38
|
+
];
|
|
39
|
+
if (registry) args.push("--registry", registry);
|
|
40
|
+
try {
|
|
41
|
+
const raw = await runArgsAsync(args);
|
|
42
|
+
const parsed = JSON.parse(raw);
|
|
43
|
+
if (Array.isArray(parsed)) return parsed.filter((v) => typeof v === "string");
|
|
44
|
+
if (typeof parsed === "string") return [parsed];
|
|
45
|
+
return [];
|
|
46
|
+
} catch {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** Fetch the gitHead recorded for a published version (set by npm publish from a git checkout) */
|
|
51
|
+
async function fetchGitHead(name, version, registry) {
|
|
52
|
+
const args = [
|
|
53
|
+
"npm",
|
|
54
|
+
"info",
|
|
55
|
+
`${name}@${version}`,
|
|
56
|
+
"gitHead"
|
|
57
|
+
];
|
|
58
|
+
if (registry) args.push("--registry", registry);
|
|
59
|
+
try {
|
|
60
|
+
const sha = (await runArgsAsync(args)).trim();
|
|
61
|
+
return /^[0-9a-f]{40}$/.test(sha) ? sha : null;
|
|
62
|
+
} catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Whether a package publishes through the npm registry (vs custom command / git-tag tracking) */
|
|
67
|
+
function usesNpmRegistry(pkg) {
|
|
68
|
+
return !pkg.bumpy?.publishCommand && !pkg.bumpy?.skipNpmPublish && !pkg.private;
|
|
69
|
+
}
|
|
70
|
+
/** Query published prerelease state for one package at a target version */
|
|
71
|
+
async function getPublishedPrereleaseState(pkg, target, preid, rootDir) {
|
|
72
|
+
if (usesNpmRegistry(pkg)) {
|
|
73
|
+
const versions = await fetchPublishedVersions(pkg.name, pkg.bumpy?.registry);
|
|
74
|
+
return {
|
|
75
|
+
counters: extractPrereleaseCounters(versions, target, preid),
|
|
76
|
+
stablePublished: versions.includes(target)
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
counters: extractPrereleaseCounters(listTags(`${pkg.name}@${target}-${preid}.*`, { cwd: rootDir }).map((t) => t.slice(pkg.name.length + 1)), target, preid),
|
|
81
|
+
stablePublished: listTags(`${pkg.name}@${target}`, { cwd: rootDir }).length > 0
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Transform a stable release plan (targets computed from bump files) into a channel
|
|
86
|
+
* prerelease plan: each release's newVersion becomes `<target>-<preid>.<N>` with N
|
|
87
|
+
* derived from the registry floor.
|
|
88
|
+
*
|
|
89
|
+
* Idempotency: if a package's latest published prerelease for this target records a
|
|
90
|
+
* gitHead equal to HEAD (or, for non-npm packages, its tag points at HEAD), the package
|
|
91
|
+
* was already published from this exact commit — it is skipped rather than re-counted.
|
|
92
|
+
*/
|
|
93
|
+
async function buildChannelReleasePlan(stablePlan, channel, packages, rootDir, opts = {}) {
|
|
94
|
+
const headSha = tryRunArgs([
|
|
95
|
+
"git",
|
|
96
|
+
"rev-parse",
|
|
97
|
+
"HEAD"
|
|
98
|
+
], { cwd: rootDir });
|
|
99
|
+
const warnings = [...stablePlan.warnings];
|
|
100
|
+
const alreadyPublished = [];
|
|
101
|
+
const releases = [];
|
|
102
|
+
await Promise.all(stablePlan.releases.map(async (release) => {
|
|
103
|
+
const pkg = packages.get(release.name);
|
|
104
|
+
if (!pkg) return;
|
|
105
|
+
if (pkg.private && !pkg.bumpy?.publishCommand) return;
|
|
106
|
+
const target = release.newVersion;
|
|
107
|
+
const state = await getPublishedPrereleaseState(pkg, target, channel.preid, rootDir);
|
|
108
|
+
if (state.stablePublished) warnings.push(`${release.name}@${target} is already published as a stable release — merge ${target}'s release into the "${channel.branch}" branch so the cycle retargets.`);
|
|
109
|
+
if (!opts.forDisplay && state.counters.length > 0 && headSha) {
|
|
110
|
+
const latest = `${target}-${channel.preid}.${Math.max(...state.counters)}`;
|
|
111
|
+
if (usesNpmRegistry(pkg) ? await fetchGitHead(pkg.name, latest, pkg.bumpy?.registry) === headSha : tryRunArgs([
|
|
112
|
+
"git",
|
|
113
|
+
"rev-parse",
|
|
114
|
+
`refs/tags/${pkg.name}@${latest}`
|
|
115
|
+
], { cwd: rootDir }) === headSha) {
|
|
116
|
+
alreadyPublished.push({
|
|
117
|
+
name: release.name,
|
|
118
|
+
version: latest
|
|
119
|
+
});
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
releases.push({
|
|
124
|
+
...release,
|
|
125
|
+
newVersion: nextPrereleaseVersion(target, channel.preid, state.counters)
|
|
126
|
+
});
|
|
127
|
+
}));
|
|
128
|
+
releases.sort((a, b) => a.name.localeCompare(b.name));
|
|
129
|
+
return {
|
|
130
|
+
plan: {
|
|
131
|
+
bumpFiles: stablePlan.bumpFiles,
|
|
132
|
+
releases,
|
|
133
|
+
warnings
|
|
134
|
+
},
|
|
135
|
+
alreadyPublished,
|
|
136
|
+
warnings
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Transiently write computed prerelease versions (and exact pins for in-cycle deps)
|
|
141
|
+
* into the working tree's package.json files. Returns a restore function that puts
|
|
142
|
+
* the original contents back — call it in a `finally` after publishing.
|
|
143
|
+
*
|
|
144
|
+
* Versions must be on disk before build/pack so that:
|
|
145
|
+
* - PM pack picks up the prerelease version for the tarball
|
|
146
|
+
* - builds that bake in the version (banners, __VERSION__) see the right one
|
|
147
|
+
*
|
|
148
|
+
* In-cycle dependencies are pinned EXACTLY (`"1.2.0-rc.0"`, no range) so any
|
|
149
|
+
* combination of packages installed from the channel dist-tag resolves to the
|
|
150
|
+
* coherent set it was published with.
|
|
151
|
+
*/
|
|
152
|
+
async function writeChannelVersionsInPlace(plan, packages) {
|
|
153
|
+
const releaseMap = new Map(plan.releases.map((r) => [r.name, r]));
|
|
154
|
+
const originals = /* @__PURE__ */ new Map();
|
|
155
|
+
for (const release of plan.releases) {
|
|
156
|
+
const pkg = packages.get(release.name);
|
|
157
|
+
if (!pkg) continue;
|
|
158
|
+
const pkgJsonPath = resolve(pkg.dir, "package.json");
|
|
159
|
+
originals.set(pkgJsonPath, await readText(pkgJsonPath));
|
|
160
|
+
await updateJsonFields(pkgJsonPath, { version: release.newVersion });
|
|
161
|
+
for (const depField of [
|
|
162
|
+
"dependencies",
|
|
163
|
+
"peerDependencies",
|
|
164
|
+
"optionalDependencies"
|
|
165
|
+
]) {
|
|
166
|
+
const deps = pkg[depField];
|
|
167
|
+
for (const depName of Object.keys(deps)) {
|
|
168
|
+
const depRelease = releaseMap.get(depName);
|
|
169
|
+
if (!depRelease) continue;
|
|
170
|
+
await updateJsonNestedField(pkgJsonPath, depField, depName, depRelease.newVersion);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return async () => {
|
|
175
|
+
for (const [path, content] of originals) await writeText(path, content);
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Derive display versions for a channel cycle without touching the registry:
|
|
180
|
+
* each target gets a wildcard counter (`1.2.0-rc.x`). Everything here comes from
|
|
181
|
+
* committed state (bump files + config), so PR titles/bodies and commit messages
|
|
182
|
+
* can never disagree with what eventually publishes. Unpublishable packages are
|
|
183
|
+
* dropped, mirroring the filter in `buildChannelReleasePlan`.
|
|
184
|
+
*/
|
|
185
|
+
function channelDisplayPlan(stablePlan, channel, packages) {
|
|
186
|
+
const releases = stablePlan.releases.filter((r) => {
|
|
187
|
+
const pkg = packages.get(r.name);
|
|
188
|
+
return !!pkg && !(pkg.private && !pkg.bumpy?.publishCommand);
|
|
189
|
+
}).map((r) => ({
|
|
190
|
+
...r,
|
|
191
|
+
newVersion: `${r.newVersion}-${channel.preid}.x`
|
|
192
|
+
}));
|
|
193
|
+
return {
|
|
194
|
+
...stablePlan,
|
|
195
|
+
releases
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
/** One-line summary of a channel plan's versions, for PR titles and commit messages */
|
|
199
|
+
function formatChannelVersionSummary(releases) {
|
|
200
|
+
if (releases.length === 0) return "";
|
|
201
|
+
if (releases.length === 1) return `${releases[0].name}@${releases[0].newVersion}`;
|
|
202
|
+
return `${releases.length} packages`;
|
|
203
|
+
}
|
|
204
|
+
//#endregion
|
|
205
|
+
export { writeChannelVersionsInPlace as i, channelDisplayPlan as n, formatChannelVersionSummary as r, buildChannelReleasePlan as t };
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import { n as log, o as __require, t as colorize } from "./logger-BgksGFuf.mjs";
|
|
2
|
-
import { a as loadConfig } from "./config-
|
|
1
|
+
import { n as log, o as __require, s as __toESM, t as colorize } from "./logger-BgksGFuf.mjs";
|
|
2
|
+
import { a as loadConfig } from "./config-0we4ISZX.mjs";
|
|
3
3
|
import { r as detectWorkspaces } from "./package-manager-Db_vTztt.mjs";
|
|
4
|
-
import {
|
|
5
|
-
import { a as DependencyGraph } from "./release-plan-
|
|
4
|
+
import { c as discoverWorkspace, i as readBumpFiles } from "./bump-file-mRJeReRJ.mjs";
|
|
5
|
+
import { a as require_semver, o as DependencyGraph, t as assembleReleasePlan } from "./release-plan-C84pcBi-.mjs";
|
|
6
6
|
import { r as runArgsAsync, s as tryRunArgs } from "./shell-C8KgKnMQ.mjs";
|
|
7
|
-
import { i as loadFormatter, n as generateChangelogEntry } from "./changelog-
|
|
8
|
-
import {
|
|
9
|
-
import { n as willUseOidcExclusively, t as publishPackages } from "./publish-pipeline-
|
|
10
|
-
import {
|
|
7
|
+
import { i as loadFormatter, n as generateChangelogEntry } from "./changelog-DuFhnJRO.mjs";
|
|
8
|
+
import { f as tagExists, l as hasUncommittedChanges, n as forcePushTag } from "./git-DAWj8LyV.mjs";
|
|
9
|
+
import { n as willUseOidcExclusively, t as publishPackages } from "./publish-pipeline-BD8mLbL9.mjs";
|
|
10
|
+
import { channelNames, resolveActiveChannel } from "./channels-CFXZkyGd.mjs";
|
|
11
|
+
import { i as writeChannelVersionsInPlace, t as buildChannelReleasePlan } from "./prerelease-B2PVfXkm.mjs";
|
|
12
|
+
import { CI_PLAN_CACHE_PATH } from "./ci-ChYmDuwy.mjs";
|
|
11
13
|
//#region src/core/github-release.ts
|
|
14
|
+
var import_semver = /* @__PURE__ */ __toESM(require_semver(), 1);
|
|
12
15
|
/** Get the current HEAD commit SHA */
|
|
13
16
|
function getHeadSha(rootDir) {
|
|
14
17
|
return tryRunArgs([
|
|
@@ -67,6 +70,7 @@ async function createIndividualReleases(releases, bumpFiles, rootDir, opts = {})
|
|
|
67
70
|
"--notes",
|
|
68
71
|
body
|
|
69
72
|
];
|
|
73
|
+
if (/-/.test(release.newVersion)) args.push("--prerelease");
|
|
70
74
|
if (headSha) args.push("--target", headSha);
|
|
71
75
|
await withReleaseToken(() => runArgsAsync(args, { cwd: rootDir }));
|
|
72
76
|
log.dim(` Created GitHub release: ${title}`);
|
|
@@ -193,7 +197,7 @@ async function findReleaseByTag(tag, rootDir) {
|
|
|
193
197
|
}
|
|
194
198
|
}
|
|
195
199
|
/** Create a draft GitHub release */
|
|
196
|
-
async function createDraftRelease(tag, title, body, rootDir, targetSha) {
|
|
200
|
+
async function createDraftRelease(tag, title, body, rootDir, targetSha, opts) {
|
|
197
201
|
const args = [
|
|
198
202
|
"gh",
|
|
199
203
|
"release",
|
|
@@ -205,6 +209,7 @@ async function createDraftRelease(tag, title, body, rootDir, targetSha) {
|
|
|
205
209
|
body,
|
|
206
210
|
"--draft"
|
|
207
211
|
];
|
|
212
|
+
if (opts?.prerelease) args.push("--prerelease");
|
|
208
213
|
if (targetSha) args.push("--target", targetSha);
|
|
209
214
|
await withReleaseToken(() => runArgsAsync(args, { cwd: rootDir }));
|
|
210
215
|
}
|
|
@@ -293,7 +298,13 @@ async function finalizeSupersededDrafts(packageName, newVersion, rootDir) {
|
|
|
293
298
|
//#region src/commands/publish.ts
|
|
294
299
|
/**
|
|
295
300
|
* Publish packages that have been versioned but not yet published.
|
|
296
|
-
*
|
|
301
|
+
*
|
|
302
|
+
* On the base branch: detects unpublished versions by comparing package.json versions
|
|
303
|
+
* against the npm registry.
|
|
304
|
+
*
|
|
305
|
+
* On a channel branch: prerelease versions are never committed, so they are computed
|
|
306
|
+
* here — targets from the cycle's bump files, counters from the registry — written
|
|
307
|
+
* transiently into the working tree, published to the channel's dist-tag, and restored.
|
|
297
308
|
*/
|
|
298
309
|
async function publishCommand(rootDir, opts) {
|
|
299
310
|
const config = await loadConfig(rootDir);
|
|
@@ -304,7 +315,21 @@ async function publishCommand(rootDir, opts) {
|
|
|
304
315
|
log.warn("You have uncommitted changes. Commit or stash them before publishing.");
|
|
305
316
|
process.exit(1);
|
|
306
317
|
}
|
|
318
|
+
const channel = resolveActiveChannel(rootDir, config, opts.channel);
|
|
319
|
+
if (channel) {
|
|
320
|
+
await publishChannel(rootDir, config, packages, catalogs, detectedPm, depGraph, channel, opts);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
307
323
|
let toPublish = await findUnpublishedWithCache(rootDir, packages, config);
|
|
324
|
+
if (Object.keys(config.channels || {}).length > 0) {
|
|
325
|
+
const prereleases = toPublish.filter((r) => import_semver.default.prerelease(r.newVersion) !== null);
|
|
326
|
+
if (prereleases.length > 0) {
|
|
327
|
+
log.error("Refusing to publish prerelease versions outside a channel:");
|
|
328
|
+
for (const r of prereleases) log.error(` • ${r.name}@${r.newVersion}`);
|
|
329
|
+
log.error("Prerelease versions should never be committed — see https://bumpy.varlock.dev/docs/prereleases");
|
|
330
|
+
process.exit(1);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
308
333
|
if (opts.excludePackages && opts.excludePackages.size > 0) {
|
|
309
334
|
const excluded = toPublish.filter((r) => opts.excludePackages.has(r.name));
|
|
310
335
|
if (excluded.length > 0) {
|
|
@@ -313,7 +338,7 @@ async function publishCommand(rootDir, opts) {
|
|
|
313
338
|
}
|
|
314
339
|
}
|
|
315
340
|
if (opts.filter) {
|
|
316
|
-
const { matchGlob } = await import("./config-
|
|
341
|
+
const { matchGlob } = await import("./config-0we4ISZX.mjs").then((n) => n.t);
|
|
317
342
|
const patterns = opts.filter.split(",").map((p) => p.trim());
|
|
318
343
|
toPublish = toPublish.filter((r) => patterns.some((p) => matchGlob(r.name, p)));
|
|
319
344
|
}
|
|
@@ -323,11 +348,77 @@ async function publishCommand(rootDir, opts) {
|
|
|
323
348
|
}
|
|
324
349
|
const recoveredBumpFiles = opts.recoveredBumpFiles || [];
|
|
325
350
|
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);
|
|
326
|
-
|
|
351
|
+
await runPublishFlow(rootDir, config, packages, catalogs, detectedPm, depGraph, {
|
|
327
352
|
bumpFiles: recoveredBumpFiles,
|
|
328
353
|
releases: toPublish,
|
|
329
354
|
warnings: []
|
|
330
|
-
}
|
|
355
|
+
}, {
|
|
356
|
+
dryRun: opts.dryRun,
|
|
357
|
+
tag: opts.tag,
|
|
358
|
+
noPush: opts.noPush
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Publish a prerelease cycle from a channel branch.
|
|
363
|
+
*
|
|
364
|
+
* The cycle = every bump file on the branch (pending at root or in other channels'
|
|
365
|
+
* dirs, plus shipped in this channel's dir). The whole cycle republishes together
|
|
366
|
+
* each time so the channel dist-tag always points at one coherent, exact-pinned set.
|
|
367
|
+
*/
|
|
368
|
+
async function publishChannel(rootDir, config, packages, catalogs, detectedPm, depGraph, channel, opts) {
|
|
369
|
+
const { bumpFiles, errors: parseErrors } = await readBumpFiles(rootDir, { channels: channelNames(config) });
|
|
370
|
+
if (parseErrors.length > 0) {
|
|
371
|
+
for (const err of parseErrors) log.error(err);
|
|
372
|
+
process.exit(1);
|
|
373
|
+
}
|
|
374
|
+
if (bumpFiles.filter((bf) => bf.channel === channel.name).length === 0) {
|
|
375
|
+
log.info(`Nothing has shipped on channel "${channel.name}" yet (no bump files in .bumpy/${channel.name}/).\n Run \`bumpy version\` on the channel branch (or merge the release PR) first.`);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
log.bold(`Channel "${channel.name}" — preid "-${channel.preid}.N", dist-tag @${channel.tag}\n`);
|
|
379
|
+
const { plan, alreadyPublished, warnings } = await buildChannelReleasePlan(assembleReleasePlan(bumpFiles, packages, depGraph, config, { prereleasePreid: channel.preid }), channel, packages, rootDir);
|
|
380
|
+
for (const w of warnings) log.warn(w);
|
|
381
|
+
for (const skip of alreadyPublished) log.dim(` Skipping ${skip.name}@${skip.version} — already published from this commit`);
|
|
382
|
+
if (plan.releases.length === 0) {
|
|
383
|
+
log.info("All cycle packages already published from this commit.");
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
let toPublish = plan.releases;
|
|
387
|
+
if (opts.filter) {
|
|
388
|
+
const { matchGlob } = await import("./config-0we4ISZX.mjs").then((n) => n.t);
|
|
389
|
+
const patterns = opts.filter.split(",").map((p) => p.trim());
|
|
390
|
+
toPublish = toPublish.filter((r) => patterns.some((p) => matchGlob(r.name, p)));
|
|
391
|
+
if (toPublish.length === 0) {
|
|
392
|
+
log.info("No cycle packages match the filter.");
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
let restore = null;
|
|
397
|
+
if (!opts.dryRun) restore = await writeChannelVersionsInPlace(plan, packages);
|
|
398
|
+
try {
|
|
399
|
+
await runPublishFlow(rootDir, config, packages, catalogs, detectedPm, depGraph, {
|
|
400
|
+
bumpFiles: plan.bumpFiles,
|
|
401
|
+
releases: toPublish,
|
|
402
|
+
warnings: []
|
|
403
|
+
}, {
|
|
404
|
+
dryRun: opts.dryRun,
|
|
405
|
+
tag: opts.tag ?? channel.tag,
|
|
406
|
+
noPush: opts.noPush
|
|
407
|
+
});
|
|
408
|
+
} finally {
|
|
409
|
+
if (restore) {
|
|
410
|
+
await restore();
|
|
411
|
+
log.dim(" Restored package.json files (prerelease versions are not committed)");
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* The shared publish flow: OIDC checks, draft GitHub releases, topological publish,
|
|
417
|
+
* release metadata updates, tag pushes. Used by both the stable and channel paths.
|
|
418
|
+
* Mutates `releasePlan.releases` as packages are filtered out (already published, etc.).
|
|
419
|
+
*/
|
|
420
|
+
async function runPublishFlow(rootDir, config, packages, catalogs, detectedPm, depGraph, releasePlan, opts) {
|
|
421
|
+
let toPublish = releasePlan.releases;
|
|
331
422
|
if (opts.dryRun) log.bold("Dry run — would publish:");
|
|
332
423
|
else log.bold("Publishing:");
|
|
333
424
|
for (const r of toPublish) console.log(` ${r.name}@${colorize(r.newVersion, "cyan")}`);
|
|
@@ -381,7 +472,7 @@ async function publishCommand(rootDir, opts) {
|
|
|
381
472
|
const title = `${release.name} v${release.newVersion}`;
|
|
382
473
|
const headSha = getHeadSha(rootDir);
|
|
383
474
|
try {
|
|
384
|
-
await createDraftRelease(tag, title, body, rootDir, headSha || void 0);
|
|
475
|
+
await createDraftRelease(tag, title, body, rootDir, headSha || void 0, { prerelease: import_semver.default.prerelease(release.newVersion) !== null });
|
|
385
476
|
log.dim(` Created draft release: ${title}`);
|
|
386
477
|
releaseMetadataByPkg.set(release.name, {
|
|
387
478
|
tag,
|
|
@@ -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 { s as resolveCatalogDep } from "./package-manager-Db_vTztt.mjs";
|
|
4
|
-
import { i as stripProtocol } from "./release-plan-
|
|
4
|
+
import { i as stripProtocol } from "./release-plan-C84pcBi-.mjs";
|
|
5
5
|
import { i as runAsync, o as sq, r as runArgsAsync, s as tryRunArgs } from "./shell-C8KgKnMQ.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { f as tagExists, t as createTag } from "./git-DAWj8LyV.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";
|
|
@@ -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-lpiG-Zxh.mjs";
|
|
3
|
+
import { s as matchGlob } from "./config-0we4ISZX.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 */
|
|
@@ -1347,8 +1347,8 @@ var require_subset = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
1347
1347
|
module.exports = subset;
|
|
1348
1348
|
}));
|
|
1349
1349
|
//#endregion
|
|
1350
|
-
//#region
|
|
1351
|
-
var
|
|
1350
|
+
//#region ../../node_modules/.bun/semver@7.7.4/node_modules/semver/index.js
|
|
1351
|
+
var require_semver = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
1352
1352
|
const internalRe = require_re();
|
|
1353
1353
|
const constants = require_constants();
|
|
1354
1354
|
const SemVer = require_semver$1();
|
|
@@ -1400,7 +1400,10 @@ var import_semver = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exp
|
|
|
1400
1400
|
compareIdentifiers: identifiers.compareIdentifiers,
|
|
1401
1401
|
rcompareIdentifiers: identifiers.rcompareIdentifiers
|
|
1402
1402
|
};
|
|
1403
|
-
}))
|
|
1403
|
+
}));
|
|
1404
|
+
//#endregion
|
|
1405
|
+
//#region src/core/semver.ts
|
|
1406
|
+
var import_semver = /* @__PURE__ */ __toESM(require_semver(), 1);
|
|
1404
1407
|
function bumpVersion(version, type) {
|
|
1405
1408
|
const result = import_semver.default.inc(version, type);
|
|
1406
1409
|
if (!result) throw new Error(`Failed to bump ${version} by ${type}`);
|
|
@@ -1433,16 +1436,7 @@ function stripProtocol(range) {
|
|
|
1433
1436
|
}
|
|
1434
1437
|
//#endregion
|
|
1435
1438
|
//#region src/core/release-plan.ts
|
|
1436
|
-
|
|
1437
|
-
* Build a release plan from pending bump files, the dependency graph, and config.
|
|
1438
|
-
* This is the core algorithm of bumpy.
|
|
1439
|
-
*
|
|
1440
|
-
* The propagation loop runs three phases until stable:
|
|
1441
|
-
* Phase A — fix out-of-range dependencies (always runs)
|
|
1442
|
-
* Phase B — enforce fixed/linked group constraints
|
|
1443
|
-
* Phase C — apply cascades and proactive propagation rules
|
|
1444
|
-
*/
|
|
1445
|
-
function assembleReleasePlan(bumpFiles, packages, depGraph, config) {
|
|
1439
|
+
function assembleReleasePlan(bumpFiles, packages, depGraph, config, opts = {}) {
|
|
1446
1440
|
if (bumpFiles.length === 0) return {
|
|
1447
1441
|
bumpFiles: [],
|
|
1448
1442
|
releases: [],
|
|
@@ -1485,11 +1479,12 @@ function assembleReleasePlan(bumpFiles, packages, depGraph, config) {
|
|
|
1485
1479
|
for (const [pkgName, bump] of planned) {
|
|
1486
1480
|
const pkg = packages.get(pkgName);
|
|
1487
1481
|
const newVersion = bumpVersion(pkg.version, bump.type);
|
|
1482
|
+
const versionForRangeCheck = opts.prereleasePreid ? `${newVersion}-${opts.prereleasePreid}.0` : newVersion;
|
|
1488
1483
|
const dependents = depGraph.getDependents(pkgName);
|
|
1489
1484
|
for (const dep of dependents) {
|
|
1490
1485
|
if (dep.depType === "devDependencies") continue;
|
|
1491
1486
|
const currentVersion = pkg.version;
|
|
1492
|
-
if (satisfies(
|
|
1487
|
+
if (satisfies(versionForRangeCheck, dep.versionRange, currentVersion)) continue;
|
|
1493
1488
|
let depBump;
|
|
1494
1489
|
if (dep.depType === "peerDependencies") depBump = bump.type;
|
|
1495
1490
|
else depBump = "patch";
|
|
@@ -1704,4 +1699,4 @@ function resolveRule(dependentName, depType, packages, config) {
|
|
|
1704
1699
|
};
|
|
1705
1700
|
}
|
|
1706
1701
|
//#endregion
|
|
1707
|
-
export {
|
|
1702
|
+
export { require_semver as a, stripProtocol as i, bumpVersion as n, DependencyGraph as o, satisfies as r, assembleReleasePlan as t };
|