@4pm/cli 1.19.3 → 1.19.7
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/index.js +75 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -37503,9 +37503,10 @@ var activityHeartbeatSchema = external_exports.object({
|
|
|
37503
37503
|
});
|
|
37504
37504
|
|
|
37505
37505
|
// src/core/scaffold.ts
|
|
37506
|
-
import { cp, mkdir, rm, writeFile } from "fs/promises";
|
|
37506
|
+
import { cp, mkdir, mkdtemp, rm, writeFile } from "fs/promises";
|
|
37507
37507
|
import { existsSync as existsSync4 } from "fs";
|
|
37508
37508
|
import { execFile as execFile2 } from "child_process";
|
|
37509
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
37509
37510
|
import { dirname as dirname3, join as join6, resolve } from "path";
|
|
37510
37511
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
37511
37512
|
import { promisify as promisify2 } from "util";
|
|
@@ -37595,7 +37596,7 @@ function submodulesOf(repos) {
|
|
|
37595
37596
|
const primary = singleRepo(repos);
|
|
37596
37597
|
return repos.filter((r) => r !== primary && !!r.url && !!(r.subdir ?? "").trim());
|
|
37597
37598
|
}
|
|
37598
|
-
async function attachSubmodules(root, submodules, emit) {
|
|
37599
|
+
async function attachSubmodules(root, submodules, emit, createMissingBranch = false) {
|
|
37599
37600
|
if (submodules.length === 0) return;
|
|
37600
37601
|
if (!existsSync4(join6(root, ".git"))) return;
|
|
37601
37602
|
let added = 0;
|
|
@@ -37610,8 +37611,33 @@ async function attachSubmodules(root, submodules, emit) {
|
|
|
37610
37611
|
}
|
|
37611
37612
|
continue;
|
|
37612
37613
|
}
|
|
37614
|
+
const abs = join6(root, dir2);
|
|
37615
|
+
if (existsSync4(abs)) {
|
|
37616
|
+
emit("submodule", `Removing placeholder ${dir2} before attaching the submodule\u2026`);
|
|
37617
|
+
await rm(abs, { recursive: true, force: true });
|
|
37618
|
+
}
|
|
37613
37619
|
emit("submodule", `Adding submodule ${sub.url} \u2192 ${dir2}\u2026`);
|
|
37614
37620
|
const b2 = (sub.branch ?? "").trim();
|
|
37621
|
+
if (b2 && createMissingBranch && !await ensureRemoteBranch(sub.url, b2, emit)) {
|
|
37622
|
+
try {
|
|
37623
|
+
await run2("git", ["submodule", "add", sub.url, dir2], { cwd: root, timeout: 12e4 });
|
|
37624
|
+
await run2("git", ["config", "-f", ".gitmodules", `submodule.${dir2}.branch`, b2], { cwd: root, timeout: 3e4 });
|
|
37625
|
+
try {
|
|
37626
|
+
await run2("git", ["checkout", "-b", b2], { cwd: join6(root, dir2), timeout: 3e4 });
|
|
37627
|
+
await run2("git", ["push", "-u", "origin", b2], { cwd: join6(root, dir2), timeout: 12e4 });
|
|
37628
|
+
} catch (pushErr) {
|
|
37629
|
+
const pmsg = pushErr instanceof Error ? pushErr.message : String(pushErr);
|
|
37630
|
+
emit("git-branch-push-failed", `Submodule ${dir2}: branch "${b2}" created locally but the push failed: ${pmsg}`);
|
|
37631
|
+
}
|
|
37632
|
+
added++;
|
|
37633
|
+
continue;
|
|
37634
|
+
} catch (err) {
|
|
37635
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
37636
|
+
if (!/already exists|already registered|in the index/i.test(msg)) {
|
|
37637
|
+
throw new Error(`git submodule add ${dir2} failed: ${msg}`);
|
|
37638
|
+
}
|
|
37639
|
+
}
|
|
37640
|
+
}
|
|
37615
37641
|
try {
|
|
37616
37642
|
await run2("git", ["submodule", "add", ...b2 ? ["-b", b2] : [], sub.url, dir2], { cwd: root, timeout: 12e4 });
|
|
37617
37643
|
added++;
|
|
@@ -37632,8 +37658,13 @@ async function attachSubmodules(root, submodules, emit) {
|
|
|
37632
37658
|
}
|
|
37633
37659
|
async function commitAndPushSubmodules(root, submodules, emit) {
|
|
37634
37660
|
const dirs = submodules.map((s3) => (s3.subdir ?? "").trim()).filter(Boolean);
|
|
37661
|
+
const addPaths = [
|
|
37662
|
+
...existsSync4(join6(root, ".gitmodules")) ? [".gitmodules"] : [],
|
|
37663
|
+
...dirs.filter((d) => existsSync4(join6(root, d)))
|
|
37664
|
+
];
|
|
37665
|
+
if (addPaths.length === 0) return;
|
|
37635
37666
|
emit("submodule", "Committing .gitmodules\u2026");
|
|
37636
|
-
await run2("git", ["add",
|
|
37667
|
+
await run2("git", ["add", ...addPaths], { cwd: root, timeout: 6e4 });
|
|
37637
37668
|
const staged = (await run2("git", ["diff", "--cached", "--name-only"], { cwd: root, timeout: 3e4 })).stdout.trim();
|
|
37638
37669
|
if (!staged) return;
|
|
37639
37670
|
const commitArgs = ["commit", "-m", "chore: add git submodules (4PM)"];
|
|
@@ -37654,6 +37685,31 @@ function cloneArgs(url, dest, branch) {
|
|
|
37654
37685
|
const b2 = (branch ?? "").trim();
|
|
37655
37686
|
return b2 ? ["clone", "-b", b2, url, dest] : ["clone", url, dest];
|
|
37656
37687
|
}
|
|
37688
|
+
async function remoteHasBranch(url, branch) {
|
|
37689
|
+
try {
|
|
37690
|
+
const { stdout } = await run2("git", ["ls-remote", "--heads", url, branch], { timeout: 3e4 });
|
|
37691
|
+
return stdout.trim().length > 0;
|
|
37692
|
+
} catch {
|
|
37693
|
+
return false;
|
|
37694
|
+
}
|
|
37695
|
+
}
|
|
37696
|
+
async function ensureRemoteBranch(url, branch, emit) {
|
|
37697
|
+
if (await remoteHasBranch(url, branch)) return true;
|
|
37698
|
+
const tmp = await mkdtemp(join6(tmpdir2(), "4pm-branch-"));
|
|
37699
|
+
try {
|
|
37700
|
+
emit("git", `Branch "${branch}" not found on ${url} \u2014 creating it from the default branch\u2026`);
|
|
37701
|
+
await run2("git", ["clone", "--depth", "1", url, tmp], { timeout: 12e4 });
|
|
37702
|
+
await run2("git", ["checkout", "-b", branch], { cwd: tmp, timeout: 3e4 });
|
|
37703
|
+
await run2("git", ["push", "-u", "origin", branch], { cwd: tmp, timeout: 12e4 });
|
|
37704
|
+
return true;
|
|
37705
|
+
} catch (err) {
|
|
37706
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
37707
|
+
emit("git-branch-push-failed", `Could not create branch "${branch}" on ${url}: ${msg}`);
|
|
37708
|
+
return false;
|
|
37709
|
+
} finally {
|
|
37710
|
+
await rm(tmp, { recursive: true, force: true });
|
|
37711
|
+
}
|
|
37712
|
+
}
|
|
37657
37713
|
async function updateRepo(dir2, branch, emit) {
|
|
37658
37714
|
const b2 = (branch ?? "").trim();
|
|
37659
37715
|
emit("git", `Updating repository (fetch + ${b2 ? `checkout ${b2} + ` : ""}fast-forward pull)\u2026`);
|
|
@@ -37683,6 +37739,20 @@ async function provisionRepo(target, repo, emit, opts = {}) {
|
|
|
37683
37739
|
}
|
|
37684
37740
|
return;
|
|
37685
37741
|
}
|
|
37742
|
+
const b2 = (repo.branch ?? "").trim();
|
|
37743
|
+
if (b2 && opts.createMissingBranch && !await remoteHasBranch(repo.url, b2)) {
|
|
37744
|
+
emit("git", `Cloning ${repo.url} (default branch) to create "${b2}"\u2026`);
|
|
37745
|
+
await run2("git", ["clone", repo.url, target], { timeout: 12e4 });
|
|
37746
|
+
await run2("git", ["checkout", "-b", b2], { cwd: target, timeout: 3e4 });
|
|
37747
|
+
try {
|
|
37748
|
+
await run2("git", ["push", "-u", "origin", b2], { cwd: target, timeout: 12e4 });
|
|
37749
|
+
emit("git", `Created and pushed branch "${b2}".`);
|
|
37750
|
+
} catch (err) {
|
|
37751
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
37752
|
+
emit("git-branch-push-failed", `Branch "${b2}" created locally but the push failed: ${msg} \u2014 push it when credentials are available.`);
|
|
37753
|
+
}
|
|
37754
|
+
return;
|
|
37755
|
+
}
|
|
37686
37756
|
emit("git", `Cloning ${repo.url}\u2026`);
|
|
37687
37757
|
await run2("git", cloneArgs(repo.url, target, repo.branch), { timeout: 12e4 });
|
|
37688
37758
|
}
|
|
@@ -37716,9 +37786,9 @@ async function scaffoldProject(payload, profileDir2, onProgress) {
|
|
|
37716
37786
|
const repo = singleRepo(reposOf(payload.spec));
|
|
37717
37787
|
if (!repo) throw new Error("A project must declare one repo (ADR-0314).");
|
|
37718
37788
|
emit("git", "Cloning repository\u2026");
|
|
37719
|
-
await provisionRepo(target, repo, emit);
|
|
37789
|
+
await provisionRepo(target, repo, emit, { createMissingBranch: true });
|
|
37720
37790
|
await scaffoldRepo(target, payload.projectName, payload.spec, emit);
|
|
37721
|
-
await attachSubmodules(target, submodulesOf(reposOf(payload.spec)), emit);
|
|
37791
|
+
await attachSubmodules(target, submodulesOf(reposOf(payload.spec)), emit, true);
|
|
37722
37792
|
emit("version", "Writing .4pm/.4pm.json\u2026");
|
|
37723
37793
|
await writeTemplateMarker(target);
|
|
37724
37794
|
onProgress?.({ projectId: payload.projectId, step: "done", message: "Scaffold complete.", done: true });
|