@pob/version 5.2.0 → 6.0.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/LICENSE +21 -15
- package/README.md +147 -10
- package/dist/index-node.mjs +1482 -0
- package/dist/index-node.mjs.map +1 -0
- package/package.json +78 -19
- package/src/commands/version.test.ts +120 -0
- package/src/commands/version.ts +956 -0
- package/src/index.ts +13 -0
- package/src/missing-types.d.ts +10 -0
- package/src/pm/BunPackageManager.ts +29 -0
- package/src/pm/PackageManager.ts +90 -0
- package/src/pm/PnpmPackageManager.ts +23 -0
- package/src/pm/YarnPackageManager.ts +44 -0
- package/src/utils/UsageError.ts +6 -0
- package/src/utils/bumpTypeUtils.test.ts +65 -0
- package/src/utils/bumpTypeUtils.ts +111 -0
- package/src/utils/conventionalChangelogUtils.test.ts +762 -0
- package/src/utils/conventionalChangelogUtils.ts +104 -0
- package/src/utils/conventionalCommitConfigUtils.ts +19 -0
- package/src/utils/execCommand.ts +214 -0
- package/src/utils/generatorUtils.ts +11 -0
- package/src/utils/gitUtils.ts +193 -0
- package/src/utils/githubUtils.ts +120 -0
- package/src/utils/mapUtils.ts +7 -0
- package/src/utils/mapWorkspaces.test.ts +89 -0
- package/src/utils/packageDependenciesUtils.test.ts +25 -0
- package/src/utils/packageDependenciesUtils.ts +80 -0
- package/src/utils/packageUtils.test.ts +32 -0
- package/src/utils/packageUtils.ts +129 -0
- package/src/utils/updateChangelog.ts +39 -0
- package/src/utils/workspaceUtils.ts +142 -0
- package/.editorconfig +0 -13
- package/.eslintrc.json +0 -7
- package/CHANGELOG.md +0 -125
- package/generate-index.js +0 -13
- package/lib/index.d.ts +0 -8
- package/lib/index.js +0 -5
|
@@ -0,0 +1,1482 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs$1, { existsSync } from 'node:fs';
|
|
3
|
+
import { program, Option } from 'commander';
|
|
4
|
+
import { LoggerCLI } from 'nightingale';
|
|
5
|
+
import semver, { satisfies } from 'semver';
|
|
6
|
+
import childProcess from 'node:child_process';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { writeChangelogString } from 'conventional-changelog-writer';
|
|
9
|
+
import { parseCommits } from 'conventional-commits-parser';
|
|
10
|
+
import { loadPreset } from 'conventional-changelog-preset-loader';
|
|
11
|
+
import fs, { glob } from 'node:fs/promises';
|
|
12
|
+
|
|
13
|
+
async function execvp(command, args, {
|
|
14
|
+
cwd = process.cwd(),
|
|
15
|
+
env = process.env,
|
|
16
|
+
encoding,
|
|
17
|
+
strict,
|
|
18
|
+
stdo = "pipe",
|
|
19
|
+
input
|
|
20
|
+
}) {
|
|
21
|
+
const stdoutChunks = [];
|
|
22
|
+
const stderrChunks = [];
|
|
23
|
+
if (env.PWD !== void 0) {
|
|
24
|
+
env = { ...env, PWD: cwd };
|
|
25
|
+
}
|
|
26
|
+
const subprocess = childProcess.spawn(command, args, {
|
|
27
|
+
cwd,
|
|
28
|
+
env,
|
|
29
|
+
stdio: [input ? "pipe" : "ignore", stdo, stdo]
|
|
30
|
+
});
|
|
31
|
+
if (input && subprocess.stdin) {
|
|
32
|
+
subprocess.stdin.write(input);
|
|
33
|
+
subprocess.stdin.end();
|
|
34
|
+
}
|
|
35
|
+
subprocess.stdout?.on("data", (chunk) => {
|
|
36
|
+
stdoutChunks.push(chunk);
|
|
37
|
+
});
|
|
38
|
+
subprocess.stderr?.on("data", (chunk) => {
|
|
39
|
+
stderrChunks.push(chunk);
|
|
40
|
+
});
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
subprocess.on("error", (err) => {
|
|
43
|
+
reject(new Error(`Process ${command} failed to spawn`));
|
|
44
|
+
});
|
|
45
|
+
subprocess.on("close", (code, signal) => {
|
|
46
|
+
const chunksToString = (chunks) => stdo === "inherit" ? "" : Buffer.concat(chunks).toString(encoding ?? "utf8");
|
|
47
|
+
const stdout = chunksToString(stdoutChunks);
|
|
48
|
+
const stderr = chunksToString(stderrChunks);
|
|
49
|
+
if (code === 0 || !strict) {
|
|
50
|
+
resolve({
|
|
51
|
+
code,
|
|
52
|
+
signal,
|
|
53
|
+
stdout,
|
|
54
|
+
stderr
|
|
55
|
+
});
|
|
56
|
+
} else {
|
|
57
|
+
reject(
|
|
58
|
+
new Error(
|
|
59
|
+
`Process ${[command, ...args].join(" ")} exited ${code !== null ? `with code ${code}` : `with signal ${signal || ""}`}:
|
|
60
|
+
stdout: ${stdout}
|
|
61
|
+
stderr: ${stderr}`
|
|
62
|
+
)
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async function* spawnStreamStdout(command, args, {
|
|
69
|
+
cwd = process.cwd(),
|
|
70
|
+
env = process.env,
|
|
71
|
+
encoding,
|
|
72
|
+
strict,
|
|
73
|
+
separator
|
|
74
|
+
}) {
|
|
75
|
+
const stderrChunks = [];
|
|
76
|
+
if (env.PWD !== void 0) {
|
|
77
|
+
env = { ...env, PWD: cwd };
|
|
78
|
+
}
|
|
79
|
+
const subprocess = childProcess.spawn(command, args, {
|
|
80
|
+
cwd,
|
|
81
|
+
env,
|
|
82
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
83
|
+
});
|
|
84
|
+
subprocess.stderr.on("data", (chunk2) => {
|
|
85
|
+
stderrChunks.push(chunk2);
|
|
86
|
+
});
|
|
87
|
+
let chunk;
|
|
88
|
+
let payload;
|
|
89
|
+
let buffer = "";
|
|
90
|
+
for await (chunk of subprocess.stdout) {
|
|
91
|
+
buffer += chunk.toString();
|
|
92
|
+
if (buffer.includes(separator)) {
|
|
93
|
+
payload = buffer.split(separator);
|
|
94
|
+
buffer = payload.pop() || "";
|
|
95
|
+
yield* payload;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (buffer) {
|
|
99
|
+
yield buffer;
|
|
100
|
+
}
|
|
101
|
+
return new Promise((resolve, reject) => {
|
|
102
|
+
subprocess.on("error", (err) => {
|
|
103
|
+
reject(new Error(`Process ${command} failed to spawn`));
|
|
104
|
+
});
|
|
105
|
+
subprocess.on("close", (code, signal) => {
|
|
106
|
+
const chunksToString = (chunks) => Buffer.concat(chunks).toString(encoding ?? "utf8");
|
|
107
|
+
const stderr = chunksToString(stderrChunks);
|
|
108
|
+
if (code === 0 || !strict) {
|
|
109
|
+
resolve({
|
|
110
|
+
code,
|
|
111
|
+
signal,
|
|
112
|
+
stderr
|
|
113
|
+
});
|
|
114
|
+
} else {
|
|
115
|
+
reject(
|
|
116
|
+
new Error(
|
|
117
|
+
`Process ${[command, ...args].join(" ")} exited ${code !== null ? `with code ${code}` : `with signal ${signal || ""}`}:
|
|
118
|
+
stderr: ${stderr}`
|
|
119
|
+
)
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
const execCommand = (workspace, commandAndArgs, stdo = "pipe") => {
|
|
126
|
+
const [command, ...args] = commandAndArgs;
|
|
127
|
+
if (command === void 0) {
|
|
128
|
+
throw new Error("Command is required");
|
|
129
|
+
}
|
|
130
|
+
return execvp(command, args, {
|
|
131
|
+
cwd: workspace.cwd,
|
|
132
|
+
strict: true,
|
|
133
|
+
stdo
|
|
134
|
+
});
|
|
135
|
+
};
|
|
136
|
+
const execCommandWithInput = (workspace, commandAndArgs, input, stdo = "pipe") => {
|
|
137
|
+
const [command, ...args] = commandAndArgs;
|
|
138
|
+
if (command === void 0) {
|
|
139
|
+
throw new Error("Command is required");
|
|
140
|
+
}
|
|
141
|
+
return execvp(command, args, {
|
|
142
|
+
cwd: workspace.cwd,
|
|
143
|
+
strict: true,
|
|
144
|
+
stdo,
|
|
145
|
+
input
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
const execCommandStreamStdout = (workspace, commandAndArgs, separator = "\n") => {
|
|
149
|
+
const [command, ...args] = commandAndArgs;
|
|
150
|
+
if (command === void 0) {
|
|
151
|
+
throw new Error("Command is required");
|
|
152
|
+
}
|
|
153
|
+
return spawnStreamStdout(command, args, {
|
|
154
|
+
cwd: workspace.cwd,
|
|
155
|
+
strict: true,
|
|
156
|
+
separator
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const execCommand$1 = /*#__PURE__*/Object.defineProperty({
|
|
161
|
+
__proto__: null,
|
|
162
|
+
execCommand,
|
|
163
|
+
execCommandStreamStdout,
|
|
164
|
+
execCommandWithInput
|
|
165
|
+
}, Symbol.toStringTag, { value: 'Module' });
|
|
166
|
+
|
|
167
|
+
const BunPackageManager = {
|
|
168
|
+
async runScript(workspace, scriptName) {
|
|
169
|
+
await execCommand(workspace, ["bun", "run", scriptName], "inherit");
|
|
170
|
+
},
|
|
171
|
+
async publish(workspace, options) {
|
|
172
|
+
if (options?.provenance) {
|
|
173
|
+
const packResult = await execCommand(workspace, ["bun", "pm", "pack"]);
|
|
174
|
+
const lines = packResult.stdout.trim().split("\n");
|
|
175
|
+
const tgzPath = lines.find((line) => line.endsWith(".tgz")) ?? lines.at(-1);
|
|
176
|
+
if (!tgzPath) {
|
|
177
|
+
throw new Error("Failed to extract .tgz path from bun pack output");
|
|
178
|
+
}
|
|
179
|
+
await execCommand(workspace, ["npm", "publish", "--provenance", tgzPath]);
|
|
180
|
+
} else {
|
|
181
|
+
await execCommand(workspace, ["bun", "publish"], "inherit");
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const PnpmPackageManager = {
|
|
187
|
+
async runScript(workspace, scriptName) {
|
|
188
|
+
await execCommand(workspace, ["pnpm", "run", scriptName], "inherit");
|
|
189
|
+
},
|
|
190
|
+
async publish(workspace, options) {
|
|
191
|
+
if (options?.provenance) {
|
|
192
|
+
await execCommand(
|
|
193
|
+
workspace,
|
|
194
|
+
["pnpm", "publish", "--provenance"],
|
|
195
|
+
"inherit"
|
|
196
|
+
);
|
|
197
|
+
} else {
|
|
198
|
+
await execCommand(workspace, ["pnpm", "publish"], "inherit");
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const YarnPackageManager = {
|
|
204
|
+
async installOnPackageContentChange(rootWorkspace) {
|
|
205
|
+
await execCommand(rootWorkspace, ["yarn", "install"], "inherit");
|
|
206
|
+
},
|
|
207
|
+
async runScript(workspace, scriptName) {
|
|
208
|
+
await execCommand(workspace, ["yarn", "run", scriptName], "inherit");
|
|
209
|
+
},
|
|
210
|
+
async publish(workspace, options) {
|
|
211
|
+
const publishArgs = ["yarn", "npm", "publish"];
|
|
212
|
+
if (options?.provenance) {
|
|
213
|
+
publishArgs.push("--provenance");
|
|
214
|
+
}
|
|
215
|
+
await execCommand(workspace, publishArgs, "inherit");
|
|
216
|
+
},
|
|
217
|
+
async publishWorkspaces(rootWorkspace, options) {
|
|
218
|
+
const publishArgs = [
|
|
219
|
+
"yarn",
|
|
220
|
+
"workspaces",
|
|
221
|
+
"foreach",
|
|
222
|
+
"--all",
|
|
223
|
+
"--parallel",
|
|
224
|
+
"--no-private",
|
|
225
|
+
"npm",
|
|
226
|
+
"publish"
|
|
227
|
+
];
|
|
228
|
+
if (options?.provenance) {
|
|
229
|
+
publishArgs.push("--provenance");
|
|
230
|
+
}
|
|
231
|
+
await execCommand(rootWorkspace, publishArgs, "inherit");
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const autoDetectPackageManager = (rootWorkspace) => {
|
|
236
|
+
const hasYarnLock = existsSync(`${rootWorkspace.cwd}/yarn.lock`);
|
|
237
|
+
const hasBunLockLegacy = existsSync(`${rootWorkspace.cwd}/bun.lockb`);
|
|
238
|
+
const hasBunLock = existsSync(`${rootWorkspace.cwd}/bun.lock`);
|
|
239
|
+
const hasNpmPackageLock = existsSync(
|
|
240
|
+
`${rootWorkspace.cwd}/package-lock.json`
|
|
241
|
+
);
|
|
242
|
+
const hasPnpmLock = existsSync(`${rootWorkspace.cwd}/pnpm-lock.yaml`);
|
|
243
|
+
if ([
|
|
244
|
+
hasYarnLock,
|
|
245
|
+
hasBunLockLegacy,
|
|
246
|
+
hasBunLock,
|
|
247
|
+
hasNpmPackageLock,
|
|
248
|
+
hasPnpmLock
|
|
249
|
+
].filter(Boolean).length > 1) {
|
|
250
|
+
throw new Error(
|
|
251
|
+
`Multiple lock files detected in workspace at ${rootWorkspace.cwd}. Please ensure only one lock file is present, or pass --package-manager option to bypass auto-detection.`
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
if (hasYarnLock) {
|
|
255
|
+
return YarnPackageManager;
|
|
256
|
+
}
|
|
257
|
+
if (hasBunLockLegacy || hasBunLock) {
|
|
258
|
+
return BunPackageManager;
|
|
259
|
+
}
|
|
260
|
+
if (hasPnpmLock) {
|
|
261
|
+
return PnpmPackageManager;
|
|
262
|
+
}
|
|
263
|
+
if (hasNpmPackageLock) {
|
|
264
|
+
throw new Error(
|
|
265
|
+
"NPM is not currently supported as a package manager. Please migrate to Yarn or Bun, or open an issue to request support."
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
throw new Error(
|
|
269
|
+
`No lock file detected in workspace at ${rootWorkspace.cwd}. Please ensure a supported lock file is present, or pass --package-manager option to bypass auto-detection.`
|
|
270
|
+
);
|
|
271
|
+
};
|
|
272
|
+
const getPackageManager = (rootWorkspace, specifiedPackageManager) => {
|
|
273
|
+
switch (specifiedPackageManager) {
|
|
274
|
+
case "bun":
|
|
275
|
+
return BunPackageManager;
|
|
276
|
+
case "yarn":
|
|
277
|
+
return YarnPackageManager;
|
|
278
|
+
case "pnpm":
|
|
279
|
+
return PnpmPackageManager;
|
|
280
|
+
case void 0:
|
|
281
|
+
default: {
|
|
282
|
+
throw new Error(
|
|
283
|
+
"Invalid package manager specified. Supported values are 'bun' and 'yarn'."
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
class UsageError extends Error {
|
|
290
|
+
constructor(message) {
|
|
291
|
+
super(message);
|
|
292
|
+
this.name = "UsageError";
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function getMapArrayItemForKey(map, key) {
|
|
297
|
+
let value = map.get(key);
|
|
298
|
+
if (value === void 0) {
|
|
299
|
+
map.set(key, value = []);
|
|
300
|
+
}
|
|
301
|
+
return value;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const PackageDescriptorNameUtils = {
|
|
305
|
+
parse: (value) => {
|
|
306
|
+
if (value.startsWith("@")) {
|
|
307
|
+
const [scope, name] = value.slice(1).split("/", 2);
|
|
308
|
+
if (!scope || !name) {
|
|
309
|
+
throw new Error(`Invalid package descriptor name: ${value}`);
|
|
310
|
+
}
|
|
311
|
+
return { scope, name };
|
|
312
|
+
}
|
|
313
|
+
return { name: value };
|
|
314
|
+
},
|
|
315
|
+
stringify: (descriptor) => {
|
|
316
|
+
return descriptor.scope === void 0 ? descriptor.name : `@${descriptor.scope}/${descriptor.name}`;
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
const PackageDependencyDescriptorUtils = {
|
|
320
|
+
make: (descriptor, selector) => {
|
|
321
|
+
return { key: descriptor.key, name: descriptor.name, selector };
|
|
322
|
+
},
|
|
323
|
+
parse: (dependencyKey, dependencyValue) => {
|
|
324
|
+
const [name, selector] = dependencyValue.startsWith("npm:") ? (() => {
|
|
325
|
+
const v = dependencyValue.slice("npm:".length);
|
|
326
|
+
if (!v.startsWith("@")) return v.split("@", 2);
|
|
327
|
+
const [packageNameWithoutFirstChar, selector2] = v.slice(1).split("@", 2);
|
|
328
|
+
if (!packageNameWithoutFirstChar || !selector2) {
|
|
329
|
+
throw new Error(`Invalid package descriptor: ${dependencyValue}`);
|
|
330
|
+
}
|
|
331
|
+
return [`@${packageNameWithoutFirstChar}`, selector2];
|
|
332
|
+
})() : [dependencyKey, dependencyValue];
|
|
333
|
+
return {
|
|
334
|
+
key: dependencyKey,
|
|
335
|
+
name: PackageDescriptorNameUtils.parse(name),
|
|
336
|
+
selector
|
|
337
|
+
};
|
|
338
|
+
},
|
|
339
|
+
stringify: (descriptor) => {
|
|
340
|
+
return [descriptor.key, descriptor.selector];
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
const allDependenciesTypes = [
|
|
345
|
+
"dependencies",
|
|
346
|
+
"devDependencies",
|
|
347
|
+
"peerDependencies"
|
|
348
|
+
];
|
|
349
|
+
const getWorkspaceName = (workspace) => {
|
|
350
|
+
return workspace.pkg.name ?? (path.basename(workspace.cwd) || "unnamed-workspace");
|
|
351
|
+
};
|
|
352
|
+
const buildDependenciesMaps = (project) => {
|
|
353
|
+
const dependenciesMap = /* @__PURE__ */ new Map();
|
|
354
|
+
for (const dependent of project.children.values()) {
|
|
355
|
+
for (const set of allDependenciesTypes) {
|
|
356
|
+
for (const [dependencyKey, dependencyValue] of Object.entries(
|
|
357
|
+
dependent.pkg[set] || {}
|
|
358
|
+
)) {
|
|
359
|
+
if (!dependencyValue) continue;
|
|
360
|
+
const descriptor = PackageDependencyDescriptorUtils.parse(
|
|
361
|
+
dependencyKey,
|
|
362
|
+
dependencyValue
|
|
363
|
+
);
|
|
364
|
+
const workspace = project.children.get(
|
|
365
|
+
PackageDescriptorNameUtils.stringify(descriptor.name)
|
|
366
|
+
);
|
|
367
|
+
if (!workspace) continue;
|
|
368
|
+
const dependencies = getMapArrayItemForKey(dependenciesMap, dependent);
|
|
369
|
+
dependencies.push([workspace, set, descriptor]);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return dependenciesMap;
|
|
374
|
+
};
|
|
375
|
+
const buildTopologicalOrderBatches = (project, dependenciesMap) => {
|
|
376
|
+
const batches = [];
|
|
377
|
+
const added = /* @__PURE__ */ new Set();
|
|
378
|
+
const toAdd = /* @__PURE__ */ new Set([
|
|
379
|
+
project.root,
|
|
380
|
+
...project.children.values()
|
|
381
|
+
]);
|
|
382
|
+
while (toAdd.size > 0) {
|
|
383
|
+
const batch = /* @__PURE__ */ new Set();
|
|
384
|
+
for (const workspace of toAdd) {
|
|
385
|
+
if (workspace === project.root && toAdd.size > 1) {
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
const dependencies = dependenciesMap.get(workspace);
|
|
389
|
+
if (!dependencies || dependencies.every((w) => added.has(w[0]))) {
|
|
390
|
+
batch.add(workspace);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
for (const workspace of batch) {
|
|
394
|
+
added.add(workspace);
|
|
395
|
+
toAdd.delete(workspace);
|
|
396
|
+
}
|
|
397
|
+
if (batch.size === 0) {
|
|
398
|
+
throw new Error("Circular dependency detected");
|
|
399
|
+
}
|
|
400
|
+
batches.push([...batch]);
|
|
401
|
+
}
|
|
402
|
+
return batches;
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
const SUPPORTED_UPGRADE_REGEXP = (
|
|
406
|
+
// eslint-disable-next-line regexp/no-unused-capturing-group
|
|
407
|
+
/^((?:>=|[~^])?)(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-(0|[1-9]\d*|\d*[a-z-][0-9a-z-]*)(\.(0|[1-9]\d*|\d*[a-z-][0-9a-z-]*))*)?(\+[0-9a-z-]+(\.[0-9a-z-]+)*)?$/i
|
|
408
|
+
);
|
|
409
|
+
const yarnWorkspaceProtocol = "workspace:";
|
|
410
|
+
const calcBumpRange = (workspace, range, newVersion) => {
|
|
411
|
+
if (range === "*") {
|
|
412
|
+
return range;
|
|
413
|
+
}
|
|
414
|
+
let useWorkspaceProtocol = false;
|
|
415
|
+
if (range.startsWith(yarnWorkspaceProtocol)) {
|
|
416
|
+
const slicedRange = range.slice(yarnWorkspaceProtocol.length);
|
|
417
|
+
if (slicedRange === workspace.relativeCwd) {
|
|
418
|
+
return range;
|
|
419
|
+
}
|
|
420
|
+
if (slicedRange === "*" || slicedRange === "^" || slicedRange === "~") {
|
|
421
|
+
return range;
|
|
422
|
+
}
|
|
423
|
+
range = slicedRange;
|
|
424
|
+
useWorkspaceProtocol = true;
|
|
425
|
+
}
|
|
426
|
+
const parsed = SUPPORTED_UPGRADE_REGEXP.exec(range);
|
|
427
|
+
if (!parsed) {
|
|
428
|
+
const workspaceName = getWorkspaceName(workspace);
|
|
429
|
+
throw new Error(`Couldn't bump range ${range} in ${workspaceName}`);
|
|
430
|
+
}
|
|
431
|
+
return `${useWorkspaceProtocol ? yarnWorkspaceProtocol : ""}${parsed[1] ?? ""}${newVersion}`;
|
|
432
|
+
};
|
|
433
|
+
const getHighestBumpType = (bumpTypes) => {
|
|
434
|
+
if (bumpTypes.includes("major")) {
|
|
435
|
+
return "major";
|
|
436
|
+
}
|
|
437
|
+
if (bumpTypes.includes("minor")) {
|
|
438
|
+
return "minor";
|
|
439
|
+
}
|
|
440
|
+
return "patch";
|
|
441
|
+
};
|
|
442
|
+
const calcBumpType = (bumpType, maxBumpType) => {
|
|
443
|
+
if (maxBumpType === "major") {
|
|
444
|
+
return bumpType;
|
|
445
|
+
}
|
|
446
|
+
if (maxBumpType === "minor") {
|
|
447
|
+
return bumpType === "major" || bumpType === "minor" ? "minor" : "patch";
|
|
448
|
+
}
|
|
449
|
+
return "patch";
|
|
450
|
+
};
|
|
451
|
+
const incVersion = (version, bumpType) => {
|
|
452
|
+
if (bumpType === "major" && semver.major(version) === 0) {
|
|
453
|
+
bumpType = "minor";
|
|
454
|
+
}
|
|
455
|
+
return semver.inc(version, bumpType);
|
|
456
|
+
};
|
|
457
|
+
const incrementVersion = (workspace, currentVersion, bumpType) => {
|
|
458
|
+
const newVersion = incVersion(currentVersion, bumpType);
|
|
459
|
+
if (!newVersion) {
|
|
460
|
+
throw new UsageError(
|
|
461
|
+
`Could not determine next version for "${getWorkspaceName(
|
|
462
|
+
workspace
|
|
463
|
+
)}" (currentVersion: ${currentVersion}, bumpType: ${bumpType}})`
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
return newVersion;
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
const versions = [
|
|
470
|
+
"major",
|
|
471
|
+
"minor",
|
|
472
|
+
"patch"
|
|
473
|
+
];
|
|
474
|
+
const recommendBump = async (commits, config) => {
|
|
475
|
+
const whatBump = config.whatBump;
|
|
476
|
+
if (!whatBump) {
|
|
477
|
+
throw new Error("whatBump method is missing in config");
|
|
478
|
+
}
|
|
479
|
+
const result = { ...await whatBump(commits) };
|
|
480
|
+
return {
|
|
481
|
+
...result,
|
|
482
|
+
releaseType: result.level != null ? versions[result.level] : void 0
|
|
483
|
+
};
|
|
484
|
+
};
|
|
485
|
+
const generateChangelog = (workspace, pkg, config, previousTag, newTag, commits, date) => {
|
|
486
|
+
if (!newTag) {
|
|
487
|
+
throw new Error(`Missing new tag for package "${pkg.name ?? ""}"`);
|
|
488
|
+
}
|
|
489
|
+
const originUrl = typeof pkg.repository === "string" ? pkg.repository : pkg.repository?.url;
|
|
490
|
+
const match = originUrl && typeof originUrl === "string" && /^(?:git@|https?:\/\/)(?:([^./:]+(?:\.com)?)[/:])?([^/:]+)\/([^./:]+)(?:.git)?/.exec(
|
|
491
|
+
originUrl
|
|
492
|
+
);
|
|
493
|
+
const [, gitHost, gitAccount, repoName] = match || [
|
|
494
|
+
void 0,
|
|
495
|
+
void 0,
|
|
496
|
+
void 0,
|
|
497
|
+
void 0
|
|
498
|
+
];
|
|
499
|
+
return writeChangelogString(
|
|
500
|
+
commits ?? [],
|
|
501
|
+
{
|
|
502
|
+
// @ts-expect-error - missing types
|
|
503
|
+
previousTag,
|
|
504
|
+
currentTag: newTag,
|
|
505
|
+
linkCompare: previousTag != null,
|
|
506
|
+
version: pkg.version,
|
|
507
|
+
host: gitHost ? `https://${gitHost}` : void 0,
|
|
508
|
+
owner: gitAccount,
|
|
509
|
+
repository: repoName,
|
|
510
|
+
date
|
|
511
|
+
},
|
|
512
|
+
// @ts-expect-error - missing types
|
|
513
|
+
config.writer
|
|
514
|
+
);
|
|
515
|
+
};
|
|
516
|
+
const createCommitsParser = (config) => {
|
|
517
|
+
return parseCommits(config.parser);
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
const loadConventionalCommitConfig = async (rootWorkspace, preset) => {
|
|
521
|
+
try {
|
|
522
|
+
return await loadPreset(preset);
|
|
523
|
+
} catch (error) {
|
|
524
|
+
throw new UsageError(
|
|
525
|
+
`Failed to require preset "${preset}": ${error.message}`
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
async function asyncIterableToArray(asyncIterable) {
|
|
531
|
+
const result = [];
|
|
532
|
+
for await (const value of asyncIterable) {
|
|
533
|
+
result.push(value);
|
|
534
|
+
}
|
|
535
|
+
return result;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
const getGitCurrentBranch = async (workspace) => {
|
|
539
|
+
const { stdout } = await execCommand(workspace, [
|
|
540
|
+
"git",
|
|
541
|
+
"rev-parse",
|
|
542
|
+
"--abbrev-ref",
|
|
543
|
+
"HEAD"
|
|
544
|
+
]);
|
|
545
|
+
const currentBranch = stdout.trim();
|
|
546
|
+
if (currentBranch === "HEAD") {
|
|
547
|
+
throw new UsageError("HEAD is detached. Please checkout a branch.");
|
|
548
|
+
}
|
|
549
|
+
return currentBranch;
|
|
550
|
+
};
|
|
551
|
+
const createGitCommit = async (workspace, commitMessage) => {
|
|
552
|
+
await execCommand(workspace, ["git", "add", "-A"]);
|
|
553
|
+
await execCommand(workspace, [
|
|
554
|
+
"git",
|
|
555
|
+
"commit",
|
|
556
|
+
"--no-verify",
|
|
557
|
+
"-m",
|
|
558
|
+
commitMessage
|
|
559
|
+
]);
|
|
560
|
+
};
|
|
561
|
+
const createGitTag = async (workspace, newTag) => {
|
|
562
|
+
await execCommand(workspace, ["git", "tag", newTag, "-m", newTag]);
|
|
563
|
+
};
|
|
564
|
+
const pushCommitsAndTags = async (workspace, gitRemote, currentBranch) => {
|
|
565
|
+
await execCommand(workspace, [
|
|
566
|
+
"git",
|
|
567
|
+
"push",
|
|
568
|
+
"--follow-tags",
|
|
569
|
+
"--no-verify",
|
|
570
|
+
"--atomic",
|
|
571
|
+
gitRemote,
|
|
572
|
+
currentBranch
|
|
573
|
+
]);
|
|
574
|
+
};
|
|
575
|
+
const isBehindRemote = async (workspace, gitRemote, currentBranch) => {
|
|
576
|
+
await execCommand(workspace, ["git", "remote", "update", gitRemote]);
|
|
577
|
+
const { stdout } = await execCommand(workspace, [
|
|
578
|
+
"git",
|
|
579
|
+
"rev-list",
|
|
580
|
+
"--left-right",
|
|
581
|
+
"--count",
|
|
582
|
+
`${gitRemote}/${currentBranch}..${currentBranch}`
|
|
583
|
+
]);
|
|
584
|
+
const [behind] = stdout.split(" ").map((val) => parseInt(val, 10));
|
|
585
|
+
return behind ? behind > 0 : false;
|
|
586
|
+
};
|
|
587
|
+
const getDirtyFiles = async (workspace) => {
|
|
588
|
+
const { stdout: dirtyFiles } = await execCommand(workspace, [
|
|
589
|
+
"git",
|
|
590
|
+
"status",
|
|
591
|
+
"--porcelain"
|
|
592
|
+
]);
|
|
593
|
+
return dirtyFiles;
|
|
594
|
+
};
|
|
595
|
+
const toArray = (input) => Array.isArray(input) ? input : [input];
|
|
596
|
+
const getGitLatestTagVersion = async (workspace, {
|
|
597
|
+
prefix,
|
|
598
|
+
skipUnstable,
|
|
599
|
+
since,
|
|
600
|
+
from = "",
|
|
601
|
+
to = "HEAD",
|
|
602
|
+
path
|
|
603
|
+
} = {}) => {
|
|
604
|
+
const tagRegex = /tag:\s*(.+?)[,)]/gi;
|
|
605
|
+
for await (const chunk of execCommandStreamStdout(workspace, [
|
|
606
|
+
"git",
|
|
607
|
+
"log",
|
|
608
|
+
"--no-color",
|
|
609
|
+
"--date-order",
|
|
610
|
+
"--format=%d",
|
|
611
|
+
...since ? [`--since=${since instanceof Date ? since.toISOString() : since}`] : [],
|
|
612
|
+
[from, to].filter(Boolean).join(".."),
|
|
613
|
+
...path ? ["--", ...toArray(path)] : []
|
|
614
|
+
])) {
|
|
615
|
+
const trimmed = chunk.trim();
|
|
616
|
+
if (!trimmed) continue;
|
|
617
|
+
const matches = trimmed.matchAll(tagRegex);
|
|
618
|
+
for (const match of matches) {
|
|
619
|
+
const tag = match[1];
|
|
620
|
+
if (!tag) continue;
|
|
621
|
+
if (prefix && !tag.startsWith(prefix)) {
|
|
622
|
+
continue;
|
|
623
|
+
}
|
|
624
|
+
const version = prefix ? tag.slice(prefix.length) : tag;
|
|
625
|
+
if (!semver.valid(version)) {
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
628
|
+
if (skipUnstable && semver.prerelease(version)) {
|
|
629
|
+
continue;
|
|
630
|
+
}
|
|
631
|
+
return { tag, version };
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
return null;
|
|
635
|
+
};
|
|
636
|
+
const GIT_COMMIT_SEPARATOR = "------------------------ >8 ------------------------";
|
|
637
|
+
const getGitCommits = (workspace, {
|
|
638
|
+
path,
|
|
639
|
+
from = "",
|
|
640
|
+
to = "HEAD",
|
|
641
|
+
format = "%B",
|
|
642
|
+
reverse,
|
|
643
|
+
merges,
|
|
644
|
+
since
|
|
645
|
+
} = {}) => {
|
|
646
|
+
return execCommandStreamStdout(
|
|
647
|
+
workspace,
|
|
648
|
+
[
|
|
649
|
+
"git",
|
|
650
|
+
"log",
|
|
651
|
+
`--format=${format}%n${GIT_COMMIT_SEPARATOR}`,
|
|
652
|
+
...since ? [`--since=${since instanceof Date ? since.toISOString() : since}`] : [],
|
|
653
|
+
...reverse ? ["--reverse"] : [],
|
|
654
|
+
...merges ? ["--merges"] : [],
|
|
655
|
+
...merges === false ? ["--no-merges"] : [],
|
|
656
|
+
[from, to].filter(Boolean).join(".."),
|
|
657
|
+
...path ? ["--", ...toArray(path)] : []
|
|
658
|
+
],
|
|
659
|
+
GIT_COMMIT_SEPARATOR
|
|
660
|
+
);
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
async function ensureGhCliAvailable(workspace) {
|
|
664
|
+
const { GH_TOKEN } = process.env;
|
|
665
|
+
if (!GH_TOKEN) {
|
|
666
|
+
throw new UsageError('"GH_TOKEN" environment variable required');
|
|
667
|
+
}
|
|
668
|
+
try {
|
|
669
|
+
const { execCommand } = await Promise.resolve().then(() => execCommand$1);
|
|
670
|
+
await execCommand(workspace, ["gh", "--version"]);
|
|
671
|
+
} catch {
|
|
672
|
+
throw new UsageError(
|
|
673
|
+
'The "gh" CLI is required. Please install GitHub CLI: https://cli.github.com/'
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
const githubRegex = /^https?:\/\/github.com\/([^#/]+)\/([^#/]+?)(?:\.git)?$/;
|
|
678
|
+
const parseGithubRepoUrl = (workspace) => {
|
|
679
|
+
const repository = workspace.pkg.repository;
|
|
680
|
+
const url = typeof repository === "string" ? repository : repository?.url;
|
|
681
|
+
if (!url) {
|
|
682
|
+
throw new Error(
|
|
683
|
+
"No repository URL found in manifest. Please add one and try again. https://docs.npmjs.com/cli/v9/configuring-npm/package-json#repository"
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
const match = githubRegex.exec(url);
|
|
687
|
+
if (!match) {
|
|
688
|
+
throw new Error(`Invalid GitHub repository URL: "${url}"`);
|
|
689
|
+
}
|
|
690
|
+
const [, username, reponame] = match;
|
|
691
|
+
if (!username || !reponame) {
|
|
692
|
+
throw new Error(`Invalid GitHub repository URL: "${url}"`);
|
|
693
|
+
}
|
|
694
|
+
return { username, reponame };
|
|
695
|
+
};
|
|
696
|
+
const extractHostnameFromGheApiUrl = (gheApiUrl) => {
|
|
697
|
+
try {
|
|
698
|
+
const url = new URL(gheApiUrl);
|
|
699
|
+
return url.hostname || null;
|
|
700
|
+
} catch {
|
|
701
|
+
const m = /^https?:\/\/([^/]+)/.exec(gheApiUrl);
|
|
702
|
+
return m?.[1] || null;
|
|
703
|
+
}
|
|
704
|
+
};
|
|
705
|
+
const createGhRelease = async (workspace, opts) => {
|
|
706
|
+
const { parsedRepoUrl, tag, title, body, prerelease } = opts;
|
|
707
|
+
const { GH_TOKEN, GHE_API_URL } = process.env;
|
|
708
|
+
if (!GH_TOKEN) {
|
|
709
|
+
await ensureGhCliAvailable(workspace);
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
const args = [
|
|
713
|
+
"release",
|
|
714
|
+
"create",
|
|
715
|
+
tag,
|
|
716
|
+
"--title",
|
|
717
|
+
title,
|
|
718
|
+
"--repo",
|
|
719
|
+
`${parsedRepoUrl.username}/${parsedRepoUrl.reponame}`
|
|
720
|
+
];
|
|
721
|
+
if (prerelease) {
|
|
722
|
+
args.push("--prerelease");
|
|
723
|
+
}
|
|
724
|
+
if (GHE_API_URL) {
|
|
725
|
+
const hostname = extractHostnameFromGheApiUrl(GHE_API_URL);
|
|
726
|
+
if (hostname) {
|
|
727
|
+
args.push("--hostname", hostname);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
try {
|
|
731
|
+
await execCommandWithInput(
|
|
732
|
+
workspace,
|
|
733
|
+
["gh", ...args, "--notes-file", "-"],
|
|
734
|
+
body
|
|
735
|
+
);
|
|
736
|
+
} catch (error) {
|
|
737
|
+
throw new Error(
|
|
738
|
+
`gh release create failed: ${String(error instanceof Error ? error.message : error)}`,
|
|
739
|
+
{ cause: error }
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
|
|
744
|
+
async function mapWorkspacesFromPkg({
|
|
745
|
+
cwd,
|
|
746
|
+
pkg
|
|
747
|
+
}) {
|
|
748
|
+
const workspacesPatterns = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces?.packages || [];
|
|
749
|
+
const map = /* @__PURE__ */ new Map();
|
|
750
|
+
for (const pattern of workspacesPatterns) {
|
|
751
|
+
const normalized = pattern.endsWith("package.json") ? pattern : `${pattern.replace(/\/*$/, "")}/package.json`;
|
|
752
|
+
const files = glob(normalized, { cwd });
|
|
753
|
+
for await (const file of files) {
|
|
754
|
+
const packageDir = path.join(cwd, path.dirname(file));
|
|
755
|
+
const packageJson = await readPkg(packageDir);
|
|
756
|
+
const workspaceName = packageJson.name || path.basename(packageDir);
|
|
757
|
+
if (map.has(workspaceName)) {
|
|
758
|
+
const existingWorkspace = map.get(workspaceName);
|
|
759
|
+
if (existingWorkspace && existingWorkspace.cwd !== packageDir) {
|
|
760
|
+
throw new Error(
|
|
761
|
+
`must not have multiple workspaces with the same name
|
|
762
|
+
package '${workspaceName}' has conflicts in the following paths:
|
|
763
|
+
${existingWorkspace.cwd}
|
|
764
|
+
${packageDir}`
|
|
765
|
+
);
|
|
766
|
+
}
|
|
767
|
+
if (existingWorkspace?.cwd === packageDir) continue;
|
|
768
|
+
}
|
|
769
|
+
map.set(workspaceName, { cwd: packageDir, pkg: packageJson });
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
return map;
|
|
773
|
+
}
|
|
774
|
+
const createProjectWorkspace = async (root) => {
|
|
775
|
+
const map = root.pkg.workspaces ? await mapWorkspacesFromPkg({ cwd: root.cwd, pkg: root.pkg }) : /* @__PURE__ */ new Map();
|
|
776
|
+
const children = new Map(
|
|
777
|
+
[...map.entries()].map(([packageName, workspace]) => {
|
|
778
|
+
const w = {
|
|
779
|
+
...workspace,
|
|
780
|
+
relativeCwd: path.relative(root.cwd, workspace.cwd)
|
|
781
|
+
};
|
|
782
|
+
return [packageName, w];
|
|
783
|
+
})
|
|
784
|
+
);
|
|
785
|
+
return { root, children };
|
|
786
|
+
};
|
|
787
|
+
const isAccessible = (path2) => fs.access(path2).then(
|
|
788
|
+
() => true,
|
|
789
|
+
() => false
|
|
790
|
+
);
|
|
791
|
+
const findRootWorkspace = async (cwd) => {
|
|
792
|
+
let currentPath = cwd;
|
|
793
|
+
do {
|
|
794
|
+
const isRootIfOneOfThesePathsExists = await Promise.all([
|
|
795
|
+
isAccessible(path.join(currentPath, ".yarnrc.yml")),
|
|
796
|
+
isAccessible(path.join(currentPath, "yarn.lock")),
|
|
797
|
+
isAccessible(path.join(currentPath, "bun.lock")),
|
|
798
|
+
isAccessible(path.join(currentPath, "pnpm-lock.yaml"))
|
|
799
|
+
]);
|
|
800
|
+
if (isRootIfOneOfThesePathsExists.some(Boolean)) {
|
|
801
|
+
return createWorkspace(currentPath);
|
|
802
|
+
}
|
|
803
|
+
currentPath = path.dirname(currentPath);
|
|
804
|
+
} while (currentPath && currentPath !== "/");
|
|
805
|
+
return null;
|
|
806
|
+
};
|
|
807
|
+
const getPackageJsonPath = (cwd) => path.join(cwd, "package.json");
|
|
808
|
+
const createWorkspace = async (path2) => {
|
|
809
|
+
const pkg = await readPkg(path2);
|
|
810
|
+
return { cwd: path2, pkg };
|
|
811
|
+
};
|
|
812
|
+
async function writePkg(workspace) {
|
|
813
|
+
const string = `${JSON.stringify(workspace.pkg, null, 2)}
|
|
814
|
+
`;
|
|
815
|
+
await fs.writeFile(getPackageJsonPath(workspace.cwd), string, "utf8");
|
|
816
|
+
}
|
|
817
|
+
async function readPkg(cwd) {
|
|
818
|
+
const packagePath = getPackageJsonPath(cwd);
|
|
819
|
+
const pkg = await fs.readFile(packagePath, "utf8").catch((error) => {
|
|
820
|
+
throw new Error(
|
|
821
|
+
`Failed to read "${packagePath}": ${error instanceof Error ? error.message : String(error)}`
|
|
822
|
+
);
|
|
823
|
+
});
|
|
824
|
+
try {
|
|
825
|
+
return JSON.parse(pkg);
|
|
826
|
+
} catch (error) {
|
|
827
|
+
throw new Error(
|
|
828
|
+
`Failed to parse "${packagePath}": ${error instanceof Error ? error.message : String(error)}`,
|
|
829
|
+
{ cause: error }
|
|
830
|
+
);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
const COMMIT_GUIDELINE = "See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.";
|
|
835
|
+
const CHANGELOG_HEADER = [
|
|
836
|
+
"# Changelog",
|
|
837
|
+
"",
|
|
838
|
+
"All notable changes to this project will be documented in this file.",
|
|
839
|
+
COMMIT_GUIDELINE,
|
|
840
|
+
""
|
|
841
|
+
].join("\n");
|
|
842
|
+
const updateChangelogFile = async (versionChangelogContent, tagPrefix, file = "CHANGELOG.md") => {
|
|
843
|
+
const START_OF_LAST_RELEASE_PATTERN = new RegExp(
|
|
844
|
+
`(^#+ \\[?(?:${tagPrefix})?\\d+\\.\\d+\\.\\d+|<a name=)`,
|
|
845
|
+
"m"
|
|
846
|
+
);
|
|
847
|
+
let oldContent = "";
|
|
848
|
+
try {
|
|
849
|
+
oldContent = await fs.readFile(file, "utf8");
|
|
850
|
+
const oldContentStart = oldContent.search(START_OF_LAST_RELEASE_PATTERN);
|
|
851
|
+
if (oldContentStart !== -1) {
|
|
852
|
+
oldContent = oldContent.slice(Math.max(0, oldContentStart));
|
|
853
|
+
}
|
|
854
|
+
} catch {
|
|
855
|
+
}
|
|
856
|
+
await fs.writeFile(
|
|
857
|
+
file,
|
|
858
|
+
`${CHANGELOG_HEADER}
|
|
859
|
+
${versionChangelogContent}
|
|
860
|
+
${oldContent}`
|
|
861
|
+
);
|
|
862
|
+
};
|
|
863
|
+
|
|
864
|
+
const todayInYYYYMMDD = () => {
|
|
865
|
+
const dateFormatter = Intl.DateTimeFormat("sv-SE", {
|
|
866
|
+
timeZone: "UTC"
|
|
867
|
+
});
|
|
868
|
+
return dateFormatter.format(/* @__PURE__ */ new Date());
|
|
869
|
+
};
|
|
870
|
+
const versionCommandAction = async (options, { logger = new LoggerCLI("@pob/version", { json: options.json }) } = {}) => {
|
|
871
|
+
const rootWorkspace = await (options.cwdIsRoot ? createWorkspace(options.cwd) : findRootWorkspace(options.cwd));
|
|
872
|
+
if (!rootWorkspace) {
|
|
873
|
+
throw new UsageError("Could not find root workspace from this path.");
|
|
874
|
+
}
|
|
875
|
+
const packageManager = options.packageManager ? getPackageManager(rootWorkspace, options.packageManager) : autoDetectPackageManager(rootWorkspace);
|
|
876
|
+
const project = await createProjectWorkspace(rootWorkspace);
|
|
877
|
+
if (!options.dryRun) {
|
|
878
|
+
const dirtyFiles = await getDirtyFiles(project.root);
|
|
879
|
+
if (dirtyFiles) {
|
|
880
|
+
throw new Error(
|
|
881
|
+
`Dirty Files:
|
|
882
|
+
${dirtyFiles}
|
|
883
|
+
|
|
884
|
+
There are uncommitted changes in the git repository. Please commit or stash them first.`
|
|
885
|
+
);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
const rootWorkspaceChildren = [...project.children.values()];
|
|
889
|
+
let rootNewVersion = "";
|
|
890
|
+
let rootNewTag = "";
|
|
891
|
+
const isMonorepo = rootWorkspaceChildren.length > 0;
|
|
892
|
+
const isMonorepoVersionIndependent = isMonorepo && !rootWorkspace.pkg.version;
|
|
893
|
+
const workspaces = !(isMonorepo && !rootWorkspaceChildren) || options.includesRoot ? [rootWorkspace, ...rootWorkspaceChildren] : rootWorkspaceChildren;
|
|
894
|
+
if (options.prerelease) {
|
|
895
|
+
throw new UsageError("--prerelease is not supported yet.");
|
|
896
|
+
}
|
|
897
|
+
if (options.ignoreChanges) {
|
|
898
|
+
throw new UsageError("--ignore-changes is not supported yet.");
|
|
899
|
+
}
|
|
900
|
+
const [conventionalCommitConfig, parsedRepoUrl, gitCurrentBranch] = await Promise.all([
|
|
901
|
+
loadConventionalCommitConfig(rootWorkspace, options.preset),
|
|
902
|
+
options.createRelease ? parseGithubRepoUrl(rootWorkspace) : Promise.resolve(null),
|
|
903
|
+
getGitCurrentBranch(rootWorkspace),
|
|
904
|
+
// ensure gh CLI is available early to fail fast if necessary
|
|
905
|
+
options.createRelease ? ensureGhCliAvailable(rootWorkspace) : Promise.resolve(void 0)
|
|
906
|
+
]);
|
|
907
|
+
const rootPreviousVersionTagPromise = options.force || isMonorepoVersionIndependent ? null : getGitLatestTagVersion(rootWorkspace, {
|
|
908
|
+
prefix: options.tagVersionPrefix,
|
|
909
|
+
skipUnstable: true
|
|
910
|
+
});
|
|
911
|
+
const buildTagName = (workspace, version) => `${isMonorepo && workspace !== rootWorkspace ? `${getWorkspaceName(workspace)}@` : options.tagVersionPrefix}${version}`;
|
|
912
|
+
const changedWorkspaces = /* @__PURE__ */ new Map();
|
|
913
|
+
const dependenciesMap = isMonorepo ? buildDependenciesMaps(project) : null;
|
|
914
|
+
const bumpableWorkspaces = [];
|
|
915
|
+
logger.group(
|
|
916
|
+
options.force ? "Check all workspaces (force option)" : "Finding changed workspaces",
|
|
917
|
+
() => {
|
|
918
|
+
for (const workspace of workspaces) {
|
|
919
|
+
const workspaceName = getWorkspaceName(workspace);
|
|
920
|
+
const isRoot = workspace === rootWorkspace;
|
|
921
|
+
if (isRoot && isMonorepo && isMonorepoVersionIndependent) continue;
|
|
922
|
+
const version = workspace.pkg.version;
|
|
923
|
+
if (!version || version === "0.0.0") {
|
|
924
|
+
if ((isRoot || isMonorepoVersionIndependent) && (!isMonorepo || !isMonorepoVersionIndependent)) {
|
|
925
|
+
throw new UsageError(
|
|
926
|
+
'package.json has no version in its manifest. For the first release, set to "1.0.0-pre" or "0.1.0-pre".'
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
logger.info(`${workspaceName}: skipped (no version)`);
|
|
930
|
+
continue;
|
|
931
|
+
}
|
|
932
|
+
bumpableWorkspaces.push({
|
|
933
|
+
workspace,
|
|
934
|
+
workspaceName,
|
|
935
|
+
isRoot,
|
|
936
|
+
version
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
if (bumpableWorkspaces.length > 0) {
|
|
940
|
+
logger.info("Found bumpable workspaces", {
|
|
941
|
+
count: bumpableWorkspaces.length
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
);
|
|
946
|
+
const previousTagVersionByWorkspace = new Map(
|
|
947
|
+
await Promise.all(
|
|
948
|
+
bumpableWorkspaces.map(async ({ workspace, workspaceName, isRoot }) => {
|
|
949
|
+
const packageOption = isMonorepo && isMonorepoVersionIndependent ? workspaceName : void 0;
|
|
950
|
+
const tagPrefix = packageOption ? `${packageOption}@` : options.tagVersionPrefix;
|
|
951
|
+
const previousTagAndVersion = await (isRoot || !isMonorepoVersionIndependent ? rootPreviousVersionTagPromise : getGitLatestTagVersion(rootWorkspace, {
|
|
952
|
+
path: workspace.relativeCwd,
|
|
953
|
+
prefix: tagPrefix,
|
|
954
|
+
skipUnstable: true
|
|
955
|
+
}));
|
|
956
|
+
return [workspace, previousTagAndVersion];
|
|
957
|
+
})
|
|
958
|
+
)
|
|
959
|
+
);
|
|
960
|
+
if (options.dryRun) {
|
|
961
|
+
logger.info("Previous tags", {
|
|
962
|
+
previousTagByWorkspace: Object.fromEntries(
|
|
963
|
+
[...previousTagVersionByWorkspace.entries()].map(
|
|
964
|
+
([workspace, previousTagVersion]) => [
|
|
965
|
+
getWorkspaceName(workspace),
|
|
966
|
+
previousTagVersion?.tag
|
|
967
|
+
]
|
|
968
|
+
)
|
|
969
|
+
)
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
const parseCommits = createCommitsParser(conventionalCommitConfig);
|
|
973
|
+
const commitsByWorkspace = options.force ? void 0 : new Map(
|
|
974
|
+
await Promise.all(
|
|
975
|
+
bumpableWorkspaces.map(async ({ workspace }) => {
|
|
976
|
+
const previousTagVersion = previousTagVersionByWorkspace.get(workspace);
|
|
977
|
+
const commits = await asyncIterableToArray(
|
|
978
|
+
parseCommits(
|
|
979
|
+
getGitCommits(rootWorkspace, {
|
|
980
|
+
path: workspace.relativeCwd,
|
|
981
|
+
from: previousTagVersion?.tag || void 0
|
|
982
|
+
})
|
|
983
|
+
)
|
|
984
|
+
);
|
|
985
|
+
return [workspace, commits];
|
|
986
|
+
})
|
|
987
|
+
)
|
|
988
|
+
);
|
|
989
|
+
await logger.group("Finding changed workspaces", async () => {
|
|
990
|
+
for (const { workspace, workspaceName } of bumpableWorkspaces) {
|
|
991
|
+
let bumpType = null;
|
|
992
|
+
let bumpReason;
|
|
993
|
+
if (options.force) {
|
|
994
|
+
bumpType = options.force;
|
|
995
|
+
bumpReason = "forced by --force flag";
|
|
996
|
+
} else {
|
|
997
|
+
const commits = commitsByWorkspace?.get(workspace);
|
|
998
|
+
if (!commits || commits.length === 0) {
|
|
999
|
+
logger.info(`${workspaceName}: skipped (no changes)`);
|
|
1000
|
+
continue;
|
|
1001
|
+
}
|
|
1002
|
+
const { releaseType, reason } = await recommendBump(
|
|
1003
|
+
commits,
|
|
1004
|
+
conventionalCommitConfig
|
|
1005
|
+
);
|
|
1006
|
+
bumpReason = reason;
|
|
1007
|
+
if (releaseType) {
|
|
1008
|
+
bumpType = releaseType;
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
if (bumpType) {
|
|
1012
|
+
if (isMonorepo && !workspace.pkg.name) {
|
|
1013
|
+
throw new Error("Workspace name is required");
|
|
1014
|
+
}
|
|
1015
|
+
const currentVersion = workspace.pkg.version;
|
|
1016
|
+
if (!currentVersion) {
|
|
1017
|
+
throw new UsageError(
|
|
1018
|
+
`Invalid "${getWorkspaceName(workspace)}" version`
|
|
1019
|
+
);
|
|
1020
|
+
}
|
|
1021
|
+
changedWorkspaces.set(workspace, {
|
|
1022
|
+
bumpType,
|
|
1023
|
+
bumpReason
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
if (changedWorkspaces.size === 0) {
|
|
1028
|
+
logger.info("No changed workspaces");
|
|
1029
|
+
}
|
|
1030
|
+
});
|
|
1031
|
+
const bumpedWorkspaces = /* @__PURE__ */ new Map();
|
|
1032
|
+
const noVersionToUpdateWorkspaces = /* @__PURE__ */ new Map();
|
|
1033
|
+
const batches = dependenciesMap ? buildTopologicalOrderBatches(project, dependenciesMap) : [[rootWorkspace]];
|
|
1034
|
+
logger.group("Preparing bumping", () => {
|
|
1035
|
+
for (const batch of batches) {
|
|
1036
|
+
for (const workspace of batch) {
|
|
1037
|
+
const currentVersion = workspace.pkg.version;
|
|
1038
|
+
if (!currentVersion && !workspace.pkg.private) {
|
|
1039
|
+
throw new UsageError(
|
|
1040
|
+
`Invalid "${getWorkspaceName(workspace)}" version`
|
|
1041
|
+
);
|
|
1042
|
+
}
|
|
1043
|
+
const changedWorkspace = changedWorkspaces.get(workspace);
|
|
1044
|
+
let bumpType = null;
|
|
1045
|
+
const bumpReasons = [];
|
|
1046
|
+
const dependenciesToBump = [];
|
|
1047
|
+
if (changedWorkspace) {
|
|
1048
|
+
bumpType = changedWorkspace.bumpType;
|
|
1049
|
+
bumpReasons.push(changedWorkspace.bumpReason || "by commits");
|
|
1050
|
+
}
|
|
1051
|
+
const dependencies = dependenciesMap?.get(workspace);
|
|
1052
|
+
if (dependencies) {
|
|
1053
|
+
for (const [
|
|
1054
|
+
dependencyWorkspace,
|
|
1055
|
+
dependencyType,
|
|
1056
|
+
dependencyDescriptor
|
|
1057
|
+
] of dependencies) {
|
|
1058
|
+
const dependencyBumpedWorkspace = bumpedWorkspaces.get(dependencyWorkspace);
|
|
1059
|
+
if (!dependencyBumpedWorkspace) {
|
|
1060
|
+
continue;
|
|
1061
|
+
}
|
|
1062
|
+
if (dependencyType === "peerDependencies" && !options.alwaysBumpPeerDependencies && // skip when peerdependency with a new version satisfied by the existing range.
|
|
1063
|
+
satisfies(
|
|
1064
|
+
dependencyBumpedWorkspace.newVersion,
|
|
1065
|
+
dependencyDescriptor.selector,
|
|
1066
|
+
{
|
|
1067
|
+
includePrerelease: true
|
|
1068
|
+
}
|
|
1069
|
+
)) {
|
|
1070
|
+
continue;
|
|
1071
|
+
}
|
|
1072
|
+
const newRange = calcBumpRange(
|
|
1073
|
+
workspace,
|
|
1074
|
+
dependencyDescriptor.selector,
|
|
1075
|
+
dependencyBumpedWorkspace.newVersion
|
|
1076
|
+
);
|
|
1077
|
+
if (dependencyDescriptor.selector === newRange && newRange !== "workspace:*" && newRange !== "workspace:^" && newRange !== "workspace:~") {
|
|
1078
|
+
continue;
|
|
1079
|
+
}
|
|
1080
|
+
dependenciesToBump.push([
|
|
1081
|
+
dependencyType,
|
|
1082
|
+
dependencyDescriptor,
|
|
1083
|
+
newRange
|
|
1084
|
+
]);
|
|
1085
|
+
bumpType = getHighestBumpType([
|
|
1086
|
+
bumpType ?? "patch",
|
|
1087
|
+
calcBumpType(
|
|
1088
|
+
dependencyBumpedWorkspace.bumpType,
|
|
1089
|
+
options.bumpDependentsHighestAs
|
|
1090
|
+
)
|
|
1091
|
+
]);
|
|
1092
|
+
bumpReasons.push(
|
|
1093
|
+
`Version bump for dependency: ${PackageDescriptorNameUtils.stringify(dependencyDescriptor.name)}`
|
|
1094
|
+
);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
const workspaceName = getWorkspaceName(workspace);
|
|
1098
|
+
if (!currentVersion) {
|
|
1099
|
+
logger.info(`${workspaceName}: skipped (no version)`);
|
|
1100
|
+
if (workspace !== rootWorkspace) {
|
|
1101
|
+
noVersionToUpdateWorkspaces.set(workspace, {
|
|
1102
|
+
dependenciesToBump
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
} else if (!bumpType) {
|
|
1106
|
+
logger.info(
|
|
1107
|
+
`${workspaceName}: skipped (${changedWorkspace ? `no bump recommended by ${options.preset}` : "no changes"})`
|
|
1108
|
+
);
|
|
1109
|
+
} else {
|
|
1110
|
+
const newVersion = incrementVersion(
|
|
1111
|
+
workspace,
|
|
1112
|
+
currentVersion,
|
|
1113
|
+
bumpType
|
|
1114
|
+
);
|
|
1115
|
+
const tagName = buildTagName(workspace, newVersion);
|
|
1116
|
+
if (workspace === rootWorkspace) {
|
|
1117
|
+
rootNewVersion = newVersion;
|
|
1118
|
+
rootNewTag = tagName;
|
|
1119
|
+
}
|
|
1120
|
+
if (workspace !== rootWorkspace || !isMonorepo) {
|
|
1121
|
+
const bumpReason = bumpReasons.join("\n");
|
|
1122
|
+
bumpedWorkspaces.set(workspace, {
|
|
1123
|
+
currentVersion,
|
|
1124
|
+
bumpType,
|
|
1125
|
+
bumpReason,
|
|
1126
|
+
bumpForDependenciesReasons: changedWorkspace ? bumpReasons.slice(1) : bumpReasons,
|
|
1127
|
+
newVersion,
|
|
1128
|
+
newTag: tagName,
|
|
1129
|
+
hasChanged: changedWorkspace !== void 0,
|
|
1130
|
+
dependenciesToBump
|
|
1131
|
+
});
|
|
1132
|
+
logger.info(
|
|
1133
|
+
`${workspaceName}: ${currentVersion} -> ${!isMonorepo || isMonorepoVersionIndependent ? newVersion : "bump"} (${bumpReason.replaceAll("\n", " ; ")})`
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
});
|
|
1140
|
+
if (isMonorepo && !isMonorepoVersionIndependent) {
|
|
1141
|
+
const currentVersion = rootWorkspace.pkg.version;
|
|
1142
|
+
const highestBumpType = getHighestBumpType(
|
|
1143
|
+
[...bumpedWorkspaces.values()].map(({ bumpType }) => bumpType)
|
|
1144
|
+
);
|
|
1145
|
+
const newVersion = incrementVersion(
|
|
1146
|
+
rootWorkspace,
|
|
1147
|
+
currentVersion,
|
|
1148
|
+
highestBumpType
|
|
1149
|
+
);
|
|
1150
|
+
rootNewVersion = newVersion;
|
|
1151
|
+
rootNewTag = buildTagName(rootWorkspace, newVersion);
|
|
1152
|
+
[...bumpedWorkspaces.entries()].forEach(([workspace, bumpedWorkspace]) => {
|
|
1153
|
+
const isRoot = workspace === rootWorkspace;
|
|
1154
|
+
if (isRoot) {
|
|
1155
|
+
throw new Error("Unexpected root found in bumped workspaces");
|
|
1156
|
+
}
|
|
1157
|
+
bumpedWorkspace.bumpType = highestBumpType;
|
|
1158
|
+
bumpedWorkspace.newVersion = newVersion;
|
|
1159
|
+
bumpedWorkspace.newTag = null;
|
|
1160
|
+
});
|
|
1161
|
+
[
|
|
1162
|
+
...bumpedWorkspaces.entries(),
|
|
1163
|
+
...noVersionToUpdateWorkspaces.entries()
|
|
1164
|
+
].forEach(([workspace, { dependenciesToBump }]) => {
|
|
1165
|
+
dependenciesToBump.forEach((dependencyToBump) => {
|
|
1166
|
+
dependencyToBump[2] = calcBumpRange(
|
|
1167
|
+
workspace,
|
|
1168
|
+
dependencyToBump[1].selector,
|
|
1169
|
+
newVersion
|
|
1170
|
+
);
|
|
1171
|
+
});
|
|
1172
|
+
});
|
|
1173
|
+
bumpedWorkspaces.set(rootWorkspace, {
|
|
1174
|
+
currentVersion,
|
|
1175
|
+
bumpType: highestBumpType,
|
|
1176
|
+
hasChanged: true,
|
|
1177
|
+
newVersion: rootNewVersion,
|
|
1178
|
+
newTag: rootNewTag,
|
|
1179
|
+
dependenciesToBump: []
|
|
1180
|
+
});
|
|
1181
|
+
logger.info(
|
|
1182
|
+
`${currentVersion} -> ${newVersion}`
|
|
1183
|
+
// TODO Json only:
|
|
1184
|
+
// {
|
|
1185
|
+
// oldVersion: currentVersion,
|
|
1186
|
+
// newVersion, },
|
|
1187
|
+
);
|
|
1188
|
+
}
|
|
1189
|
+
await logger.group("Applying changes", async () => {
|
|
1190
|
+
if (!options.dryRun) {
|
|
1191
|
+
if (packageManager.installOnPackageContentChange) {
|
|
1192
|
+
logger.info(`${getWorkspaceName(rootWorkspace)}: Running install`);
|
|
1193
|
+
await packageManager.installOnPackageContentChange(rootWorkspace);
|
|
1194
|
+
}
|
|
1195
|
+
logger.info("Lifecycle script: preversion");
|
|
1196
|
+
if (isMonorepoVersionIndependent && rootWorkspace.pkg.scripts?.preversion) {
|
|
1197
|
+
await packageManager.runScript(rootWorkspace, "preversion");
|
|
1198
|
+
}
|
|
1199
|
+
for (const workspace of bumpedWorkspaces.keys()) {
|
|
1200
|
+
if (workspace.pkg.scripts?.preversion) {
|
|
1201
|
+
await packageManager.runScript(workspace, "preversion");
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
logger.info("Modifying versions in package.json");
|
|
1205
|
+
await Promise.all(
|
|
1206
|
+
[...bumpedWorkspaces.entries()].map(
|
|
1207
|
+
([workspace, { newVersion, dependenciesToBump }]) => {
|
|
1208
|
+
workspace.pkg.version = newVersion;
|
|
1209
|
+
for (const [
|
|
1210
|
+
dependencyType,
|
|
1211
|
+
dependencyDescriptor,
|
|
1212
|
+
dependencyNewRange
|
|
1213
|
+
] of dependenciesToBump) {
|
|
1214
|
+
const newDescriptor = PackageDependencyDescriptorUtils.make(
|
|
1215
|
+
dependencyDescriptor,
|
|
1216
|
+
dependencyNewRange
|
|
1217
|
+
);
|
|
1218
|
+
const [key, newValue] = PackageDependencyDescriptorUtils.stringify(newDescriptor);
|
|
1219
|
+
workspace.pkg[dependencyType][key] = newValue;
|
|
1220
|
+
}
|
|
1221
|
+
return writePkg(workspace);
|
|
1222
|
+
}
|
|
1223
|
+
)
|
|
1224
|
+
);
|
|
1225
|
+
if (packageManager.installOnPackageContentChange) {
|
|
1226
|
+
logger.info(`${getWorkspaceName(rootWorkspace)}: Running install`);
|
|
1227
|
+
await packageManager.installOnPackageContentChange(rootWorkspace);
|
|
1228
|
+
}
|
|
1229
|
+
logger.info("Lifecycle script: version");
|
|
1230
|
+
for (const workspace of bumpedWorkspaces.keys()) {
|
|
1231
|
+
if (workspace.pkg.scripts?.version) {
|
|
1232
|
+
await packageManager.runScript(workspace, "version");
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
if (isMonorepoVersionIndependent && rootWorkspace.pkg.scripts?.version) {
|
|
1237
|
+
await packageManager.runScript(rootWorkspace, "version");
|
|
1238
|
+
}
|
|
1239
|
+
});
|
|
1240
|
+
const changelogs = /* @__PURE__ */ new Map();
|
|
1241
|
+
await Promise.all(
|
|
1242
|
+
[...bumpedWorkspaces.entries()].map(
|
|
1243
|
+
async ([
|
|
1244
|
+
workspace,
|
|
1245
|
+
{ newTag, hasChanged, bumpReason, bumpForDependenciesReasons }
|
|
1246
|
+
]) => {
|
|
1247
|
+
const commits = commitsByWorkspace?.get(workspace);
|
|
1248
|
+
let changelog = await generateChangelog(
|
|
1249
|
+
rootWorkspace,
|
|
1250
|
+
workspace.pkg,
|
|
1251
|
+
conventionalCommitConfig,
|
|
1252
|
+
previousTagVersionByWorkspace.get(workspace)?.tag || null,
|
|
1253
|
+
isMonorepoVersionIndependent ? newTag : rootNewTag,
|
|
1254
|
+
commits,
|
|
1255
|
+
todayInYYYYMMDD()
|
|
1256
|
+
// {
|
|
1257
|
+
// path: workspaceRelativePath,
|
|
1258
|
+
// previousTag: previousTagByWorkspace.get(workspace) || undefined,
|
|
1259
|
+
// verbose: options.verbose,
|
|
1260
|
+
// tagPrefix: options.tagVersionPrefix,
|
|
1261
|
+
// lernaPackage:
|
|
1262
|
+
// rootWorkspace === workspace
|
|
1263
|
+
// ? undefined
|
|
1264
|
+
// : getWorkspaceName(workspace),
|
|
1265
|
+
// },
|
|
1266
|
+
);
|
|
1267
|
+
if (bumpForDependenciesReasons && workspace !== rootWorkspace) {
|
|
1268
|
+
if (bumpForDependenciesReasons.length > 0) {
|
|
1269
|
+
changelog += `${!changelog.endsWith("\n\n") ? "\n" : ""}${bumpForDependenciesReasons.join("\n")}
|
|
1270
|
+
|
|
1271
|
+
`;
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
if (changelog.slice(changelog.indexOf("\n")).trim().length === 0) {
|
|
1275
|
+
changelog += `${!changelog.endsWith("\n\n") ? "\n" : ""}Note: no notable changes
|
|
1276
|
+
|
|
1277
|
+
`;
|
|
1278
|
+
}
|
|
1279
|
+
if (!changelog && rootWorkspace === workspace) {
|
|
1280
|
+
throw new Error("No changelog found for root workspace");
|
|
1281
|
+
}
|
|
1282
|
+
changelogs.set(workspace, changelog);
|
|
1283
|
+
if (options.changelog) {
|
|
1284
|
+
if (options.dryRun) {
|
|
1285
|
+
logger.info(
|
|
1286
|
+
`${getWorkspaceName(workspace)}: ${options.changelog}
|
|
1287
|
+
${changelog}`
|
|
1288
|
+
);
|
|
1289
|
+
} else {
|
|
1290
|
+
await updateChangelogFile(
|
|
1291
|
+
changelog,
|
|
1292
|
+
options.tagVersionPrefix,
|
|
1293
|
+
`${workspace.cwd}/${options.changelog}`
|
|
1294
|
+
);
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
)
|
|
1299
|
+
);
|
|
1300
|
+
if (!options.dryRun) {
|
|
1301
|
+
logger.separator();
|
|
1302
|
+
if (packageManager.installOnPackageContentChange) {
|
|
1303
|
+
logger.info(`${getWorkspaceName(rootWorkspace)}: Running install`);
|
|
1304
|
+
await packageManager.installOnPackageContentChange(rootWorkspace);
|
|
1305
|
+
}
|
|
1306
|
+
logger.separator();
|
|
1307
|
+
const changedFiles = await getDirtyFiles(project.root);
|
|
1308
|
+
if (changedFiles) {
|
|
1309
|
+
logger.info("Commit, tag and push", {
|
|
1310
|
+
changedFiles
|
|
1311
|
+
});
|
|
1312
|
+
const tagsSet = new Set(
|
|
1313
|
+
[...bumpedWorkspaces.values()].map(({ newTag }) => newTag).filter((newTag) => newTag !== null)
|
|
1314
|
+
);
|
|
1315
|
+
const tagsInCommitMessage = [...tagsSet].map((tag) => `- ${tag}`).join("\n");
|
|
1316
|
+
const message = options.commitMessage.replace(/\\n/g, "\n").replace(
|
|
1317
|
+
/%a/g,
|
|
1318
|
+
isMonorepoVersionIndependent ? `
|
|
1319
|
+
|
|
1320
|
+
${tagsInCommitMessage}` : rootNewVersion
|
|
1321
|
+
).replace(/%s/g, rootNewTag).replace(/%v/g, rootNewVersion).replace(/%t/g, tagsInCommitMessage);
|
|
1322
|
+
await createGitCommit(rootWorkspace, message);
|
|
1323
|
+
for (const [workspace, { newTag }] of bumpedWorkspaces.entries()) {
|
|
1324
|
+
if (newTag === null) continue;
|
|
1325
|
+
await createGitTag(workspace, newTag);
|
|
1326
|
+
}
|
|
1327
|
+
if (await isBehindRemote(rootWorkspace, options.gitRemote, gitCurrentBranch)) {
|
|
1328
|
+
logger.error("Remote is ahead, aborting");
|
|
1329
|
+
process.exit(1);
|
|
1330
|
+
}
|
|
1331
|
+
await pushCommitsAndTags(
|
|
1332
|
+
rootWorkspace,
|
|
1333
|
+
options.gitRemote,
|
|
1334
|
+
gitCurrentBranch
|
|
1335
|
+
);
|
|
1336
|
+
if (rootWorkspace.pkg.scripts?.postversion) {
|
|
1337
|
+
logger.info("Lifecycle script: postversion");
|
|
1338
|
+
await packageManager.runScript(rootWorkspace, "postversion");
|
|
1339
|
+
}
|
|
1340
|
+
if (options.createRelease && parsedRepoUrl) {
|
|
1341
|
+
logger.info("Create git release");
|
|
1342
|
+
await Promise.all(
|
|
1343
|
+
[...bumpedWorkspaces.entries()].map(([workspace, { newTag }]) => {
|
|
1344
|
+
if (newTag === null) return Promise.resolve(void 0);
|
|
1345
|
+
const changelog = changelogs.get(workspace);
|
|
1346
|
+
if (!changelog) {
|
|
1347
|
+
logger.warn(
|
|
1348
|
+
`No changelog found for workspace: ${getWorkspaceName(workspace)}`
|
|
1349
|
+
);
|
|
1350
|
+
return Promise.resolve(void 0);
|
|
1351
|
+
}
|
|
1352
|
+
return createGhRelease(workspace, {
|
|
1353
|
+
parsedRepoUrl,
|
|
1354
|
+
title: newTag,
|
|
1355
|
+
tag: newTag,
|
|
1356
|
+
body: changelog,
|
|
1357
|
+
prerelease: !!options.prerelease
|
|
1358
|
+
});
|
|
1359
|
+
})
|
|
1360
|
+
);
|
|
1361
|
+
}
|
|
1362
|
+
if (!options.publish) {
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
await logger.group("Publishing packages", async () => {
|
|
1366
|
+
if (packageManager.publishWorkspaces) {
|
|
1367
|
+
await packageManager.publishWorkspaces(rootWorkspace, {
|
|
1368
|
+
provenance: options.publishProvenance
|
|
1369
|
+
});
|
|
1370
|
+
} else {
|
|
1371
|
+
for (const [workspace] of bumpedWorkspaces.entries()) {
|
|
1372
|
+
if (workspace.pkg.private) {
|
|
1373
|
+
logger.info(
|
|
1374
|
+
`Skipping private workspace ${getWorkspaceName(workspace)}`
|
|
1375
|
+
);
|
|
1376
|
+
continue;
|
|
1377
|
+
}
|
|
1378
|
+
logger.info(`Publishing ${getWorkspaceName(workspace)}`);
|
|
1379
|
+
await packageManager.publish(workspace, {
|
|
1380
|
+
provenance: options.publishProvenance
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
});
|
|
1385
|
+
} else {
|
|
1386
|
+
logger.warn("No changes made. Nothing to commit.");
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
};
|
|
1390
|
+
const Defaults = {
|
|
1391
|
+
cwd: process.cwd(),
|
|
1392
|
+
includesRoot: false,
|
|
1393
|
+
dryRun: false,
|
|
1394
|
+
tagVersionPrefix: "v",
|
|
1395
|
+
changelog: "CHANGELOG.md",
|
|
1396
|
+
commitMessage: "chore: release %a",
|
|
1397
|
+
bumpDependentsHighestAs: "major",
|
|
1398
|
+
alwaysBumpPeerDependencies: false,
|
|
1399
|
+
gitRemote: "origin",
|
|
1400
|
+
verbose: false};
|
|
1401
|
+
program.command("version", { isDefault: true }).usage("Bump package version using conventional commit").addOption(
|
|
1402
|
+
new Option("--includes-root", "Release root workspace [untested]").default(
|
|
1403
|
+
Defaults.includesRoot
|
|
1404
|
+
)
|
|
1405
|
+
).addOption(new Option("--cwd", "working directory").default(process.cwd())).addOption(
|
|
1406
|
+
new Option(
|
|
1407
|
+
"--dry-run",
|
|
1408
|
+
"Print the versions without actually generating the package archive"
|
|
1409
|
+
).default(Defaults.dryRun)
|
|
1410
|
+
).addOption(new Option("-v,--verbose").default(Defaults.verbose)).addOption(
|
|
1411
|
+
new Option("--force <type>", "Specify the release type").choices([
|
|
1412
|
+
"major",
|
|
1413
|
+
"minor",
|
|
1414
|
+
"patch"
|
|
1415
|
+
])
|
|
1416
|
+
).addOption(
|
|
1417
|
+
new Option(
|
|
1418
|
+
"--prerelease [releaseType]",
|
|
1419
|
+
"Add a prerelease identifier to new versions"
|
|
1420
|
+
)
|
|
1421
|
+
).addOption(
|
|
1422
|
+
new Option(
|
|
1423
|
+
"--preset <presetName>",
|
|
1424
|
+
"Conventional Changelog preset to require. Defaults to conventional-changelog-conventionalcommits."
|
|
1425
|
+
).default("conventional-changelog-conventionalcommits")
|
|
1426
|
+
).addOption(
|
|
1427
|
+
new Option("--tag-version-prefix <prefix>", "Tag version prefix").default(
|
|
1428
|
+
Defaults.tagVersionPrefix
|
|
1429
|
+
)
|
|
1430
|
+
).addOption(
|
|
1431
|
+
new Option(
|
|
1432
|
+
"--changelog <path>",
|
|
1433
|
+
"Changelog path. Default to CHANGELOG.md."
|
|
1434
|
+
).default(Defaults.changelog)
|
|
1435
|
+
).addOption(
|
|
1436
|
+
new Option(
|
|
1437
|
+
"-m,--commit-message <message>",
|
|
1438
|
+
'Commit message. Default to "chore: release %a". You can use %v for the version, %s for the version with prefix, %t to list tags, %a for auto best display.'
|
|
1439
|
+
).default(Defaults.commitMessage)
|
|
1440
|
+
).addOption(
|
|
1441
|
+
new Option("--create-release <type>", "Create a release").choices([
|
|
1442
|
+
"github"
|
|
1443
|
+
])
|
|
1444
|
+
).addOption(
|
|
1445
|
+
new Option(
|
|
1446
|
+
"--bump-dependents-highest-as <type>",
|
|
1447
|
+
"Bump dependents highest version as major, minor or patch"
|
|
1448
|
+
).choices(["major", "minor", "patch"]).default(Defaults.bumpDependentsHighestAs)
|
|
1449
|
+
).addOption(
|
|
1450
|
+
new Option(
|
|
1451
|
+
"--always-bump-peer-dependencies",
|
|
1452
|
+
"Always bump peer dependencies. Default to bumping only if the version doesn't satisfies the peer dependency range."
|
|
1453
|
+
).default(Defaults.alwaysBumpPeerDependencies)
|
|
1454
|
+
).addOption(
|
|
1455
|
+
new Option(
|
|
1456
|
+
"--git-remote <remote-name>",
|
|
1457
|
+
"Git remote to push commits and tags to"
|
|
1458
|
+
).default(Defaults.gitRemote)
|
|
1459
|
+
).addOption(
|
|
1460
|
+
new Option(
|
|
1461
|
+
"--ignore-changes <glob>",
|
|
1462
|
+
'Ignore changes in files matching the glob. Example: "**/*.test.js"'
|
|
1463
|
+
)
|
|
1464
|
+
).addOption(new Option("--json", "Logger json")).addOption(
|
|
1465
|
+
new Option(
|
|
1466
|
+
"--package-manager <manager>",
|
|
1467
|
+
"Specify the package manager to use (bun or yarn). Autodetected if not specified."
|
|
1468
|
+
).choices(["bun", "yarn"])
|
|
1469
|
+
).addOption(new Option("--publish", "Publish after versioning").default(false)).addOption(
|
|
1470
|
+
new Option(
|
|
1471
|
+
"--publish-provenance",
|
|
1472
|
+
"Generate provenance attestation when publishing (if supported by package manager)"
|
|
1473
|
+
).default(false)
|
|
1474
|
+
).action((options) => versionCommandAction(options));
|
|
1475
|
+
|
|
1476
|
+
const pkg = JSON.parse(
|
|
1477
|
+
// eslint-disable-next-line unicorn/prefer-json-parse-buffer
|
|
1478
|
+
fs$1.readFileSync(new URL("../package.json", import.meta.url), "utf8")
|
|
1479
|
+
);
|
|
1480
|
+
program.name(pkg.name).description(pkg.description).version(pkg.version);
|
|
1481
|
+
program.parse();
|
|
1482
|
+
//# sourceMappingURL=index-node.mjs.map
|