@biffo/cli 0.50.2 → 0.51.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/core.version +1 -1
- package/dist/index.js +271 -178
- package/package.json +1 -1
package/core.version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.51.0
|
package/dist/index.js
CHANGED
|
@@ -337,8 +337,9 @@ async function runCoreStatus(options) {
|
|
|
337
337
|
|
|
338
338
|
// src/commands/core-upgrade.ts
|
|
339
339
|
import { execSync as execSync2 } from "child_process";
|
|
340
|
-
import { join as
|
|
340
|
+
import { join as join9, resolve as resolve3 } from "path";
|
|
341
341
|
import chalk4 from "chalk";
|
|
342
|
+
import { execa as execa3 } from "execa";
|
|
342
343
|
import { Command as Command3 } from "commander";
|
|
343
344
|
|
|
344
345
|
// src/adapters/git/index.ts
|
|
@@ -1467,7 +1468,10 @@ var gitMergeFile = async (base, ours, theirs) => {
|
|
|
1467
1468
|
writeFileSync3(b, base);
|
|
1468
1469
|
writeFileSync3(o, ours);
|
|
1469
1470
|
writeFileSync3(t, theirs);
|
|
1470
|
-
const result = await execa2("git", ["merge-file", "-p", o, b, t], {
|
|
1471
|
+
const result = await execa2("git", ["merge-file", "-p", o, b, t], {
|
|
1472
|
+
reject: false,
|
|
1473
|
+
stripFinalNewline: false
|
|
1474
|
+
});
|
|
1471
1475
|
if (typeof result.exitCode !== "number" || result.exitCode < 0) {
|
|
1472
1476
|
throw new Error(`git merge-file failed: ${result.stderr}`);
|
|
1473
1477
|
}
|
|
@@ -1630,6 +1634,45 @@ var GLOBAL_DISPATCH_WORKFLOW_PATHS = [
|
|
|
1630
1634
|
".github/workflows/deploy-global.yml"
|
|
1631
1635
|
];
|
|
1632
1636
|
|
|
1637
|
+
// src/lib/lockfile-refresh.ts
|
|
1638
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1639
|
+
import { join as join8 } from "path";
|
|
1640
|
+
var LOCKFILE_TRIGGERS = [
|
|
1641
|
+
{
|
|
1642
|
+
manifest: "package.json",
|
|
1643
|
+
lockfile: "pnpm-lock.yaml",
|
|
1644
|
+
command: ["pnpm", "install", "--lockfile-only"],
|
|
1645
|
+
ecosystem: "pnpm"
|
|
1646
|
+
},
|
|
1647
|
+
{
|
|
1648
|
+
manifest: "pyproject.toml",
|
|
1649
|
+
lockfile: "uv.lock",
|
|
1650
|
+
command: ["uv", "lock"],
|
|
1651
|
+
ecosystem: "uv"
|
|
1652
|
+
}
|
|
1653
|
+
];
|
|
1654
|
+
function lockfilesNeedingRefresh(changedPaths, instanceDir, triggers = LOCKFILE_TRIGGERS) {
|
|
1655
|
+
return triggers.filter((t) => {
|
|
1656
|
+
const touched = changedPaths.some((p) => p === t.manifest || p.endsWith(`/${t.manifest}`));
|
|
1657
|
+
return touched && existsSync6(join8(instanceDir, t.lockfile));
|
|
1658
|
+
});
|
|
1659
|
+
}
|
|
1660
|
+
async function refreshLockfiles(instanceDir, triggers, run) {
|
|
1661
|
+
const outcomes = [];
|
|
1662
|
+
for (const trigger of triggers) {
|
|
1663
|
+
const result = await run(trigger.command, instanceDir);
|
|
1664
|
+
const outcome = { trigger, ok: result.ok };
|
|
1665
|
+
if (result.error !== void 0) outcome.error = result.error;
|
|
1666
|
+
outcomes.push(outcome);
|
|
1667
|
+
}
|
|
1668
|
+
return outcomes;
|
|
1669
|
+
}
|
|
1670
|
+
function describeFailures(outcomes) {
|
|
1671
|
+
return outcomes.filter((o) => !o.ok).map(
|
|
1672
|
+
(o) => `${o.trigger.lockfile} could not be refreshed (${o.trigger.ecosystem}): ${o.error ?? "unknown error"}. Run \`${o.trigger.command.join(" ")}\` in the instance and commit the result, or CI will fail on a lockfile that disagrees with ${o.trigger.manifest}.`
|
|
1673
|
+
);
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1633
1676
|
// src/commands/core-upgrade.ts
|
|
1634
1677
|
var MISSING_TEMPLATE_ROOT_GUIDANCE2 = "Pass --template-repo <path> to a biffo-template git checkout, e.g. `biffo core upgrade --template-repo /path/to/biffo-template`.";
|
|
1635
1678
|
var coreUpgradeCommand = new Command3("upgrade").description("Three-way-merge template-owned files for a core upgrade; preview it or open a PR").option("--cwd <path>", "Instance repo root to upgrade (defaults to the current directory)").option(
|
|
@@ -1700,9 +1743,9 @@ async function runCoreUpgradeResolved(options, deps, cleanups) {
|
|
|
1700
1743
|
let toVersion;
|
|
1701
1744
|
if (options.theirsDir) {
|
|
1702
1745
|
theirsDir = options.theirsDir;
|
|
1703
|
-
toVersion = readCoreVersionFile(
|
|
1746
|
+
toVersion = readCoreVersionFile(join9(theirsDir, "core.version"));
|
|
1704
1747
|
} else {
|
|
1705
|
-
const workingVersion = readCoreVersionFile(
|
|
1748
|
+
const workingVersion = readCoreVersionFile(join9(templateRepo, "core.version"));
|
|
1706
1749
|
toVersion = options.toVersion ?? workingVersion;
|
|
1707
1750
|
if (toVersion === workingVersion) {
|
|
1708
1751
|
theirsDir = templateRepo;
|
|
@@ -1716,7 +1759,7 @@ async function runCoreUpgradeResolved(options, deps, cleanups) {
|
|
|
1716
1759
|
let fromVersion;
|
|
1717
1760
|
if (options.baseDir) {
|
|
1718
1761
|
baseDir = options.baseDir;
|
|
1719
|
-
fromVersion = readCoreVersionFile(
|
|
1762
|
+
fromVersion = readCoreVersionFile(join9(baseDir, "core.version"));
|
|
1720
1763
|
} else {
|
|
1721
1764
|
if (instanceVersion === null) {
|
|
1722
1765
|
throw new Error(
|
|
@@ -1791,6 +1834,7 @@ async function applyAndOpenPr(options, deps, plan, migrations, fromVersion, toVe
|
|
|
1791
1834
|
4,
|
|
1792
1835
|
`Applied ${applied.written.length} change(s), ${applied.deleted.length} deletion(s), ${carried.length} new migration(s)`
|
|
1793
1836
|
);
|
|
1837
|
+
const lockfiles = await refreshInstanceLockfiles(options.cwd, plan, deps);
|
|
1794
1838
|
await git.add(options.cwd, ["-A"]);
|
|
1795
1839
|
await git.commit(options.cwd, `chore(core): upgrade template core ${fromVersion} -> ${toVersion}`);
|
|
1796
1840
|
log.step(3, 4, `Pushing ${branch}`);
|
|
@@ -1806,7 +1850,7 @@ async function applyAndOpenPr(options, deps, plan, migrations, fromVersion, toVe
|
|
|
1806
1850
|
head: branch,
|
|
1807
1851
|
base,
|
|
1808
1852
|
title: `Upgrade Biffo core ${fromVersion} \u2192 ${toVersion}`,
|
|
1809
|
-
body: buildPrBody(fromVersion, toVersion, plan, migrations, base)
|
|
1853
|
+
body: buildPrBody(fromVersion, toVersion, plan, migrations, base, lockfiles)
|
|
1810
1854
|
});
|
|
1811
1855
|
if (plan.conflicts.length > 0) {
|
|
1812
1856
|
log.warn(`PR opened with ${plan.conflicts.length} conflict(s) to resolve: ${pr.url}`);
|
|
@@ -1814,7 +1858,7 @@ async function applyAndOpenPr(options, deps, plan, migrations, fromVersion, toVe
|
|
|
1814
1858
|
log.success(`Opened PR #${pr.number}: ${pr.url}`);
|
|
1815
1859
|
}
|
|
1816
1860
|
}
|
|
1817
|
-
function buildPrBody(from, to, plan, migrations, base = GLOBAL_DISPATCH_REF) {
|
|
1861
|
+
function buildPrBody(from, to, plan, migrations, base = GLOBAL_DISPATCH_REF, lockfiles = []) {
|
|
1818
1862
|
const lines = [
|
|
1819
1863
|
"Automated core upgrade generated by `biffo core upgrade` (ADR-0006).",
|
|
1820
1864
|
"",
|
|
@@ -1856,6 +1900,26 @@ function buildPrBody(from, to, plan, migrations, base = GLOBAL_DISPATCH_REF) {
|
|
|
1856
1900
|
`Merging here lands the fix on \`${base}\` \u2014 **but the deploy runs it from \`${GLOBAL_DISPATCH_REF}\`.** Until you promote \`${base}\` \u2192 \`${GLOBAL_DISPATCH_REF}\` (open a PR from \`${base}\` into \`${GLOBAL_DISPATCH_REF}\` and merge it), the old workflow keeps running and this fix will not take effect.`
|
|
1857
1901
|
);
|
|
1858
1902
|
}
|
|
1903
|
+
const lockfileFailures = describeFailures(lockfiles);
|
|
1904
|
+
if (lockfileFailures.length > 0) {
|
|
1905
|
+
lines.push(
|
|
1906
|
+
"",
|
|
1907
|
+
"## \u26A0 Lockfiles could not be refreshed",
|
|
1908
|
+
"",
|
|
1909
|
+
"This upgrade changed a dependency manifest, but the matching lockfile could not be regenerated on the machine that ran the upgrade. **CI will fail on the mismatch** until this is done by hand:",
|
|
1910
|
+
"",
|
|
1911
|
+
...lockfileFailures.map((f) => `- ${f}`)
|
|
1912
|
+
);
|
|
1913
|
+
} else if (lockfiles.length > 0) {
|
|
1914
|
+
lines.push(
|
|
1915
|
+
"",
|
|
1916
|
+
`## Lockfiles refreshed (${lockfiles.length})`,
|
|
1917
|
+
"",
|
|
1918
|
+
"This upgrade changed a dependency manifest. The manifests are template-owned and the lockfiles are not, so the lockfiles were regenerated **in this repo** \u2014 against its own registry config \u2014 and committed alongside, rather than left disagreeing:",
|
|
1919
|
+
"",
|
|
1920
|
+
...lockfiles.map((o) => `- \`${o.trigger.lockfile}\` (\`${o.trigger.command.join(" ")}\`)`)
|
|
1921
|
+
);
|
|
1922
|
+
}
|
|
1859
1923
|
if (plan.conflicts.length > 0) {
|
|
1860
1924
|
lines.push(
|
|
1861
1925
|
"",
|
|
@@ -1898,6 +1962,35 @@ function printPlan(plan) {
|
|
|
1898
1962
|
console.log(` ${color(e.status.padEnd(15))} ${e.path}`);
|
|
1899
1963
|
}
|
|
1900
1964
|
}
|
|
1965
|
+
async function refreshInstanceLockfiles(cwd, plan, deps) {
|
|
1966
|
+
const triggers = lockfilesNeedingRefresh(
|
|
1967
|
+
plan.changes.map((c) => c.path),
|
|
1968
|
+
cwd
|
|
1969
|
+
);
|
|
1970
|
+
if (triggers.length === 0) return [];
|
|
1971
|
+
const run = deps.runCommand ?? defaultRunCommand;
|
|
1972
|
+
const outcomes = await refreshLockfiles(cwd, triggers, run);
|
|
1973
|
+
const refreshed = outcomes.filter((o) => o.ok);
|
|
1974
|
+
if (refreshed.length > 0) {
|
|
1975
|
+
log.info(
|
|
1976
|
+
`Refreshed ${refreshed.map((o) => o.trigger.lockfile).join(", ")} \u2014 the upgrade changed a dependency manifest they lock.`
|
|
1977
|
+
);
|
|
1978
|
+
}
|
|
1979
|
+
for (const message of describeFailures(outcomes)) log.warn(message);
|
|
1980
|
+
return outcomes;
|
|
1981
|
+
}
|
|
1982
|
+
var defaultRunCommand = async (command, cwd) => {
|
|
1983
|
+
const [bin, ...args] = command;
|
|
1984
|
+
if (!bin) return { ok: false, error: "empty command" };
|
|
1985
|
+
try {
|
|
1986
|
+
await execa3(bin, args, { cwd });
|
|
1987
|
+
return { ok: true };
|
|
1988
|
+
} catch (err) {
|
|
1989
|
+
const cause = err;
|
|
1990
|
+
const detail = cause.stderr?.trim() || cause.shortMessage || cause.message || "failed";
|
|
1991
|
+
return { ok: false, error: detail.split("\n")[0] ?? "failed" };
|
|
1992
|
+
}
|
|
1993
|
+
};
|
|
1901
1994
|
|
|
1902
1995
|
// src/commands/core.ts
|
|
1903
1996
|
var coreCommand = new Command4("core").description(
|
|
@@ -1911,7 +2004,7 @@ coreCommand.addCommand(coreUpgradeCommand);
|
|
|
1911
2004
|
import { Command as Command8 } from "commander";
|
|
1912
2005
|
|
|
1913
2006
|
// src/commands/data-apply.ts
|
|
1914
|
-
import { existsSync as
|
|
2007
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
|
|
1915
2008
|
import { resolve as resolve4 } from "path";
|
|
1916
2009
|
import chalk5 from "chalk";
|
|
1917
2010
|
import { Command as Command5 } from "commander";
|
|
@@ -2362,7 +2455,7 @@ function isTemplatePlaceholderConfig(raw) {
|
|
|
2362
2455
|
|
|
2363
2456
|
// src/lib/session.ts
|
|
2364
2457
|
import {
|
|
2365
|
-
existsSync as
|
|
2458
|
+
existsSync as existsSync7,
|
|
2366
2459
|
mkdirSync as mkdirSync3,
|
|
2367
2460
|
readdirSync as readdirSync3,
|
|
2368
2461
|
readFileSync as readFileSync5,
|
|
@@ -2371,7 +2464,7 @@ import {
|
|
|
2371
2464
|
writeFileSync as writeFileSync4
|
|
2372
2465
|
} from "fs";
|
|
2373
2466
|
import { homedir } from "os";
|
|
2374
|
-
import { join as
|
|
2467
|
+
import { join as join10 } from "path";
|
|
2375
2468
|
var LEGACY_STEP_ALIASES = {
|
|
2376
2469
|
github_config: ["github_branches", "github_instance_files", "github_settings"]
|
|
2377
2470
|
};
|
|
@@ -2380,14 +2473,14 @@ function hasCompleted(session, step) {
|
|
|
2380
2473
|
return session.completedSteps.some((done) => LEGACY_STEP_ALIASES[done]?.includes(step) ?? false);
|
|
2381
2474
|
}
|
|
2382
2475
|
function sessionsDir() {
|
|
2383
|
-
return process.env["BIFFO_SESSIONS_DIR"] ??
|
|
2476
|
+
return process.env["BIFFO_SESSIONS_DIR"] ?? join10(homedir(), ".biffo", "sessions");
|
|
2384
2477
|
}
|
|
2385
2478
|
function sessionPath(projectName) {
|
|
2386
|
-
return
|
|
2479
|
+
return join10(sessionsDir(), `${projectName}.json`);
|
|
2387
2480
|
}
|
|
2388
2481
|
function loadSession(projectName) {
|
|
2389
2482
|
const path = sessionPath(projectName);
|
|
2390
|
-
if (!
|
|
2483
|
+
if (!existsSync7(path)) return null;
|
|
2391
2484
|
try {
|
|
2392
2485
|
return JSON.parse(readFileSync5(path, "utf8"));
|
|
2393
2486
|
} catch {
|
|
@@ -2396,23 +2489,23 @@ function loadSession(projectName) {
|
|
|
2396
2489
|
}
|
|
2397
2490
|
function findLatestSession() {
|
|
2398
2491
|
const dir = sessionsDir();
|
|
2399
|
-
if (!
|
|
2492
|
+
if (!existsSync7(dir)) return null;
|
|
2400
2493
|
const files = readdirSync3(dir).filter((f) => f.endsWith(".json"));
|
|
2401
2494
|
if (files.length === 0) return null;
|
|
2402
2495
|
const sorted = files.map((f) => {
|
|
2403
|
-
const fullPath =
|
|
2404
|
-
const mtime =
|
|
2496
|
+
const fullPath = join10(dir, f);
|
|
2497
|
+
const mtime = existsSync7(fullPath) ? statSync(fullPath).mtimeMs : -1;
|
|
2405
2498
|
return { f, mtime };
|
|
2406
2499
|
}).sort((a, b) => b.mtime - a.mtime);
|
|
2407
2500
|
try {
|
|
2408
|
-
return JSON.parse(readFileSync5(
|
|
2501
|
+
return JSON.parse(readFileSync5(join10(dir, sorted[0].f), "utf8"));
|
|
2409
2502
|
} catch {
|
|
2410
2503
|
return null;
|
|
2411
2504
|
}
|
|
2412
2505
|
}
|
|
2413
2506
|
function saveSession(session) {
|
|
2414
2507
|
const dir = sessionsDir();
|
|
2415
|
-
if (!
|
|
2508
|
+
if (!existsSync7(dir)) mkdirSync3(dir, { recursive: true });
|
|
2416
2509
|
const name = session.config.project?.name ?? "unknown";
|
|
2417
2510
|
const prior = loadSession(name);
|
|
2418
2511
|
if (prior) {
|
|
@@ -2434,19 +2527,19 @@ function markStepComplete(session, step) {
|
|
|
2434
2527
|
}
|
|
2435
2528
|
function deleteSession(projectName) {
|
|
2436
2529
|
const path = sessionPath(projectName);
|
|
2437
|
-
if (
|
|
2530
|
+
if (existsSync7(path)) rmSync4(path);
|
|
2438
2531
|
}
|
|
2439
2532
|
function projectsDir() {
|
|
2440
|
-
return process.env["BIFFO_PROJECTS_DIR"] ??
|
|
2533
|
+
return process.env["BIFFO_PROJECTS_DIR"] ?? join10(homedir(), ".biffo", "projects");
|
|
2441
2534
|
}
|
|
2442
2535
|
function saveProjectConfig(config) {
|
|
2443
2536
|
const dir = projectsDir();
|
|
2444
|
-
if (!
|
|
2445
|
-
writeFileSync4(
|
|
2537
|
+
if (!existsSync7(dir)) mkdirSync3(dir, { recursive: true });
|
|
2538
|
+
writeFileSync4(join10(dir, `${config.project.name}.json`), JSON.stringify(config, null, 2));
|
|
2446
2539
|
}
|
|
2447
2540
|
function loadProjectConfig(name) {
|
|
2448
|
-
const path =
|
|
2449
|
-
if (!
|
|
2541
|
+
const path = join10(projectsDir(), `${name}.json`);
|
|
2542
|
+
if (!existsSync7(path)) return null;
|
|
2450
2543
|
try {
|
|
2451
2544
|
const result = BiffoConfigSchema.safeParse(JSON.parse(readFileSync5(path, "utf8")));
|
|
2452
2545
|
return result.success ? result.data : null;
|
|
@@ -2455,15 +2548,15 @@ function loadProjectConfig(name) {
|
|
|
2455
2548
|
}
|
|
2456
2549
|
}
|
|
2457
2550
|
function deleteProjectConfig(name) {
|
|
2458
|
-
const path =
|
|
2459
|
-
if (
|
|
2551
|
+
const path = join10(projectsDir(), `${name}.json`);
|
|
2552
|
+
if (existsSync7(path)) rmSync4(path);
|
|
2460
2553
|
}
|
|
2461
2554
|
function listProjectConfigs() {
|
|
2462
2555
|
const dir = projectsDir();
|
|
2463
|
-
if (!
|
|
2556
|
+
if (!existsSync7(dir)) return [];
|
|
2464
2557
|
return readdirSync3(dir).filter((f) => f.endsWith(".json")).flatMap((f) => {
|
|
2465
2558
|
try {
|
|
2466
|
-
const result = BiffoConfigSchema.safeParse(JSON.parse(readFileSync5(
|
|
2559
|
+
const result = BiffoConfigSchema.safeParse(JSON.parse(readFileSync5(join10(dir, f), "utf8")));
|
|
2467
2560
|
return result.success ? [result.data] : [];
|
|
2468
2561
|
} catch {
|
|
2469
2562
|
return [];
|
|
@@ -2552,7 +2645,7 @@ async function resolveConfig(options) {
|
|
|
2552
2645
|
return cfg;
|
|
2553
2646
|
}
|
|
2554
2647
|
const localConfigPath = resolve4(process.cwd(), "biffo.config.json");
|
|
2555
|
-
if (
|
|
2648
|
+
if (existsSync8(localConfigPath)) {
|
|
2556
2649
|
const raw = JSON.parse(readFileSync6(localConfigPath, "utf8"));
|
|
2557
2650
|
const result = BiffoConfigSchema.safeParse(raw);
|
|
2558
2651
|
if (result.success) return result.data;
|
|
@@ -2599,8 +2692,8 @@ async function resolveConfig(options) {
|
|
|
2599
2692
|
|
|
2600
2693
|
// src/commands/data-import.ts
|
|
2601
2694
|
import { execSync as execSync3 } from "child_process";
|
|
2602
|
-
import { cpSync, existsSync as
|
|
2603
|
-
import { join as
|
|
2695
|
+
import { cpSync, existsSync as existsSync9, mkdirSync as mkdirSync4, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
|
|
2696
|
+
import { join as join11, resolve as resolve5 } from "path";
|
|
2604
2697
|
import chalk6 from "chalk";
|
|
2605
2698
|
import { Command as Command6 } from "commander";
|
|
2606
2699
|
import inquirer2 from "inquirer";
|
|
@@ -2640,23 +2733,23 @@ async function runDataImport(name, options, deps) {
|
|
|
2640
2733
|
`Invalid import name '${name}'. Use lowercase letters, numbers, and hyphens, starting with a letter.`
|
|
2641
2734
|
);
|
|
2642
2735
|
}
|
|
2643
|
-
const servicesDir =
|
|
2644
|
-
if (!
|
|
2736
|
+
const servicesDir = join11(options.cwd, "services");
|
|
2737
|
+
if (!existsSync9(servicesDir)) {
|
|
2645
2738
|
throw new Error(
|
|
2646
2739
|
`${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
|
|
2647
2740
|
);
|
|
2648
2741
|
}
|
|
2649
|
-
const targetDir =
|
|
2650
|
-
if (
|
|
2742
|
+
const targetDir = join11(options.cwd, "db", "imports", name);
|
|
2743
|
+
if (existsSync9(targetDir)) {
|
|
2651
2744
|
throw new Error(
|
|
2652
2745
|
`DDL import '${name}' is already present at db/imports/${name}/. Remove it first to re-import.`
|
|
2653
2746
|
);
|
|
2654
2747
|
}
|
|
2655
|
-
const isLocalDir =
|
|
2748
|
+
const isLocalDir = existsSync9(options.source) && statSync2(options.source).isDirectory();
|
|
2656
2749
|
let sourceDir;
|
|
2657
2750
|
let cleanupClone = null;
|
|
2658
2751
|
if (isLocalDir) {
|
|
2659
|
-
sourceDir = options.path ?
|
|
2752
|
+
sourceDir = options.path ? join11(options.source, options.path) : options.source;
|
|
2660
2753
|
} else {
|
|
2661
2754
|
const token = options.token ?? await resolveDdlImportToken();
|
|
2662
2755
|
log.info(`Cloning ${options.source}...`);
|
|
@@ -2664,10 +2757,10 @@ async function runDataImport(name, options, deps) {
|
|
|
2664
2757
|
cleanupClone = () => {
|
|
2665
2758
|
deps.git.cleanup(tmpDir);
|
|
2666
2759
|
};
|
|
2667
|
-
sourceDir = options.path ?
|
|
2760
|
+
sourceDir = options.path ? join11(tmpDir, options.path) : tmpDir;
|
|
2668
2761
|
}
|
|
2669
2762
|
try {
|
|
2670
|
-
if (!
|
|
2763
|
+
if (!existsSync9(sourceDir)) {
|
|
2671
2764
|
throw new Error(`Source directory does not exist: ${sourceDir}`);
|
|
2672
2765
|
}
|
|
2673
2766
|
const sqlFiles = readdirSync4(sourceDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".sql")).map((entry) => entry.name).sort();
|
|
@@ -2692,7 +2785,7 @@ async function runDataImport(name, options, deps) {
|
|
|
2692
2785
|
}
|
|
2693
2786
|
mkdirSync4(targetDir, { recursive: true });
|
|
2694
2787
|
for (const file of sqlFiles) {
|
|
2695
|
-
cpSync(
|
|
2788
|
+
cpSync(join11(sourceDir, file), join11(targetDir, file));
|
|
2696
2789
|
}
|
|
2697
2790
|
log.success(`Imported ${String(sqlFiles.length)} .sql file(s) to db/imports/${name}/`);
|
|
2698
2791
|
const commitMessage = `feat(data): import ${name} (${String(sqlFiles.length)} SQL file(s))`;
|
|
@@ -2744,8 +2837,8 @@ function printDryRun(name, sqlFiles) {
|
|
|
2744
2837
|
}
|
|
2745
2838
|
|
|
2746
2839
|
// src/commands/data-list.ts
|
|
2747
|
-
import { existsSync as
|
|
2748
|
-
import { join as
|
|
2840
|
+
import { existsSync as existsSync10, readdirSync as readdirSync5 } from "fs";
|
|
2841
|
+
import { join as join12, resolve as resolve6 } from "path";
|
|
2749
2842
|
import chalk7 from "chalk";
|
|
2750
2843
|
import { Command as Command7 } from "commander";
|
|
2751
2844
|
var dataListCommand = new Command7("list").description("List DDL imports vendored in this project checkout").option("--cwd <path>", "Project root to scan (defaults to the current directory)").action(async (options) => {
|
|
@@ -2758,15 +2851,15 @@ var dataListCommand = new Command7("list").description("List DDL imports vendore
|
|
|
2758
2851
|
}
|
|
2759
2852
|
});
|
|
2760
2853
|
async function runDataList(options) {
|
|
2761
|
-
const importsDir =
|
|
2762
|
-
if (!
|
|
2854
|
+
const importsDir = join12(options.cwd, "db", "imports");
|
|
2855
|
+
if (!existsSync10(importsDir)) {
|
|
2763
2856
|
console.log(chalk7.dim("\n No DDL imports in this checkout.\n"));
|
|
2764
2857
|
return;
|
|
2765
2858
|
}
|
|
2766
2859
|
const candidates = readdirSync5(importsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
|
|
2767
2860
|
const imports = [];
|
|
2768
2861
|
for (const name of candidates) {
|
|
2769
|
-
const fileCount = readdirSync5(
|
|
2862
|
+
const fileCount = readdirSync5(join12(importsDir, name)).filter((f) => f.endsWith(".sql")).length;
|
|
2770
2863
|
if (fileCount > 0) imports.push({ name, fileCount });
|
|
2771
2864
|
}
|
|
2772
2865
|
if (imports.length === 0) {
|
|
@@ -2796,7 +2889,7 @@ dataCommand.addCommand(dataListCommand);
|
|
|
2796
2889
|
|
|
2797
2890
|
// src/commands/deploy.ts
|
|
2798
2891
|
import { execSync as execSync4 } from "child_process";
|
|
2799
|
-
import { existsSync as
|
|
2892
|
+
import { existsSync as existsSync11, readFileSync as readFileSync7 } from "fs";
|
|
2800
2893
|
import { resolve as resolve7 } from "path";
|
|
2801
2894
|
import chalk8 from "chalk";
|
|
2802
2895
|
import { Command as Command9 } from "commander";
|
|
@@ -3170,7 +3263,7 @@ async function resolveConfig2(options) {
|
|
|
3170
3263
|
return cfg;
|
|
3171
3264
|
}
|
|
3172
3265
|
const localConfigPath = resolve7(process.cwd(), "biffo.config.json");
|
|
3173
|
-
if (
|
|
3266
|
+
if (existsSync11(localConfigPath)) {
|
|
3174
3267
|
const raw = JSON.parse(readFileSync7(localConfigPath, "utf8"));
|
|
3175
3268
|
const result = BiffoConfigSchema.safeParse(raw);
|
|
3176
3269
|
if (result.success) return result.data;
|
|
@@ -3722,8 +3815,8 @@ import { Command as Command12 } from "commander";
|
|
|
3722
3815
|
import inquirer5 from "inquirer";
|
|
3723
3816
|
|
|
3724
3817
|
// src/lib/build-freshness.ts
|
|
3725
|
-
import { existsSync as
|
|
3726
|
-
import { dirname as dirname5, join as
|
|
3818
|
+
import { existsSync as existsSync12, readdirSync as readdirSync6, statSync as statSync3 } from "fs";
|
|
3819
|
+
import { dirname as dirname5, join as join13, relative as relative2, sep as sep2 } from "path";
|
|
3727
3820
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
3728
3821
|
var SKIP_ENV_VAR = "BIFFO_SKIP_BUILD_FRESHNESS_CHECK";
|
|
3729
3822
|
function checkBuildFreshness(options = {}) {
|
|
@@ -3737,7 +3830,7 @@ function checkBuildFreshness(options = {}) {
|
|
|
3737
3830
|
if (!packageRoot) {
|
|
3738
3831
|
return { status: "skipped", reason: `no package.json above ${moduleDir}`, newerSources: [] };
|
|
3739
3832
|
}
|
|
3740
|
-
const distDir =
|
|
3833
|
+
const distDir = join13(packageRoot, "dist");
|
|
3741
3834
|
if (!isInside(distDir, moduleDir)) {
|
|
3742
3835
|
return {
|
|
3743
3836
|
status: "skipped",
|
|
@@ -3745,16 +3838,16 @@ function checkBuildFreshness(options = {}) {
|
|
|
3745
3838
|
newerSources: []
|
|
3746
3839
|
};
|
|
3747
3840
|
}
|
|
3748
|
-
const srcDir =
|
|
3749
|
-
if (!
|
|
3841
|
+
const srcDir = join13(packageRoot, "src");
|
|
3842
|
+
if (!existsSync12(srcDir)) {
|
|
3750
3843
|
return {
|
|
3751
3844
|
status: "skipped",
|
|
3752
3845
|
reason: "no src/ alongside dist/ \u2014 this is a shipped package",
|
|
3753
3846
|
newerSources: []
|
|
3754
3847
|
};
|
|
3755
3848
|
}
|
|
3756
|
-
const entry =
|
|
3757
|
-
if (!
|
|
3849
|
+
const entry = join13(distDir, "index.js");
|
|
3850
|
+
if (!existsSync12(entry)) {
|
|
3758
3851
|
return { status: "skipped", reason: `${entry} not found`, newerSources: [] };
|
|
3759
3852
|
}
|
|
3760
3853
|
const builtAt = statSync3(entry).mtimeMs;
|
|
@@ -3798,7 +3891,7 @@ function collectSourceFiles(srcDir) {
|
|
|
3798
3891
|
const found = [];
|
|
3799
3892
|
const walk = (dir) => {
|
|
3800
3893
|
for (const entry of readdirSync6(dir, { withFileTypes: true })) {
|
|
3801
|
-
const full =
|
|
3894
|
+
const full = join13(dir, entry.name);
|
|
3802
3895
|
if (entry.isDirectory()) {
|
|
3803
3896
|
if (entry.name === "node_modules") continue;
|
|
3804
3897
|
walk(full);
|
|
@@ -3817,7 +3910,7 @@ function collectSourceFiles(srcDir) {
|
|
|
3817
3910
|
function findPackageRoot(from) {
|
|
3818
3911
|
let dir = from;
|
|
3819
3912
|
for (; ; ) {
|
|
3820
|
-
if (
|
|
3913
|
+
if (existsSync12(join13(dir, "package.json"))) return dir;
|
|
3821
3914
|
const parent = dirname5(dir);
|
|
3822
3915
|
if (parent === dir) return null;
|
|
3823
3916
|
dir = parent;
|
|
@@ -3831,9 +3924,9 @@ function isInside(parent, child) {
|
|
|
3831
3924
|
|
|
3832
3925
|
// src/lib/credentials.ts
|
|
3833
3926
|
import { execSync as execSync6 } from "child_process";
|
|
3834
|
-
import { existsSync as
|
|
3927
|
+
import { existsSync as existsSync13, readFileSync as readFileSync9 } from "fs";
|
|
3835
3928
|
import { homedir as homedir2 } from "os";
|
|
3836
|
-
import { join as
|
|
3929
|
+
import { join as join14 } from "path";
|
|
3837
3930
|
import { GetCallerIdentityCommand as GetCallerIdentityCommand2, STSClient as STSClient2 } from "@aws-sdk/client-sts";
|
|
3838
3931
|
import chalk10 from "chalk";
|
|
3839
3932
|
import inquirer4 from "inquirer";
|
|
@@ -4012,10 +4105,10 @@ async function verifySelectedAwsCredentials(profile, region) {
|
|
|
4012
4105
|
return sts.send(new GetCallerIdentityCommand2({}));
|
|
4013
4106
|
}
|
|
4014
4107
|
function discoverAwsProfiles() {
|
|
4015
|
-
const files = [
|
|
4108
|
+
const files = [join14(homedir2(), ".aws", "credentials"), join14(homedir2(), ".aws", "config")];
|
|
4016
4109
|
const profiles = /* @__PURE__ */ new Set();
|
|
4017
4110
|
for (const file of files) {
|
|
4018
|
-
if (!
|
|
4111
|
+
if (!existsSync13(file)) continue;
|
|
4019
4112
|
const content = readFileSync9(file, "utf8");
|
|
4020
4113
|
for (const match of content.matchAll(/^\s*\[([^\]]+)\]\s*$/gm)) {
|
|
4021
4114
|
const section = match[1]?.trim();
|
|
@@ -4106,7 +4199,7 @@ var SiblingConfigSchema = z4.object({
|
|
|
4106
4199
|
|
|
4107
4200
|
// src/lib/sibling-session.ts
|
|
4108
4201
|
import {
|
|
4109
|
-
existsSync as
|
|
4202
|
+
existsSync as existsSync14,
|
|
4110
4203
|
mkdirSync as mkdirSync5,
|
|
4111
4204
|
readdirSync as readdirSync7,
|
|
4112
4205
|
readFileSync as readFileSync10,
|
|
@@ -4115,16 +4208,16 @@ import {
|
|
|
4115
4208
|
writeFileSync as writeFileSync5
|
|
4116
4209
|
} from "fs";
|
|
4117
4210
|
import { homedir as homedir3 } from "os";
|
|
4118
|
-
import { join as
|
|
4211
|
+
import { join as join15 } from "path";
|
|
4119
4212
|
function sessionsDir2() {
|
|
4120
|
-
return process.env["BIFFO_SIBLING_SESSIONS_DIR"] ??
|
|
4213
|
+
return process.env["BIFFO_SIBLING_SESSIONS_DIR"] ?? join15(homedir3(), ".biffo", "sibling-sessions");
|
|
4121
4214
|
}
|
|
4122
4215
|
function sessionPath2(projectName) {
|
|
4123
|
-
return
|
|
4216
|
+
return join15(sessionsDir2(), `${projectName}.json`);
|
|
4124
4217
|
}
|
|
4125
4218
|
function loadSiblingSession(projectName) {
|
|
4126
4219
|
const path = sessionPath2(projectName);
|
|
4127
|
-
if (!
|
|
4220
|
+
if (!existsSync14(path)) return null;
|
|
4128
4221
|
try {
|
|
4129
4222
|
return JSON.parse(readFileSync10(path, "utf8"));
|
|
4130
4223
|
} catch {
|
|
@@ -4133,7 +4226,7 @@ function loadSiblingSession(projectName) {
|
|
|
4133
4226
|
}
|
|
4134
4227
|
function saveSiblingSession(session) {
|
|
4135
4228
|
const dir = sessionsDir2();
|
|
4136
|
-
if (!
|
|
4229
|
+
if (!existsSync14(dir)) mkdirSync5(dir, { recursive: true });
|
|
4137
4230
|
const name = session.config.project?.name ?? "unknown";
|
|
4138
4231
|
const prior = loadSiblingSession(name);
|
|
4139
4232
|
if (prior) {
|
|
@@ -4155,30 +4248,30 @@ function markSiblingStepComplete(session, step) {
|
|
|
4155
4248
|
}
|
|
4156
4249
|
function deleteSiblingSession(projectName) {
|
|
4157
4250
|
const path = sessionPath2(projectName);
|
|
4158
|
-
if (
|
|
4251
|
+
if (existsSync14(path)) rmSync5(path);
|
|
4159
4252
|
}
|
|
4160
4253
|
|
|
4161
4254
|
// src/commands/sibling-create.ts
|
|
4162
|
-
import { cpSync as cpSync2, existsSync as
|
|
4255
|
+
import { cpSync as cpSync2, existsSync as existsSync15, mkdirSync as mkdirSync6, mkdtempSync as mkdtempSync4, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "fs";
|
|
4163
4256
|
import { tmpdir as tmpdir4 } from "os";
|
|
4164
|
-
import { dirname as dirname6, join as
|
|
4257
|
+
import { dirname as dirname6, join as join17, resolve as resolve9 } from "path";
|
|
4165
4258
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
4166
4259
|
import chalk11 from "chalk";
|
|
4167
4260
|
import { Command as Command11 } from "commander";
|
|
4168
4261
|
|
|
4169
4262
|
// src/lib/skeleton-dotfiles.ts
|
|
4170
4263
|
import { readdirSync as readdirSync8, renameSync } from "fs";
|
|
4171
|
-
import { join as
|
|
4264
|
+
import { join as join16 } from "path";
|
|
4172
4265
|
var PACKAGED_GITIGNORE = "_gitignore";
|
|
4173
4266
|
var REAL_GITIGNORE = ".gitignore";
|
|
4174
4267
|
function restorePackagedDotfiles(dir) {
|
|
4175
4268
|
const restored = [];
|
|
4176
4269
|
for (const entry of readdirSync8(dir, { withFileTypes: true })) {
|
|
4177
|
-
const full =
|
|
4270
|
+
const full = join16(dir, entry.name);
|
|
4178
4271
|
if (entry.isDirectory()) {
|
|
4179
4272
|
restored.push(...restorePackagedDotfiles(full));
|
|
4180
4273
|
} else if (entry.name === PACKAGED_GITIGNORE) {
|
|
4181
|
-
const target =
|
|
4274
|
+
const target = join16(dir, REAL_GITIGNORE);
|
|
4182
4275
|
renameSync(full, target);
|
|
4183
4276
|
restored.push(target);
|
|
4184
4277
|
}
|
|
@@ -4223,7 +4316,7 @@ async function runSiblingCreateCommand(name, options) {
|
|
|
4223
4316
|
printDryRun2(config, coreConfig, options.templateRoot);
|
|
4224
4317
|
return;
|
|
4225
4318
|
}
|
|
4226
|
-
if (!
|
|
4319
|
+
if (!existsSync15(options.templateRoot)) {
|
|
4227
4320
|
throw new Error(`Sibling template not found at ${options.templateRoot}`);
|
|
4228
4321
|
}
|
|
4229
4322
|
let session = null;
|
|
@@ -4481,7 +4574,7 @@ async function resolveCoreIdentity(coreAws, coreConfig, environments) {
|
|
|
4481
4574
|
return coreIdentity;
|
|
4482
4575
|
}
|
|
4483
4576
|
async function pushSkeleton(git, skeletonRoot, cloneUrl, config, coreConfig, githubToken) {
|
|
4484
|
-
const workDir = mkdtempSync4(
|
|
4577
|
+
const workDir = mkdtempSync4(join17(tmpdir4(), `biffo-sibling-${config.project.name}-`));
|
|
4485
4578
|
try {
|
|
4486
4579
|
writeSiblingTemplate(skeletonRoot, workDir, config, {
|
|
4487
4580
|
coreProjectName: coreConfig.project.name,
|
|
@@ -4497,13 +4590,13 @@ async function pushSkeleton(git, skeletonRoot, cloneUrl, config, coreConfig, git
|
|
|
4497
4590
|
}
|
|
4498
4591
|
}
|
|
4499
4592
|
function writeSiblingTemplate(templateRoot, targetDir, config, context) {
|
|
4500
|
-
if (!
|
|
4593
|
+
if (!existsSync15(templateRoot)) {
|
|
4501
4594
|
throw new Error(`Sibling template not found at ${templateRoot}`);
|
|
4502
4595
|
}
|
|
4503
4596
|
cpSync2(templateRoot, targetDir, { recursive: true });
|
|
4504
4597
|
restorePackagedDotfiles(targetDir);
|
|
4505
4598
|
writeFileSync6(
|
|
4506
|
-
|
|
4599
|
+
join17(targetDir, "biffo.sibling.json"),
|
|
4507
4600
|
JSON.stringify(
|
|
4508
4601
|
{
|
|
4509
4602
|
name: config.project.name,
|
|
@@ -4519,7 +4612,7 @@ function writeSiblingTemplate(templateRoot, targetDir, config, context) {
|
|
|
4519
4612
|
2
|
|
4520
4613
|
) + "\n"
|
|
4521
4614
|
);
|
|
4522
|
-
const envPath =
|
|
4615
|
+
const envPath = join17(targetDir, "apps", "frontend", ".env.example");
|
|
4523
4616
|
try {
|
|
4524
4617
|
const path = basePathFor(context.pathPrefix);
|
|
4525
4618
|
const content = readFileSync11(envPath, "utf8").replace(/^NEXT_PUBLIC_SIBLING_NAME=.*$/m, `NEXT_PUBLIC_SIBLING_NAME=${config.project.name}`).replace(/^NEXT_PUBLIC_SIBLING_PATH_PREFIX=.*$/m, `NEXT_PUBLIC_SIBLING_PATH_PREFIX=${path}`).replace(/^NEXT_PUBLIC_BASE_PATH=.*$/m, `NEXT_PUBLIC_BASE_PATH=${path}`);
|
|
@@ -4575,7 +4668,7 @@ function readExistingSiblingOrigins(filePath) {
|
|
|
4575
4668
|
}
|
|
4576
4669
|
}
|
|
4577
4670
|
function assertCoreSupportsSiblingRouting(cloneDir, coreRepo, pathPrefix = "x") {
|
|
4578
|
-
const cdnVarsPath =
|
|
4671
|
+
const cdnVarsPath = join17(cloneDir, "modules", "cloud", "aws", "cdn", "variables.tf");
|
|
4579
4672
|
let declaresSiblingOrigins = false;
|
|
4580
4673
|
try {
|
|
4581
4674
|
declaresSiblingOrigins = /variable\s+"sibling_origins"/.test(readFileSync11(cdnVarsPath, "utf8"));
|
|
@@ -4588,7 +4681,7 @@ function assertCoreSupportsSiblingRouting(cloneDir, coreRepo, pathPrefix = "x")
|
|
|
4588
4681
|
);
|
|
4589
4682
|
}
|
|
4590
4683
|
if (!isRootPathPrefix(pathPrefix)) return;
|
|
4591
|
-
const cdnMainPath =
|
|
4684
|
+
const cdnMainPath = join17(cloneDir, "modules", "cloud", "aws", "cdn", "main.tf");
|
|
4592
4685
|
let supportsRoot = false;
|
|
4593
4686
|
try {
|
|
4594
4687
|
supportsRoot = /root_sibling_registered/.test(readFileSync11(cdnMainPath, "utf8"));
|
|
@@ -4621,8 +4714,8 @@ async function registerWithCore(git, github, config, coreConfig, pathPrefix, git
|
|
|
4621
4714
|
for (const env of config.environments) {
|
|
4622
4715
|
const bucketName = siteBucketName(config.project.name, env, siblingAccountId);
|
|
4623
4716
|
const domain = bucketRegionalDomain(bucketName, coreAwsRegion);
|
|
4624
|
-
const relativePath =
|
|
4625
|
-
const filePath =
|
|
4717
|
+
const relativePath = join17("infra", "environments", env, "siblings.auto.tfvars.json");
|
|
4718
|
+
const filePath = join17(cloneDir, relativePath);
|
|
4626
4719
|
const existing = readExistingSiblingOrigins(filePath);
|
|
4627
4720
|
const siblings = upsertSiblingOrigin(existing.sibling_origins ?? [], {
|
|
4628
4721
|
name,
|
|
@@ -4702,8 +4795,8 @@ function defaultSiblingTemplateRoot() {
|
|
|
4702
4795
|
const start = dirname6(fileURLToPath4(import.meta.url));
|
|
4703
4796
|
let dir = start;
|
|
4704
4797
|
for (; ; ) {
|
|
4705
|
-
const candidate =
|
|
4706
|
-
if (
|
|
4798
|
+
const candidate = join17(dir, "_skeletons", "sibling-template");
|
|
4799
|
+
if (existsSync15(candidate)) return candidate;
|
|
4707
4800
|
const parent = dirname6(dir);
|
|
4708
4801
|
if (parent === dir) break;
|
|
4709
4802
|
dir = parent;
|
|
@@ -5160,28 +5253,28 @@ async function promptForConfig(awsAccountId, awsRegion, awsProfile) {
|
|
|
5160
5253
|
import { Command as Command20 } from "commander";
|
|
5161
5254
|
|
|
5162
5255
|
// src/commands/plugin-create.ts
|
|
5163
|
-
import { existsSync as
|
|
5164
|
-
import { dirname as dirname8, join as
|
|
5256
|
+
import { existsSync as existsSync18, readFileSync as readFileSync14 } from "fs";
|
|
5257
|
+
import { dirname as dirname8, join as join20, resolve as resolve11 } from "path";
|
|
5165
5258
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
5166
5259
|
import chalk13 from "chalk";
|
|
5167
5260
|
import { Command as Command13 } from "commander";
|
|
5168
5261
|
|
|
5169
5262
|
// src/lib/plugin-locations.ts
|
|
5170
|
-
import { existsSync as
|
|
5171
|
-
import { join as
|
|
5263
|
+
import { existsSync as existsSync16, readdirSync as readdirSync9 } from "fs";
|
|
5264
|
+
import { join as join18 } from "path";
|
|
5172
5265
|
var FIRST_PARTY_PLUGINS_DIR = "_plugins";
|
|
5173
5266
|
var PLUGIN_MANIFEST_FILE = "biffo.plugin.json";
|
|
5174
5267
|
function pluginDir(name, channel) {
|
|
5175
5268
|
return channel === "first-party" ? `services/${FIRST_PARTY_PLUGINS_DIR}/${name}` : `services/${name}`;
|
|
5176
5269
|
}
|
|
5177
5270
|
function scanDir(absDir, relDir, channel) {
|
|
5178
|
-
if (!
|
|
5271
|
+
if (!existsSync16(absDir)) return [];
|
|
5179
5272
|
const found = [];
|
|
5180
5273
|
for (const entry of readdirSync9(absDir, { withFileTypes: true })) {
|
|
5181
5274
|
if (!entry.isDirectory()) continue;
|
|
5182
5275
|
if (channel === "third-party" && entry.name === FIRST_PARTY_PLUGINS_DIR) continue;
|
|
5183
|
-
const manifestPath =
|
|
5184
|
-
if (!
|
|
5276
|
+
const manifestPath = join18(absDir, entry.name, PLUGIN_MANIFEST_FILE);
|
|
5277
|
+
if (!existsSync16(manifestPath)) continue;
|
|
5185
5278
|
found.push({
|
|
5186
5279
|
dirName: entry.name,
|
|
5187
5280
|
relDir: `${relDir}/${entry.name}`,
|
|
@@ -5192,11 +5285,11 @@ function scanDir(absDir, relDir, channel) {
|
|
|
5192
5285
|
return found;
|
|
5193
5286
|
}
|
|
5194
5287
|
function findInstalledPlugins(cwd) {
|
|
5195
|
-
const servicesDir =
|
|
5288
|
+
const servicesDir = join18(cwd, "services");
|
|
5196
5289
|
return [
|
|
5197
5290
|
...scanDir(servicesDir, "services", "third-party"),
|
|
5198
5291
|
...scanDir(
|
|
5199
|
-
|
|
5292
|
+
join18(servicesDir, FIRST_PARTY_PLUGINS_DIR),
|
|
5200
5293
|
`services/${FIRST_PARTY_PLUGINS_DIR}`,
|
|
5201
5294
|
"first-party"
|
|
5202
5295
|
)
|
|
@@ -5356,13 +5449,13 @@ function validateManifest(raw) {
|
|
|
5356
5449
|
// src/lib/plugin-scaffold.ts
|
|
5357
5450
|
import {
|
|
5358
5451
|
copyFileSync,
|
|
5359
|
-
existsSync as
|
|
5452
|
+
existsSync as existsSync17,
|
|
5360
5453
|
mkdirSync as mkdirSync7,
|
|
5361
5454
|
readFileSync as readFileSync13,
|
|
5362
5455
|
readdirSync as readdirSync10,
|
|
5363
5456
|
writeFileSync as writeFileSync7
|
|
5364
5457
|
} from "fs";
|
|
5365
|
-
import { dirname as dirname7, join as
|
|
5458
|
+
import { dirname as dirname7, join as join19 } from "path";
|
|
5366
5459
|
var STANDALONE_ONLY_ENTRIES = {
|
|
5367
5460
|
".github": "standalone-repo CI/release workflows \u2014 the host monorepo already runs lint/type/test/security over services/",
|
|
5368
5461
|
"registry-schema.json": "the plugin-registry publishing schema, used when submitting a *published* plugin to the registry repo, not by an in-tree plugin"
|
|
@@ -5415,10 +5508,10 @@ function applySubstitutions(text, names) {
|
|
|
5415
5508
|
}
|
|
5416
5509
|
var BINARY_EXTENSIONS = /\.(png|jpe?g|gif|ico|woff2?|ttf|zip|gz)$/i;
|
|
5417
5510
|
function scaffoldPlugin(skeletonRoot, destDir, names) {
|
|
5418
|
-
if (!
|
|
5511
|
+
if (!existsSync17(skeletonRoot)) {
|
|
5419
5512
|
throw new Error(`Plugin skeleton not found at ${skeletonRoot}`);
|
|
5420
5513
|
}
|
|
5421
|
-
if (!
|
|
5514
|
+
if (!existsSync17(join19(skeletonRoot, "terraform"))) {
|
|
5422
5515
|
throw new Error(
|
|
5423
5516
|
`Plugin skeleton at ${skeletonRoot} has no terraform/ directory. Refusing to scaffold a plugin that cannot receive events (issue #194) \u2014 the skeleton is broken.`
|
|
5424
5517
|
);
|
|
@@ -5426,7 +5519,7 @@ function scaffoldPlugin(skeletonRoot, destDir, names) {
|
|
|
5426
5519
|
const skipped = [];
|
|
5427
5520
|
const files = [];
|
|
5428
5521
|
const walk = (relDir) => {
|
|
5429
|
-
const absDir =
|
|
5522
|
+
const absDir = join19(skeletonRoot, relDir);
|
|
5430
5523
|
for (const entry of readdirSync10(absDir, { withFileTypes: true }).sort(
|
|
5431
5524
|
(a, b) => a.name.localeCompare(b.name)
|
|
5432
5525
|
)) {
|
|
@@ -5441,14 +5534,14 @@ function scaffoldPlugin(skeletonRoot, destDir, names) {
|
|
|
5441
5534
|
continue;
|
|
5442
5535
|
}
|
|
5443
5536
|
const destRel = applySubstitutions(relPath, names);
|
|
5444
|
-
const destPath =
|
|
5537
|
+
const destPath = join19(destDir, destRel);
|
|
5445
5538
|
mkdirSync7(dirname7(destPath), { recursive: true });
|
|
5446
5539
|
if (BINARY_EXTENSIONS.test(entry.name)) {
|
|
5447
|
-
copyFileSync(
|
|
5540
|
+
copyFileSync(join19(skeletonRoot, relPath), destPath);
|
|
5448
5541
|
} else {
|
|
5449
5542
|
writeFileSync7(
|
|
5450
5543
|
destPath,
|
|
5451
|
-
applySubstitutions(readFileSync13(
|
|
5544
|
+
applySubstitutions(readFileSync13(join19(skeletonRoot, relPath), "utf8"), names)
|
|
5452
5545
|
);
|
|
5453
5546
|
}
|
|
5454
5547
|
files.push(destRel);
|
|
@@ -5465,8 +5558,8 @@ function scaffoldPlugin(skeletonRoot, destDir, names) {
|
|
|
5465
5558
|
function findSkeletonRoot(startDir, skeleton) {
|
|
5466
5559
|
let dir = startDir;
|
|
5467
5560
|
for (; ; ) {
|
|
5468
|
-
const candidate =
|
|
5469
|
-
if (
|
|
5561
|
+
const candidate = join19(dir, "_skeletons", skeleton);
|
|
5562
|
+
if (existsSync17(candidate)) return candidate;
|
|
5470
5563
|
const parent = dirname7(dir);
|
|
5471
5564
|
if (parent === dir) return null;
|
|
5472
5565
|
dir = parent;
|
|
@@ -5503,7 +5596,7 @@ var pluginCreateCommand = new Command13("create").description("Scaffold a new pl
|
|
|
5503
5596
|
);
|
|
5504
5597
|
async function runPluginCreate(name, options, deps) {
|
|
5505
5598
|
const names = deriveNames(name);
|
|
5506
|
-
const isInstance =
|
|
5599
|
+
const isInstance = existsSync18(join20(options.cwd, INSTANCE_CORE_FILE));
|
|
5507
5600
|
if (options.firstParty && isInstance) {
|
|
5508
5601
|
throw new Error(
|
|
5509
5602
|
`--first-party scaffolds into services/_plugins/, which is template-owned: \`biffo core upgrade\` three-way-merges it against the template on every upgrade, and the template has no '${names.slug}'. This checkout is a Biffo instance (${INSTANCE_CORE_FILE} is present), so your plugin belongs in the user-owned ${pluginDir(names.slug, "third-party")}/ \u2014 re-run without --first-party.`
|
|
@@ -5511,19 +5604,19 @@ async function runPluginCreate(name, options, deps) {
|
|
|
5511
5604
|
}
|
|
5512
5605
|
const channel = options.firstParty ? "first-party" : "third-party";
|
|
5513
5606
|
const relDir = pluginDir(names.slug, channel);
|
|
5514
|
-
const destDir =
|
|
5515
|
-
const servicesDir =
|
|
5516
|
-
if (!
|
|
5607
|
+
const destDir = join20(options.cwd, relDir);
|
|
5608
|
+
const servicesDir = join20(options.cwd, "services");
|
|
5609
|
+
if (!existsSync18(servicesDir)) {
|
|
5517
5610
|
throw new Error(
|
|
5518
5611
|
`${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
|
|
5519
5612
|
);
|
|
5520
5613
|
}
|
|
5521
|
-
if (
|
|
5614
|
+
if (existsSync18(destDir)) {
|
|
5522
5615
|
throw new Error(`${relDir}/ already exists. Choose a different name, or remove it first.`);
|
|
5523
5616
|
}
|
|
5524
5617
|
const here = dirname8(fileURLToPath5(import.meta.url));
|
|
5525
|
-
const skeletonRoot = options.skeletonRoot ?? findSkeletonRoot(here, "plugin-template") ??
|
|
5526
|
-
if (!
|
|
5618
|
+
const skeletonRoot = options.skeletonRoot ?? findSkeletonRoot(here, "plugin-template") ?? join20(options.cwd, "_skeletons", "plugin-template");
|
|
5619
|
+
if (!existsSync18(skeletonRoot)) {
|
|
5527
5620
|
throw new Error(
|
|
5528
5621
|
`Could not find the plugin skeleton (_skeletons/plugin-template/). Pass --skeleton <path> to point at it explicitly.`
|
|
5529
5622
|
);
|
|
@@ -5538,7 +5631,7 @@ async function runPluginCreate(name, options, deps) {
|
|
|
5538
5631
|
for (const { entry, reason } of skipped) {
|
|
5539
5632
|
log.info(`Skipped ${entry} \u2014 ${reason}`);
|
|
5540
5633
|
}
|
|
5541
|
-
const manifestPath =
|
|
5634
|
+
const manifestPath = join20(destDir, "biffo.plugin.json");
|
|
5542
5635
|
const manifest = validateManifest(JSON.parse(readFileSync14(manifestPath, "utf8")));
|
|
5543
5636
|
if (manifest.name !== names.slug) {
|
|
5544
5637
|
throw new Error(
|
|
@@ -5729,14 +5822,14 @@ function printEntry(entry) {
|
|
|
5729
5822
|
}
|
|
5730
5823
|
|
|
5731
5824
|
// src/commands/plugin-install.ts
|
|
5732
|
-
import { cpSync as cpSync3, existsSync as
|
|
5733
|
-
import { basename, join as
|
|
5825
|
+
import { cpSync as cpSync3, existsSync as existsSync20, mkdirSync as mkdirSync9, readFileSync as readFileSync16, statSync as statSync5 } from "fs";
|
|
5826
|
+
import { basename, join as join23, relative as relative3, resolve as resolve12 } from "path";
|
|
5734
5827
|
import chalk15 from "chalk";
|
|
5735
5828
|
import { Command as Command15 } from "commander";
|
|
5736
5829
|
|
|
5737
5830
|
// src/adapters/plugin-migrations/index.ts
|
|
5738
|
-
import { execa as
|
|
5739
|
-
import { join as
|
|
5831
|
+
import { execa as execa4 } from "execa";
|
|
5832
|
+
import { join as join21 } from "path";
|
|
5740
5833
|
var PluginMigrationsAdapter = class {
|
|
5741
5834
|
/**
|
|
5742
5835
|
* Generates migration file(s) for `pluginNames` (every discovered
|
|
@@ -5745,22 +5838,22 @@ var PluginMigrationsAdapter = class {
|
|
|
5745
5838
|
* or declared no tables.
|
|
5746
5839
|
*/
|
|
5747
5840
|
async generate(cwd, pluginNames) {
|
|
5748
|
-
const scriptPath =
|
|
5841
|
+
const scriptPath = join21(cwd, "services", "api", "scripts", "generate_plugin_migrations.py");
|
|
5749
5842
|
const args = [
|
|
5750
5843
|
"run",
|
|
5751
5844
|
"python",
|
|
5752
5845
|
scriptPath,
|
|
5753
5846
|
"--services-root",
|
|
5754
|
-
|
|
5847
|
+
join21(cwd, "services"),
|
|
5755
5848
|
"--versions-dir",
|
|
5756
|
-
|
|
5849
|
+
join21(cwd, "services", "api", "migrations", "versions")
|
|
5757
5850
|
];
|
|
5758
5851
|
for (const name of pluginNames ?? []) {
|
|
5759
5852
|
args.push("--plugin", name);
|
|
5760
5853
|
}
|
|
5761
5854
|
let result;
|
|
5762
5855
|
try {
|
|
5763
|
-
result = await
|
|
5856
|
+
result = await execa4("uv", args, { cwd: join21(cwd, "services", "api") });
|
|
5764
5857
|
} catch (err) {
|
|
5765
5858
|
const cause = err;
|
|
5766
5859
|
if (cause.code === "ENOENT") {
|
|
@@ -5777,8 +5870,8 @@ var PluginMigrationsAdapter = class {
|
|
|
5777
5870
|
};
|
|
5778
5871
|
|
|
5779
5872
|
// src/lib/plugin-terraform-wiring.ts
|
|
5780
|
-
import { existsSync as
|
|
5781
|
-
import { join as
|
|
5873
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync8, readFileSync as readFileSync15, readdirSync as readdirSync11, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "fs";
|
|
5874
|
+
import { join as join22 } from "path";
|
|
5782
5875
|
var TEMPLATE_MODULE_DIR = "_template";
|
|
5783
5876
|
var DEFAULT_PLUGIN_HANDLER = "src.lambda.main.handler";
|
|
5784
5877
|
var GENERATED_TF_FILE = "plugins.generated.tf";
|
|
@@ -5796,7 +5889,7 @@ function standardArguments(pluginName, handler) {
|
|
|
5796
5889
|
];
|
|
5797
5890
|
}
|
|
5798
5891
|
function listPluginModules(cwd) {
|
|
5799
|
-
const dir =
|
|
5892
|
+
const dir = join22(cwd, "modules", "plugins");
|
|
5800
5893
|
let entries;
|
|
5801
5894
|
try {
|
|
5802
5895
|
entries = readdirSync11(dir, { withFileTypes: true });
|
|
@@ -5806,7 +5899,7 @@ function listPluginModules(cwd) {
|
|
|
5806
5899
|
return entries.filter((e) => e.isDirectory() && e.name !== TEMPLATE_MODULE_DIR && !e.name.startsWith(".")).map((e) => e.name).sort();
|
|
5807
5900
|
}
|
|
5808
5901
|
function listEnvironments(cwd) {
|
|
5809
|
-
const dir =
|
|
5902
|
+
const dir = join22(cwd, "infra", "environments");
|
|
5810
5903
|
let entries;
|
|
5811
5904
|
try {
|
|
5812
5905
|
entries = readdirSync11(dir, { withFileTypes: true });
|
|
@@ -5814,12 +5907,12 @@ function listEnvironments(cwd) {
|
|
|
5814
5907
|
return [];
|
|
5815
5908
|
}
|
|
5816
5909
|
return entries.filter((e) => {
|
|
5817
|
-
if (!e.isDirectory() || !
|
|
5818
|
-
return declaredVariables(
|
|
5910
|
+
if (!e.isDirectory() || !existsSync19(join22(dir, e.name, "main.tf"))) return false;
|
|
5911
|
+
return declaredVariables(join22(dir, e.name)).has("enabled_plugins");
|
|
5819
5912
|
}).map((e) => e.name).sort();
|
|
5820
5913
|
}
|
|
5821
5914
|
function listUnwirableEnvironments(cwd) {
|
|
5822
|
-
const dir =
|
|
5915
|
+
const dir = join22(cwd, "infra", "environments");
|
|
5823
5916
|
let entries;
|
|
5824
5917
|
try {
|
|
5825
5918
|
entries = readdirSync11(dir, { withFileTypes: true });
|
|
@@ -5827,7 +5920,7 @@ function listUnwirableEnvironments(cwd) {
|
|
|
5827
5920
|
return [];
|
|
5828
5921
|
}
|
|
5829
5922
|
return entries.filter(
|
|
5830
|
-
(e) => e.isDirectory() &&
|
|
5923
|
+
(e) => e.isDirectory() && existsSync19(join22(dir, e.name, "main.tf")) && !declaredVariables(join22(dir, e.name)).has("enabled_plugins")
|
|
5831
5924
|
).map((e) => e.name).sort();
|
|
5832
5925
|
}
|
|
5833
5926
|
function declaredVariables(moduleDir) {
|
|
@@ -5842,7 +5935,7 @@ function declaredVariables(moduleDir) {
|
|
|
5842
5935
|
if (!entry.isFile() || !entry.name.endsWith(".tf")) continue;
|
|
5843
5936
|
let contents;
|
|
5844
5937
|
try {
|
|
5845
|
-
contents = readFileSync15(
|
|
5938
|
+
contents = readFileSync15(join22(moduleDir, entry.name), "utf8");
|
|
5846
5939
|
} catch {
|
|
5847
5940
|
continue;
|
|
5848
5941
|
}
|
|
@@ -5914,19 +6007,19 @@ function syncPluginTerraform(cwd) {
|
|
|
5914
6007
|
const changedPaths = [];
|
|
5915
6008
|
const rendered = plugins.map((name) => ({
|
|
5916
6009
|
name,
|
|
5917
|
-
declaredVariables: declaredVariables(
|
|
6010
|
+
declaredVariables: declaredVariables(join22(cwd, "modules", "plugins", name))
|
|
5918
6011
|
}));
|
|
5919
6012
|
for (const env of environments) {
|
|
5920
|
-
const envDir =
|
|
5921
|
-
const tfPath =
|
|
5922
|
-
const tfvarsPath =
|
|
6013
|
+
const envDir = join22(cwd, "infra", "environments", env);
|
|
6014
|
+
const tfPath = join22(envDir, GENERATED_TF_FILE);
|
|
6015
|
+
const tfvarsPath = join22(envDir, GENERATED_TFVARS_FILE);
|
|
5923
6016
|
const relBase = `infra/environments/${env}`;
|
|
5924
6017
|
if (plugins.length === 0) {
|
|
5925
6018
|
for (const [abs, rel] of [
|
|
5926
6019
|
[tfPath, `${relBase}/${GENERATED_TF_FILE}`],
|
|
5927
6020
|
[tfvarsPath, `${relBase}/${GENERATED_TFVARS_FILE}`]
|
|
5928
6021
|
]) {
|
|
5929
|
-
if (
|
|
6022
|
+
if (existsSync19(abs)) {
|
|
5930
6023
|
rmSync6(abs);
|
|
5931
6024
|
changedPaths.push(rel);
|
|
5932
6025
|
}
|
|
@@ -5983,14 +6076,14 @@ var LOCAL_COPY_EXCLUDES = /* @__PURE__ */ new Set([
|
|
|
5983
6076
|
".terraform"
|
|
5984
6077
|
]);
|
|
5985
6078
|
function resolveLocalPlugin(localPath) {
|
|
5986
|
-
if (!
|
|
6079
|
+
if (!existsSync20(localPath)) {
|
|
5987
6080
|
throw new Error(`--local path does not exist: ${localPath}`);
|
|
5988
6081
|
}
|
|
5989
6082
|
if (!statSync5(localPath).isDirectory()) {
|
|
5990
6083
|
throw new Error(`--local path is not a directory: ${localPath}`);
|
|
5991
6084
|
}
|
|
5992
|
-
const manifestPath =
|
|
5993
|
-
if (!
|
|
6085
|
+
const manifestPath = join23(localPath, "biffo.plugin.json");
|
|
6086
|
+
if (!existsSync20(manifestPath)) {
|
|
5994
6087
|
throw new Error(
|
|
5995
6088
|
`${localPath} does not contain a biffo.plugin.json manifest at its root \u2014 is it a plugin directory? (Scaffold one with \`biffo plugin create <name>\`.)`
|
|
5996
6089
|
);
|
|
@@ -6016,8 +6109,8 @@ function parsePluginTarget(target) {
|
|
|
6016
6109
|
async function cloneAndValidatePlugin(entry, git) {
|
|
6017
6110
|
const tmpDir = await git.cloneToTemp(entry.repo, `biffo-plugin-${entry.name}`);
|
|
6018
6111
|
try {
|
|
6019
|
-
const manifestPath =
|
|
6020
|
-
if (!
|
|
6112
|
+
const manifestPath = join23(tmpDir, "biffo.plugin.json");
|
|
6113
|
+
if (!existsSync20(manifestPath)) {
|
|
6021
6114
|
throw new Error(
|
|
6022
6115
|
`Plugin repo ${entry.repo} does not contain a biffo.plugin.json manifest at its root.`
|
|
6023
6116
|
);
|
|
@@ -6045,8 +6138,8 @@ async function runPluginInstall(target, options, deps) {
|
|
|
6045
6138
|
`Nothing to install. Pass a registry target (e.g. \`biffo plugin install acme-crm@1.0\`) or a local plugin directory (\`biffo plugin install --local services/acme-crm\`).`
|
|
6046
6139
|
);
|
|
6047
6140
|
}
|
|
6048
|
-
const servicesDir =
|
|
6049
|
-
if (!
|
|
6141
|
+
const servicesDir = join23(options.cwd, "services");
|
|
6142
|
+
if (!existsSync20(servicesDir)) {
|
|
6050
6143
|
throw new Error(
|
|
6051
6144
|
`${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
|
|
6052
6145
|
);
|
|
@@ -6064,10 +6157,10 @@ async function runPluginInstall(target, options, deps) {
|
|
|
6064
6157
|
}
|
|
6065
6158
|
const pluginName = entry ? entry.name : source.name;
|
|
6066
6159
|
const relTargetDir = pluginDir(pluginName, "third-party");
|
|
6067
|
-
const targetDir =
|
|
6068
|
-
const modulesDir =
|
|
6160
|
+
const targetDir = join23(options.cwd, relTargetDir);
|
|
6161
|
+
const modulesDir = join23(options.cwd, "modules", "plugins", pluginName);
|
|
6069
6162
|
const inTreeSource = options.local !== void 0 && resolve12(options.local) === resolve12(targetDir);
|
|
6070
|
-
if (
|
|
6163
|
+
if (existsSync20(targetDir) && !inTreeSource) {
|
|
6071
6164
|
throw new Error(
|
|
6072
6165
|
`Plugin '${pluginName}' is already installed at ${relTargetDir}/. Remove it first, or wait for a future 'biffo plugin upgrade' command.`
|
|
6073
6166
|
);
|
|
@@ -6110,8 +6203,8 @@ async function runPluginInstall(target, options, deps) {
|
|
|
6110
6203
|
log.success(`Installed plugin source at ${relTargetDir}/`);
|
|
6111
6204
|
}
|
|
6112
6205
|
const stagePaths = [relTargetDir];
|
|
6113
|
-
const tfSourceDir =
|
|
6114
|
-
if (
|
|
6206
|
+
const tfSourceDir = join23(targetDir, "terraform");
|
|
6207
|
+
if (existsSync20(tfSourceDir)) {
|
|
6115
6208
|
mkdirSync9(modulesDir, { recursive: true });
|
|
6116
6209
|
cpSync3(tfSourceDir, modulesDir, { recursive: true });
|
|
6117
6210
|
stagePaths.push(`modules/plugins/${pluginName}`);
|
|
@@ -6205,8 +6298,8 @@ function printDryRun4(entry, source, relTargetDir, inTreeSource) {
|
|
|
6205
6298
|
}
|
|
6206
6299
|
|
|
6207
6300
|
// src/commands/plugin-list.ts
|
|
6208
|
-
import { existsSync as
|
|
6209
|
-
import { join as
|
|
6301
|
+
import { existsSync as existsSync21, readFileSync as readFileSync17 } from "fs";
|
|
6302
|
+
import { join as join24, resolve as resolve13 } from "path";
|
|
6210
6303
|
import chalk16 from "chalk";
|
|
6211
6304
|
import { Command as Command16 } from "commander";
|
|
6212
6305
|
var pluginListCommand = new Command16("list").description("List plugins installed in this project checkout").option("--cwd <path>", "Project root to scan (defaults to the current directory)").action(async (options) => {
|
|
@@ -6219,8 +6312,8 @@ var pluginListCommand = new Command16("list").description("List plugins installe
|
|
|
6219
6312
|
}
|
|
6220
6313
|
});
|
|
6221
6314
|
async function runPluginList(options) {
|
|
6222
|
-
const servicesDir =
|
|
6223
|
-
if (!
|
|
6315
|
+
const servicesDir = join24(options.cwd, "services");
|
|
6316
|
+
if (!existsSync21(servicesDir)) {
|
|
6224
6317
|
throw new Error(
|
|
6225
6318
|
`${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
|
|
6226
6319
|
);
|
|
@@ -6265,8 +6358,8 @@ async function runPluginList(options) {
|
|
|
6265
6358
|
}
|
|
6266
6359
|
|
|
6267
6360
|
// src/commands/plugin-sync-migrations.ts
|
|
6268
|
-
import { existsSync as
|
|
6269
|
-
import { join as
|
|
6361
|
+
import { existsSync as existsSync22 } from "fs";
|
|
6362
|
+
import { join as join25, relative as relative4, resolve as resolve14 } from "path";
|
|
6270
6363
|
import chalk17 from "chalk";
|
|
6271
6364
|
import { Command as Command17 } from "commander";
|
|
6272
6365
|
var pluginSyncMigrationsCommand = new Command17("sync-migrations").description(
|
|
@@ -6287,11 +6380,11 @@ var pluginSyncMigrationsCommand = new Command17("sync-migrations").description(
|
|
|
6287
6380
|
}
|
|
6288
6381
|
);
|
|
6289
6382
|
async function runPluginSyncMigrations(name, options, deps) {
|
|
6290
|
-
const servicesDir =
|
|
6291
|
-
if (!
|
|
6383
|
+
const servicesDir = join25(options.cwd, "services");
|
|
6384
|
+
if (!existsSync22(servicesDir)) {
|
|
6292
6385
|
throw new Error(`${servicesDir} does not exist \u2014 is ${options.cwd} a Biffo project checkout?`);
|
|
6293
6386
|
}
|
|
6294
|
-
if (name && !
|
|
6387
|
+
if (name && !existsSync22(join25(servicesDir, name, "biffo.plugin.json"))) {
|
|
6295
6388
|
throw new Error(`Plugin '${name}' is not installed at services/${name}/.`);
|
|
6296
6389
|
}
|
|
6297
6390
|
if (options.dryRun) {
|
|
@@ -6327,8 +6420,8 @@ async function runPluginSyncMigrations(name, options, deps) {
|
|
|
6327
6420
|
}
|
|
6328
6421
|
|
|
6329
6422
|
// src/commands/plugin-uninstall.ts
|
|
6330
|
-
import { existsSync as
|
|
6331
|
-
import { join as
|
|
6423
|
+
import { existsSync as existsSync23, readFileSync as readFileSync18, rmSync as rmSync7 } from "fs";
|
|
6424
|
+
import { join as join26, resolve as resolve15 } from "path";
|
|
6332
6425
|
import chalk18 from "chalk";
|
|
6333
6426
|
import { Command as Command18 } from "commander";
|
|
6334
6427
|
import inquirer6 from "inquirer";
|
|
@@ -6360,16 +6453,16 @@ async function runPluginUninstall(name, options, deps) {
|
|
|
6360
6453
|
if (!NAME_PATTERN2.test(name)) {
|
|
6361
6454
|
throw new Error(`Invalid plugin name '${name}'. Expected a lowercase kebab-case slug.`);
|
|
6362
6455
|
}
|
|
6363
|
-
const servicesDir =
|
|
6364
|
-
if (!
|
|
6456
|
+
const servicesDir = join26(options.cwd, "services");
|
|
6457
|
+
if (!existsSync23(servicesDir)) {
|
|
6365
6458
|
throw new Error(
|
|
6366
6459
|
`${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
|
|
6367
6460
|
);
|
|
6368
6461
|
}
|
|
6369
|
-
const targetDir =
|
|
6370
|
-
if (!
|
|
6371
|
-
const firstParty =
|
|
6372
|
-
if (
|
|
6462
|
+
const targetDir = join26(servicesDir, name);
|
|
6463
|
+
if (!existsSync23(targetDir)) {
|
|
6464
|
+
const firstParty = join26(servicesDir, FIRST_PARTY_PLUGINS_DIR, name);
|
|
6465
|
+
if (existsSync23(firstParty)) {
|
|
6373
6466
|
throw new Error(
|
|
6374
6467
|
`Plugin '${name}' is a first-party plugin at ${pluginDir(name, "first-party")}/, which is template-owned \u2014 \`biffo core upgrade\` would restore it on the next upgrade. Disable it instead by removing '${name}' from \`enabled_plugins\` in infra/environments/<env>/main.tf and re-applying.`
|
|
6375
6468
|
);
|
|
@@ -6377,9 +6470,9 @@ async function runPluginUninstall(name, options, deps) {
|
|
|
6377
6470
|
throw new Error(`Plugin '${name}' is not installed at services/${name}/.`);
|
|
6378
6471
|
}
|
|
6379
6472
|
const version = readInstalledVersion(targetDir);
|
|
6380
|
-
const modulesDir =
|
|
6473
|
+
const modulesDir = join26(options.cwd, "modules", "plugins", name);
|
|
6381
6474
|
const stagePaths = [`services/${name}`];
|
|
6382
|
-
if (
|
|
6475
|
+
if (existsSync23(modulesDir)) {
|
|
6383
6476
|
stagePaths.push(`modules/plugins/${name}`);
|
|
6384
6477
|
}
|
|
6385
6478
|
if (options.dryRun) {
|
|
@@ -6401,7 +6494,7 @@ async function runPluginUninstall(name, options, deps) {
|
|
|
6401
6494
|
}
|
|
6402
6495
|
rmSync7(targetDir, { recursive: true, force: true });
|
|
6403
6496
|
log.success(`Removed services/${name}/`);
|
|
6404
|
-
if (
|
|
6497
|
+
if (existsSync23(modulesDir)) {
|
|
6405
6498
|
rmSync7(modulesDir, { recursive: true, force: true });
|
|
6406
6499
|
log.success(`Removed modules/plugins/${name}/`);
|
|
6407
6500
|
const wiring = syncPluginTerraform(options.cwd);
|
|
@@ -6438,8 +6531,8 @@ async function runPluginUninstall(name, options, deps) {
|
|
|
6438
6531
|
}
|
|
6439
6532
|
}
|
|
6440
6533
|
function readInstalledVersion(targetDir) {
|
|
6441
|
-
const manifestPath =
|
|
6442
|
-
if (!
|
|
6534
|
+
const manifestPath = join26(targetDir, "biffo.plugin.json");
|
|
6535
|
+
if (!existsSync23(manifestPath)) return void 0;
|
|
6443
6536
|
try {
|
|
6444
6537
|
return validateManifest(JSON.parse(readFileSync18(manifestPath, "utf8"))).version;
|
|
6445
6538
|
} catch {
|
|
@@ -6474,8 +6567,8 @@ function printDryRun5(name, version, stagePaths, keepData) {
|
|
|
6474
6567
|
}
|
|
6475
6568
|
|
|
6476
6569
|
// src/commands/plugin-upgrade.ts
|
|
6477
|
-
import { cpSync as cpSync4, existsSync as
|
|
6478
|
-
import { join as
|
|
6570
|
+
import { cpSync as cpSync4, existsSync as existsSync24, mkdirSync as mkdirSync10, readFileSync as readFileSync19, rmSync as rmSync8 } from "fs";
|
|
6571
|
+
import { join as join27, relative as relative5, resolve as resolve16 } from "path";
|
|
6479
6572
|
import chalk19 from "chalk";
|
|
6480
6573
|
import { Command as Command19 } from "commander";
|
|
6481
6574
|
import inquirer7 from "inquirer";
|
|
@@ -6500,14 +6593,14 @@ var pluginUpgradeCommand = new Command19("upgrade").description(
|
|
|
6500
6593
|
});
|
|
6501
6594
|
async function runPluginUpgrade(target, options, deps) {
|
|
6502
6595
|
const { name, minor } = parsePluginTarget(target);
|
|
6503
|
-
const servicesDir =
|
|
6504
|
-
if (!
|
|
6596
|
+
const servicesDir = join27(options.cwd, "services");
|
|
6597
|
+
if (!existsSync24(servicesDir)) {
|
|
6505
6598
|
throw new Error(
|
|
6506
6599
|
`${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
|
|
6507
6600
|
);
|
|
6508
6601
|
}
|
|
6509
|
-
const targetDir =
|
|
6510
|
-
if (!
|
|
6602
|
+
const targetDir = join27(servicesDir, name);
|
|
6603
|
+
if (!existsSync24(targetDir)) {
|
|
6511
6604
|
throw new Error(
|
|
6512
6605
|
`Plugin '${name}' is not installed at services/${name}/. Use 'biffo plugin install ${name}@${minor}' instead.`
|
|
6513
6606
|
);
|
|
@@ -6521,7 +6614,7 @@ async function runPluginUpgrade(target, options, deps) {
|
|
|
6521
6614
|
`Plugin declares required_core_version '${entry.required_core_version}'. The CLI cannot verify this against your deployment \u2014 the Core API exposes no version endpoint and services/api/pyproject.toml's version is a static placeholder, not a real release marker. Confirm compatibility yourself before deploying.`
|
|
6522
6615
|
);
|
|
6523
6616
|
}
|
|
6524
|
-
const modulesDir =
|
|
6617
|
+
const modulesDir = join27(options.cwd, "modules", "plugins", entry.name);
|
|
6525
6618
|
if (options.dryRun) {
|
|
6526
6619
|
printDryRun6(entry, currentVersion);
|
|
6527
6620
|
return;
|
|
@@ -6554,11 +6647,11 @@ async function runPluginUpgrade(target, options, deps) {
|
|
|
6554
6647
|
cpSync4(tmpDir, targetDir, { recursive: true });
|
|
6555
6648
|
log.success(`Upgraded plugin source at services/${entry.name}/`);
|
|
6556
6649
|
const stagePaths = [`services/${entry.name}`];
|
|
6557
|
-
if (
|
|
6650
|
+
if (existsSync24(modulesDir)) {
|
|
6558
6651
|
rmSync8(modulesDir, { recursive: true, force: true });
|
|
6559
6652
|
}
|
|
6560
|
-
const tfSourceDir =
|
|
6561
|
-
if (
|
|
6653
|
+
const tfSourceDir = join27(targetDir, "terraform");
|
|
6654
|
+
if (existsSync24(tfSourceDir)) {
|
|
6562
6655
|
mkdirSync10(modulesDir, { recursive: true });
|
|
6563
6656
|
cpSync4(tfSourceDir, modulesDir, { recursive: true });
|
|
6564
6657
|
stagePaths.push(`modules/plugins/${entry.name}`);
|
|
@@ -6592,8 +6685,8 @@ async function runPluginUpgrade(target, options, deps) {
|
|
|
6592
6685
|
}
|
|
6593
6686
|
}
|
|
6594
6687
|
function readInstalledVersion2(targetDir) {
|
|
6595
|
-
const manifestPath =
|
|
6596
|
-
if (!
|
|
6688
|
+
const manifestPath = join27(targetDir, "biffo.plugin.json");
|
|
6689
|
+
if (!existsSync24(manifestPath)) return void 0;
|
|
6597
6690
|
try {
|
|
6598
6691
|
return validateManifest(JSON.parse(readFileSync19(manifestPath, "utf8"))).version;
|
|
6599
6692
|
} catch {
|