@plexus-ms/cli 0.3.0 → 0.5.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/dist/plx.js +495 -189
- package/package.json +8 -6
package/dist/plx.js
CHANGED
|
@@ -7268,7 +7268,7 @@ var require_public_api = __commonJS({
|
|
|
7268
7268
|
}
|
|
7269
7269
|
return doc;
|
|
7270
7270
|
}
|
|
7271
|
-
function
|
|
7271
|
+
function parse5(src, reviver, options) {
|
|
7272
7272
|
let _reviver = void 0;
|
|
7273
7273
|
if (typeof reviver === "function") {
|
|
7274
7274
|
_reviver = reviver;
|
|
@@ -7309,7 +7309,7 @@ var require_public_api = __commonJS({
|
|
|
7309
7309
|
return value.toString(options);
|
|
7310
7310
|
return new Document.Document(value, _replacer, options).toString(options);
|
|
7311
7311
|
}
|
|
7312
|
-
exports.parse =
|
|
7312
|
+
exports.parse = parse5;
|
|
7313
7313
|
exports.parseAllDocuments = parseAllDocuments;
|
|
7314
7314
|
exports.parseDocument = parseDocument;
|
|
7315
7315
|
exports.stringify = stringify;
|
|
@@ -7655,63 +7655,84 @@ async function readCatalog(ctx, compendium) {
|
|
|
7655
7655
|
return catalog;
|
|
7656
7656
|
}
|
|
7657
7657
|
|
|
7658
|
-
// ../lib/dist/
|
|
7659
|
-
import {
|
|
7660
|
-
|
|
7661
|
-
|
|
7662
|
-
|
|
7663
|
-
|
|
7664
|
-
|
|
7665
|
-
if (
|
|
7666
|
-
return "
|
|
7667
|
-
|
|
7658
|
+
// ../lib/dist/changedApps.js
|
|
7659
|
+
import { join as join2 } from "node:path";
|
|
7660
|
+
var BOOKKEEPING = /(^|\/)(PLEXUS\.md|\.copier-answers\.yml)$/;
|
|
7661
|
+
var GLOBALS = /^(pnpm-lock\.yaml|pnpm-workspace\.yaml|package\.json|turbo\.json|mise\.toml|\.npmrc|\.node-version)$/;
|
|
7662
|
+
var NO_REF = /^0*$/;
|
|
7663
|
+
async function selectChangedApps(input) {
|
|
7664
|
+
const all = [...input.all].sort();
|
|
7665
|
+
if (input.changed === void 0)
|
|
7666
|
+
return { apps: all, reason: "no base ref: every app counts as changed" };
|
|
7667
|
+
const changed = input.changed.filter((f) => !BOOKKEEPING.test(f));
|
|
7668
|
+
const under = (app) => changed.some((f) => f.startsWith(`apps/${app}/`));
|
|
7669
|
+
if (!input.workspace)
|
|
7670
|
+
return { apps: all.filter(under), reason: "files under the apps changed" };
|
|
7671
|
+
if (changed.length === 0)
|
|
7672
|
+
return { apps: [], reason: "nothing but bookkeeping changed" };
|
|
7673
|
+
if (changed.some((f) => GLOBALS.test(f)))
|
|
7674
|
+
return { apps: all, reason: "a workspace-global input changed: every app counts as changed" };
|
|
7675
|
+
const outside = changed.some((f) => !f.startsWith("apps/"));
|
|
7676
|
+
const listed = new Set(await input.dependents?.() ?? []);
|
|
7677
|
+
return {
|
|
7678
|
+
apps: all.filter((app) => listed.has(app) && (outside || under(app))),
|
|
7679
|
+
reason: "sources or their dependencies changed"
|
|
7680
|
+
};
|
|
7668
7681
|
}
|
|
7669
|
-
|
|
7670
|
-
|
|
7671
|
-
const
|
|
7672
|
-
const
|
|
7673
|
-
|
|
7674
|
-
|
|
7675
|
-
|
|
7676
|
-
|
|
7677
|
-
const
|
|
7678
|
-
|
|
7679
|
-
|
|
7680
|
-
|
|
7681
|
-
orphans.push(site);
|
|
7682
|
-
else {
|
|
7683
|
-
let consumer = consumers.get(here.path);
|
|
7684
|
-
if (!consumer) {
|
|
7685
|
-
consumer = {
|
|
7686
|
-
path: here.path,
|
|
7687
|
-
rel: relative(root.path, here.path) || ".",
|
|
7688
|
-
branch: here.git?.branch ?? "?",
|
|
7689
|
-
expected: expectedBranch(here.declaration),
|
|
7690
|
-
sites: []
|
|
7691
|
-
};
|
|
7692
|
-
consumers.set(here.path, consumer);
|
|
7693
|
-
}
|
|
7694
|
-
consumer.sites.push(site);
|
|
7695
|
-
}
|
|
7696
|
-
}
|
|
7697
|
-
for (const child of dir.children)
|
|
7698
|
-
visit(child, here, template);
|
|
7682
|
+
async function readChangedApps(ctx, dir, from, to = "HEAD") {
|
|
7683
|
+
const git2 = (args) => ctx.read("git", args, { cwd: dir });
|
|
7684
|
+
const workspace = await exists(ctx, join2(dir, "pnpm-workspace.yaml"));
|
|
7685
|
+
const all = await deployableApps(ctx, dir, workspace);
|
|
7686
|
+
let changed;
|
|
7687
|
+
if (from !== "" && !NO_REF.test(from) && await isCommit(git2, from))
|
|
7688
|
+
changed = (await git2(["diff", "--name-only", from, to])).split("\n").filter(Boolean);
|
|
7689
|
+
const dependents = async () => {
|
|
7690
|
+
const out = await ctx.read("pnpm", ["--filter", `...[${from}...${to}]`, "ls", "--depth", "-1", "--parseable"], {
|
|
7691
|
+
cwd: dir
|
|
7692
|
+
});
|
|
7693
|
+
return out.split("\n").map((line) => /(?:^|\/)apps\/([^/]+)$/.exec(line.trim())?.[1]).filter((app) => app !== void 0);
|
|
7699
7694
|
};
|
|
7700
|
-
|
|
7701
|
-
|
|
7702
|
-
|
|
7703
|
-
|
|
7704
|
-
|
|
7705
|
-
|
|
7695
|
+
const result = await selectChangedApps({ workspace, all, changed, dependents });
|
|
7696
|
+
ctx.say(`\u2192 ${result.reason}`, 1);
|
|
7697
|
+
return result;
|
|
7698
|
+
}
|
|
7699
|
+
async function isCommit(git2, ref) {
|
|
7700
|
+
try {
|
|
7701
|
+
await git2(["rev-parse", "--verify", "--quiet", `${ref}^{commit}`]);
|
|
7702
|
+
return true;
|
|
7703
|
+
} catch {
|
|
7704
|
+
return false;
|
|
7705
|
+
}
|
|
7706
|
+
}
|
|
7707
|
+
async function deployableApps(ctx, dir, workspace) {
|
|
7708
|
+
const entries = await ctx.readDir(join2(dir, "apps")).catch(() => []);
|
|
7709
|
+
const apps = [];
|
|
7710
|
+
for (const e of entries) {
|
|
7711
|
+
if (!e.directory)
|
|
7706
7712
|
continue;
|
|
7713
|
+
const marks = workspace ? ["package.json"] : ["docker/compose.yaml", "compose.yaml"];
|
|
7714
|
+
for (const mark of marks) {
|
|
7715
|
+
if (await exists(ctx, join2(dir, "apps", e.name, mark))) {
|
|
7716
|
+
apps.push(e.name);
|
|
7717
|
+
break;
|
|
7718
|
+
}
|
|
7707
7719
|
}
|
|
7708
|
-
const tracked = (await ctx.read("git", ["status", "--porcelain", "--untracked-files=no"], { cwd: c.path })).trim();
|
|
7709
|
-
if (tracked !== "")
|
|
7710
|
-
c.skipped = "has uncommitted changes";
|
|
7711
7720
|
}
|
|
7712
|
-
return
|
|
7721
|
+
return apps.sort();
|
|
7722
|
+
}
|
|
7723
|
+
async function exists(ctx, path3) {
|
|
7724
|
+
try {
|
|
7725
|
+
await ctx.readFile(path3);
|
|
7726
|
+
return true;
|
|
7727
|
+
} catch {
|
|
7728
|
+
return false;
|
|
7729
|
+
}
|
|
7713
7730
|
}
|
|
7714
7731
|
|
|
7732
|
+
// ../lib/dist/changeset.js
|
|
7733
|
+
var import_yaml2 = __toESM(require_dist(), 1);
|
|
7734
|
+
import { join as join3 } from "node:path";
|
|
7735
|
+
|
|
7715
7736
|
// ../lib/dist/io.js
|
|
7716
7737
|
import { spawn } from "node:child_process";
|
|
7717
7738
|
import { readdir, readFile, writeFile } from "node:fs/promises";
|
|
@@ -7733,13 +7754,13 @@ function realIo(prompt) {
|
|
|
7733
7754
|
stdout: process.stdout,
|
|
7734
7755
|
stderr: process.stderr,
|
|
7735
7756
|
prompt: prompt && isInteractive() ? prompt : void 0,
|
|
7736
|
-
exec: (command, args, options = {}) => new Promise((
|
|
7757
|
+
exec: (command, args, options = {}) => new Promise((resolve9, reject) => {
|
|
7737
7758
|
const child = spawn(command, args, {
|
|
7738
7759
|
cwd: options.cwd,
|
|
7739
7760
|
env: options.env,
|
|
7740
7761
|
stdio: [
|
|
7741
7762
|
options.input !== void 0 ? "pipe" : options.inherit ? "inherit" : "ignore",
|
|
7742
|
-
options.inherit ?
|
|
7763
|
+
options.inherit ? process.stderr.fd : "pipe",
|
|
7743
7764
|
"pipe"
|
|
7744
7765
|
]
|
|
7745
7766
|
});
|
|
@@ -7756,7 +7777,7 @@ function realIo(prompt) {
|
|
|
7756
7777
|
child.on("error", reject);
|
|
7757
7778
|
child.on("close", (code) => {
|
|
7758
7779
|
if (code === 0)
|
|
7759
|
-
|
|
7780
|
+
resolve9(stdout);
|
|
7760
7781
|
else
|
|
7761
7782
|
reject(new ExecError(render(command, args), code, stderr));
|
|
7762
7783
|
});
|
|
@@ -7805,6 +7826,194 @@ EOF`;
|
|
|
7805
7826
|
}
|
|
7806
7827
|
}
|
|
7807
7828
|
|
|
7829
|
+
// ../lib/dist/changeset.js
|
|
7830
|
+
var CHANGESET_DIR = ".changeset";
|
|
7831
|
+
var FIELDS = ["dependencies", "optionalDependencies", "peerDependencies"];
|
|
7832
|
+
function changesetPath(branch) {
|
|
7833
|
+
const slug = branch.replace(/^(renovate|dependabot)\//, "").replace(/[^a-z0-9]+/gi, "-").replace(/^-|-$/g, "").toLowerCase();
|
|
7834
|
+
return `${CHANGESET_DIR}/bot-${slug || "branch"}.md`;
|
|
7835
|
+
}
|
|
7836
|
+
function planChangeset(input) {
|
|
7837
|
+
const path3 = changesetPath(input.branch);
|
|
7838
|
+
const changes = [];
|
|
7839
|
+
const releases = {};
|
|
7840
|
+
for (const diff of input.packages) {
|
|
7841
|
+
const { before, after } = diff;
|
|
7842
|
+
if (!before || !after?.name || after.private || input.ignore.includes(after.name))
|
|
7843
|
+
continue;
|
|
7844
|
+
for (const field of FIELDS) {
|
|
7845
|
+
const names = /* @__PURE__ */ new Set([...Object.keys(before[field] ?? {}), ...Object.keys(after[field] ?? {})]);
|
|
7846
|
+
for (const dependency of [...names].sort()) {
|
|
7847
|
+
const from = resolveRange(before[field]?.[dependency], dependency, input.catalogsBefore);
|
|
7848
|
+
const to = resolveRange(after[field]?.[dependency], dependency, input.catalogsAfter);
|
|
7849
|
+
if (from === to)
|
|
7850
|
+
continue;
|
|
7851
|
+
changes.push({ package: after.name, dependency, field, from, to });
|
|
7852
|
+
const bump = field === "peerDependencies" ? "major" : "patch";
|
|
7853
|
+
if (releases[after.name] !== "major")
|
|
7854
|
+
releases[after.name] = bump;
|
|
7855
|
+
}
|
|
7856
|
+
}
|
|
7857
|
+
}
|
|
7858
|
+
const root = input.root;
|
|
7859
|
+
if (changes.length === 0) {
|
|
7860
|
+
const remove = run("rm", ["-f", path3], root ? { cwd: root } : {});
|
|
7861
|
+
return { path: path3, releases, changes, plan: input.exists ? [remove] : [] };
|
|
7862
|
+
}
|
|
7863
|
+
const header = Object.entries(releases).sort(([a], [b]) => a.localeCompare(b)).map(([name, bump]) => `'${name}': ${bump}`);
|
|
7864
|
+
const lines = [...new Set(changes.map(describeChange))].sort((a, b) => a.localeCompare(b));
|
|
7865
|
+
const content = `---
|
|
7866
|
+
${header.join("\n")}
|
|
7867
|
+
---
|
|
7868
|
+
|
|
7869
|
+
${lines.join("\n")}
|
|
7870
|
+
`;
|
|
7871
|
+
return { path: path3, releases, changes, content, plan: [write(root ? join3(root, path3) : path3, content)] };
|
|
7872
|
+
}
|
|
7873
|
+
function describeChange(c) {
|
|
7874
|
+
const peer = c.field === "peerDependencies" ? " (peer)" : "";
|
|
7875
|
+
if (c.from === void 0)
|
|
7876
|
+
return `Add \`${c.dependency}\`${peer} at ${c.to}`;
|
|
7877
|
+
if (c.to === void 0)
|
|
7878
|
+
return `Remove \`${c.dependency}\`${peer}`;
|
|
7879
|
+
return `Update \`${c.dependency}\`${peer} from ${c.from} to ${c.to}`;
|
|
7880
|
+
}
|
|
7881
|
+
function resolveRange(value, dependency, catalogs) {
|
|
7882
|
+
if (value === void 0)
|
|
7883
|
+
return void 0;
|
|
7884
|
+
if (value === "catalog:" || value === "catalog:default")
|
|
7885
|
+
return catalogs.default[dependency] ?? value;
|
|
7886
|
+
if (value.startsWith("catalog:"))
|
|
7887
|
+
return catalogs.named[value.slice("catalog:".length)]?.[dependency] ?? value;
|
|
7888
|
+
return value;
|
|
7889
|
+
}
|
|
7890
|
+
function parseCatalogs(text) {
|
|
7891
|
+
const data = text === void 0 ? void 0 : (0, import_yaml2.parse)(text);
|
|
7892
|
+
const ws = data !== null && typeof data === "object" ? data : {};
|
|
7893
|
+
const map = (v) => v !== null && typeof v === "object" ? Object.fromEntries(Object.entries(v).map(([k, r]) => [k, String(r)])) : {};
|
|
7894
|
+
const named = ws.catalogs !== null && typeof ws.catalogs === "object" ? ws.catalogs : {};
|
|
7895
|
+
return {
|
|
7896
|
+
default: map(ws.catalog),
|
|
7897
|
+
named: Object.fromEntries(Object.entries(named).map(([name, entries]) => [name, map(entries)]))
|
|
7898
|
+
};
|
|
7899
|
+
}
|
|
7900
|
+
function workspaceGlobs(text) {
|
|
7901
|
+
const data = text === void 0 ? void 0 : (0, import_yaml2.parse)(text);
|
|
7902
|
+
const globs = data !== null && typeof data === "object" ? data.packages : void 0;
|
|
7903
|
+
return Array.isArray(globs) ? globs.map(String).filter((g) => !g.startsWith("!")) : [];
|
|
7904
|
+
}
|
|
7905
|
+
async function readChangeset(ctx, dir, base, branch) {
|
|
7906
|
+
const git2 = (args) => ctx.read("git", args, { cwd: dir });
|
|
7907
|
+
const head = branch ?? (await git2(["rev-parse", "--abbrev-ref", "HEAD"])).trim();
|
|
7908
|
+
const mergeBase = (await git2(["merge-base", base, "HEAD"])).trim();
|
|
7909
|
+
const show = async (path4) => {
|
|
7910
|
+
try {
|
|
7911
|
+
return await git2(["show", `${mergeBase}:${path4}`]);
|
|
7912
|
+
} catch (error) {
|
|
7913
|
+
if (error instanceof ExecError)
|
|
7914
|
+
return void 0;
|
|
7915
|
+
throw error;
|
|
7916
|
+
}
|
|
7917
|
+
};
|
|
7918
|
+
const current = async (path4) => {
|
|
7919
|
+
try {
|
|
7920
|
+
return await ctx.readFile(join3(dir, path4));
|
|
7921
|
+
} catch {
|
|
7922
|
+
return void 0;
|
|
7923
|
+
}
|
|
7924
|
+
};
|
|
7925
|
+
const workspaceAfter = await current("pnpm-workspace.yaml");
|
|
7926
|
+
const packages = [];
|
|
7927
|
+
for (const glob of workspaceGlobs(workspaceAfter)) {
|
|
7928
|
+
for (const pkgDir of await expand(ctx, dir, glob)) {
|
|
7929
|
+
const path4 = `${pkgDir}/package.json`;
|
|
7930
|
+
const [before, after] = await Promise.all([show(path4), current(path4)]);
|
|
7931
|
+
packages.push({ path: path4, before: manifest(before), after: manifest(after) });
|
|
7932
|
+
}
|
|
7933
|
+
}
|
|
7934
|
+
const config = await current(`${CHANGESET_DIR}/config.json`);
|
|
7935
|
+
const ignore = config ? JSON.parse(config).ignore ?? [] : [];
|
|
7936
|
+
const entries = await ctx.readDir(join3(dir, CHANGESET_DIR)).catch(() => []);
|
|
7937
|
+
const path3 = changesetPath(head);
|
|
7938
|
+
return planChangeset({
|
|
7939
|
+
branch: head,
|
|
7940
|
+
packages,
|
|
7941
|
+
catalogsBefore: parseCatalogs(await show("pnpm-workspace.yaml")),
|
|
7942
|
+
catalogsAfter: parseCatalogs(workspaceAfter),
|
|
7943
|
+
ignore,
|
|
7944
|
+
exists: entries.some((e) => !e.directory && `${CHANGESET_DIR}/${e.name}` === path3),
|
|
7945
|
+
root: dir
|
|
7946
|
+
});
|
|
7947
|
+
}
|
|
7948
|
+
function manifest(text) {
|
|
7949
|
+
return text === void 0 ? void 0 : JSON.parse(text);
|
|
7950
|
+
}
|
|
7951
|
+
async function expand(ctx, root, glob) {
|
|
7952
|
+
const star = glob.indexOf("*");
|
|
7953
|
+
if (star < 0)
|
|
7954
|
+
return [glob.replace(/\/$/, "")];
|
|
7955
|
+
const parent = glob.slice(0, star).replace(/\/$/, "");
|
|
7956
|
+
const entries = await ctx.readDir(join3(root, parent)).catch(() => []);
|
|
7957
|
+
return entries.filter((e) => e.directory && !e.name.startsWith(".")).map((e) => parent ? `${parent}/${e.name}` : e.name).sort();
|
|
7958
|
+
}
|
|
7959
|
+
|
|
7960
|
+
// ../lib/dist/consumers.js
|
|
7961
|
+
import { relative } from "node:path";
|
|
7962
|
+
function expectedBranch(declaration) {
|
|
7963
|
+
if (!declaration)
|
|
7964
|
+
return void 0;
|
|
7965
|
+
if (declaration.modules.includes("PLX-Platform"))
|
|
7966
|
+
return "main";
|
|
7967
|
+
if (declaration.modules.includes("PLX-Repo"))
|
|
7968
|
+
return "develop";
|
|
7969
|
+
return void 0;
|
|
7970
|
+
}
|
|
7971
|
+
var presetSource = (preset) => `gh:plexus-ms/${preset}`;
|
|
7972
|
+
async function findConsumers(ctx, root, presets) {
|
|
7973
|
+
const sources = new Map(presets.map((p) => [presetSource(p), p]));
|
|
7974
|
+
const consumers = /* @__PURE__ */ new Map();
|
|
7975
|
+
const orphans = [];
|
|
7976
|
+
const visit = (dir, repo, inTemplate) => {
|
|
7977
|
+
const here = dir.git ? dir : repo;
|
|
7978
|
+
const template = inTemplate || Boolean(dir.template);
|
|
7979
|
+
const preset = dir.copier?.src ? sources.get(dir.copier.src) : void 0;
|
|
7980
|
+
if (preset && !template) {
|
|
7981
|
+
const site = { path: dir.path, rel: relative(root.path, dir.path) || ".", preset };
|
|
7982
|
+
if (!here)
|
|
7983
|
+
orphans.push(site);
|
|
7984
|
+
else {
|
|
7985
|
+
let consumer = consumers.get(here.path);
|
|
7986
|
+
if (!consumer) {
|
|
7987
|
+
consumer = {
|
|
7988
|
+
path: here.path,
|
|
7989
|
+
rel: relative(root.path, here.path) || ".",
|
|
7990
|
+
branch: here.git?.branch ?? "?",
|
|
7991
|
+
expected: expectedBranch(here.declaration),
|
|
7992
|
+
sites: []
|
|
7993
|
+
};
|
|
7994
|
+
consumers.set(here.path, consumer);
|
|
7995
|
+
}
|
|
7996
|
+
consumer.sites.push(site);
|
|
7997
|
+
}
|
|
7998
|
+
}
|
|
7999
|
+
for (const child of dir.children)
|
|
8000
|
+
visit(child, here, template);
|
|
8001
|
+
};
|
|
8002
|
+
visit(root, void 0, false);
|
|
8003
|
+
for (const site of orphans)
|
|
8004
|
+
ctx.say(`\u26A0 ${site.rel}: not inside a git repo \u2014 skipped`);
|
|
8005
|
+
for (const c of consumers.values()) {
|
|
8006
|
+
if (c.expected && c.branch !== c.expected) {
|
|
8007
|
+
c.skipped = `on '${c.branch}', expected '${c.expected}'`;
|
|
8008
|
+
continue;
|
|
8009
|
+
}
|
|
8010
|
+
const tracked = (await ctx.read("git", ["status", "--porcelain", "--untracked-files=no"], { cwd: c.path })).trim();
|
|
8011
|
+
if (tracked !== "")
|
|
8012
|
+
c.skipped = "has uncommitted changes";
|
|
8013
|
+
}
|
|
8014
|
+
return [...consumers.values()];
|
|
8015
|
+
}
|
|
8016
|
+
|
|
7808
8017
|
// ../lib/dist/context.js
|
|
7809
8018
|
var Context = class {
|
|
7810
8019
|
dryRun;
|
|
@@ -7840,6 +8049,8 @@ var Context = class {
|
|
|
7840
8049
|
*/
|
|
7841
8050
|
result(data, human = renderHuman) {
|
|
7842
8051
|
const text = this.json ? JSON.stringify(data, null, 2) : human(data);
|
|
8052
|
+
if (text === "")
|
|
8053
|
+
return;
|
|
7843
8054
|
this.io.stdout.write(text.endsWith("\n") ? text : `${text}
|
|
7844
8055
|
`);
|
|
7845
8056
|
}
|
|
@@ -7919,11 +8130,11 @@ function indent(text) {
|
|
|
7919
8130
|
}
|
|
7920
8131
|
|
|
7921
8132
|
// ../lib/dist/copier.js
|
|
7922
|
-
var
|
|
8133
|
+
var import_yaml3 = __toESM(require_dist(), 1);
|
|
7923
8134
|
var ANSWERS_FILE = ".copier-answers.yml";
|
|
7924
8135
|
var TEMPLATE_FILES = ["copier.yml", "copier.yaml"];
|
|
7925
8136
|
function parseAnswers(text) {
|
|
7926
|
-
const data = (0,
|
|
8137
|
+
const data = (0, import_yaml3.parse)(text);
|
|
7927
8138
|
if (data === null || typeof data !== "object" || Array.isArray(data))
|
|
7928
8139
|
throw new Error("answers file is not a mapping");
|
|
7929
8140
|
const { _commit, _src_path, ...answers } = data;
|
|
@@ -7934,6 +8145,13 @@ function parseAnswers(text) {
|
|
|
7934
8145
|
};
|
|
7935
8146
|
}
|
|
7936
8147
|
|
|
8148
|
+
// ../lib/dist/deployTargets.js
|
|
8149
|
+
var import_yaml4 = __toESM(require_dist(), 1);
|
|
8150
|
+
import { join as join5 } from "node:path";
|
|
8151
|
+
|
|
8152
|
+
// ../lib/dist/train.js
|
|
8153
|
+
import { join as join4 } from "node:path";
|
|
8154
|
+
|
|
7937
8155
|
// ../lib/dist/doctor.js
|
|
7938
8156
|
import { relative as relative2 } from "node:path";
|
|
7939
8157
|
function diagnose(root, catalog) {
|
|
@@ -7993,6 +8211,130 @@ function compareTrains(a, b) {
|
|
|
7993
8211
|
return 0;
|
|
7994
8212
|
}
|
|
7995
8213
|
|
|
8214
|
+
// ../lib/dist/train.js
|
|
8215
|
+
var TRAIN_REPOS = [
|
|
8216
|
+
"platform",
|
|
8217
|
+
"ci-cd",
|
|
8218
|
+
"preset-repo-web",
|
|
8219
|
+
"preset-app-nextjs",
|
|
8220
|
+
"preset-repo-platform",
|
|
8221
|
+
"compendium"
|
|
8222
|
+
];
|
|
8223
|
+
var UPSTREAM = "plexus-ms";
|
|
8224
|
+
var isTrainVersion = (v) => /^\d+\.\d+\.\d+$/.test(v);
|
|
8225
|
+
function nextMinor(version2) {
|
|
8226
|
+
const [major = "0", minor = "0"] = version2.split(".");
|
|
8227
|
+
return `${major}.${Number(minor) + 1}.0`;
|
|
8228
|
+
}
|
|
8229
|
+
function latestTag(tags) {
|
|
8230
|
+
return tags.filter((t) => /^v?\d/.test(t)).sort((a, b) => compareTrains(a.replace(/^v/, ""), b.replace(/^v/, ""))).at(-1);
|
|
8231
|
+
}
|
|
8232
|
+
async function readTrain(ctx, upstream) {
|
|
8233
|
+
const repos = [];
|
|
8234
|
+
const all = /* @__PURE__ */ new Set();
|
|
8235
|
+
for (const name of TRAIN_REPOS) {
|
|
8236
|
+
const path3 = join4(upstream, name);
|
|
8237
|
+
try {
|
|
8238
|
+
await ctx.read("git", ["rev-parse", "--git-dir"], { cwd: path3 });
|
|
8239
|
+
} catch {
|
|
8240
|
+
throw new Error(`${path3} is not a git repo`);
|
|
8241
|
+
}
|
|
8242
|
+
try {
|
|
8243
|
+
await ctx.read("git", ["fetch", "origin", "--tags", "--quiet"], { cwd: path3 });
|
|
8244
|
+
} catch {
|
|
8245
|
+
throw new Error(`${name}: fetch from origin failed`);
|
|
8246
|
+
}
|
|
8247
|
+
const tags = (await ctx.read("git", ["tag", "--list", "v*", "[0-9]*"], { cwd: path3 })).split("\n").filter(Boolean);
|
|
8248
|
+
for (const t of tags)
|
|
8249
|
+
all.add(t.replace(/^v/, ""));
|
|
8250
|
+
const tag = latestTag(tags);
|
|
8251
|
+
const range = tag ? `${tag}..HEAD` : "HEAD";
|
|
8252
|
+
const pending = (await ctx.read("git", ["log", "--oneline", range], { cwd: path3 })).split("\n").filter(Boolean);
|
|
8253
|
+
repos.push({ name, path: path3, tag, latest: tag?.replace(/^v/, ""), pending });
|
|
8254
|
+
}
|
|
8255
|
+
const latest = repos.map((r) => r.latest);
|
|
8256
|
+
return { repos, last: latestTag([...all]), skew: latest.some((l) => l !== latest[0]) };
|
|
8257
|
+
}
|
|
8258
|
+
async function preflightRelease(ctx, train) {
|
|
8259
|
+
for (const { name, path: path3 } of train.repos) {
|
|
8260
|
+
const branch = (await ctx.read("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: path3 })).trim();
|
|
8261
|
+
if (branch !== "main")
|
|
8262
|
+
throw new Error(`${name} is on '${branch}', trains cut from main`);
|
|
8263
|
+
if ((await ctx.read("git", ["status", "--porcelain"], { cwd: path3 })).trim() !== "")
|
|
8264
|
+
throw new Error(`${name} has uncommitted changes \u2014 commit first`);
|
|
8265
|
+
const behind = (await ctx.read("git", ["rev-list", "--count", "main..origin/main"], { cwd: path3 })).trim();
|
|
8266
|
+
if (behind !== "0")
|
|
8267
|
+
throw new Error(`${name} is ${behind} commit(s) behind origin/main \u2014 pull first, or the tag lands on a stale commit`);
|
|
8268
|
+
}
|
|
8269
|
+
}
|
|
8270
|
+
function resolveVersion(train, explicit, propagateOnly) {
|
|
8271
|
+
if (explicit !== void 0 && !isTrainVersion(explicit))
|
|
8272
|
+
throw new UsageError(`'${explicit}' is not plain semver (X.Y.Z, no v)`);
|
|
8273
|
+
if (propagateOnly) {
|
|
8274
|
+
const v = explicit ?? train.last;
|
|
8275
|
+
if (!v)
|
|
8276
|
+
throw new Error("no train version found to propagate");
|
|
8277
|
+
return v;
|
|
8278
|
+
}
|
|
8279
|
+
if (explicit)
|
|
8280
|
+
return explicit;
|
|
8281
|
+
if (train.skew) {
|
|
8282
|
+
const report = train.repos.map((r) => ` ${r.name}: ${r.latest ?? "none"}`).join("\n");
|
|
8283
|
+
throw new Error(`the train repos' latest tags disagree:
|
|
8284
|
+
${report}
|
|
8285
|
+
pass the version explicitly (--train ${train.last}) to finish the half-tagged train, or a higher one to start a new one`);
|
|
8286
|
+
}
|
|
8287
|
+
return train.last ? nextMinor(train.last) : "0.1.0";
|
|
8288
|
+
}
|
|
8289
|
+
var UsageError = class extends Error {
|
|
8290
|
+
};
|
|
8291
|
+
|
|
8292
|
+
// ../lib/dist/deployTargets.js
|
|
8293
|
+
var VARS_DIRS = ["group_vars", "host_vars"];
|
|
8294
|
+
function parseInventoryFile(path3, text) {
|
|
8295
|
+
const data = (0, import_yaml4.parse)(text);
|
|
8296
|
+
const vars = data !== null && typeof data === "object" ? data : {};
|
|
8297
|
+
const target = typeof vars.plexus_deploy_target === "string" ? vars.plexus_deploy_target : void 0;
|
|
8298
|
+
const records = Array.isArray(vars.plexus_apps) ? vars.plexus_apps : [];
|
|
8299
|
+
const apps = records.map((r) => r !== null && typeof r === "object" ? r.name : void 0).filter((n) => typeof n === "string");
|
|
8300
|
+
return { path: path3, target, apps };
|
|
8301
|
+
}
|
|
8302
|
+
function selectDeployTargets(files) {
|
|
8303
|
+
const targets = {};
|
|
8304
|
+
const declaredIn = {};
|
|
8305
|
+
for (const file of [...files].sort((a, b) => a.path.localeCompare(b.path))) {
|
|
8306
|
+
if (file.target === void 0)
|
|
8307
|
+
continue;
|
|
8308
|
+
for (const app of file.apps) {
|
|
8309
|
+
if (app in targets)
|
|
8310
|
+
throw new Error(`app "${app}" is declared with a deploy target in both ${declaredIn[app]} and ${file.path}`);
|
|
8311
|
+
targets[app] = file.target;
|
|
8312
|
+
declaredIn[app] = file.path;
|
|
8313
|
+
}
|
|
8314
|
+
}
|
|
8315
|
+
return Object.fromEntries(Object.entries(targets).sort(([a], [b]) => a.localeCompare(b)));
|
|
8316
|
+
}
|
|
8317
|
+
async function readDeployTargets(ctx, inventory) {
|
|
8318
|
+
const files = [];
|
|
8319
|
+
let found = false;
|
|
8320
|
+
for (const sub of VARS_DIRS) {
|
|
8321
|
+
const dir = join5(inventory, sub);
|
|
8322
|
+
const entries = await ctx.readDir(dir).catch(() => void 0);
|
|
8323
|
+
if (entries === void 0)
|
|
8324
|
+
continue;
|
|
8325
|
+
found = true;
|
|
8326
|
+
for (const e of entries) {
|
|
8327
|
+
if (e.directory || !/\.ya?ml$/.test(e.name))
|
|
8328
|
+
continue;
|
|
8329
|
+
const path3 = join5(dir, e.name);
|
|
8330
|
+
files.push(parseInventoryFile(path3, await ctx.readFile(path3)));
|
|
8331
|
+
}
|
|
8332
|
+
}
|
|
8333
|
+
if (!found && await ctx.readDir(inventory).catch(() => void 0) === void 0)
|
|
8334
|
+
throw new UsageError(`no inventory directory at ${inventory}`);
|
|
8335
|
+
return selectDeployTargets(files);
|
|
8336
|
+
}
|
|
8337
|
+
|
|
7996
8338
|
// ../lib/dist/model.js
|
|
7997
8339
|
function kindOf(declaration) {
|
|
7998
8340
|
const has = (code) => declaration.modules.includes(code);
|
|
@@ -8014,7 +8356,7 @@ function isNotable(dir) {
|
|
|
8014
8356
|
}
|
|
8015
8357
|
|
|
8016
8358
|
// ../lib/dist/overview.js
|
|
8017
|
-
import { join as
|
|
8359
|
+
import { join as join6 } from "node:path";
|
|
8018
8360
|
var PRUNED = /* @__PURE__ */ new Set([
|
|
8019
8361
|
"node_modules",
|
|
8020
8362
|
".hg",
|
|
@@ -8062,7 +8404,7 @@ async function walk(ctx, path3, name, options) {
|
|
|
8062
8404
|
}
|
|
8063
8405
|
const subdirs = entries.filter((e) => e.directory && e.name !== ".git" && !PRUNED.has(e.name)).map((e) => e.name).sort();
|
|
8064
8406
|
for (const sub of subdirs) {
|
|
8065
|
-
const child = await walk(ctx,
|
|
8407
|
+
const child = await walk(ctx, join6(path3, sub), sub, options);
|
|
8066
8408
|
if (isNotable(child) || child.children.length > 0)
|
|
8067
8409
|
dir.children.push(child);
|
|
8068
8410
|
}
|
|
@@ -8070,7 +8412,7 @@ async function walk(ctx, path3, name, options) {
|
|
|
8070
8412
|
}
|
|
8071
8413
|
async function facet(ctx, dir, file, read) {
|
|
8072
8414
|
try {
|
|
8073
|
-
read(await ctx.readFile(
|
|
8415
|
+
read(await ctx.readFile(join6(dir.path, file)));
|
|
8074
8416
|
} catch (error) {
|
|
8075
8417
|
dir.problems ??= [];
|
|
8076
8418
|
dir.problems.push(`${file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -8109,93 +8451,12 @@ async function templateFacet(ctx, cwd) {
|
|
|
8109
8451
|
}
|
|
8110
8452
|
|
|
8111
8453
|
// ../lib/dist/propagate.js
|
|
8112
|
-
import { join as
|
|
8113
|
-
|
|
8114
|
-
// ../lib/dist/train.js
|
|
8115
|
-
import { join as join3 } from "node:path";
|
|
8116
|
-
var TRAIN_REPOS = [
|
|
8117
|
-
"platform",
|
|
8118
|
-
"ci-cd",
|
|
8119
|
-
"preset-repo-web",
|
|
8120
|
-
"preset-app-nextjs",
|
|
8121
|
-
"preset-repo-platform",
|
|
8122
|
-
"compendium"
|
|
8123
|
-
];
|
|
8124
|
-
var UPSTREAM = "plexus-ms";
|
|
8125
|
-
var isTrainVersion = (v) => /^\d+\.\d+\.\d+$/.test(v);
|
|
8126
|
-
function nextMinor(version2) {
|
|
8127
|
-
const [major = "0", minor = "0"] = version2.split(".");
|
|
8128
|
-
return `${major}.${Number(minor) + 1}.0`;
|
|
8129
|
-
}
|
|
8130
|
-
function latestTag(tags) {
|
|
8131
|
-
return tags.filter((t) => /^v?\d/.test(t)).sort((a, b) => compareTrains(a.replace(/^v/, ""), b.replace(/^v/, ""))).at(-1);
|
|
8132
|
-
}
|
|
8133
|
-
async function readTrain(ctx, upstream) {
|
|
8134
|
-
const repos = [];
|
|
8135
|
-
const all = /* @__PURE__ */ new Set();
|
|
8136
|
-
for (const name of TRAIN_REPOS) {
|
|
8137
|
-
const path3 = join3(upstream, name);
|
|
8138
|
-
try {
|
|
8139
|
-
await ctx.read("git", ["rev-parse", "--git-dir"], { cwd: path3 });
|
|
8140
|
-
} catch {
|
|
8141
|
-
throw new Error(`${path3} is not a git repo`);
|
|
8142
|
-
}
|
|
8143
|
-
try {
|
|
8144
|
-
await ctx.read("git", ["fetch", "origin", "--tags", "--quiet"], { cwd: path3 });
|
|
8145
|
-
} catch {
|
|
8146
|
-
throw new Error(`${name}: fetch from origin failed`);
|
|
8147
|
-
}
|
|
8148
|
-
const tags = (await ctx.read("git", ["tag", "--list", "v*", "[0-9]*"], { cwd: path3 })).split("\n").filter(Boolean);
|
|
8149
|
-
for (const t of tags)
|
|
8150
|
-
all.add(t.replace(/^v/, ""));
|
|
8151
|
-
const tag = latestTag(tags);
|
|
8152
|
-
const range = tag ? `${tag}..HEAD` : "HEAD";
|
|
8153
|
-
const pending = (await ctx.read("git", ["log", "--oneline", range], { cwd: path3 })).split("\n").filter(Boolean);
|
|
8154
|
-
repos.push({ name, path: path3, tag, latest: tag?.replace(/^v/, ""), pending });
|
|
8155
|
-
}
|
|
8156
|
-
const latest = repos.map((r) => r.latest);
|
|
8157
|
-
return { repos, last: latestTag([...all]), skew: latest.some((l) => l !== latest[0]) };
|
|
8158
|
-
}
|
|
8159
|
-
async function preflightRelease(ctx, train) {
|
|
8160
|
-
for (const { name, path: path3 } of train.repos) {
|
|
8161
|
-
const branch = (await ctx.read("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: path3 })).trim();
|
|
8162
|
-
if (branch !== "main")
|
|
8163
|
-
throw new Error(`${name} is on '${branch}', trains cut from main`);
|
|
8164
|
-
if ((await ctx.read("git", ["status", "--porcelain"], { cwd: path3 })).trim() !== "")
|
|
8165
|
-
throw new Error(`${name} has uncommitted changes \u2014 commit first`);
|
|
8166
|
-
const behind = (await ctx.read("git", ["rev-list", "--count", "main..origin/main"], { cwd: path3 })).trim();
|
|
8167
|
-
if (behind !== "0")
|
|
8168
|
-
throw new Error(`${name} is ${behind} commit(s) behind origin/main \u2014 pull first, or the tag lands on a stale commit`);
|
|
8169
|
-
}
|
|
8170
|
-
}
|
|
8171
|
-
function resolveVersion(train, explicit, propagateOnly) {
|
|
8172
|
-
if (explicit !== void 0 && !isTrainVersion(explicit))
|
|
8173
|
-
throw new UsageError(`'${explicit}' is not plain semver (X.Y.Z, no v)`);
|
|
8174
|
-
if (propagateOnly) {
|
|
8175
|
-
const v = explicit ?? train.last;
|
|
8176
|
-
if (!v)
|
|
8177
|
-
throw new Error("no train version found to propagate");
|
|
8178
|
-
return v;
|
|
8179
|
-
}
|
|
8180
|
-
if (explicit)
|
|
8181
|
-
return explicit;
|
|
8182
|
-
if (train.skew) {
|
|
8183
|
-
const report = train.repos.map((r) => ` ${r.name}: ${r.latest ?? "none"}`).join("\n");
|
|
8184
|
-
throw new Error(`the train repos' latest tags disagree:
|
|
8185
|
-
${report}
|
|
8186
|
-
pass the version explicitly (--train ${train.last}) to finish the half-tagged train, or a higher one to start a new one`);
|
|
8187
|
-
}
|
|
8188
|
-
return train.last ? nextMinor(train.last) : "0.1.0";
|
|
8189
|
-
}
|
|
8190
|
-
var UsageError = class extends Error {
|
|
8191
|
-
};
|
|
8192
|
-
|
|
8193
|
-
// ../lib/dist/propagate.js
|
|
8454
|
+
import { join as join7, relative as relative3 } from "node:path";
|
|
8194
8455
|
var isClean = (s) => s.conflicted.length === 0 && s.failed.length === 0 && s.skipped.length === 0;
|
|
8195
8456
|
var PREVIEW_BASE = "ship-base";
|
|
8196
8457
|
async function propagate(ctx, workspace, train) {
|
|
8197
8458
|
const summary = { train, preview: ctx.dryRun, clean: [], conflicted: [], failed: [], skipped: [] };
|
|
8198
|
-
const upstream =
|
|
8459
|
+
const upstream = join7(workspace, UPSTREAM);
|
|
8199
8460
|
const tree = await overview(ctx, workspace);
|
|
8200
8461
|
const upstreamDir = tree.children.find((c) => c.path === upstream);
|
|
8201
8462
|
const presets = TRAIN_REPOS.filter((r) => upstreamDir?.children.some((c) => c.name === r && c.template));
|
|
@@ -8212,7 +8473,7 @@ async function propagate(ctx, workspace, train) {
|
|
|
8212
8473
|
const cloneOf = async (consumer) => {
|
|
8213
8474
|
let clone = clones.get(consumer.path);
|
|
8214
8475
|
if (!clone) {
|
|
8215
|
-
clone =
|
|
8476
|
+
clone = join7(scratch ?? "", consumer.rel);
|
|
8216
8477
|
await ctx.stage(run("git", ["clone", "--quiet", consumer.path, clone]));
|
|
8217
8478
|
await ctx.stage(run("git", ["tag", PREVIEW_BASE], { cwd: clone }));
|
|
8218
8479
|
clones.set(consumer.path, clone);
|
|
@@ -8239,7 +8500,7 @@ async function propagate(ctx, workspace, train) {
|
|
|
8239
8500
|
if (sites.length === 0)
|
|
8240
8501
|
ctx.say(`\u2022 no consumers of ${presetSource(preset)} found under ${workspace}`);
|
|
8241
8502
|
for (const [consumer, site] of sites)
|
|
8242
|
-
await updateSite(ctx, consumer, site,
|
|
8503
|
+
await updateSite(ctx, consumer, site, join7(upstream, preset), summary, cloneOf, previewCommit);
|
|
8243
8504
|
}
|
|
8244
8505
|
await refreshLockfiles(ctx, active, summary, clones);
|
|
8245
8506
|
await bumpConsumerRefs(ctx, consumers, train);
|
|
@@ -8256,9 +8517,9 @@ async function updateSite(ctx, consumer, site, localPreset, summary, cloneOf, pr
|
|
|
8256
8517
|
if (ctx.dryRun) {
|
|
8257
8518
|
ctx.say(`\u2192 ${site.rel}: copier update (preview from ${localPreset}@main)`);
|
|
8258
8519
|
repo = await cloneOf(consumer);
|
|
8259
|
-
target =
|
|
8260
|
-
const answers = await ctx.readFile(
|
|
8261
|
-
await ctx.stage(write(
|
|
8520
|
+
target = join7(repo, relative3(consumer.path, site.path));
|
|
8521
|
+
const answers = await ctx.readFile(join7(site.path, ANSWERS_FILE));
|
|
8522
|
+
await ctx.stage(write(join7(target, ANSWERS_FILE), answers.replace(/^_src_path: .*$/m, `_src_path: ${localPreset}`)));
|
|
8262
8523
|
await previewCommit(repo, `plx preview: ${site.rel} reads ${localPreset} from the local checkout`);
|
|
8263
8524
|
await ctx.stage(run("copier", ["update", "--skip-answered", "--trust", "--defaults", "--skip-tasks", "--vcs-ref", "main"], {
|
|
8264
8525
|
cwd: target
|
|
@@ -8277,7 +8538,7 @@ async function updateSite(ctx, consumer, site, localPreset, summary, cloneOf, pr
|
|
|
8277
8538
|
let conflict = hasUnmergedEntries(status);
|
|
8278
8539
|
const changed = (await ctx.read("git", ["diff", "--name-only"], { cwd: repo })).split("\n").filter(Boolean);
|
|
8279
8540
|
for (const f of changed) {
|
|
8280
|
-
const text = await ctx.readFile(
|
|
8541
|
+
const text = await ctx.readFile(join7(repo, f)).catch(() => "");
|
|
8281
8542
|
if (hasConflictMarkers(text))
|
|
8282
8543
|
conflict = true;
|
|
8283
8544
|
}
|
|
@@ -8295,17 +8556,17 @@ async function updateSite(ctx, consumer, site, localPreset, summary, cloneOf, pr
|
|
|
8295
8556
|
await previewCommit(repo, `plx preview: ${site.rel} updated`);
|
|
8296
8557
|
}
|
|
8297
8558
|
async function syncSiteMarker(ctx, site, target) {
|
|
8298
|
-
const marker = await ctx.readFile(
|
|
8559
|
+
const marker = await ctx.readFile(join7(target, MARKER_FILE)).catch(() => void 0);
|
|
8299
8560
|
if (marker === void 0)
|
|
8300
8561
|
return;
|
|
8301
|
-
const { commit } = parseAnswers(await ctx.readFile(
|
|
8562
|
+
const { commit } = parseAnswers(await ctx.readFile(join7(target, ANSWERS_FILE)));
|
|
8302
8563
|
if (!commit)
|
|
8303
8564
|
return;
|
|
8304
8565
|
const synced = syncMarker(marker, commit);
|
|
8305
8566
|
if (synced === marker)
|
|
8306
8567
|
return;
|
|
8307
8568
|
ctx.say(` marker: plexus \u2192 ${commit} in ${site.rel}/${MARKER_FILE}`);
|
|
8308
|
-
const step = write(
|
|
8569
|
+
const step = write(join7(target, MARKER_FILE), synced);
|
|
8309
8570
|
if (ctx.dryRun)
|
|
8310
8571
|
await ctx.stage(step);
|
|
8311
8572
|
else
|
|
@@ -8314,7 +8575,7 @@ async function syncSiteMarker(ctx, site, target) {
|
|
|
8314
8575
|
async function refreshLockfiles(ctx, consumers, summary, clones) {
|
|
8315
8576
|
const manifests = ["*package.json", "pnpm-workspace.yaml"];
|
|
8316
8577
|
for (const c of consumers) {
|
|
8317
|
-
if (await ctx.readFile(
|
|
8578
|
+
if (await ctx.readFile(join7(c.path, "pnpm-workspace.yaml")).catch(() => void 0) === void 0)
|
|
8318
8579
|
continue;
|
|
8319
8580
|
const repo = ctx.dryRun ? clones.get(c.path) : c.path;
|
|
8320
8581
|
if (!repo)
|
|
@@ -8334,7 +8595,7 @@ async function refreshLockfiles(ctx, consumers, summary, clones) {
|
|
|
8334
8595
|
}
|
|
8335
8596
|
async function bumpConsumerRefs(ctx, consumers, train) {
|
|
8336
8597
|
for (const c of consumers) {
|
|
8337
|
-
const dir =
|
|
8598
|
+
const dir = join7(c.path, ".github", "workflows");
|
|
8338
8599
|
const entries = await ctx.readDir(dir).catch(() => []);
|
|
8339
8600
|
const files = entries.filter((e) => !e.directory && /\.ya?ml$/.test(e.name)).map((e) => e.name).sort();
|
|
8340
8601
|
if (files.length === 0)
|
|
@@ -8344,14 +8605,14 @@ async function bumpConsumerRefs(ctx, consumers, train) {
|
|
|
8344
8605
|
continue;
|
|
8345
8606
|
}
|
|
8346
8607
|
for (const f of files) {
|
|
8347
|
-
const text = await ctx.readFile(
|
|
8608
|
+
const text = await ctx.readFile(join7(dir, f));
|
|
8348
8609
|
if (!text.includes(WORKFLOW_REF))
|
|
8349
8610
|
continue;
|
|
8350
8611
|
const bumped = bumpWorkflowRefs(text, train);
|
|
8351
8612
|
if (bumped === text)
|
|
8352
8613
|
continue;
|
|
8353
8614
|
ctx.say(`\u2192 ${c.rel}: ${f} ci-cd refs \u2192 @${train}`);
|
|
8354
|
-
await ctx.apply([write(
|
|
8615
|
+
await ctx.apply([write(join7(dir, f), bumped)]);
|
|
8355
8616
|
}
|
|
8356
8617
|
}
|
|
8357
8618
|
}
|
|
@@ -8362,7 +8623,7 @@ var detail = (error) => {
|
|
|
8362
8623
|
};
|
|
8363
8624
|
|
|
8364
8625
|
// ../lib/dist/requirements.js
|
|
8365
|
-
import { join as
|
|
8626
|
+
import { join as join8 } from "node:path";
|
|
8366
8627
|
var KEYWORD = /MUST|SHOULD|MAY/;
|
|
8367
8628
|
var hasKeyword = (line) => KEYWORD.test(line.replace(/MUSTs|SHOULDs|MAYs/g, ""));
|
|
8368
8629
|
var list = (value) => value.replace(/^\s*\[/, "").replace(/\].*$/, "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -8455,7 +8716,7 @@ var MAP_PAGE = "essentials/standard.md";
|
|
|
8455
8716
|
async function modulePages(ctx, compendium) {
|
|
8456
8717
|
const pages = [];
|
|
8457
8718
|
for (const dir of Object.keys(CATALOG_DIRS)) {
|
|
8458
|
-
const entries = await ctx.readDir(
|
|
8719
|
+
const entries = await ctx.readDir(join8(compendium, dir));
|
|
8459
8720
|
for (const e of entries.filter((e2) => !e2.directory && e2.name.endsWith(".md") && e2.name !== "index.md").sort())
|
|
8460
8721
|
pages.push(`${dir}/${e.name}`);
|
|
8461
8722
|
}
|
|
@@ -8463,11 +8724,11 @@ async function modulePages(ctx, compendium) {
|
|
|
8463
8724
|
}
|
|
8464
8725
|
async function checkCompendium(ctx, compendium) {
|
|
8465
8726
|
const defects = [];
|
|
8466
|
-
defects.push(...scanPage(await ctx.readFile(
|
|
8727
|
+
defects.push(...scanPage(await ctx.readFile(join8(compendium, MAP_PAGE)), MAP_PAGE, "map").defects);
|
|
8467
8728
|
const pages = await modulePages(ctx, compendium);
|
|
8468
8729
|
const scans = /* @__PURE__ */ new Map();
|
|
8469
8730
|
for (const page of pages) {
|
|
8470
|
-
const scan = scanPage(await ctx.readFile(
|
|
8731
|
+
const scan = scanPage(await ctx.readFile(join8(compendium, page)), page, "module");
|
|
8471
8732
|
defects.push(...scan.defects);
|
|
8472
8733
|
scans.set(page, scan);
|
|
8473
8734
|
}
|
|
@@ -8514,9 +8775,9 @@ async function checkCompendium(ctx, compendium) {
|
|
|
8514
8775
|
}
|
|
8515
8776
|
|
|
8516
8777
|
// ../lib/dist/ship.js
|
|
8517
|
-
import { join as
|
|
8778
|
+
import { join as join9 } from "node:path";
|
|
8518
8779
|
async function ship(ctx, options) {
|
|
8519
|
-
const upstream =
|
|
8780
|
+
const upstream = join9(options.workspace, UPSTREAM);
|
|
8520
8781
|
const propagateOnly = options.propagateOnly ?? false;
|
|
8521
8782
|
const train = await readTrain(ctx, upstream);
|
|
8522
8783
|
if (!propagateOnly)
|
|
@@ -8567,7 +8828,7 @@ async function planBookkeeping(ctx, train, next, today2) {
|
|
|
8567
8828
|
return r;
|
|
8568
8829
|
};
|
|
8569
8830
|
const edit = async (r, rel, what, transform) => {
|
|
8570
|
-
const path3 =
|
|
8831
|
+
const path3 = join9(r.path, rel);
|
|
8571
8832
|
const text = await ctx.readFile(path3).catch(() => void 0);
|
|
8572
8833
|
if (text === void 0)
|
|
8573
8834
|
return;
|
|
@@ -8588,7 +8849,7 @@ async function planBookkeeping(ctx, train, next, today2) {
|
|
|
8588
8849
|
const comp = repo("compendium");
|
|
8589
8850
|
await edit(comp, MAP_PAGE, `version \u2192 ${next}, timestamp \u2192 ${today2}`, (t) => stampStandard(t, next, today2));
|
|
8590
8851
|
for (const page of await modulePages(ctx, comp.path)) {
|
|
8591
|
-
const text = await ctx.readFile(
|
|
8852
|
+
const text = await ctx.readFile(join9(comp.path, page));
|
|
8592
8853
|
if (frontmatterField(text, "maturity") !== "standard")
|
|
8593
8854
|
continue;
|
|
8594
8855
|
const previous = comp.tag ? await ctx.read("git", ["show", `${comp.tag}:${page}`], { cwd: comp.path }).catch(() => "") : "";
|
|
@@ -9551,13 +9812,13 @@ import { AsyncResource as AsyncResource3 } from "node:async_hooks";
|
|
|
9551
9812
|
|
|
9552
9813
|
// ../../node_modules/.pnpm/@inquirer+type@4.2.0_@types+node@26.6.5/node_modules/@inquirer/type/dist/utils.js
|
|
9553
9814
|
function withResolver() {
|
|
9554
|
-
let
|
|
9815
|
+
let resolve9;
|
|
9555
9816
|
let reject;
|
|
9556
9817
|
const promise = new Promise((res, rej) => {
|
|
9557
|
-
|
|
9818
|
+
resolve9 = res;
|
|
9558
9819
|
reject = rej;
|
|
9559
9820
|
});
|
|
9560
|
-
return { promise, resolve:
|
|
9821
|
+
return { promise, resolve: resolve9, reject };
|
|
9561
9822
|
}
|
|
9562
9823
|
|
|
9563
9824
|
// ../../node_modules/.pnpm/@inquirer+core@12.0.5_@types+node@26.6.5/node_modules/@inquirer/core/dist/lib/create-prompt.js
|
|
@@ -9936,7 +10197,7 @@ function createPrompt(view) {
|
|
|
9936
10197
|
});
|
|
9937
10198
|
output.mute();
|
|
9938
10199
|
const screen = new ScreenManager(rl);
|
|
9939
|
-
const { promise, resolve:
|
|
10200
|
+
const { promise, resolve: resolve9, reject } = withResolver();
|
|
9940
10201
|
return withHooks(rl, (cycle) => {
|
|
9941
10202
|
const clearEffects = AsyncResource3.bind(() => effectScheduler.clearAll());
|
|
9942
10203
|
const settlePrompt = (settle) => {
|
|
@@ -9947,7 +10208,7 @@ function createPrompt(view) {
|
|
|
9947
10208
|
reject(error);
|
|
9948
10209
|
}
|
|
9949
10210
|
};
|
|
9950
|
-
const resolvePrompt = (value) => settlePrompt(() =>
|
|
10211
|
+
const resolvePrompt = (value) => settlePrompt(() => resolve9(value));
|
|
9951
10212
|
const rejectPrompt = (error) => settlePrompt(() => reject(error));
|
|
9952
10213
|
const promptPromise = Object.assign(promise.finally(() => {
|
|
9953
10214
|
cleanups.forEach((cleanup) => cleanup());
|
|
@@ -13466,9 +13727,45 @@ function useColor() {
|
|
|
13466
13727
|
// ../../node_modules/.pnpm/commander@15.0.0/node_modules/commander/index.js
|
|
13467
13728
|
var program = new Command();
|
|
13468
13729
|
|
|
13730
|
+
// src/commands/changed-apps.ts
|
|
13731
|
+
import { resolve } from "node:path";
|
|
13732
|
+
function register(program2) {
|
|
13733
|
+
program2.command("changed-apps").description("list the apps whose sources changed between two refs, dependents of a changed package included").argument("<from>", "the base ref; empty, all zeros or unknown lists every app").argument("[to]", "the ref compared against", "HEAD").option("-C, --dir <dir>", "the checkout to read", ".").action(async (from, to, opts, command) => {
|
|
13734
|
+
const ctx = contextOf2(command);
|
|
13735
|
+
const result = await readChangedApps(ctx, resolve(opts.dir), from, to);
|
|
13736
|
+
ctx.result(result.apps);
|
|
13737
|
+
});
|
|
13738
|
+
}
|
|
13739
|
+
|
|
13740
|
+
// src/commands/changeset.ts
|
|
13741
|
+
import { resolve as resolve2 } from "node:path";
|
|
13742
|
+
function register2(program2) {
|
|
13743
|
+
program2.command("changeset").description("write the changeset a dependency bump owes: a patch per package whose runtime dependencies moved").argument("[dir]", "a checkout of the package repo, on the bump branch", ".").option("--base <ref>", "the trunk the branch is diffed against, at the merge base", "origin/main").option("--branch <name>", "the branch name that names the file (default: the checked-out branch)").action(async (dir, opts, command) => {
|
|
13744
|
+
const ctx = contextOf2(command);
|
|
13745
|
+
const changeset = await readChangeset(ctx, resolve2(dir), opts.base, opts.branch);
|
|
13746
|
+
await ctx.apply(changeset.plan);
|
|
13747
|
+
ctx.result(changeset, renderChangeset);
|
|
13748
|
+
});
|
|
13749
|
+
}
|
|
13750
|
+
function renderChangeset(c) {
|
|
13751
|
+
const releases = Object.entries(c.releases);
|
|
13752
|
+
if (releases.length === 0) return `\u2713 nothing owed; no ${c.path}`;
|
|
13753
|
+
return [`\u2713 ${c.path}`, ...releases.map(([name, bump]) => ` ${name}: ${bump}`)].join("\n");
|
|
13754
|
+
}
|
|
13755
|
+
|
|
13756
|
+
// src/commands/deploy-targets.ts
|
|
13757
|
+
import { resolve as resolve3 } from "node:path";
|
|
13758
|
+
function register3(program2) {
|
|
13759
|
+
program2.command("deploy-targets").description("map each app of a platform repo to the deploy target of the inventory file that declares it").argument("[inventory]", "the inventory directory", "inventory").action(async (inventory, _opts, command) => {
|
|
13760
|
+
const ctx = contextOf2(command);
|
|
13761
|
+
const targets = await readDeployTargets(ctx, resolve3(inventory));
|
|
13762
|
+
ctx.result(targets, (t) => JSON.stringify(t));
|
|
13763
|
+
});
|
|
13764
|
+
}
|
|
13765
|
+
|
|
13469
13766
|
// src/commands/doctor.ts
|
|
13470
13767
|
import { existsSync } from "node:fs";
|
|
13471
|
-
import { join as
|
|
13768
|
+
import { join as join10, resolve as resolve4 } from "node:path";
|
|
13472
13769
|
|
|
13473
13770
|
// src/style.ts
|
|
13474
13771
|
function style(enabled) {
|
|
@@ -13479,15 +13776,15 @@ var colorByDefault = () => process.stdout.isTTY === true && !process.env.NO_COLO
|
|
|
13479
13776
|
|
|
13480
13777
|
// src/commands/doctor.ts
|
|
13481
13778
|
var WORKSPACE_COMPENDIUM = "plexus-ms/compendium";
|
|
13482
|
-
function
|
|
13779
|
+
function register4(program2) {
|
|
13483
13780
|
program2.command("doctor").description("check every PLEXUS.md under a directory for modules that changed since and dependencies not declared").argument("[dir]", "directory to check", ".").option(
|
|
13484
13781
|
"--compendium <path>",
|
|
13485
13782
|
`compendium checkout to read the modules from (default: <dir>/${WORKSPACE_COMPENDIUM})`
|
|
13486
13783
|
).option("--color", "colour the report even when piped").option("--no-color", "never colour the report").action(async (dir, opts, command) => {
|
|
13487
13784
|
const ctx = contextOf2(command);
|
|
13488
|
-
const root =
|
|
13489
|
-
const compendium =
|
|
13490
|
-
if (!existsSync(
|
|
13785
|
+
const root = resolve4(dir);
|
|
13786
|
+
const compendium = resolve4(opts.compendium ?? join10(root, WORKSPACE_COMPENDIUM));
|
|
13787
|
+
if (!existsSync(join10(compendium, "contracts")))
|
|
13491
13788
|
throw new Error(
|
|
13492
13789
|
`no compendium at ${compendium}; pass --compendium <path to a checkout of plexus-ms/compendium>`
|
|
13493
13790
|
);
|
|
@@ -13515,11 +13812,11 @@ function renderReport(report, s, showCurrent) {
|
|
|
13515
13812
|
}
|
|
13516
13813
|
|
|
13517
13814
|
// src/commands/overview.ts
|
|
13518
|
-
import { resolve as
|
|
13519
|
-
function
|
|
13815
|
+
import { resolve as resolve5 } from "node:path";
|
|
13816
|
+
function register5(program2) {
|
|
13520
13817
|
program2.command("overview").description("map the git repos, copier templates and projects, and Plexus units under a directory").argument("[dir]", "directory to map", ".").option("--color", "colour the tree even when piped").option("--no-color", "never colour the tree").action(async (dir, opts, command) => {
|
|
13521
13818
|
const ctx = contextOf2(command);
|
|
13522
|
-
const tree = await overview(ctx,
|
|
13819
|
+
const tree = await overview(ctx, resolve5(dir));
|
|
13523
13820
|
const s = style(opts.color ?? colorByDefault());
|
|
13524
13821
|
ctx.result(tree, (t) => renderTree(t, s, ctx.verbose >= 1));
|
|
13525
13822
|
});
|
|
@@ -13562,14 +13859,14 @@ function annotate(dir, s, modules) {
|
|
|
13562
13859
|
}
|
|
13563
13860
|
|
|
13564
13861
|
// src/commands/propagate.ts
|
|
13565
|
-
import { resolve as
|
|
13862
|
+
import { resolve as resolve7 } from "node:path";
|
|
13566
13863
|
|
|
13567
13864
|
// src/commands/ship.ts
|
|
13568
|
-
import { resolve as
|
|
13569
|
-
function
|
|
13865
|
+
import { resolve as resolve6 } from "node:path";
|
|
13866
|
+
function register6(program2) {
|
|
13570
13867
|
program2.command("ship").description("release the train: tag every train repo identically, then copier-update every consumer").argument("[dir]", "the workspace: plexus-ms/ and the consumers sit under it", ".").option("--train <version>", "the train version, plain semver (default: minor+1 over the latest train)").action(async (dir, opts, command) => {
|
|
13571
13868
|
const ctx = contextOf2(command);
|
|
13572
|
-
const result = await ship(ctx, { workspace:
|
|
13869
|
+
const result = await ship(ctx, { workspace: resolve6(dir), train: opts.train });
|
|
13573
13870
|
ctx.result(result, (r) => renderSummary(r, `plx -y ship --train ${r.train} ${dir}`));
|
|
13574
13871
|
if (!isClean(result)) process.exitCode = 1;
|
|
13575
13872
|
});
|
|
@@ -13592,23 +13889,23 @@ function renderSummary(s, realCommand) {
|
|
|
13592
13889
|
}
|
|
13593
13890
|
|
|
13594
13891
|
// src/commands/propagate.ts
|
|
13595
|
-
function
|
|
13892
|
+
function register7(program2) {
|
|
13596
13893
|
program2.command("propagate").description("copier-update every consumer of the presets to the current train, and pin their ci-cd workflow refs").argument("[dir]", "the workspace: plexus-ms/ and the consumers sit under it", ".").option("--train <version>", "the train to propagate (default: the current one)").action(async (dir, opts, command) => {
|
|
13597
13894
|
const ctx = contextOf2(command);
|
|
13598
|
-
const result = await ship(ctx, { workspace:
|
|
13895
|
+
const result = await ship(ctx, { workspace: resolve7(dir), train: opts.train, propagateOnly: true });
|
|
13599
13896
|
ctx.result(result, (r) => renderSummary(r, `plx -y propagate --train ${r.train} ${dir}`));
|
|
13600
13897
|
if (!isClean(result)) process.exitCode = 1;
|
|
13601
13898
|
});
|
|
13602
13899
|
}
|
|
13603
13900
|
|
|
13604
13901
|
// src/commands/requirements.ts
|
|
13605
|
-
import { join as
|
|
13606
|
-
function
|
|
13902
|
+
import { join as join11, resolve as resolve8 } from "node:path";
|
|
13903
|
+
function register8(program2) {
|
|
13607
13904
|
program2.command("requirements").description("lint the standard's map and module pages, or extract one module's requirement lines").argument("[dir]", "a checkout of the compendium", ".").option("--extract <page>", "print a module page's requirement lines instead of linting (path relative to dir)").action(async (dir, opts, command) => {
|
|
13608
13905
|
const ctx = contextOf2(command);
|
|
13609
|
-
const compendium =
|
|
13906
|
+
const compendium = resolve8(dir);
|
|
13610
13907
|
if (opts.extract) {
|
|
13611
|
-
ctx.result(extractRequirements(await ctx.readFile(
|
|
13908
|
+
ctx.result(extractRequirements(await ctx.readFile(join11(compendium, opts.extract))));
|
|
13612
13909
|
return;
|
|
13613
13910
|
}
|
|
13614
13911
|
const check = await checkCompendium(ctx, compendium);
|
|
@@ -13622,7 +13919,16 @@ function renderCheck(c) {
|
|
|
13622
13919
|
}
|
|
13623
13920
|
|
|
13624
13921
|
// src/commands/index.ts
|
|
13625
|
-
var commands = [
|
|
13922
|
+
var commands = [
|
|
13923
|
+
register5,
|
|
13924
|
+
register4,
|
|
13925
|
+
register6,
|
|
13926
|
+
register7,
|
|
13927
|
+
register8,
|
|
13928
|
+
register2,
|
|
13929
|
+
register,
|
|
13930
|
+
register3
|
|
13931
|
+
];
|
|
13626
13932
|
|
|
13627
13933
|
// src/program.ts
|
|
13628
13934
|
var { version, description } = createRequire(import.meta.url)("../package.json");
|
|
@@ -13631,7 +13937,7 @@ function buildProgram() {
|
|
|
13631
13937
|
program2.addOption(new Option("-n, --dry-run", "print every effect this run would have, perform none").env("PLX_DRY_RUN")).addOption(
|
|
13632
13938
|
new Option("-v, --verbose", "say more: once for reads and inputs, twice for command output").argParser((_value, previous) => previous + 1).default(0)
|
|
13633
13939
|
).addOption(new Option("-q, --quiet", "say nothing but results and errors").env("PLX_QUIET")).addOption(new Option("-y, --yes", "answer every confirmation with yes (needed off a terminal)").env("PLX_YES")).addOption(new Option("--json", "print results as JSON").env("PLX_JSON"));
|
|
13634
|
-
for (const
|
|
13940
|
+
for (const register9 of commands) register9(program2);
|
|
13635
13941
|
return program2;
|
|
13636
13942
|
}
|
|
13637
13943
|
function contextOf2(command) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plexus-ms/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "plx, the Plexus command-line tool: a single-script adapter over @plexus-ms/lib",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -20,15 +20,17 @@
|
|
|
20
20
|
"access": "public"
|
|
21
21
|
},
|
|
22
22
|
"engines": {
|
|
23
|
-
"node": ">=24"
|
|
23
|
+
"node": ">=24.22.1"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@inquirer/prompts": "^8.7.4",
|
|
27
|
+
"@plexus-ms/lib": "0.5.0",
|
|
28
|
+
"commander": "^15.0.0"
|
|
24
29
|
},
|
|
25
30
|
"devDependencies": {
|
|
26
|
-
"@inquirer/prompts": "^8.0.0",
|
|
27
31
|
"@types/node": "^26.6.5",
|
|
28
|
-
"commander": "^15.0.0",
|
|
29
32
|
"esbuild": "^0.28.2",
|
|
30
|
-
"typescript": "^6.0.3"
|
|
31
|
-
"@plexus-ms/lib": "0.3.0"
|
|
33
|
+
"typescript": "^6.0.3"
|
|
32
34
|
},
|
|
33
35
|
"scripts": {
|
|
34
36
|
"build": "node build.js",
|