@plexus-ms/cli 0.100.0 → 0.102.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 +176 -103
- package/package.json +3 -3
package/dist/plx.js
CHANGED
|
@@ -7589,6 +7589,7 @@ var PLX_PIN = "npm:@plexus-ms/cli";
|
|
|
7589
7589
|
var bumpPlxPin = (text, train) => text.replace(/("npm:@plexus-ms\/cli"\s*=\s*(?:\{\s*version\s*=\s*)?")[^"]*(")/g, `$1${train}$2`);
|
|
7590
7590
|
var CONFIG_PIN = "@plexus-ms/config";
|
|
7591
7591
|
var bumpConfigPin = (text, train) => text.replace(/("@plexus-ms\/config":\s*")(?!workspace:|catalog:)[^"]*(")/g, `$1${train}$2`);
|
|
7592
|
+
var bumpCatalogPin = (text, train) => text.replace(/^(\s+"@plexus-ms\/config":\s*)\S+$/m, `$1${train}`);
|
|
7592
7593
|
var stampStandard = (text, train, today2) => setFrontmatterField(setFrontmatterField(text, "version", train), "timestamp", today2);
|
|
7593
7594
|
var stampChanged = (text, train) => setFrontmatterField(text, "changed", train, { after: "binds" });
|
|
7594
7595
|
var hasConflictMarkers = (text) => /^<<<<<<< /m.test(text);
|
|
@@ -7760,7 +7761,7 @@ function realIo(prompt) {
|
|
|
7760
7761
|
stdout: process.stdout,
|
|
7761
7762
|
stderr: process.stderr,
|
|
7762
7763
|
prompt: prompt && isInteractive() ? prompt : void 0,
|
|
7763
|
-
exec: (command, args, options = {}) => new Promise((
|
|
7764
|
+
exec: (command, args, options = {}) => new Promise((resolve11, reject) => {
|
|
7764
7765
|
const child = spawn(command, args, {
|
|
7765
7766
|
cwd: options.cwd,
|
|
7766
7767
|
env: options.env,
|
|
@@ -7783,7 +7784,7 @@ function realIo(prompt) {
|
|
|
7783
7784
|
child.on("error", reject);
|
|
7784
7785
|
child.on("close", (code) => {
|
|
7785
7786
|
if (code === 0)
|
|
7786
|
-
|
|
7787
|
+
resolve11(stdout);
|
|
7787
7788
|
else
|
|
7788
7789
|
reject(new ExecError(render(command, args), code, stderr));
|
|
7789
7790
|
});
|
|
@@ -8682,22 +8683,22 @@ async function templateFacet(ctx, cwd) {
|
|
|
8682
8683
|
// ../lib/dist/propagate.js
|
|
8683
8684
|
import { join as join8, relative as relative3 } from "node:path";
|
|
8684
8685
|
var isClean = (s) => s.conflicted.length === 0 && s.failed.length === 0 && s.skipped.length === 0;
|
|
8685
|
-
var
|
|
8686
|
-
|
|
8687
|
-
|
|
8686
|
+
var emptySummary = (train, preview) => ({
|
|
8687
|
+
train,
|
|
8688
|
+
preview,
|
|
8689
|
+
clean: [],
|
|
8690
|
+
conflicted: [],
|
|
8691
|
+
failed: [],
|
|
8692
|
+
skipped: [],
|
|
8693
|
+
pulls: []
|
|
8694
|
+
});
|
|
8695
|
+
var trainBranch = (train) => `plexus/${train}`;
|
|
8696
|
+
var copierIn = (dir, args) => [
|
|
8688
8697
|
"env",
|
|
8689
|
-
[`MISE_IGNORED_CONFIG_PATHS=${
|
|
8698
|
+
[`MISE_IGNORED_CONFIG_PATHS=${dir}`, "mise", ...copierVia(args)]
|
|
8690
8699
|
];
|
|
8691
8700
|
async function propagate(ctx, workspace, train) {
|
|
8692
|
-
const summary =
|
|
8693
|
-
train,
|
|
8694
|
-
preview: ctx.dryRun,
|
|
8695
|
-
clean: [],
|
|
8696
|
-
conflicted: [],
|
|
8697
|
-
failed: [],
|
|
8698
|
-
skipped: [],
|
|
8699
|
-
pulls: []
|
|
8700
|
-
};
|
|
8701
|
+
const summary = emptySummary(train, ctx.dryRun);
|
|
8701
8702
|
const upstream = join8(workspace, UPSTREAM);
|
|
8702
8703
|
const tree = await overview(ctx, workspace);
|
|
8703
8704
|
const upstreamDir = tree.children.find((c) => c.path === upstream);
|
|
@@ -8712,6 +8713,7 @@ async function propagate(ctx, workspace, train) {
|
|
|
8712
8713
|
for (const preset of presets)
|
|
8713
8714
|
if (!consumers.some((c) => c.sites.some((s) => s.preset === preset)))
|
|
8714
8715
|
ctx.say(`\u2022 no consumers of ${presetSource(preset)} found under ${workspace}`);
|
|
8716
|
+
const take = { train, opener: "`plx ship`", ...ctx.dryRun ? { previewFrom: upstream } : {} };
|
|
8715
8717
|
const scratch = (await ctx.stage(run("mktemp", ["-d"])))?.trim() ?? "";
|
|
8716
8718
|
try {
|
|
8717
8719
|
for (const consumer of consumers) {
|
|
@@ -8721,17 +8723,12 @@ async function propagate(ctx, workspace, train) {
|
|
|
8721
8723
|
ctx.say(`\u2192 ${consumer.rel}: clone ${consumer.origin} at ${consumer.branch}`);
|
|
8722
8724
|
try {
|
|
8723
8725
|
await ctx.stage(run("git", ["clone", "--quiet", "--branch", consumer.branch, consumer.origin, clone]));
|
|
8724
|
-
await ctx.stage(run("git", ["tag", BASE_TAG], { cwd: clone }));
|
|
8725
8726
|
} catch (error) {
|
|
8726
8727
|
ctx.say(` \u2717 FAILED \u2014 clone${detail(error)}`);
|
|
8727
8728
|
summary.failed.push(consumer.rel);
|
|
8728
8729
|
continue;
|
|
8729
8730
|
}
|
|
8730
|
-
|
|
8731
|
-
await updateSite(ctx, consumer, site, clone, upstream, summary);
|
|
8732
|
-
await refreshLockfile(ctx, consumer, clone, summary);
|
|
8733
|
-
await bumpConsumerRefs(ctx, consumer, clone, train);
|
|
8734
|
-
await openPull(ctx, consumer, clone, train, summary);
|
|
8731
|
+
await takeTrain(ctx, consumer, clone, take, summary);
|
|
8735
8732
|
}
|
|
8736
8733
|
} finally {
|
|
8737
8734
|
if (scratch)
|
|
@@ -8739,7 +8736,15 @@ async function propagate(ctx, workspace, train) {
|
|
|
8739
8736
|
}
|
|
8740
8737
|
return summary;
|
|
8741
8738
|
}
|
|
8742
|
-
|
|
8739
|
+
async function takeTrain(ctx, consumer, dir, take, summary) {
|
|
8740
|
+
const base = (await ctx.read("git", ["rev-parse", "HEAD"], { cwd: dir })).trim();
|
|
8741
|
+
for (const site of consumer.sites)
|
|
8742
|
+
await updateSite(ctx, consumer, site, dir, take, summary);
|
|
8743
|
+
await refreshLockfile(ctx, consumer, dir, base, summary);
|
|
8744
|
+
await bumpConsumerRefs(ctx, consumer, dir, take.train);
|
|
8745
|
+
await openPull(ctx, consumer, dir, take, summary);
|
|
8746
|
+
}
|
|
8747
|
+
var previewCommit = (ctx, dir, message) => ctx.stage(run("git", ["add", "-A"], { cwd: dir })).then(() => ctx.stage(run("git", [
|
|
8743
8748
|
"-c",
|
|
8744
8749
|
"commit.gpgsign=false",
|
|
8745
8750
|
"-c",
|
|
@@ -8752,31 +8757,32 @@ var previewCommit = (ctx, clone, message) => ctx.stage(run("git", ["add", "-A"],
|
|
|
8752
8757
|
"--allow-empty",
|
|
8753
8758
|
"-m",
|
|
8754
8759
|
message
|
|
8755
|
-
], { cwd:
|
|
8756
|
-
async function updateSite(ctx, consumer, site,
|
|
8757
|
-
const target = join8(
|
|
8758
|
-
const localPreset = join8(upstream, site.preset);
|
|
8760
|
+
], { cwd: dir })));
|
|
8761
|
+
async function updateSite(ctx, consumer, site, dir, take, summary) {
|
|
8762
|
+
const target = join8(dir, relative3(consumer.path, site.path));
|
|
8759
8763
|
try {
|
|
8760
|
-
if (
|
|
8764
|
+
if (take.previewFrom) {
|
|
8765
|
+
const localPreset = join8(take.previewFrom, site.preset);
|
|
8761
8766
|
ctx.say(`\u2192 ${site.rel}: copier update (preview from ${localPreset}@main)`);
|
|
8762
8767
|
const answers = await ctx.readFile(join8(target, ANSWERS_FILE));
|
|
8763
8768
|
await ctx.stage(write(join8(target, ANSWERS_FILE), answers.replace(/^_src_path: .*$/m, `_src_path: ${localPreset}`)));
|
|
8764
|
-
await previewCommit(ctx,
|
|
8765
|
-
await ctx.stage(run(...copierIn(
|
|
8769
|
+
await previewCommit(ctx, dir, `plx preview: ${site.rel} reads ${localPreset} from the local checkout`);
|
|
8770
|
+
await ctx.stage(run(...copierIn(dir, ["update", "--skip-answered", "--trust", "--defaults", "--skip-tasks", "--vcs-ref", "main"]), { cwd: target }));
|
|
8771
|
+
} else {
|
|
8772
|
+
ctx.say(`\u2192 ${site.rel}: copier update${take.ref ? ` to ${take.ref}` : ""}`);
|
|
8773
|
+
const args = [
|
|
8766
8774
|
"update",
|
|
8767
8775
|
"--skip-answered",
|
|
8768
8776
|
"--trust",
|
|
8769
|
-
"--defaults",
|
|
8770
8777
|
"--skip-tasks",
|
|
8771
|
-
"--vcs-ref",
|
|
8772
|
-
"
|
|
8773
|
-
]
|
|
8774
|
-
|
|
8775
|
-
|
|
8776
|
-
|
|
8777
|
-
|
|
8778
|
-
|
|
8779
|
-
await ctx.apply([run(...copierIn(clone, args), { cwd: target, interactive: true })]);
|
|
8778
|
+
...take.ref ? ["--vcs-ref", take.ref] : [],
|
|
8779
|
+
...ctx.yes || ctx.dryRun ? ["--defaults"] : []
|
|
8780
|
+
];
|
|
8781
|
+
const step = run(...copierIn(dir, args), { cwd: target, interactive: !ctx.dryRun });
|
|
8782
|
+
if (ctx.dryRun)
|
|
8783
|
+
await ctx.stage(step);
|
|
8784
|
+
else
|
|
8785
|
+
await ctx.apply([step]);
|
|
8780
8786
|
}
|
|
8781
8787
|
} catch (error) {
|
|
8782
8788
|
ctx.say(` \u2717 FAILED \u2014 copier update exited nonzero${detail(error)}`);
|
|
@@ -8801,8 +8807,10 @@ async function updateSite(ctx, consumer, site, clone, upstream, summary) {
|
|
|
8801
8807
|
}
|
|
8802
8808
|
for (const line of status.split("\n").filter(Boolean))
|
|
8803
8809
|
ctx.say(` ${line}`);
|
|
8804
|
-
if (
|
|
8805
|
-
await previewCommit(ctx,
|
|
8810
|
+
if (take.previewFrom)
|
|
8811
|
+
await previewCommit(ctx, dir, `plx preview: ${site.rel} updated`);
|
|
8812
|
+
else
|
|
8813
|
+
await ctx.stage(run("git", ["add", "-A"], { cwd: dir }));
|
|
8806
8814
|
}
|
|
8807
8815
|
async function syncSiteMarker(ctx, site, target) {
|
|
8808
8816
|
const marker = await ctx.readFile(join8(target, MARKER_FILE)).catch(() => void 0);
|
|
@@ -8817,77 +8825,71 @@ async function syncSiteMarker(ctx, site, target) {
|
|
|
8817
8825
|
ctx.say(` marker: plexus \u2192 ${commit} in ${site.rel}/${MARKER_FILE}`);
|
|
8818
8826
|
await ctx.stage(write(join8(target, MARKER_FILE), synced));
|
|
8819
8827
|
}
|
|
8820
|
-
async function refreshLockfile(ctx, consumer,
|
|
8821
|
-
if (await ctx.readFile(join8(
|
|
8828
|
+
async function refreshLockfile(ctx, consumer, dir, base, summary) {
|
|
8829
|
+
if (await ctx.readFile(join8(dir, "pnpm-workspace.yaml")).catch(() => void 0) === void 0)
|
|
8822
8830
|
return;
|
|
8823
8831
|
const manifests = ["*package.json", "pnpm-workspace.yaml"];
|
|
8824
|
-
const unchanged = await ctx.read("git", ["diff", "--quiet",
|
|
8832
|
+
const unchanged = await ctx.read("git", ["diff", "--quiet", base, "--", ...manifests], { cwd: dir }).then(() => true).catch(() => false);
|
|
8825
8833
|
if (unchanged)
|
|
8826
8834
|
return;
|
|
8827
8835
|
ctx.say(`\u2192 ${consumer.rel}: pnpm install (manifests changed)`);
|
|
8828
8836
|
try {
|
|
8829
|
-
await ctx.apply([run("mise", ["exec", "--", "pnpm", "install", "--reporter=silent"], { cwd:
|
|
8837
|
+
await ctx.apply([run("mise", ["exec", "--", "pnpm", "install", "--reporter=silent"], { cwd: dir })]);
|
|
8830
8838
|
} catch (error) {
|
|
8831
8839
|
ctx.say(` \u2717 pnpm install failed${detail(error)}`);
|
|
8832
8840
|
summary.failed.push(`${consumer.rel}/pnpm-lock.yaml`);
|
|
8833
8841
|
}
|
|
8834
8842
|
}
|
|
8835
|
-
async function bumpConsumerRefs(ctx, consumer,
|
|
8836
|
-
const
|
|
8837
|
-
const entries = await ctx.readDir(
|
|
8843
|
+
async function bumpConsumerRefs(ctx, consumer, dir, train) {
|
|
8844
|
+
const workflows = join8(dir, ".github", "workflows");
|
|
8845
|
+
const entries = await ctx.readDir(workflows).catch(() => []);
|
|
8838
8846
|
const files = entries.filter((e) => !e.directory && /\.ya?ml$/.test(e.name)).map((e) => e.name).sort();
|
|
8839
8847
|
for (const f of files) {
|
|
8840
|
-
const text = await ctx.readFile(join8(
|
|
8848
|
+
const text = await ctx.readFile(join8(workflows, f));
|
|
8841
8849
|
if (!text.includes(WORKFLOW_REF))
|
|
8842
8850
|
continue;
|
|
8843
8851
|
const bumped = bumpWorkflowRefs(text, train);
|
|
8844
8852
|
if (bumped === text)
|
|
8845
8853
|
continue;
|
|
8846
8854
|
ctx.say(`\u2192 ${consumer.rel}: ${f} workflow refs \u2192 @${train}`);
|
|
8847
|
-
await ctx.stage(write(join8(
|
|
8855
|
+
await ctx.stage(write(join8(workflows, f), bumped));
|
|
8848
8856
|
}
|
|
8849
8857
|
}
|
|
8850
|
-
async function
|
|
8858
|
+
async function openPullUrl(ctx, consumer, dir, train) {
|
|
8859
|
+
const args = ["pr", "list", "--head", trainBranch(train), "--base", consumer.branch, "--state", "open"];
|
|
8860
|
+
return (await ctx.read("gh", [...args, "--json", "url", "--jq", ".[0].url"], { cwd: dir }).catch(() => "")).trim();
|
|
8861
|
+
}
|
|
8862
|
+
async function openPull(ctx, consumer, dir, take, summary) {
|
|
8863
|
+
const { train } = take;
|
|
8851
8864
|
const branch = trainBranch(train);
|
|
8852
8865
|
if (ctx.dryRun) {
|
|
8853
8866
|
ctx.say(` (preview) ${consumer.rel}: would commit, push ${branch} and open a pull request against ${consumer.branch}`);
|
|
8854
8867
|
return;
|
|
8855
8868
|
}
|
|
8856
|
-
if ((await ctx.read("git", ["status", "--porcelain"], { cwd:
|
|
8869
|
+
if ((await ctx.read("git", ["status", "--porcelain"], { cwd: dir })).trim() === "") {
|
|
8857
8870
|
ctx.say(`\u2022 ${consumer.rel}: nothing changed \u2014 no pull request`);
|
|
8858
8871
|
return;
|
|
8859
8872
|
}
|
|
8860
|
-
const head = `${consumer.origin} ${branch}`;
|
|
8873
|
+
const head = `${consumer.origin ?? "origin"} ${branch}`;
|
|
8861
8874
|
try {
|
|
8862
8875
|
await ctx.apply([
|
|
8863
|
-
run("git", ["add", "-A"], { cwd:
|
|
8864
|
-
run("git", ["commit", "--quiet", "--no-verify", "-m", `chore: train ${train}`], { cwd:
|
|
8876
|
+
run("git", ["add", "-A"], { cwd: dir }),
|
|
8877
|
+
run("git", ["commit", "--quiet", "--no-verify", "-m", `chore: train ${train}`], { cwd: dir })
|
|
8865
8878
|
]);
|
|
8866
8879
|
await ctx.read("git", ["fetch", "--quiet", "origin", `+refs/heads/${branch}:refs/remotes/origin/${branch}`], {
|
|
8867
|
-
cwd:
|
|
8880
|
+
cwd: dir
|
|
8868
8881
|
}).catch(() => "");
|
|
8869
8882
|
await ctx.apply([
|
|
8870
|
-
run("git", ["push", "--quiet", "--force-with-lease", "origin", `HEAD:refs/heads/${branch}`], {
|
|
8883
|
+
run("git", ["push", "--quiet", "--no-verify", "--force-with-lease", "origin", `HEAD:refs/heads/${branch}`], {
|
|
8884
|
+
cwd: dir
|
|
8885
|
+
})
|
|
8871
8886
|
]);
|
|
8872
8887
|
} catch (error) {
|
|
8873
8888
|
ctx.say(` \u2717 FAILED \u2014 push to ${head}${detail(error)}`);
|
|
8874
8889
|
summary.failed.push(consumer.rel);
|
|
8875
8890
|
return;
|
|
8876
8891
|
}
|
|
8877
|
-
const existing =
|
|
8878
|
-
"pr",
|
|
8879
|
-
"list",
|
|
8880
|
-
"--head",
|
|
8881
|
-
branch,
|
|
8882
|
-
"--base",
|
|
8883
|
-
consumer.branch,
|
|
8884
|
-
"--state",
|
|
8885
|
-
"open",
|
|
8886
|
-
"--json",
|
|
8887
|
-
"url",
|
|
8888
|
-
"--jq",
|
|
8889
|
-
".[0].url"
|
|
8890
|
-
], { cwd: clone }).catch(() => "")).trim();
|
|
8892
|
+
const existing = await openPullUrl(ctx, consumer, dir, train);
|
|
8891
8893
|
if (existing) {
|
|
8892
8894
|
ctx.say(`\u2022 ${consumer.rel}: pull request updated: ${existing}`);
|
|
8893
8895
|
summary.pulls.push(existing);
|
|
@@ -8898,7 +8900,7 @@ async function openPull(ctx, consumer, clone, train, summary) {
|
|
|
8898
8900
|
const body2 = [
|
|
8899
8901
|
`Train ${train} (\xA7 1.5 PLX): \`copier update\` at ${sites.join(", ")}, the marker, the lockfile where manifests moved, and the workflow refs.`,
|
|
8900
8902
|
conflicted.length > 0 ? `Conflicts to resolve, committed with their markers: ${conflicted.join(", ")}.` : "Merged clean at every site.",
|
|
8901
|
-
|
|
8903
|
+
`Opened by ${take.opener}.`
|
|
8902
8904
|
].join("\n\n");
|
|
8903
8905
|
try {
|
|
8904
8906
|
await ctx.apply([
|
|
@@ -8913,29 +8915,14 @@ async function openPull(ctx, consumer, clone, train, summary) {
|
|
|
8913
8915
|
`chore: train ${train}`,
|
|
8914
8916
|
"--body",
|
|
8915
8917
|
body2
|
|
8916
|
-
], { cwd:
|
|
8918
|
+
], { cwd: dir })
|
|
8917
8919
|
]);
|
|
8918
8920
|
} catch (error) {
|
|
8919
8921
|
ctx.say(` \u2717 FAILED \u2014 pull request${detail(error)}`);
|
|
8920
8922
|
summary.failed.push(consumer.rel);
|
|
8921
8923
|
return;
|
|
8922
8924
|
}
|
|
8923
|
-
const url =
|
|
8924
|
-
"pr",
|
|
8925
|
-
"list",
|
|
8926
|
-
"--head",
|
|
8927
|
-
branch,
|
|
8928
|
-
"--base",
|
|
8929
|
-
consumer.branch,
|
|
8930
|
-
"--state",
|
|
8931
|
-
"open",
|
|
8932
|
-
"--json",
|
|
8933
|
-
"url",
|
|
8934
|
-
"--jq",
|
|
8935
|
-
".[0].url"
|
|
8936
|
-
], {
|
|
8937
|
-
cwd: clone
|
|
8938
|
-
}).catch(() => "")).trim();
|
|
8925
|
+
const url = await openPullUrl(ctx, consumer, dir, train);
|
|
8939
8926
|
ctx.say(`\u2022 ${consumer.rel}: pull request opened${url ? `: ${url}` : ""}`);
|
|
8940
8927
|
summary.pulls.push(url || `${consumer.rel} ${branch}`);
|
|
8941
8928
|
}
|
|
@@ -9215,7 +9202,7 @@ async function awaitPublished(ctx, next, wait) {
|
|
|
9215
9202
|
break;
|
|
9216
9203
|
if (Date.now() - started >= wait.timeout)
|
|
9217
9204
|
throw new Error(`${missing.join(", ")} not on npm at ${next} after ${Math.round(wait.timeout / 6e4)} min \u2014 check the release workflow (gh run list -R plexus-ms/${PRIMITIVES})`);
|
|
9218
|
-
await new Promise((
|
|
9205
|
+
await new Promise((resolve11) => setTimeout(resolve11, wait.interval));
|
|
9219
9206
|
}
|
|
9220
9207
|
ctx.say(` \u2713 ${list2} on npm`);
|
|
9221
9208
|
}
|
|
@@ -9252,6 +9239,8 @@ async function planBookkeeping(ctx, train, repos2, next, today2) {
|
|
|
9252
9239
|
await edit(r, f, `plx pin \u2192 ${next}`, (t) => bumpPlxPin(t, next));
|
|
9253
9240
|
for (const f of await grep(r, `"${CONFIG_PIN}"`, ["*package.json", "*package.json.jinja"]))
|
|
9254
9241
|
await edit(r, f, `${CONFIG_PIN} pin \u2192 ${next}`, (t) => bumpConfigPin(t, next));
|
|
9242
|
+
for (const f of await grep(r, `"${CONFIG_PIN}"`, ["*pnpm-workspace.yaml", "*pnpm-workspace.yaml.jinja"]))
|
|
9243
|
+
await edit(r, f, `${CONFIG_PIN} catalog entry \u2192 ${next}`, (t) => bumpCatalogPin(t, next));
|
|
9255
9244
|
}
|
|
9256
9245
|
const comp = repo("compendium");
|
|
9257
9246
|
if (comp) {
|
|
@@ -9275,6 +9264,76 @@ async function planBookkeeping(ctx, train, repos2, next, today2) {
|
|
|
9275
9264
|
return plan;
|
|
9276
9265
|
}
|
|
9277
9266
|
|
|
9267
|
+
// ../lib/dist/update.js
|
|
9268
|
+
import { basename, join as join11 } from "node:path";
|
|
9269
|
+
var PRESETS = TRAIN_REPOS.filter((r) => r.startsWith("preset-"));
|
|
9270
|
+
async function update(ctx, options) {
|
|
9271
|
+
const { repo } = options;
|
|
9272
|
+
const name = basename(repo);
|
|
9273
|
+
if (options.train !== void 0 && !isTrainVersion(options.train))
|
|
9274
|
+
throw new UsageError(`'${options.train}' is not plain semver (X.Y.Z, no v)`);
|
|
9275
|
+
const tree = await overview(ctx, repo);
|
|
9276
|
+
const consumer = (await findConsumers(ctx, tree, PRESETS)).find((c) => c.path === tree.path);
|
|
9277
|
+
if (!consumer)
|
|
9278
|
+
throw new Error(`${repo} is not a repo generated from the presets: no ${ANSWERS_FILE} at its root naming one of ${PRESETS.map(presetSource).join(", ")}`);
|
|
9279
|
+
if (consumer.skipped)
|
|
9280
|
+
throw new Error(`${name}: ${consumer.skipped}`);
|
|
9281
|
+
consumer.rel = name;
|
|
9282
|
+
for (const s of consumer.sites)
|
|
9283
|
+
s.rel = s.rel === "." ? name : join11(name, s.rel);
|
|
9284
|
+
const root = consumer.sites.find((s) => s.path === consumer.path);
|
|
9285
|
+
if (!root)
|
|
9286
|
+
throw new Error(`${name}: its root is not a site of a repo preset`);
|
|
9287
|
+
await preflightForge(ctx);
|
|
9288
|
+
const train = options.train ?? await newestTrain(ctx, root.preset);
|
|
9289
|
+
const summary = emptySummary(train, ctx.dryRun);
|
|
9290
|
+
const recorded = await Promise.all(consumer.sites.map(async (s) => parseAnswers(await ctx.readFile(join11(s.path, ANSWERS_FILE))).commit));
|
|
9291
|
+
if (recorded.every((c) => c === train)) {
|
|
9292
|
+
ctx.say(`\u2022 ${name}: already at ${train}`);
|
|
9293
|
+
return summary;
|
|
9294
|
+
}
|
|
9295
|
+
const past = recorded.find((c) => c && isTrainVersion(c) && compareTrains(c, train) > 0);
|
|
9296
|
+
if (past)
|
|
9297
|
+
throw new Error(`${name} is at ${past}, past ${train}; a repo is never taken back to an older train`);
|
|
9298
|
+
const branch = trainBranch(train);
|
|
9299
|
+
const onOrigin = await ctx.read("git", ["ls-remote", "--exit-code", "--heads", "origin", `refs/heads/${branch}`], { cwd: repo }).then(() => true).catch(() => false);
|
|
9300
|
+
if (onOrigin) {
|
|
9301
|
+
const url = await openPullUrl(ctx, consumer, repo, train);
|
|
9302
|
+
ctx.say(`\u2022 ${name}: ${branch} is already on origin${url ? ` (${url})` : ""} \u2014 nothing to do; delete the branch to pull the train again`);
|
|
9303
|
+
if (url)
|
|
9304
|
+
summary.pulls.push(url);
|
|
9305
|
+
return summary;
|
|
9306
|
+
}
|
|
9307
|
+
const scratch = (await ctx.stage(run("mktemp", ["-d"])))?.trim() ?? "";
|
|
9308
|
+
const dir = join11(scratch, name);
|
|
9309
|
+
try {
|
|
9310
|
+
ctx.say(`\u2192 ${name}: worktree of origin/${consumer.branch}`);
|
|
9311
|
+
try {
|
|
9312
|
+
await ctx.stage(run("git", ["fetch", "--quiet", "origin", consumer.branch], { cwd: repo }));
|
|
9313
|
+
await ctx.stage(run("git", ["worktree", "add", "--quiet", "--detach", dir, `origin/${consumer.branch}`], { cwd: repo }));
|
|
9314
|
+
} catch (error) {
|
|
9315
|
+
ctx.say(` \u2717 FAILED \u2014 worktree of origin/${consumer.branch}${detail(error)}`);
|
|
9316
|
+
summary.failed.push(name);
|
|
9317
|
+
return summary;
|
|
9318
|
+
}
|
|
9319
|
+
await takeTrain(ctx, consumer, dir, { train, ref: train, opener: "`plx update`" }, summary);
|
|
9320
|
+
} finally {
|
|
9321
|
+
await ctx.stage(run("git", ["worktree", "remove", "--force", dir], { cwd: repo })).catch(() => void 0);
|
|
9322
|
+
if (scratch)
|
|
9323
|
+
await ctx.stage(run("rm", ["-rf", scratch]));
|
|
9324
|
+
}
|
|
9325
|
+
return summary;
|
|
9326
|
+
}
|
|
9327
|
+
async function newestTrain(ctx, preset) {
|
|
9328
|
+
const source = presetSource(preset).replace(/^gh:/, "");
|
|
9329
|
+
const tag = (await ctx.read("gh", ["api", `repos/${source}/releases/latest`, "--jq", ".tag_name"]).catch(() => "")).trim();
|
|
9330
|
+
if (!tag)
|
|
9331
|
+
throw new Error(`no release of ${source} found \u2014 pass the train explicitly (--train <version>)`);
|
|
9332
|
+
if (!isTrainVersion(tag))
|
|
9333
|
+
throw new Error(`the newest release of ${source} is '${tag}', not a train`);
|
|
9334
|
+
return tag;
|
|
9335
|
+
}
|
|
9336
|
+
|
|
9278
9337
|
// src/program.ts
|
|
9279
9338
|
import { createRequire } from "node:module";
|
|
9280
9339
|
|
|
@@ -10221,13 +10280,13 @@ import { AsyncResource as AsyncResource3 } from "node:async_hooks";
|
|
|
10221
10280
|
|
|
10222
10281
|
// ../../node_modules/.pnpm/@inquirer+type@4.2.1_@types+node@26.6.5/node_modules/@inquirer/type/dist/utils.js
|
|
10223
10282
|
function withResolver() {
|
|
10224
|
-
let
|
|
10283
|
+
let resolve11;
|
|
10225
10284
|
let reject;
|
|
10226
10285
|
const promise = new Promise((res, rej) => {
|
|
10227
|
-
|
|
10286
|
+
resolve11 = res;
|
|
10228
10287
|
reject = rej;
|
|
10229
10288
|
});
|
|
10230
|
-
return { promise, resolve:
|
|
10289
|
+
return { promise, resolve: resolve11, reject };
|
|
10231
10290
|
}
|
|
10232
10291
|
|
|
10233
10292
|
// ../../node_modules/.pnpm/@inquirer+core@12.0.6_@types+node@26.6.5/node_modules/@inquirer/core/dist/lib/create-prompt.js
|
|
@@ -10606,7 +10665,7 @@ function createPrompt(view) {
|
|
|
10606
10665
|
});
|
|
10607
10666
|
output.mute();
|
|
10608
10667
|
const screen = new ScreenManager(rl);
|
|
10609
|
-
const { promise, resolve:
|
|
10668
|
+
const { promise, resolve: resolve11, reject } = withResolver();
|
|
10610
10669
|
return withHooks(rl, (cycle) => {
|
|
10611
10670
|
const clearEffects = AsyncResource3.bind(() => effectScheduler.clearAll());
|
|
10612
10671
|
const settlePrompt = (settle) => {
|
|
@@ -10617,7 +10676,7 @@ function createPrompt(view) {
|
|
|
10617
10676
|
reject(error);
|
|
10618
10677
|
}
|
|
10619
10678
|
};
|
|
10620
|
-
const resolvePrompt = (value) => settlePrompt(() =>
|
|
10679
|
+
const resolvePrompt = (value) => settlePrompt(() => resolve11(value));
|
|
10621
10680
|
const rejectPrompt = (error) => settlePrompt(() => reject(error));
|
|
10622
10681
|
const promptPromise = Object.assign(promise.finally(() => {
|
|
10623
10682
|
cleanups.forEach((cleanup) => cleanup());
|
|
@@ -14206,7 +14265,7 @@ function register4(program2) {
|
|
|
14206
14265
|
|
|
14207
14266
|
// src/commands/doctor.ts
|
|
14208
14267
|
import { existsSync } from "node:fs";
|
|
14209
|
-
import { join as
|
|
14268
|
+
import { join as join12, resolve as resolve5 } from "node:path";
|
|
14210
14269
|
|
|
14211
14270
|
// src/style.ts
|
|
14212
14271
|
function style(enabled) {
|
|
@@ -14224,8 +14283,8 @@ function register5(program2) {
|
|
|
14224
14283
|
).option("--color", "colour the report even when piped").option("--no-color", "never colour the report").action(async (dir, opts, command) => {
|
|
14225
14284
|
const ctx = contextOf2(command);
|
|
14226
14285
|
const root = resolve5(dir);
|
|
14227
|
-
const compendium = resolve5(opts.compendium ??
|
|
14228
|
-
if (!existsSync(
|
|
14286
|
+
const compendium = resolve5(opts.compendium ?? join12(root, WORKSPACE_COMPENDIUM));
|
|
14287
|
+
if (!existsSync(join12(compendium, "contracts")))
|
|
14229
14288
|
throw new Error(
|
|
14230
14289
|
`no compendium at ${compendium}; pass --compendium <path to a checkout of plexus-ms/compendium>`
|
|
14231
14290
|
);
|
|
@@ -14315,7 +14374,7 @@ function register7(program2) {
|
|
|
14315
14374
|
if (!isClean(result)) process.exitCode = 1;
|
|
14316
14375
|
});
|
|
14317
14376
|
}
|
|
14318
|
-
function renderSummary(s, realCommand) {
|
|
14377
|
+
function renderSummary(s, realCommand, verb = "propagate") {
|
|
14319
14378
|
const lines = ["\u2500\u2500 summary \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"];
|
|
14320
14379
|
for (const p of s.clean) lines.push(` \u2713 clean: ${p}`);
|
|
14321
14380
|
for (const p of s.conflicted) lines.push(` \u2717 conflicts: ${p}`);
|
|
@@ -14329,7 +14388,7 @@ function renderSummary(s, realCommand) {
|
|
|
14329
14388
|
} else if (clean) {
|
|
14330
14389
|
lines.push("\u2713 done \u2014 review and merge the pull requests");
|
|
14331
14390
|
}
|
|
14332
|
-
if (!clean) lines.push(
|
|
14391
|
+
if (!clean) lines.push(`\u2717 needs attention \u2014 see above; fix the consumer, then re-run ${verb}`);
|
|
14333
14392
|
return lines.join("\n");
|
|
14334
14393
|
}
|
|
14335
14394
|
|
|
@@ -14344,13 +14403,13 @@ function register8(program2) {
|
|
|
14344
14403
|
}
|
|
14345
14404
|
|
|
14346
14405
|
// src/commands/requirements.ts
|
|
14347
|
-
import { join as
|
|
14406
|
+
import { join as join13, resolve as resolve9 } from "node:path";
|
|
14348
14407
|
function register9(program2) {
|
|
14349
14408
|
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) => {
|
|
14350
14409
|
const ctx = contextOf2(command);
|
|
14351
14410
|
const compendium = resolve9(dir);
|
|
14352
14411
|
if (opts.extract) {
|
|
14353
|
-
ctx.result(extractRequirements(await ctx.readFile(
|
|
14412
|
+
ctx.result(extractRequirements(await ctx.readFile(join13(compendium, opts.extract))));
|
|
14354
14413
|
return;
|
|
14355
14414
|
}
|
|
14356
14415
|
const check = await checkCompendium(ctx, compendium);
|
|
@@ -14363,12 +14422,26 @@ function renderCheck(c) {
|
|
|
14363
14422
|
return `\u2713 the map, ${c.contracts} contracts and ${c.approaches} approaches are well-formed`;
|
|
14364
14423
|
}
|
|
14365
14424
|
|
|
14425
|
+
// src/commands/update.ts
|
|
14426
|
+
import { resolve as resolve10 } from "node:path";
|
|
14427
|
+
function register10(program2) {
|
|
14428
|
+
program2.command("update").description(
|
|
14429
|
+
"bring this repo up to a train, as a pull request on plexus/<version>: copier update at every site, the marker, the lockfile, the workflow refs"
|
|
14430
|
+
).argument("[dir]", "a repo generated from the presets", ".").option("--train <version>", "the train to take (default: the preset's newest release)").action(async (dir, opts, command) => {
|
|
14431
|
+
const ctx = contextOf2(command);
|
|
14432
|
+
const result = await update(ctx, { repo: resolve10(dir), train: opts.train });
|
|
14433
|
+
ctx.result(result, (r) => renderSummary(r, `plx -y update --train ${r.train} ${dir}`, "update"));
|
|
14434
|
+
if (!isClean(result)) process.exitCode = 1;
|
|
14435
|
+
});
|
|
14436
|
+
}
|
|
14437
|
+
|
|
14366
14438
|
// src/commands/index.ts
|
|
14367
14439
|
var commands = [
|
|
14368
14440
|
register6,
|
|
14369
14441
|
register5,
|
|
14370
14442
|
register7,
|
|
14371
14443
|
register8,
|
|
14444
|
+
register10,
|
|
14372
14445
|
register9,
|
|
14373
14446
|
register2,
|
|
14374
14447
|
register,
|
|
@@ -14383,7 +14456,7 @@ function buildProgram() {
|
|
|
14383
14456
|
program2.addOption(new Option("-n, --dry-run", "print every effect this run would have, perform none").env("PLX_DRY_RUN")).addOption(
|
|
14384
14457
|
new Option("-v, --verbose", "say more: once for reads and inputs, twice for command output").argParser((_value, previous) => previous + 1).default(0)
|
|
14385
14458
|
).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"));
|
|
14386
|
-
for (const
|
|
14459
|
+
for (const register11 of commands) register11(program2);
|
|
14387
14460
|
return program2;
|
|
14388
14461
|
}
|
|
14389
14462
|
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.102.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",
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"node": ">=24.22.1"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@inquirer/prompts": "^8.7.
|
|
27
|
-
"@plexus-ms/lib": "0.
|
|
26
|
+
"@inquirer/prompts": "^8.7.5",
|
|
27
|
+
"@plexus-ms/lib": "0.102.0",
|
|
28
28
|
"commander": "^15.0.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|