@varlock/bumpy 1.10.2 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -2
- package/config-schema.json +10 -0
- package/dist/{add-B_1I_sen.mjs → add-DQA1TVHA.mjs} +8 -22
- package/dist/{bump-file-B9DpXK5X.mjs → bump-file-B7hmXZlB.mjs} +1 -1
- package/dist/{check-pbyTB4KC.mjs → check-CsF0zh8r.mjs} +32 -4
- package/dist/{ci-B4vNuXEb.mjs → ci-DBZW9k4S.mjs} +32 -25
- package/dist/{ci-setup-DKjyiF2-.mjs → ci-setup-CvA2CCmQ.mjs} +1 -1
- package/dist/cli.mjs +26 -14
- package/dist/{generate-BlZIe3aC.mjs → generate-CvCvUaRV.mjs} +2 -2
- package/dist/{git-JGLQtk-M.mjs → git-DJJ64SW9.mjs} +27 -9
- package/dist/index.mjs +2 -2
- package/dist/{init-Cs6amsw5.mjs → init-BCkm6Nfa.mjs} +1 -1
- package/dist/{package-manager-BQPwXwu5.mjs → package-manager-Db_vTztt.mjs} +72 -18
- package/dist/{publish-B9xzQREV.mjs → publish-BDA1bOZ7.mjs} +5 -5
- package/dist/{publish-pipeline-BRnqVylg.mjs → publish-pipeline-DSj14dW6.mjs} +2 -2
- package/dist/{status-DeMcLxiM.mjs → status-BbsDr6t7.mjs} +2 -2
- package/dist/{version-Bxin6aQC.mjs → version-Cm0nRAFF.mjs} +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -171,8 +171,6 @@ jobs:
|
|
|
171
171
|
|
|
172
172
|
</details>
|
|
173
173
|
|
|
174
|
-
You can also use `bumpy ci release --auto-publish` to version + publish directly on merge without the intermediate PR.
|
|
175
|
-
|
|
176
174
|
### Token setup
|
|
177
175
|
|
|
178
176
|
The default `github.token` works for basic functionality, but GitHub's anti-recursion guard means PRs created by the default token won't trigger other workflows - so your regular CI (tests, linting, etc.) won't run automatically on the Version Packages PR. To fix this, provide a `BUMPY_GH_TOKEN` secret using either a **fine-grained PAT** or a **GitHub App token**. See the [full token setup guide](https://github.com/dmno-dev/bumpy/blob/main/docs/github-actions.md#token-setup) for details.
|
package/config-schema.json
CHANGED
|
@@ -151,6 +151,16 @@
|
|
|
151
151
|
"enum": ["pack", "in-place", "none"],
|
|
152
152
|
"description": "How to handle workspace:/catalog: protocol resolution. \"pack\" = use PM's pack to build a clean tarball, \"in-place\" = rewrite package.json before publish, \"none\" = don't resolve.",
|
|
153
153
|
"default": "pack"
|
|
154
|
+
},
|
|
155
|
+
"provenance": {
|
|
156
|
+
"type": "boolean",
|
|
157
|
+
"description": "Attach provenance attestation when publishing via npm. Requires a supported CI environment with OIDC (GitHub Actions, GitLab CI, etc.). Only works with publishManager \"npm\".",
|
|
158
|
+
"default": false
|
|
159
|
+
},
|
|
160
|
+
"npmStaged": {
|
|
161
|
+
"type": "boolean",
|
|
162
|
+
"description": "Use npm staged publishing (`npm stage publish`). Stages the publish on npmjs.com, requiring manual 2FA approval before going live. Only works with publishManager \"npm\" and requires npm >= 11.15.0.",
|
|
163
|
+
"default": false
|
|
154
164
|
}
|
|
155
165
|
},
|
|
156
166
|
"additionalProperties": false
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { n as log, r as require_picocolors, s as __toESM } from "./logger-BgksGFuf.mjs";
|
|
2
2
|
import { n as exists, t as ensureDir } from "./fs-CBXKZhoU.mjs";
|
|
3
|
-
import { a as loadConfig,
|
|
4
|
-
import { a as writeBumpFile,
|
|
5
|
-
import {
|
|
3
|
+
import { a as loadConfig, r as getBumpyDir } from "./config-D_4GYDJi.mjs";
|
|
4
|
+
import { a as writeBumpFile, r as readBumpFiles, s as discoverWorkspace, t as filterBranchBumpFiles } from "./bump-file-B7hmXZlB.mjs";
|
|
5
|
+
import { i as getChangedFiles } from "./git-DJJ64SW9.mjs";
|
|
6
6
|
import { l as pt, o as gt, r as Ot, s as mt, t as unwrap, u as wt } from "./clack-W95rXis0.mjs";
|
|
7
7
|
import { n as slugify, t as randomName } from "./names-COooXAFg.mjs";
|
|
8
|
-
import { n as findChangedPackages
|
|
9
|
-
import {
|
|
8
|
+
import { n as findChangedPackages } from "./check-CsF0zh8r.mjs";
|
|
9
|
+
import { resolve } from "node:path";
|
|
10
10
|
import * as readline from "node:readline";
|
|
11
11
|
//#region src/prompts/bump-select.ts
|
|
12
|
-
var import_picomatch = /* @__PURE__ */ __toESM(require_picomatch(), 1);
|
|
13
12
|
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
14
13
|
const LEVELS = [
|
|
15
14
|
"skip",
|
|
@@ -208,26 +207,13 @@ async function addCommand(rootDir, opts) {
|
|
|
208
207
|
filename = opts.name ? slugify(opts.name) : randomName();
|
|
209
208
|
} else {
|
|
210
209
|
mt(import_picocolors.default.bgCyan(import_picocolors.default.black(" bumpy add ")));
|
|
211
|
-
const pkgs = await
|
|
210
|
+
const { packages: pkgs } = await discoverWorkspace(rootDir, config);
|
|
212
211
|
if (pkgs.size === 0) {
|
|
213
212
|
pt("No managed packages found in this workspace.");
|
|
214
213
|
process.exit(1);
|
|
215
214
|
}
|
|
216
|
-
const
|
|
217
|
-
const
|
|
218
|
-
const matchers = /* @__PURE__ */ new Map();
|
|
219
|
-
for (const [name, pkg] of pkgs) {
|
|
220
|
-
const patterns = (await loadPackageConfig(pkg.dir, config, name)).changedFilePatterns ?? config.changedFilePatterns;
|
|
221
|
-
matchers.set(name, (0, import_picomatch.default)(patterns));
|
|
222
|
-
}
|
|
223
|
-
const changedPackageNames = /* @__PURE__ */ new Set();
|
|
224
|
-
for (const file of changedFiles) for (const [name, pkg] of pkgs) {
|
|
225
|
-
const pkgRelDir = relative(rootDir, pkg.dir);
|
|
226
|
-
if (file.startsWith(pkgRelDir + "/")) {
|
|
227
|
-
const relToPackage = file.slice(pkgRelDir.length + 1);
|
|
228
|
-
if (matchers.get(name)(relToPackage)) changedPackageNames.add(name);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
215
|
+
const changedFiles = getChangedFiles(rootDir, config.baseBranch);
|
|
216
|
+
const changedPackageNames = new Set(await findChangedPackages(changedFiles, pkgs, rootDir, config));
|
|
231
217
|
const { bumpFiles: allBumpFiles } = await readBumpFiles(rootDir);
|
|
232
218
|
const { branchBumpFiles } = filterBranchBumpFiles(allBumpFiles, changedFiles, rootDir);
|
|
233
219
|
const alreadyCoveredPackages = /* @__PURE__ */ new Map();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as readJson, f as writeText, i as listFiles, n as exists, s as readText } from "./fs-CBXKZhoU.mjs";
|
|
2
2
|
import { i as isPackageManaged, o as loadPackageConfig, r as getBumpyDir } from "./config-D_4GYDJi.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { c as jsYaml, r as detectWorkspaces } from "./package-manager-Db_vTztt.mjs";
|
|
4
4
|
import { s as tryRunArgs } from "./shell-C8KgKnMQ.mjs";
|
|
5
5
|
import { relative, resolve } from "node:path";
|
|
6
6
|
import { readdir, stat } from "node:fs/promises";
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { a as __exportAll, i as __commonJSMin, n as log, s as __toESM, t as colorize } from "./logger-BgksGFuf.mjs";
|
|
2
|
+
import { n as exists, s as readText } from "./fs-CBXKZhoU.mjs";
|
|
3
|
+
import { a as DEP_TYPES } from "./types-Bkh-igOJ.mjs";
|
|
2
4
|
import { a as loadConfig, o as loadPackageConfig, r as getBumpyDir } from "./config-D_4GYDJi.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
5
|
+
import { a as isCatalogRefAffected, i as diffCatalogMaps, n as detectPackageManager, o as parseCatalogs, t as CATALOG_FILES } from "./package-manager-Db_vTztt.mjs";
|
|
6
|
+
import { r as readBumpFiles, s as discoverWorkspace, t as filterBranchBumpFiles } from "./bump-file-B7hmXZlB.mjs";
|
|
7
|
+
import { i as getChangedFiles, n as getBaseCompareRef, o as getFileStatuses, u as readFileAtRef } from "./git-DJJ64SW9.mjs";
|
|
8
|
+
import { relative, resolve } from "node:path";
|
|
6
9
|
//#region ../../node_modules/.bun/picomatch@4.0.4/node_modules/picomatch/lib/constants.js
|
|
7
10
|
var require_constants = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
8
11
|
const WIN_SLASH = "\\\\/";
|
|
@@ -1996,7 +1999,32 @@ async function findChangedPackages(changedFiles, packages, rootDir, config) {
|
|
|
1996
1999
|
if (matchers.get(name)(relToPackage)) changed.add(name);
|
|
1997
2000
|
}
|
|
1998
2001
|
}
|
|
2002
|
+
const catalogChanges = await getChangedCatalogEntries(rootDir, config.baseBranch, changedFiles);
|
|
2003
|
+
if (catalogChanges.size > 0) for (const [name, pkg] of packages) {
|
|
2004
|
+
if (changed.has(name)) continue;
|
|
2005
|
+
for (const depType of DEP_TYPES) {
|
|
2006
|
+
const deps = pkg[depType];
|
|
2007
|
+
let matched = false;
|
|
2008
|
+
for (const [depName, range] of Object.entries(deps)) if (isCatalogRefAffected(range, depName, catalogChanges)) {
|
|
2009
|
+
changed.add(name);
|
|
2010
|
+
matched = true;
|
|
2011
|
+
break;
|
|
2012
|
+
}
|
|
2013
|
+
if (matched) break;
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
1999
2016
|
return [...changed];
|
|
2000
2017
|
}
|
|
2018
|
+
/**
|
|
2019
|
+
* Compute which catalog entries changed between the base ref and HEAD.
|
|
2020
|
+
* Returns Map<catalogName, Set<depName>>. Empty if no catalog files changed.
|
|
2021
|
+
*/
|
|
2022
|
+
async function getChangedCatalogEntries(rootDir, baseBranch, changedFiles) {
|
|
2023
|
+
if (!changedFiles.some((f) => CATALOG_FILES.includes(f))) return /* @__PURE__ */ new Map();
|
|
2024
|
+
const baseRef = getBaseCompareRef(rootDir, baseBranch);
|
|
2025
|
+
const pm = await detectPackageManager(rootDir);
|
|
2026
|
+
const afterCatalogs = parseCatalogs(pm === "pnpm" && await exists(resolve(rootDir, "pnpm-workspace.yaml")) ? await readText(resolve(rootDir, "pnpm-workspace.yaml")) : null, await exists(resolve(rootDir, "package.json")) ? await readText(resolve(rootDir, "package.json")) : null);
|
|
2027
|
+
return diffCatalogMaps(parseCatalogs(pm === "pnpm" ? readFileAtRef(rootDir, baseRef, "pnpm-workspace.yaml") : null, readFileAtRef(rootDir, baseRef, "package.json")), afterCatalogs);
|
|
2028
|
+
}
|
|
2001
2029
|
//#endregion
|
|
2002
|
-
export { findChangedPackages as n,
|
|
2030
|
+
export { findChangedPackages as n, check_exports as t };
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { n as log, t as colorize } from "./logger-BgksGFuf.mjs";
|
|
2
2
|
import { a as loadConfig } from "./config-D_4GYDJi.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import { i as recoverDeletedBumpFiles, r as readBumpFiles, s as discoverWorkspace, t as filterBranchBumpFiles } from "./bump-file-
|
|
3
|
+
import { n as detectPackageManager } from "./package-manager-Db_vTztt.mjs";
|
|
4
|
+
import { i as recoverDeletedBumpFiles, r as readBumpFiles, s as discoverWorkspace, t as filterBranchBumpFiles } from "./bump-file-B7hmXZlB.mjs";
|
|
5
5
|
import { a as DependencyGraph, t as assembleReleasePlan } from "./release-plan-mK7iGeGq.mjs";
|
|
6
6
|
import { n as runArgs, r as runArgsAsync, s as tryRunArgs } from "./shell-C8KgKnMQ.mjs";
|
|
7
|
-
import {
|
|
7
|
+
import { f as withGitToken, i as getChangedFiles } from "./git-DJJ64SW9.mjs";
|
|
8
8
|
import { t as randomName } from "./names-COooXAFg.mjs";
|
|
9
|
-
import { n as findChangedPackages } from "./check-
|
|
9
|
+
import { n as findChangedPackages } from "./check-CsF0zh8r.mjs";
|
|
10
10
|
import { t as resolveCommitMessage } from "./commit-message-CSWVKPJ-.mjs";
|
|
11
11
|
import { appendFileSync, mkdirSync, writeFileSync } from "node:fs";
|
|
12
12
|
import { createHash } from "node:crypto";
|
|
@@ -157,7 +157,7 @@ async function ciPlanCommand(rootDir) {
|
|
|
157
157
|
packageNames: plan.releases.map((r) => r.name)
|
|
158
158
|
};
|
|
159
159
|
else {
|
|
160
|
-
const { findUnpublishedPackages } = await import("./publish-
|
|
160
|
+
const { findUnpublishedPackages } = await import("./publish-BDA1bOZ7.mjs");
|
|
161
161
|
const unpublished = await findUnpublishedPackages(packages, config);
|
|
162
162
|
if (unpublished.length > 0) output = {
|
|
163
163
|
mode: "publish",
|
|
@@ -212,8 +212,10 @@ function writeGitHubOutput(key, value) {
|
|
|
212
212
|
appendFileSync(outputFile, `${key}<<${delimiter}\n${value}\n${delimiter}\n`);
|
|
213
213
|
}
|
|
214
214
|
/**
|
|
215
|
-
* CI release: either
|
|
216
|
-
*
|
|
215
|
+
* CI release: either create a version PR (bump files present) or publish unpublished
|
|
216
|
+
* packages (no bump files — i.e. a version PR was just merged). Pass `autoPublish` to
|
|
217
|
+
* collapse both steps into a single push-to-main, or `assertMode` to refuse running
|
|
218
|
+
* when the detected state doesn't match expectations (used by split-job workflows).
|
|
217
219
|
*/
|
|
218
220
|
async function ciReleaseCommand(rootDir, opts) {
|
|
219
221
|
const config = await loadConfig(rootDir);
|
|
@@ -225,33 +227,38 @@ async function ciReleaseCommand(rootDir, opts) {
|
|
|
225
227
|
for (const err of releaseParseErrors) log.error(err);
|
|
226
228
|
throw new Error("Bump file parse errors must be fixed before releasing.");
|
|
227
229
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
recoveredBumpFiles
|
|
235
|
-
});
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
238
|
-
const plan = assembleReleasePlan(bumpFiles, packages, depGraph, config);
|
|
239
|
-
if (plan.releases.length === 0) {
|
|
240
|
-
log.info("Bump files found but no packages would be released — checking for unpublished packages...");
|
|
230
|
+
const plan = bumpFiles.length > 0 ? assembleReleasePlan(bumpFiles, packages, depGraph, config) : null;
|
|
231
|
+
const detectedMode = plan && plan.releases.length > 0 ? "version-pr" : "publish";
|
|
232
|
+
if (opts.assertMode && opts.assertMode !== detectedMode) throw new Error(`Expected mode "${opts.assertMode}" but detected "${detectedMode}". Either remove --expect-mode, or gate this step on the output of "bumpy ci plan".`);
|
|
233
|
+
if (detectedMode === "publish") {
|
|
234
|
+
const msg = bumpFiles.length === 0 ? "No pending bump files — checking for unpublished packages..." : "Bump files found but no packages would be released — checking for unpublished packages...";
|
|
235
|
+
log.info(msg);
|
|
241
236
|
const recoveredBumpFiles = recoverDeletedBumpFiles(rootDir);
|
|
242
|
-
const { publishCommand } = await import("./publish-
|
|
237
|
+
const { publishCommand } = await import("./publish-BDA1bOZ7.mjs");
|
|
243
238
|
await publishCommand(rootDir, {
|
|
244
239
|
tag: opts.tag,
|
|
245
240
|
recoveredBumpFiles
|
|
246
241
|
});
|
|
247
242
|
return;
|
|
248
243
|
}
|
|
249
|
-
if (opts.
|
|
244
|
+
if (opts.autoPublish) await autoPublish(rootDir, config, plan, opts.tag);
|
|
250
245
|
else await createVersionPr(rootDir, plan, config, new Map([...packages.values()].map((p) => [p.name, p.relativeDir])), opts.branch);
|
|
251
246
|
}
|
|
247
|
+
/**
|
|
248
|
+
* "Auto-publish" mode: skip the Version Packages PR and ship version+publish in one run.
|
|
249
|
+
*
|
|
250
|
+
* The only thing forfeited vs. the default flow is the preview/review gate on version
|
|
251
|
+
* bumps. Credentials are NOT a differentiator — a single-job non-auto-publish workflow
|
|
252
|
+
* also carries both PR-write and publish creds, just split across two runs. The real
|
|
253
|
+
* credential separation comes from the split-job pattern, which is orthogonal to (and
|
|
254
|
+
* incompatible with) this flag, since this collapses both paths into one execution.
|
|
255
|
+
*
|
|
256
|
+
* That incompatibility is also why --auto-publish and --expect-mode are mutually exclusive:
|
|
257
|
+
* --expect-mode is for split-job workflows where each job runs exactly one path.
|
|
258
|
+
*/
|
|
252
259
|
async function autoPublish(rootDir, config, plan, tag) {
|
|
253
260
|
log.step("Running bumpy version...");
|
|
254
|
-
const { versionCommand } = await import("./version-
|
|
261
|
+
const { versionCommand } = await import("./version-Cm0nRAFF.mjs");
|
|
255
262
|
await versionCommand(rootDir);
|
|
256
263
|
log.step("Committing version changes...");
|
|
257
264
|
runArgs([
|
|
@@ -280,7 +287,7 @@ async function autoPublish(rootDir, config, plan, tag) {
|
|
|
280
287
|
], { cwd: rootDir });
|
|
281
288
|
}
|
|
282
289
|
log.step("Running bumpy publish...");
|
|
283
|
-
const { publishCommand } = await import("./publish-
|
|
290
|
+
const { publishCommand } = await import("./publish-BDA1bOZ7.mjs");
|
|
284
291
|
await publishCommand(rootDir, { tag });
|
|
285
292
|
}
|
|
286
293
|
/**
|
|
@@ -354,7 +361,7 @@ async function createVersionPr(rootDir, plan, config, packageDirs, branchName) {
|
|
|
354
361
|
branch
|
|
355
362
|
], { cwd: rootDir });
|
|
356
363
|
log.step("Running bumpy version...");
|
|
357
|
-
const { versionCommand } = await import("./version-
|
|
364
|
+
const { versionCommand } = await import("./version-Cm0nRAFF.mjs");
|
|
358
365
|
await versionCommand(rootDir);
|
|
359
366
|
runArgs([
|
|
360
367
|
"git",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as log, r as require_picocolors, s as __toESM } from "./logger-BgksGFuf.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { n as detectPackageManager } from "./package-manager-Db_vTztt.mjs";
|
|
3
3
|
import { s as tryRunArgs } from "./shell-C8KgKnMQ.mjs";
|
|
4
4
|
import { a as fe, c as ot, i as _t, n as O, o as gt, r as Ot, s as mt, t as unwrap, u as wt } from "./clack-W95rXis0.mjs";
|
|
5
5
|
//#region src/commands/ci-setup.ts
|
package/dist/cli.mjs
CHANGED
|
@@ -25,13 +25,13 @@ async function main() {
|
|
|
25
25
|
switch (command) {
|
|
26
26
|
case "init": {
|
|
27
27
|
const rootDir = await findRoot();
|
|
28
|
-
const { initCommand } = await import("./init-
|
|
28
|
+
const { initCommand } = await import("./init-BCkm6Nfa.mjs");
|
|
29
29
|
await initCommand(rootDir, { force: flags.force === true });
|
|
30
30
|
break;
|
|
31
31
|
}
|
|
32
32
|
case "add": {
|
|
33
33
|
const rootDir = await findRoot();
|
|
34
|
-
const { addCommand } = await import("./add-
|
|
34
|
+
const { addCommand } = await import("./add-DQA1TVHA.mjs");
|
|
35
35
|
await addCommand(rootDir, {
|
|
36
36
|
packages: flags.packages,
|
|
37
37
|
message: flags.message,
|
|
@@ -43,7 +43,7 @@ async function main() {
|
|
|
43
43
|
}
|
|
44
44
|
case "status": {
|
|
45
45
|
const rootDir = await findRoot();
|
|
46
|
-
const { statusCommand } = await import("./status-
|
|
46
|
+
const { statusCommand } = await import("./status-BbsDr6t7.mjs");
|
|
47
47
|
await statusCommand(rootDir, {
|
|
48
48
|
json: flags.json === true,
|
|
49
49
|
packagesOnly: flags.packages === true,
|
|
@@ -55,13 +55,13 @@ async function main() {
|
|
|
55
55
|
}
|
|
56
56
|
case "version": {
|
|
57
57
|
const rootDir = await findRoot();
|
|
58
|
-
const { versionCommand } = await import("./version-
|
|
58
|
+
const { versionCommand } = await import("./version-Cm0nRAFF.mjs");
|
|
59
59
|
await versionCommand(rootDir, { commit: flags.commit === true });
|
|
60
60
|
break;
|
|
61
61
|
}
|
|
62
62
|
case "generate": {
|
|
63
63
|
const rootDir = await findRoot();
|
|
64
|
-
const { generateCommand } = await import("./generate-
|
|
64
|
+
const { generateCommand } = await import("./generate-CvCvUaRV.mjs");
|
|
65
65
|
await generateCommand(rootDir, {
|
|
66
66
|
from: flags.from,
|
|
67
67
|
dryRun: flags["dry-run"] === true,
|
|
@@ -71,7 +71,7 @@ async function main() {
|
|
|
71
71
|
}
|
|
72
72
|
case "check": {
|
|
73
73
|
const rootDir = await findRoot();
|
|
74
|
-
const { checkCommand } = await import("./check-
|
|
74
|
+
const { checkCommand } = await import("./check-CsF0zh8r.mjs").then((n) => n.t);
|
|
75
75
|
const hookValue = flags.hook;
|
|
76
76
|
if (hookValue && hookValue !== "pre-commit" && hookValue !== "pre-push") {
|
|
77
77
|
log.error(`Invalid --hook value "${hookValue}". Expected "pre-commit" or "pre-push".`);
|
|
@@ -89,24 +89,35 @@ async function main() {
|
|
|
89
89
|
const subcommand = args[1];
|
|
90
90
|
const ciFlags = parseFlags(args.slice(2));
|
|
91
91
|
if (subcommand === "check") {
|
|
92
|
-
const { ciCheckCommand } = await import("./ci-
|
|
92
|
+
const { ciCheckCommand } = await import("./ci-DBZW9k4S.mjs");
|
|
93
93
|
await ciCheckCommand(rootDir, {
|
|
94
94
|
comment: ciFlags.comment !== void 0 ? ciFlags.comment === true : void 0,
|
|
95
95
|
strict: ciFlags.strict === true,
|
|
96
96
|
noFail: ciFlags["no-fail"] === true
|
|
97
97
|
});
|
|
98
98
|
} else if (subcommand === "plan") {
|
|
99
|
-
const { ciPlanCommand } = await import("./ci-
|
|
99
|
+
const { ciPlanCommand } = await import("./ci-DBZW9k4S.mjs");
|
|
100
100
|
await ciPlanCommand(rootDir);
|
|
101
101
|
} else if (subcommand === "release") {
|
|
102
|
-
const { ciReleaseCommand } = await import("./ci-
|
|
102
|
+
const { ciReleaseCommand } = await import("./ci-DBZW9k4S.mjs");
|
|
103
|
+
const expectModeFlag = ciFlags["expect-mode"];
|
|
104
|
+
const autoPublishFlag = ciFlags["auto-publish"] === true;
|
|
105
|
+
if (expectModeFlag !== void 0 && expectModeFlag !== "version-pr" && expectModeFlag !== "publish") {
|
|
106
|
+
log.error(`Invalid --expect-mode value: "${expectModeFlag}". Must be "version-pr" or "publish".`);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
if (expectModeFlag !== void 0 && autoPublishFlag) {
|
|
110
|
+
log.error("--expect-mode and --auto-publish cannot be used together.");
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
103
113
|
await ciReleaseCommand(rootDir, {
|
|
104
|
-
|
|
114
|
+
autoPublish: autoPublishFlag,
|
|
115
|
+
assertMode: expectModeFlag,
|
|
105
116
|
tag: ciFlags.tag,
|
|
106
117
|
branch: ciFlags.branch
|
|
107
118
|
});
|
|
108
119
|
} else if (subcommand === "setup") {
|
|
109
|
-
const { ciSetupCommand } = await import("./ci-setup-
|
|
120
|
+
const { ciSetupCommand } = await import("./ci-setup-CvA2CCmQ.mjs");
|
|
110
121
|
await ciSetupCommand(rootDir);
|
|
111
122
|
} else {
|
|
112
123
|
log.error(`Unknown ci subcommand: ${subcommand}. Use "ci check", "ci plan", "ci release", or "ci setup".`);
|
|
@@ -116,7 +127,7 @@ async function main() {
|
|
|
116
127
|
}
|
|
117
128
|
case "publish": {
|
|
118
129
|
const rootDir = await findRoot();
|
|
119
|
-
const { publishCommand } = await import("./publish-
|
|
130
|
+
const { publishCommand } = await import("./publish-BDA1bOZ7.mjs");
|
|
120
131
|
await publishCommand(rootDir, {
|
|
121
132
|
dryRun: flags["dry-run"] === true,
|
|
122
133
|
tag: flags.tag,
|
|
@@ -140,7 +151,7 @@ async function main() {
|
|
|
140
151
|
}
|
|
141
152
|
case "--version":
|
|
142
153
|
case "-v":
|
|
143
|
-
console.log(`bumpy 1.
|
|
154
|
+
console.log(`bumpy 1.12.0`);
|
|
144
155
|
break;
|
|
145
156
|
case "help":
|
|
146
157
|
case "--help":
|
|
@@ -160,7 +171,7 @@ async function main() {
|
|
|
160
171
|
}
|
|
161
172
|
function printHelp() {
|
|
162
173
|
console.log(`
|
|
163
|
-
${colorize(`🐸 bumpy v1.
|
|
174
|
+
${colorize(`🐸 bumpy v1.12.0`, "bold")} - Modern monorepo versioning
|
|
164
175
|
|
|
165
176
|
Usage: bumpy <command> [options]
|
|
166
177
|
|
|
@@ -213,6 +224,7 @@ function printHelp() {
|
|
|
213
224
|
--no-fail Warn only, never exit 1
|
|
214
225
|
|
|
215
226
|
CI release options:
|
|
227
|
+
--expect-mode <mode> Assert detected mode: "version-pr" or "publish" (errors if mismatched)
|
|
216
228
|
--auto-publish Version + publish directly (default: create version PR)
|
|
217
229
|
--tag <tag> npm dist-tag for auto-publish
|
|
218
230
|
--branch <name> Branch name for version PR (default: bumpy/version-packages)
|
|
@@ -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
3
|
import { a as loadConfig, r as getBumpyDir } from "./config-D_4GYDJi.mjs";
|
|
4
|
-
import { a as writeBumpFile, o as discoverPackages } from "./bump-file-
|
|
4
|
+
import { a as writeBumpFile, o as discoverPackages } from "./bump-file-B7hmXZlB.mjs";
|
|
5
5
|
import { s as tryRunArgs } from "./shell-C8KgKnMQ.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { r as getBranchCommits, s as getFilesChangedInCommit } from "./git-DJJ64SW9.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
|
|
@@ -141,8 +141,11 @@ function tagExists(tag, opts) {
|
|
|
141
141
|
tag
|
|
142
142
|
], opts) === tag;
|
|
143
143
|
}
|
|
144
|
-
/**
|
|
145
|
-
|
|
144
|
+
/**
|
|
145
|
+
* Resolve the ref to use as the comparison base for "this branch vs main".
|
|
146
|
+
* Prefers the merge-base, falls back to `origin/<baseBranch>`.
|
|
147
|
+
*/
|
|
148
|
+
function getBaseCompareRef(rootDir, baseBranch) {
|
|
146
149
|
if (!tryRunArgs([
|
|
147
150
|
"git",
|
|
148
151
|
"rev-parse",
|
|
@@ -155,20 +158,35 @@ function getChangedFiles(rootDir, baseBranch) {
|
|
|
155
158
|
baseBranch,
|
|
156
159
|
"--depth=1"
|
|
157
160
|
], { cwd: rootDir });
|
|
161
|
+
return tryRunArgs([
|
|
162
|
+
"git",
|
|
163
|
+
"merge-base",
|
|
164
|
+
"HEAD",
|
|
165
|
+
`origin/${baseBranch}`
|
|
166
|
+
], { cwd: rootDir }) || `origin/${baseBranch}`;
|
|
167
|
+
}
|
|
168
|
+
/** Get files changed on this branch compared to a base branch */
|
|
169
|
+
function getChangedFiles(rootDir, baseBranch) {
|
|
158
170
|
const diff = tryRunArgs([
|
|
159
171
|
"git",
|
|
160
172
|
"diff",
|
|
161
173
|
"--name-only",
|
|
162
|
-
|
|
163
|
-
"git",
|
|
164
|
-
"merge-base",
|
|
165
|
-
"HEAD",
|
|
166
|
-
`origin/${baseBranch}`
|
|
167
|
-
], { cwd: rootDir }) || `origin/${baseBranch}`
|
|
174
|
+
getBaseCompareRef(rootDir, baseBranch)
|
|
168
175
|
], { cwd: rootDir });
|
|
169
176
|
if (!diff) return [];
|
|
170
177
|
return diff.split("\n").filter(Boolean);
|
|
171
178
|
}
|
|
179
|
+
/**
|
|
180
|
+
* Read a file's contents at a given git ref.
|
|
181
|
+
* Returns null if the file does not exist at that ref.
|
|
182
|
+
*/
|
|
183
|
+
function readFileAtRef(rootDir, ref, path) {
|
|
184
|
+
return tryRunArgs([
|
|
185
|
+
"git",
|
|
186
|
+
"show",
|
|
187
|
+
`${ref}:${path}`
|
|
188
|
+
], { cwd: rootDir });
|
|
189
|
+
}
|
|
172
190
|
/** Get commits on the current branch since it diverged from baseBranch */
|
|
173
191
|
function getBranchCommits(rootDir, baseBranch) {
|
|
174
192
|
if (!tryRunArgs([
|
|
@@ -242,4 +260,4 @@ function getFileStatuses(dir, opts) {
|
|
|
242
260
|
return statuses;
|
|
243
261
|
}
|
|
244
262
|
//#endregion
|
|
245
|
-
export {
|
|
263
|
+
export { getCurrentBranch as a, hasUncommittedChanges as c, tagExists as d, withGitToken as f, getChangedFiles as i, pushWithTags as l, getBaseCompareRef as n, getFileStatuses as o, getBranchCommits as r, getFilesChangedInCommit as s, createTag as t, readFileAtRef as u };
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
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-Bkh-igOJ.mjs";
|
|
2
2
|
import { a as loadConfig, n as findRoot, r as getBumpyDir, s as matchGlob } from "./config-D_4GYDJi.mjs";
|
|
3
|
-
import { a as writeBumpFile, n as parseBumpFile, o as discoverPackages, r as readBumpFiles } from "./bump-file-
|
|
3
|
+
import { a as writeBumpFile, n as parseBumpFile, o as discoverPackages, r as readBumpFiles } from "./bump-file-B7hmXZlB.mjs";
|
|
4
4
|
import { a as DependencyGraph, i as stripProtocol, n as bumpVersion, r as satisfies, t as assembleReleasePlan } from "./release-plan-mK7iGeGq.mjs";
|
|
5
5
|
import { a as prependToChangelog, i as loadFormatter, n as generateChangelogEntry, t as defaultFormatter } from "./changelog-CbaET5V6.mjs";
|
|
6
6
|
import { t as applyReleasePlan } from "./apply-release-plan-DD2R7SL2.mjs";
|
|
7
|
-
import { t as publishPackages } from "./publish-pipeline-
|
|
7
|
+
import { t as publishPackages } from "./publish-pipeline-DSj14dW6.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 };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as log, r as require_picocolors, s as __toESM } from "./logger-BgksGFuf.mjs";
|
|
2
2
|
import { a as readJson, d as writeJson, f as writeText, i as listFiles, n as exists, s as readText, t as ensureDir } from "./fs-CBXKZhoU.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { n as detectPackageManager } from "./package-manager-Db_vTztt.mjs";
|
|
4
4
|
import { t as run } from "./shell-C8KgKnMQ.mjs";
|
|
5
5
|
import { c as ot, o as gt, s as mt, t as unwrap } from "./clack-W95rXis0.mjs";
|
|
6
6
|
import { resolve } from "node:path";
|
|
@@ -2072,38 +2072,92 @@ async function getWorkspaceGlobs(rootDir, pm) {
|
|
|
2072
2072
|
} catch {}
|
|
2073
2073
|
return [];
|
|
2074
2074
|
}
|
|
2075
|
-
/**
|
|
2076
|
-
|
|
2075
|
+
/**
|
|
2076
|
+
* Files that may contain catalog definitions, in the order they're applied.
|
|
2077
|
+
* Later entries override earlier ones (matching loadCatalogs behavior).
|
|
2078
|
+
*/
|
|
2079
|
+
const CATALOG_FILES = ["pnpm-workspace.yaml", "package.json"];
|
|
2080
|
+
/**
|
|
2081
|
+
* Normalize a catalog name to its canonical form.
|
|
2082
|
+
* pnpm/bun treat "default" and the unnamed top-level catalog interchangeably,
|
|
2083
|
+
* so we store and look up the default catalog under "" regardless of which alias
|
|
2084
|
+
* the user wrote.
|
|
2085
|
+
*/
|
|
2086
|
+
function normalizeCatalogName(name) {
|
|
2087
|
+
return name === "default" ? "" : name;
|
|
2088
|
+
}
|
|
2089
|
+
/** Parse catalog definitions from the raw contents of pnpm-workspace.yaml and root package.json */
|
|
2090
|
+
function parseCatalogs(pnpmWorkspaceYaml, rootPackageJson) {
|
|
2077
2091
|
const catalogs = /* @__PURE__ */ new Map();
|
|
2078
|
-
|
|
2079
|
-
const
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
const pkg = await readJson(resolve(rootDir, "package.json"));
|
|
2092
|
+
const addNamed = (raw) => {
|
|
2093
|
+
for (const [name, deps] of Object.entries(raw)) catalogs.set(normalizeCatalogName(name), deps);
|
|
2094
|
+
};
|
|
2095
|
+
if (pnpmWorkspaceYaml) try {
|
|
2096
|
+
const parsed = jsYaml.load(pnpmWorkspaceYaml);
|
|
2097
|
+
if (parsed?.catalog) catalogs.set("", parsed.catalog);
|
|
2098
|
+
if (parsed?.catalogs) addNamed(parsed.catalogs);
|
|
2099
|
+
} catch {}
|
|
2100
|
+
if (rootPackageJson) try {
|
|
2101
|
+
const pkg = JSON.parse(rootPackageJson);
|
|
2089
2102
|
if (pkg.catalog && typeof pkg.catalog === "object") catalogs.set("", pkg.catalog);
|
|
2090
|
-
if (pkg.catalogs && typeof pkg.catalogs === "object")
|
|
2103
|
+
if (pkg.catalogs && typeof pkg.catalogs === "object") addNamed(pkg.catalogs);
|
|
2091
2104
|
const workspaces = pkg.workspaces;
|
|
2092
2105
|
if (workspaces && typeof workspaces === "object" && !Array.isArray(workspaces)) {
|
|
2093
2106
|
const ws = workspaces;
|
|
2094
2107
|
if (ws.catalog && typeof ws.catalog === "object") catalogs.set("", ws.catalog);
|
|
2095
|
-
if (ws.catalogs && typeof ws.catalogs === "object")
|
|
2108
|
+
if (ws.catalogs && typeof ws.catalogs === "object") addNamed(ws.catalogs);
|
|
2096
2109
|
}
|
|
2097
2110
|
} catch {}
|
|
2098
2111
|
return catalogs;
|
|
2099
2112
|
}
|
|
2113
|
+
/** Load catalog definitions from pnpm-workspace.yaml or root package.json */
|
|
2114
|
+
async function loadCatalogs(rootDir, pm) {
|
|
2115
|
+
let pnpmYaml = null;
|
|
2116
|
+
if (pm === "pnpm") {
|
|
2117
|
+
const wsFile = resolve(rootDir, "pnpm-workspace.yaml");
|
|
2118
|
+
if (await exists(wsFile)) pnpmYaml = await readText(wsFile);
|
|
2119
|
+
}
|
|
2120
|
+
let pkgJsonText = null;
|
|
2121
|
+
const pkgJsonPath = resolve(rootDir, "package.json");
|
|
2122
|
+
if (await exists(pkgJsonPath)) pkgJsonText = await readText(pkgJsonPath);
|
|
2123
|
+
return parseCatalogs(pnpmYaml, pkgJsonText);
|
|
2124
|
+
}
|
|
2125
|
+
/** Extract the catalog name from a `catalog:` / `catalog:<name>` range, normalizing the default alias */
|
|
2126
|
+
function catalogNameFromRange(range) {
|
|
2127
|
+
return normalizeCatalogName(range.slice(8).trim());
|
|
2128
|
+
}
|
|
2100
2129
|
/** Resolve a specific dependency's catalog: reference */
|
|
2101
2130
|
function resolveCatalogDep(depName, range, catalogs) {
|
|
2102
2131
|
if (!range.startsWith("catalog:")) return null;
|
|
2103
|
-
const
|
|
2104
|
-
const catalog = catalogs.get(catalogName);
|
|
2132
|
+
const catalog = catalogs.get(catalogNameFromRange(range));
|
|
2105
2133
|
if (!catalog) return null;
|
|
2106
2134
|
return catalog[depName] ?? null;
|
|
2107
2135
|
}
|
|
2136
|
+
/**
|
|
2137
|
+
* Diff two catalog states and return the set of (catalogName → changed depNames).
|
|
2138
|
+
* Includes added, removed, and version-changed entries.
|
|
2139
|
+
*/
|
|
2140
|
+
function diffCatalogMaps(before, after) {
|
|
2141
|
+
const changes = /* @__PURE__ */ new Map();
|
|
2142
|
+
const catalogNames = new Set([...before.keys(), ...after.keys()]);
|
|
2143
|
+
for (const catalogName of catalogNames) {
|
|
2144
|
+
const beforeDeps = before.get(catalogName) ?? {};
|
|
2145
|
+
const afterDeps = after.get(catalogName) ?? {};
|
|
2146
|
+
const depNames = new Set([...Object.keys(beforeDeps), ...Object.keys(afterDeps)]);
|
|
2147
|
+
const changedDeps = /* @__PURE__ */ new Set();
|
|
2148
|
+
for (const depName of depNames) if (beforeDeps[depName] !== afterDeps[depName]) changedDeps.add(depName);
|
|
2149
|
+
if (changedDeps.size > 0) changes.set(catalogName, changedDeps);
|
|
2150
|
+
}
|
|
2151
|
+
return changes;
|
|
2152
|
+
}
|
|
2153
|
+
/**
|
|
2154
|
+
* Given a set of catalog entries that have changed, return the set of catalog
|
|
2155
|
+
* references (e.g. "catalog:" or "catalog:testing") that affect those entries.
|
|
2156
|
+
* Used to match package.json dep ranges against changed catalog entries.
|
|
2157
|
+
*/
|
|
2158
|
+
function isCatalogRefAffected(range, depName, catalogChanges) {
|
|
2159
|
+
if (!range.startsWith("catalog:")) return false;
|
|
2160
|
+
return catalogChanges.get(catalogNameFromRange(range))?.has(depName) ?? false;
|
|
2161
|
+
}
|
|
2108
2162
|
//#endregion
|
|
2109
|
-
export { jsYaml as i,
|
|
2163
|
+
export { isCatalogRefAffected as a, jsYaml as c, diffCatalogMaps as i, detectPackageManager as n, parseCatalogs as o, detectWorkspaces as r, resolveCatalogDep as s, CATALOG_FILES as t };
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { n as log, o as __require, t as colorize } from "./logger-BgksGFuf.mjs";
|
|
2
2
|
import { a as loadConfig } from "./config-D_4GYDJi.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import { s as discoverWorkspace } from "./bump-file-
|
|
3
|
+
import { r as detectWorkspaces } from "./package-manager-Db_vTztt.mjs";
|
|
4
|
+
import { s as discoverWorkspace } from "./bump-file-B7hmXZlB.mjs";
|
|
5
5
|
import { a as DependencyGraph } from "./release-plan-mK7iGeGq.mjs";
|
|
6
6
|
import { r as runArgsAsync, s as tryRunArgs } from "./shell-C8KgKnMQ.mjs";
|
|
7
7
|
import { i as loadFormatter, n as generateChangelogEntry } from "./changelog-CbaET5V6.mjs";
|
|
8
|
-
import { c as
|
|
9
|
-
import { t as publishPackages } from "./publish-pipeline-
|
|
10
|
-
import { CI_PLAN_CACHE_PATH } from "./ci-
|
|
8
|
+
import { c as hasUncommittedChanges, l as pushWithTags } from "./git-DJJ64SW9.mjs";
|
|
9
|
+
import { t as publishPackages } from "./publish-pipeline-DSj14dW6.mjs";
|
|
10
|
+
import { CI_PLAN_CACHE_PATH } from "./ci-DBZW9k4S.mjs";
|
|
11
11
|
//#region src/core/github-release.ts
|
|
12
12
|
/** Get the current HEAD commit SHA */
|
|
13
13
|
function getHeadSha(rootDir) {
|
|
@@ -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
|
-
import {
|
|
3
|
+
import { s as resolveCatalogDep } from "./package-manager-Db_vTztt.mjs";
|
|
4
4
|
import { i as stripProtocol } from "./release-plan-mK7iGeGq.mjs";
|
|
5
5
|
import { i as runAsync, o as sq, r as runArgsAsync, s as tryRunArgs } from "./shell-C8KgKnMQ.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { d as tagExists, t as createTag } from "./git-DJJ64SW9.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,8 +1,8 @@
|
|
|
1
1
|
import { n as log, t as colorize } from "./logger-BgksGFuf.mjs";
|
|
2
2
|
import { a as loadConfig } from "./config-D_4GYDJi.mjs";
|
|
3
|
-
import { o as discoverPackages, r as readBumpFiles, t as filterBranchBumpFiles } from "./bump-file-
|
|
3
|
+
import { o as discoverPackages, r as readBumpFiles, t as filterBranchBumpFiles } from "./bump-file-B7hmXZlB.mjs";
|
|
4
4
|
import { a as DependencyGraph, t as assembleReleasePlan } from "./release-plan-mK7iGeGq.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { a as getCurrentBranch, i as getChangedFiles } from "./git-DJJ64SW9.mjs";
|
|
6
6
|
//#region src/commands/status.ts
|
|
7
7
|
async function statusCommand(rootDir, opts) {
|
|
8
8
|
const config = await loadConfig(rootDir);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { n as log, t as colorize } from "./logger-BgksGFuf.mjs";
|
|
2
2
|
import { a as loadConfig } from "./config-D_4GYDJi.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import { o as discoverPackages, r as readBumpFiles } from "./bump-file-
|
|
3
|
+
import { r as detectWorkspaces } from "./package-manager-Db_vTztt.mjs";
|
|
4
|
+
import { o as discoverPackages, r as readBumpFiles } from "./bump-file-B7hmXZlB.mjs";
|
|
5
5
|
import { a as DependencyGraph, t as assembleReleasePlan } from "./release-plan-mK7iGeGq.mjs";
|
|
6
6
|
import { n as runArgs, s as tryRunArgs } from "./shell-C8KgKnMQ.mjs";
|
|
7
7
|
import { t as applyReleasePlan } from "./apply-release-plan-DD2R7SL2.mjs";
|