@biffo/cli 0.231.0 → 0.231.2
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 +364 -286
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -570,8 +570,8 @@ async function runCoreStatus(options) {
|
|
|
570
570
|
|
|
571
571
|
// src/commands/core-upgrade.ts
|
|
572
572
|
import { execSync as execSync2 } from "child_process";
|
|
573
|
-
import { existsSync as
|
|
574
|
-
import { join as
|
|
573
|
+
import { existsSync as existsSync12, rmSync as rmSync5 } from "fs";
|
|
574
|
+
import { join as join13, resolve as resolve3 } from "path";
|
|
575
575
|
import chalk4 from "chalk";
|
|
576
576
|
import { execa as execa3 } from "execa";
|
|
577
577
|
import { Command as Command3 } from "commander";
|
|
@@ -2774,9 +2774,48 @@ function materializeTemplateAtTag(repo, version, git = defaultGit2) {
|
|
|
2774
2774
|
return { dir, cleanup: () => rmSync3(dir, { recursive: true, force: true }) };
|
|
2775
2775
|
}
|
|
2776
2776
|
|
|
2777
|
-
// src/lib/
|
|
2777
|
+
// src/lib/instance-seams.ts
|
|
2778
2778
|
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
|
|
2779
2779
|
import { join as join9 } from "path";
|
|
2780
|
+
var INSTANCE_SEAM_PREFIX = "@/instance-";
|
|
2781
|
+
function readSeams(templateDir, portalRelDir) {
|
|
2782
|
+
const seams = /* @__PURE__ */ new Map();
|
|
2783
|
+
const tsconfigPath = join9(templateDir, portalRelDir, "tsconfig.json");
|
|
2784
|
+
if (!existsSync8(tsconfigPath)) return seams;
|
|
2785
|
+
let parsed;
|
|
2786
|
+
try {
|
|
2787
|
+
parsed = JSON.parse(readFileSync6(tsconfigPath, "utf8"));
|
|
2788
|
+
} catch {
|
|
2789
|
+
return seams;
|
|
2790
|
+
}
|
|
2791
|
+
for (const [specifier, targets] of Object.entries(parsed.compilerOptions?.paths ?? {})) {
|
|
2792
|
+
if (!specifier.startsWith(INSTANCE_SEAM_PREFIX)) continue;
|
|
2793
|
+
const target = targets[0];
|
|
2794
|
+
if (target === void 0) continue;
|
|
2795
|
+
const name = specifier.slice("@/".length);
|
|
2796
|
+
seams.set(specifier, {
|
|
2797
|
+
specifier,
|
|
2798
|
+
instanceFile: `${portalRelDir}/src/${name}.ts`,
|
|
2799
|
+
defaultFile: `${portalRelDir}/${target.replace(/^\.\//, "")}`
|
|
2800
|
+
});
|
|
2801
|
+
}
|
|
2802
|
+
return seams;
|
|
2803
|
+
}
|
|
2804
|
+
function findNewUndeclaredSeams(baseDir, theirsDir, oursDir, portalRelDir = "apps/portal") {
|
|
2805
|
+
const baseSeams = readSeams(baseDir, portalRelDir);
|
|
2806
|
+
const theirsSeams = readSeams(theirsDir, portalRelDir);
|
|
2807
|
+
const undeclared = [];
|
|
2808
|
+
for (const [specifier, seam] of theirsSeams) {
|
|
2809
|
+
if (baseSeams.has(specifier)) continue;
|
|
2810
|
+
if (existsSync8(join9(oursDir, seam.instanceFile))) continue;
|
|
2811
|
+
undeclared.push(seam);
|
|
2812
|
+
}
|
|
2813
|
+
return undeclared.sort((a, b) => a.specifier.localeCompare(b.specifier));
|
|
2814
|
+
}
|
|
2815
|
+
|
|
2816
|
+
// src/lib/breaking-changes.ts
|
|
2817
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
|
|
2818
|
+
import { join as join10 } from "path";
|
|
2780
2819
|
var UPGRADE_GUIDE_PATH = "docs/guides/core-upgrade.md";
|
|
2781
2820
|
var SECTION_HEADING = "## Breaking changes by version";
|
|
2782
2821
|
var ENTRY_HEADING = /^###\s+(\d+\.\d+\.\d+)\s*[—-]\s*(.+?)\s*$/;
|
|
@@ -2801,9 +2840,9 @@ function parseBreakingChanges(guide) {
|
|
|
2801
2840
|
return entries;
|
|
2802
2841
|
}
|
|
2803
2842
|
function readBreakingChanges(templateRoot) {
|
|
2804
|
-
const path =
|
|
2805
|
-
if (!
|
|
2806
|
-
return parseBreakingChanges(
|
|
2843
|
+
const path = join10(templateRoot, UPGRADE_GUIDE_PATH);
|
|
2844
|
+
if (!existsSync9(path)) return [];
|
|
2845
|
+
return parseBreakingChanges(readFileSync7(path, "utf8"));
|
|
2807
2846
|
}
|
|
2808
2847
|
function breakingChangesBetween(from, to, entries) {
|
|
2809
2848
|
parseCoreVersion(from);
|
|
@@ -2820,8 +2859,8 @@ var GLOBAL_DISPATCH_WORKFLOW_PATHS = [
|
|
|
2820
2859
|
];
|
|
2821
2860
|
|
|
2822
2861
|
// src/lib/plugin-terraform-wiring.ts
|
|
2823
|
-
import { existsSync as
|
|
2824
|
-
import { join as
|
|
2862
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync8, readdirSync as readdirSync3, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
2863
|
+
import { join as join11 } from "path";
|
|
2825
2864
|
var TEMPLATE_MODULE_DIR = "_template";
|
|
2826
2865
|
var DEFAULT_PLUGIN_HANDLER = "src.lambda.main.handler";
|
|
2827
2866
|
var GENERATED_TF_FILE = "plugins.generated.tf";
|
|
@@ -2846,7 +2885,7 @@ function standardArguments(pluginName, handler) {
|
|
|
2846
2885
|
];
|
|
2847
2886
|
}
|
|
2848
2887
|
function listPluginModules(cwd) {
|
|
2849
|
-
const dir =
|
|
2888
|
+
const dir = join11(cwd, "modules", "plugins");
|
|
2850
2889
|
let entries;
|
|
2851
2890
|
try {
|
|
2852
2891
|
entries = readdirSync3(dir, { withFileTypes: true });
|
|
@@ -2858,7 +2897,7 @@ function listPluginModules(cwd) {
|
|
|
2858
2897
|
var FIRST_PARTY_TERRAFORM = (name) => `../../../services/_plugins/${name}/terraform`;
|
|
2859
2898
|
var THIRD_PARTY_TERRAFORM = (name) => `../../../modules/plugins/${name}`;
|
|
2860
2899
|
function isFirstPartyPlugin(cwd, name) {
|
|
2861
|
-
return
|
|
2900
|
+
return existsSync10(join11(cwd, "services", "_plugins", name, "terraform", "main.tf"));
|
|
2862
2901
|
}
|
|
2863
2902
|
function pluginModuleSource(cwd, name) {
|
|
2864
2903
|
return isFirstPartyPlugin(cwd, name) ? FIRST_PARTY_TERRAFORM(name) : THIRD_PARTY_TERRAFORM(name);
|
|
@@ -2867,7 +2906,7 @@ function listWireablePlugins(cwd) {
|
|
|
2867
2906
|
return listPluginModules(cwd).filter((name) => !isFirstPartyPlugin(cwd, name)).sort();
|
|
2868
2907
|
}
|
|
2869
2908
|
function firstPartyPluginNames(cwd) {
|
|
2870
|
-
const dir =
|
|
2909
|
+
const dir = join11(cwd, "services", "_plugins");
|
|
2871
2910
|
let entries;
|
|
2872
2911
|
try {
|
|
2873
2912
|
entries = readdirSync3(dir, { withFileTypes: true });
|
|
@@ -2881,7 +2920,7 @@ function staleFirstPartyCopies(cwd) {
|
|
|
2881
2920
|
return firstPartyPluginNames(cwd).filter((name) => copied.has(name));
|
|
2882
2921
|
}
|
|
2883
2922
|
function listEnvironments(cwd) {
|
|
2884
|
-
const dir =
|
|
2923
|
+
const dir = join11(cwd, "infra", "environments");
|
|
2885
2924
|
let entries;
|
|
2886
2925
|
try {
|
|
2887
2926
|
entries = readdirSync3(dir, { withFileTypes: true });
|
|
@@ -2889,12 +2928,12 @@ function listEnvironments(cwd) {
|
|
|
2889
2928
|
return [];
|
|
2890
2929
|
}
|
|
2891
2930
|
return entries.filter((e) => {
|
|
2892
|
-
if (!e.isDirectory() || !
|
|
2893
|
-
return declaredVariables(
|
|
2931
|
+
if (!e.isDirectory() || !existsSync10(join11(dir, e.name, "main.tf"))) return false;
|
|
2932
|
+
return declaredVariables(join11(dir, e.name)).has("enabled_plugins");
|
|
2894
2933
|
}).map((e) => e.name).sort();
|
|
2895
2934
|
}
|
|
2896
2935
|
function listUnwirableEnvironments(cwd) {
|
|
2897
|
-
const dir =
|
|
2936
|
+
const dir = join11(cwd, "infra", "environments");
|
|
2898
2937
|
let entries;
|
|
2899
2938
|
try {
|
|
2900
2939
|
entries = readdirSync3(dir, { withFileTypes: true });
|
|
@@ -2902,7 +2941,7 @@ function listUnwirableEnvironments(cwd) {
|
|
|
2902
2941
|
return [];
|
|
2903
2942
|
}
|
|
2904
2943
|
return entries.filter(
|
|
2905
|
-
(e) => e.isDirectory() &&
|
|
2944
|
+
(e) => e.isDirectory() && existsSync10(join11(dir, e.name, "main.tf")) && !declaredVariables(join11(dir, e.name)).has("enabled_plugins")
|
|
2906
2945
|
).map((e) => e.name).sort();
|
|
2907
2946
|
}
|
|
2908
2947
|
function declaredVariables(moduleDir) {
|
|
@@ -2917,7 +2956,7 @@ function declaredVariables(moduleDir) {
|
|
|
2917
2956
|
if (!entry.isFile() || !entry.name.endsWith(".tf")) continue;
|
|
2918
2957
|
let contents;
|
|
2919
2958
|
try {
|
|
2920
|
-
contents =
|
|
2959
|
+
contents = readFileSync8(join11(moduleDir, entry.name), "utf8");
|
|
2921
2960
|
} catch {
|
|
2922
2961
|
continue;
|
|
2923
2962
|
}
|
|
@@ -2939,7 +2978,7 @@ function declaredOutputs(moduleDir) {
|
|
|
2939
2978
|
if (!entry.isFile() || !entry.name.endsWith(".tf")) continue;
|
|
2940
2979
|
let contents;
|
|
2941
2980
|
try {
|
|
2942
|
-
contents =
|
|
2981
|
+
contents = readFileSync8(join11(moduleDir, entry.name), "utf8");
|
|
2943
2982
|
} catch {
|
|
2944
2983
|
continue;
|
|
2945
2984
|
}
|
|
@@ -3021,7 +3060,7 @@ function syncPluginTerraform(cwd) {
|
|
|
3021
3060
|
const skippedEnvironments = listUnwirableEnvironments(cwd);
|
|
3022
3061
|
const changedPaths = [];
|
|
3023
3062
|
const rendered = plugins.map((name) => {
|
|
3024
|
-
const moduleDir =
|
|
3063
|
+
const moduleDir = join11(cwd, "modules", "plugins", name);
|
|
3025
3064
|
return {
|
|
3026
3065
|
name,
|
|
3027
3066
|
declaredVariables: declaredVariables(moduleDir),
|
|
@@ -3030,16 +3069,16 @@ function syncPluginTerraform(cwd) {
|
|
|
3030
3069
|
};
|
|
3031
3070
|
});
|
|
3032
3071
|
for (const env of environments) {
|
|
3033
|
-
const envDir =
|
|
3034
|
-
const tfPath =
|
|
3035
|
-
const tfvarsPath =
|
|
3072
|
+
const envDir = join11(cwd, "infra", "environments", env);
|
|
3073
|
+
const tfPath = join11(envDir, GENERATED_TF_FILE);
|
|
3074
|
+
const tfvarsPath = join11(envDir, GENERATED_TFVARS_FILE);
|
|
3036
3075
|
const relBase = `infra/environments/${env}`;
|
|
3037
3076
|
if (plugins.length === 0) {
|
|
3038
3077
|
for (const [abs, rel] of [
|
|
3039
3078
|
[tfPath, `${relBase}/${GENERATED_TF_FILE}`],
|
|
3040
3079
|
[tfvarsPath, `${relBase}/${GENERATED_TFVARS_FILE}`]
|
|
3041
3080
|
]) {
|
|
3042
|
-
if (
|
|
3081
|
+
if (existsSync10(abs)) {
|
|
3043
3082
|
rmSync4(abs);
|
|
3044
3083
|
changedPaths.push(rel);
|
|
3045
3084
|
}
|
|
@@ -3055,8 +3094,8 @@ function syncPluginTerraform(cwd) {
|
|
|
3055
3094
|
}
|
|
3056
3095
|
|
|
3057
3096
|
// src/lib/lockfile-refresh.ts
|
|
3058
|
-
import { existsSync as
|
|
3059
|
-
import { join as
|
|
3097
|
+
import { existsSync as existsSync11 } from "fs";
|
|
3098
|
+
import { join as join12 } from "path";
|
|
3060
3099
|
var LOCKFILE_TRIGGERS = [
|
|
3061
3100
|
{
|
|
3062
3101
|
manifest: "package.json",
|
|
@@ -3079,7 +3118,7 @@ function lockfilesNeedingRefresh(changedPaths, instanceDir, triggers = LOCKFILE_
|
|
|
3079
3118
|
const locked = changedPaths.filter((p) => !isForeignManifest(p));
|
|
3080
3119
|
return triggers.filter((t) => {
|
|
3081
3120
|
const touched = locked.some((p) => p === t.manifest || p.endsWith(`/${t.manifest}`));
|
|
3082
|
-
return touched &&
|
|
3121
|
+
return touched && existsSync11(join12(instanceDir, t.lockfile));
|
|
3083
3122
|
});
|
|
3084
3123
|
}
|
|
3085
3124
|
async function refreshLockfiles(instanceDir, triggers, run) {
|
|
@@ -3288,6 +3327,7 @@ async function runCoreUpgradeResolved(options, deps, cleanups) {
|
|
|
3288
3327
|
manifest
|
|
3289
3328
|
});
|
|
3290
3329
|
const orphanRatchet = checkOrphanRatchet(plan.orphaned.length, readOrphanBaseline(options.cwd));
|
|
3330
|
+
const newSeams = findNewUndeclaredSeams(baseDir, theirsDir, options.cwd);
|
|
3291
3331
|
const heading = options.apply ? "Biffo core upgrade" : "Biffo core upgrade (dry run)";
|
|
3292
3332
|
console.log(chalk4.bold(`
|
|
3293
3333
|
${heading}
|
|
@@ -3296,6 +3336,7 @@ async function runCoreUpgradeResolved(options, deps, cleanups) {
|
|
|
3296
3336
|
console.log(` merge base: ${fromVersion}`);
|
|
3297
3337
|
console.log(` target: ${toVersion}
|
|
3298
3338
|
`);
|
|
3339
|
+
printNewInstanceSeams(newSeams);
|
|
3299
3340
|
printOrphanReport(plan.orphaned, orphanRatchet);
|
|
3300
3341
|
if (orphanRatchet.increased) {
|
|
3301
3342
|
throw new Error(
|
|
@@ -3358,10 +3399,11 @@ async function runCoreUpgradeResolved(options, deps, cleanups) {
|
|
|
3358
3399
|
breaking,
|
|
3359
3400
|
theirsDir,
|
|
3360
3401
|
coreVersionCleanup,
|
|
3361
|
-
orphanRatchet
|
|
3402
|
+
orphanRatchet,
|
|
3403
|
+
newSeams
|
|
3362
3404
|
);
|
|
3363
3405
|
}
|
|
3364
|
-
async function applyAndOpenPr(options, deps, plan, migrations, fromVersion, toVersion, breaking, theirsDir, coreVersionCleanup, orphanRatchet) {
|
|
3406
|
+
async function applyAndOpenPr(options, deps, plan, migrations, fromVersion, toVersion, breaking, theirsDir, coreVersionCleanup, orphanRatchet, newSeams) {
|
|
3365
3407
|
if (breaking.length > 0 && !options.acknowledgeBreaking) {
|
|
3366
3408
|
throw new Error(
|
|
3367
3409
|
`This upgrade crosses ${breaking.length} documented breaking change(s): ${breaking.map((b) => b.version).join(", ")}. They are printed above and in ${UPGRADE_GUIDE_PATH}. Read what each one requires \u2014 some destroy data or need manual work after the deploy \u2014 then re-run with --acknowledge-breaking.`
|
|
@@ -3393,6 +3435,7 @@ async function applyAndOpenPr(options, deps, plan, migrations, fromVersion, toVe
|
|
|
3393
3435
|
theirsDir,
|
|
3394
3436
|
coreVersionCleanup,
|
|
3395
3437
|
orphanRatchet,
|
|
3438
|
+
newSeams,
|
|
3396
3439
|
branch,
|
|
3397
3440
|
token
|
|
3398
3441
|
);
|
|
@@ -3427,7 +3470,7 @@ async function restoreCallerBranch(git, cwd, callerBranch, upgradeBranch) {
|
|
|
3427
3470
|
);
|
|
3428
3471
|
}
|
|
3429
3472
|
}
|
|
3430
|
-
async function buildCommitAndOpenPr(options, deps, plan, migrations, fromVersion, toVersion, breaking, theirsDir, coreVersionCleanup, orphanRatchet, branch, token) {
|
|
3473
|
+
async function buildCommitAndOpenPr(options, deps, plan, migrations, fromVersion, toVersion, breaking, theirsDir, coreVersionCleanup, orphanRatchet, newSeams, branch, token) {
|
|
3431
3474
|
const { git } = deps;
|
|
3432
3475
|
const applied = applyUpgradePlan(options.cwd, plan, theirsDir);
|
|
3433
3476
|
const carried = applyMigrationCarry(options.cwd, migrations);
|
|
@@ -3440,7 +3483,7 @@ async function buildCommitAndOpenPr(options, deps, plan, migrations, fromVersion
|
|
|
3440
3483
|
);
|
|
3441
3484
|
}
|
|
3442
3485
|
const cleanedCoreVersion = coreVersionCleanup?.action === "delete";
|
|
3443
|
-
if (cleanedCoreVersion &&
|
|
3486
|
+
if (cleanedCoreVersion && existsSync12(coreVersionCleanup.path)) {
|
|
3444
3487
|
rmSync5(coreVersionCleanup.path);
|
|
3445
3488
|
log.info(
|
|
3446
3489
|
coreVersionCleanup.stale ? `Deleted stale ${CORE_VERSION_FILE} (recorded ${coreVersionCleanup.found}, behind the version biffo.core.json records \u2014 this instance has moved past it) (#842).` : `Deleted orphaned ${CORE_VERSION_FILE} (inherited copy recording ${coreVersionCleanup.found}, superseded by biffo.core.json) \u2014 nothing reads it as an authority (#434).`
|
|
@@ -3479,7 +3522,8 @@ async function buildCommitAndOpenPr(options, deps, plan, migrations, fromVersion
|
|
|
3479
3522
|
lockfiles,
|
|
3480
3523
|
breaking,
|
|
3481
3524
|
cleanedCoreVersion ? coreVersionCleanup : null,
|
|
3482
|
-
carriedPrs
|
|
3525
|
+
carriedPrs,
|
|
3526
|
+
newSeams
|
|
3483
3527
|
)
|
|
3484
3528
|
});
|
|
3485
3529
|
if (plan.conflicts.length > 0) {
|
|
@@ -3535,7 +3579,7 @@ function carriedPrNumbers(subjects) {
|
|
|
3535
3579
|
}
|
|
3536
3580
|
return [...new Set(numbers)].sort((a, b) => a - b);
|
|
3537
3581
|
}
|
|
3538
|
-
function buildPrBody(from, to, plan, migrations, base = GLOBAL_DISPATCH_REF, lockfiles = [], breaking = [], coreVersionCleanup = null, carriedPrs = []) {
|
|
3582
|
+
function buildPrBody(from, to, plan, migrations, base = GLOBAL_DISPATCH_REF, lockfiles = [], breaking = [], coreVersionCleanup = null, carriedPrs = [], newSeams = []) {
|
|
3539
3583
|
const lines = [];
|
|
3540
3584
|
if (breaking.length > 0) {
|
|
3541
3585
|
lines.push(
|
|
@@ -3548,6 +3592,25 @@ function buildPrBody(from, to, plan, migrations, base = GLOBAL_DISPATCH_REF, loc
|
|
|
3548
3592
|
""
|
|
3549
3593
|
);
|
|
3550
3594
|
}
|
|
3595
|
+
if (newSeams.length > 0) {
|
|
3596
|
+
lines.push(
|
|
3597
|
+
`## \u26A0 ${newSeams.length} new instance seam(s) \u2014 no declaration yet (#1188)`,
|
|
3598
|
+
"",
|
|
3599
|
+
"This upgrade introduces a new optional `@/instance-*` module the portal can resolve to an instance-owned override. **This instance has not declared one**, so until it does, it silently gets the template-owned generic default listed below \u2014 not necessarily the behaviour this instance actually wants.",
|
|
3600
|
+
"",
|
|
3601
|
+
...newSeams.flatMap((s) => [
|
|
3602
|
+
`### \`${s.specifier}\``,
|
|
3603
|
+
"",
|
|
3604
|
+
`- Add \`${s.instanceFile}\` to declare this instance's own behaviour.`,
|
|
3605
|
+
`- Until then, this instance uses the template default: \`${s.defaultFile}\`.`,
|
|
3606
|
+
""
|
|
3607
|
+
]),
|
|
3608
|
+
"The contract this module implements is part of THIS upgrade, so it does not exist yet anywhere the instance could have written the file in advance \u2014 add it as a follow-up commit on this PR, before merging, once the files above have landed on this branch.",
|
|
3609
|
+
"",
|
|
3610
|
+
"---",
|
|
3611
|
+
""
|
|
3612
|
+
);
|
|
3613
|
+
}
|
|
3551
3614
|
lines.push(
|
|
3552
3615
|
"Automated core upgrade generated by `biffo core upgrade` (ADR-0006).",
|
|
3553
3616
|
"",
|
|
@@ -3723,6 +3786,21 @@ function printCoreVersionCleanup(cleanup, applying) {
|
|
|
3723
3786
|
const why = cleanup.reason === "repurposed" ? `does not match biffo.core.json \u2014 looks repurposed, keeping ${CORE_VERSION_FILE}` : `biffo.core.json absent or unparseable \u2014 no authority to check, keeping ${CORE_VERSION_FILE}`;
|
|
3724
3787
|
console.log(` ${chalk4.dim("cleanup".padEnd(15))} ${chalk4.dim(`${cleanup.found}: ${why}`)}`);
|
|
3725
3788
|
}
|
|
3789
|
+
function printNewInstanceSeams(seams) {
|
|
3790
|
+
if (seams.length === 0) return;
|
|
3791
|
+
console.log(
|
|
3792
|
+
chalk4.red.bold(
|
|
3793
|
+
` \u26A0 ${String(seams.length)} new instance seam(s) introduced with no declaration (#1188):`
|
|
3794
|
+
)
|
|
3795
|
+
);
|
|
3796
|
+
for (const s of seams) {
|
|
3797
|
+
console.log(` ${chalk4.bold(s.specifier)} \u2192 add ${chalk4.yellow(s.instanceFile)}`);
|
|
3798
|
+
console.log(
|
|
3799
|
+
chalk4.dim(` Until then, this instance gets the template default: ${s.defaultFile}`)
|
|
3800
|
+
);
|
|
3801
|
+
}
|
|
3802
|
+
console.log();
|
|
3803
|
+
}
|
|
3726
3804
|
function printOrphanReport(orphaned, ratchet) {
|
|
3727
3805
|
if (orphaned.length === 0 && ratchet.baseline === null) return;
|
|
3728
3806
|
console.log(
|
|
@@ -3816,8 +3894,8 @@ function printBreakingChanges(breaking, applying) {
|
|
|
3816
3894
|
}
|
|
3817
3895
|
function versionOfCheckout(dir, explicit) {
|
|
3818
3896
|
if (explicit) return explicit;
|
|
3819
|
-
const file =
|
|
3820
|
-
if (
|
|
3897
|
+
const file = join13(dir, CORE_VERSION_FILE);
|
|
3898
|
+
if (existsSync12(file)) return readCoreVersionFile(file);
|
|
3821
3899
|
throw new Error(
|
|
3822
3900
|
`Cannot determine the core version of ${dir}: it has no ${CORE_VERSION_FILE}, and a checkout supplied explicitly is not resolved from a tag. Pass --to to state which version this tree is.`
|
|
3823
3901
|
);
|
|
@@ -3825,8 +3903,8 @@ function versionOfCheckout(dir, explicit) {
|
|
|
3825
3903
|
function latestCoreVersion(repo) {
|
|
3826
3904
|
const fromTags = latestCoreVersionFromTags(repo);
|
|
3827
3905
|
if (fromTags) return fromTags;
|
|
3828
|
-
const file =
|
|
3829
|
-
if (
|
|
3906
|
+
const file = join13(repo, CORE_VERSION_FILE);
|
|
3907
|
+
if (existsSync12(file)) return readCoreVersionFile(file);
|
|
3830
3908
|
throw new Error(
|
|
3831
3909
|
`Cannot determine the template's core version: ${repo} has no core-v* tags and no ${CORE_VERSION_FILE}. Fetch tags (\`git fetch --tags\`) or pass --to explicitly.`
|
|
3832
3910
|
);
|
|
@@ -3844,7 +3922,7 @@ coreCommand.addCommand(coreUpgradeCommand);
|
|
|
3844
3922
|
import { Command as Command8 } from "commander";
|
|
3845
3923
|
|
|
3846
3924
|
// src/commands/data-apply.ts
|
|
3847
|
-
import { existsSync as
|
|
3925
|
+
import { existsSync as existsSync14, readFileSync as readFileSync10 } from "fs";
|
|
3848
3926
|
import { resolve as resolve4 } from "path";
|
|
3849
3927
|
import chalk5 from "chalk";
|
|
3850
3928
|
import { Command as Command5 } from "commander";
|
|
@@ -4295,16 +4373,16 @@ function isTemplatePlaceholderConfig(raw) {
|
|
|
4295
4373
|
|
|
4296
4374
|
// src/lib/session.ts
|
|
4297
4375
|
import {
|
|
4298
|
-
existsSync as
|
|
4376
|
+
existsSync as existsSync13,
|
|
4299
4377
|
mkdirSync as mkdirSync4,
|
|
4300
4378
|
readdirSync as readdirSync4,
|
|
4301
|
-
readFileSync as
|
|
4379
|
+
readFileSync as readFileSync9,
|
|
4302
4380
|
rmSync as rmSync6,
|
|
4303
4381
|
statSync as statSync2,
|
|
4304
4382
|
writeFileSync as writeFileSync5
|
|
4305
4383
|
} from "fs";
|
|
4306
4384
|
import { homedir } from "os";
|
|
4307
|
-
import { join as
|
|
4385
|
+
import { join as join14 } from "path";
|
|
4308
4386
|
var LEGACY_STEP_ALIASES = {
|
|
4309
4387
|
github_config: ["github_branches", "github_instance_files", "github_settings"]
|
|
4310
4388
|
};
|
|
@@ -4313,39 +4391,39 @@ function hasCompleted(session, step) {
|
|
|
4313
4391
|
return session.completedSteps.some((done) => LEGACY_STEP_ALIASES[done]?.includes(step) ?? false);
|
|
4314
4392
|
}
|
|
4315
4393
|
function sessionsDir() {
|
|
4316
|
-
return process.env["BIFFO_SESSIONS_DIR"] ??
|
|
4394
|
+
return process.env["BIFFO_SESSIONS_DIR"] ?? join14(homedir(), ".biffo", "sessions");
|
|
4317
4395
|
}
|
|
4318
4396
|
function sessionPath(projectName) {
|
|
4319
|
-
return
|
|
4397
|
+
return join14(sessionsDir(), `${projectName}.json`);
|
|
4320
4398
|
}
|
|
4321
4399
|
function loadSession(projectName) {
|
|
4322
4400
|
const path = sessionPath(projectName);
|
|
4323
|
-
if (!
|
|
4401
|
+
if (!existsSync13(path)) return null;
|
|
4324
4402
|
try {
|
|
4325
|
-
return JSON.parse(
|
|
4403
|
+
return JSON.parse(readFileSync9(path, "utf8"));
|
|
4326
4404
|
} catch {
|
|
4327
4405
|
return null;
|
|
4328
4406
|
}
|
|
4329
4407
|
}
|
|
4330
4408
|
function findLatestSession() {
|
|
4331
4409
|
const dir = sessionsDir();
|
|
4332
|
-
if (!
|
|
4410
|
+
if (!existsSync13(dir)) return null;
|
|
4333
4411
|
const files = readdirSync4(dir).filter((f) => f.endsWith(".json"));
|
|
4334
4412
|
if (files.length === 0) return null;
|
|
4335
4413
|
const sorted = files.map((f) => {
|
|
4336
|
-
const fullPath =
|
|
4337
|
-
const mtime =
|
|
4414
|
+
const fullPath = join14(dir, f);
|
|
4415
|
+
const mtime = existsSync13(fullPath) ? statSync2(fullPath).mtimeMs : -1;
|
|
4338
4416
|
return { f, mtime };
|
|
4339
4417
|
}).sort((a, b) => b.mtime - a.mtime);
|
|
4340
4418
|
try {
|
|
4341
|
-
return JSON.parse(
|
|
4419
|
+
return JSON.parse(readFileSync9(join14(dir, sorted[0].f), "utf8"));
|
|
4342
4420
|
} catch {
|
|
4343
4421
|
return null;
|
|
4344
4422
|
}
|
|
4345
4423
|
}
|
|
4346
4424
|
function saveSession(session) {
|
|
4347
4425
|
const dir = sessionsDir();
|
|
4348
|
-
if (!
|
|
4426
|
+
if (!existsSync13(dir)) mkdirSync4(dir, { recursive: true });
|
|
4349
4427
|
const name = session.config.project?.name ?? "unknown";
|
|
4350
4428
|
const prior = loadSession(name);
|
|
4351
4429
|
if (prior) {
|
|
@@ -4367,36 +4445,36 @@ function markStepComplete(session, step) {
|
|
|
4367
4445
|
}
|
|
4368
4446
|
function deleteSession(projectName) {
|
|
4369
4447
|
const path = sessionPath(projectName);
|
|
4370
|
-
if (
|
|
4448
|
+
if (existsSync13(path)) rmSync6(path);
|
|
4371
4449
|
}
|
|
4372
4450
|
function projectsDir() {
|
|
4373
|
-
return process.env["BIFFO_PROJECTS_DIR"] ??
|
|
4451
|
+
return process.env["BIFFO_PROJECTS_DIR"] ?? join14(homedir(), ".biffo", "projects");
|
|
4374
4452
|
}
|
|
4375
4453
|
function saveProjectConfig(config) {
|
|
4376
4454
|
const dir = projectsDir();
|
|
4377
|
-
if (!
|
|
4378
|
-
writeFileSync5(
|
|
4455
|
+
if (!existsSync13(dir)) mkdirSync4(dir, { recursive: true });
|
|
4456
|
+
writeFileSync5(join14(dir, `${config.project.name}.json`), JSON.stringify(config, null, 2));
|
|
4379
4457
|
}
|
|
4380
4458
|
function loadProjectConfig(name) {
|
|
4381
|
-
const path =
|
|
4382
|
-
if (!
|
|
4459
|
+
const path = join14(projectsDir(), `${name}.json`);
|
|
4460
|
+
if (!existsSync13(path)) return null;
|
|
4383
4461
|
try {
|
|
4384
|
-
const result = BiffoConfigSchema.safeParse(JSON.parse(
|
|
4462
|
+
const result = BiffoConfigSchema.safeParse(JSON.parse(readFileSync9(path, "utf8")));
|
|
4385
4463
|
return result.success ? result.data : null;
|
|
4386
4464
|
} catch {
|
|
4387
4465
|
return null;
|
|
4388
4466
|
}
|
|
4389
4467
|
}
|
|
4390
4468
|
function deleteProjectConfig(name) {
|
|
4391
|
-
const path =
|
|
4392
|
-
if (
|
|
4469
|
+
const path = join14(projectsDir(), `${name}.json`);
|
|
4470
|
+
if (existsSync13(path)) rmSync6(path);
|
|
4393
4471
|
}
|
|
4394
4472
|
function listProjectConfigs() {
|
|
4395
4473
|
const dir = projectsDir();
|
|
4396
|
-
if (!
|
|
4474
|
+
if (!existsSync13(dir)) return [];
|
|
4397
4475
|
return readdirSync4(dir).filter((f) => f.endsWith(".json")).flatMap((f) => {
|
|
4398
4476
|
try {
|
|
4399
|
-
const result = BiffoConfigSchema.safeParse(JSON.parse(
|
|
4477
|
+
const result = BiffoConfigSchema.safeParse(JSON.parse(readFileSync9(join14(dir, f), "utf8")));
|
|
4400
4478
|
return result.success ? [result.data] : [];
|
|
4401
4479
|
} catch {
|
|
4402
4480
|
return [];
|
|
@@ -4465,7 +4543,7 @@ async function runDataApply(name, environment, config, aws) {
|
|
|
4465
4543
|
}
|
|
4466
4544
|
async function resolveConfig(options) {
|
|
4467
4545
|
if (options.config) {
|
|
4468
|
-
const raw = JSON.parse(
|
|
4546
|
+
const raw = JSON.parse(readFileSync10(resolve4(options.config), "utf8"));
|
|
4469
4547
|
const result = BiffoConfigSchema.safeParse(raw);
|
|
4470
4548
|
if (!result.success) {
|
|
4471
4549
|
log.error(`Invalid config at ${options.config}:`);
|
|
@@ -4485,8 +4563,8 @@ async function resolveConfig(options) {
|
|
|
4485
4563
|
return cfg;
|
|
4486
4564
|
}
|
|
4487
4565
|
const localConfigPath = resolve4(process.cwd(), "biffo.config.json");
|
|
4488
|
-
if (
|
|
4489
|
-
const raw = JSON.parse(
|
|
4566
|
+
if (existsSync14(localConfigPath)) {
|
|
4567
|
+
const raw = JSON.parse(readFileSync10(localConfigPath, "utf8"));
|
|
4490
4568
|
const result = BiffoConfigSchema.safeParse(raw);
|
|
4491
4569
|
if (result.success) return result.data;
|
|
4492
4570
|
if (isTemplatePlaceholderConfig(raw)) {
|
|
@@ -4532,8 +4610,8 @@ async function resolveConfig(options) {
|
|
|
4532
4610
|
|
|
4533
4611
|
// src/commands/data-import.ts
|
|
4534
4612
|
import { execSync as execSync3 } from "child_process";
|
|
4535
|
-
import { cpSync, existsSync as
|
|
4536
|
-
import { join as
|
|
4613
|
+
import { cpSync, existsSync as existsSync15, mkdirSync as mkdirSync5, readdirSync as readdirSync5, statSync as statSync3 } from "fs";
|
|
4614
|
+
import { join as join15, resolve as resolve5 } from "path";
|
|
4537
4615
|
import chalk6 from "chalk";
|
|
4538
4616
|
import { Command as Command6 } from "commander";
|
|
4539
4617
|
import inquirer2 from "inquirer";
|
|
@@ -4573,23 +4651,23 @@ async function runDataImport(name, options, deps) {
|
|
|
4573
4651
|
`Invalid import name '${name}'. Use lowercase letters, numbers, and hyphens, starting with a letter.`
|
|
4574
4652
|
);
|
|
4575
4653
|
}
|
|
4576
|
-
const servicesDir =
|
|
4577
|
-
if (!
|
|
4654
|
+
const servicesDir = join15(options.cwd, "services");
|
|
4655
|
+
if (!existsSync15(servicesDir)) {
|
|
4578
4656
|
throw new Error(
|
|
4579
4657
|
`${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
|
|
4580
4658
|
);
|
|
4581
4659
|
}
|
|
4582
|
-
const targetDir =
|
|
4583
|
-
if (
|
|
4660
|
+
const targetDir = join15(options.cwd, "db", "imports", name);
|
|
4661
|
+
if (existsSync15(targetDir)) {
|
|
4584
4662
|
throw new Error(
|
|
4585
4663
|
`DDL import '${name}' is already present at db/imports/${name}/. Remove it first to re-import.`
|
|
4586
4664
|
);
|
|
4587
4665
|
}
|
|
4588
|
-
const isLocalDir =
|
|
4666
|
+
const isLocalDir = existsSync15(options.source) && statSync3(options.source).isDirectory();
|
|
4589
4667
|
let sourceDir;
|
|
4590
4668
|
let cleanupClone = null;
|
|
4591
4669
|
if (isLocalDir) {
|
|
4592
|
-
sourceDir = options.path ?
|
|
4670
|
+
sourceDir = options.path ? join15(options.source, options.path) : options.source;
|
|
4593
4671
|
} else {
|
|
4594
4672
|
const token = options.token ?? await resolveDdlImportToken();
|
|
4595
4673
|
log.info(`Cloning ${options.source}...`);
|
|
@@ -4597,10 +4675,10 @@ async function runDataImport(name, options, deps) {
|
|
|
4597
4675
|
cleanupClone = () => {
|
|
4598
4676
|
deps.git.cleanup(tmpDir);
|
|
4599
4677
|
};
|
|
4600
|
-
sourceDir = options.path ?
|
|
4678
|
+
sourceDir = options.path ? join15(tmpDir, options.path) : tmpDir;
|
|
4601
4679
|
}
|
|
4602
4680
|
try {
|
|
4603
|
-
if (!
|
|
4681
|
+
if (!existsSync15(sourceDir)) {
|
|
4604
4682
|
throw new Error(`Source directory does not exist: ${sourceDir}`);
|
|
4605
4683
|
}
|
|
4606
4684
|
const sqlFiles = readdirSync5(sourceDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".sql")).map((entry) => entry.name).sort();
|
|
@@ -4625,7 +4703,7 @@ async function runDataImport(name, options, deps) {
|
|
|
4625
4703
|
}
|
|
4626
4704
|
mkdirSync5(targetDir, { recursive: true });
|
|
4627
4705
|
for (const file of sqlFiles) {
|
|
4628
|
-
cpSync(
|
|
4706
|
+
cpSync(join15(sourceDir, file), join15(targetDir, file));
|
|
4629
4707
|
}
|
|
4630
4708
|
log.success(`Imported ${String(sqlFiles.length)} .sql file(s) to db/imports/${name}/`);
|
|
4631
4709
|
const commitMessage = `feat(data): import ${name} (${String(sqlFiles.length)} SQL file(s))`;
|
|
@@ -4677,8 +4755,8 @@ function printDryRun(name, sqlFiles) {
|
|
|
4677
4755
|
}
|
|
4678
4756
|
|
|
4679
4757
|
// src/commands/data-list.ts
|
|
4680
|
-
import { existsSync as
|
|
4681
|
-
import { join as
|
|
4758
|
+
import { existsSync as existsSync16, readdirSync as readdirSync6 } from "fs";
|
|
4759
|
+
import { join as join16, resolve as resolve6 } from "path";
|
|
4682
4760
|
import chalk7 from "chalk";
|
|
4683
4761
|
import { Command as Command7 } from "commander";
|
|
4684
4762
|
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) => {
|
|
@@ -4691,15 +4769,15 @@ var dataListCommand = new Command7("list").description("List DDL imports vendore
|
|
|
4691
4769
|
}
|
|
4692
4770
|
});
|
|
4693
4771
|
async function runDataList(options) {
|
|
4694
|
-
const importsDir =
|
|
4695
|
-
if (!
|
|
4772
|
+
const importsDir = join16(options.cwd, "db", "imports");
|
|
4773
|
+
if (!existsSync16(importsDir)) {
|
|
4696
4774
|
console.log(chalk7.dim("\n No DDL imports in this checkout.\n"));
|
|
4697
4775
|
return;
|
|
4698
4776
|
}
|
|
4699
4777
|
const candidates = readdirSync6(importsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
|
|
4700
4778
|
const imports = [];
|
|
4701
4779
|
for (const name of candidates) {
|
|
4702
|
-
const fileCount = readdirSync6(
|
|
4780
|
+
const fileCount = readdirSync6(join16(importsDir, name)).filter((f) => f.endsWith(".sql")).length;
|
|
4703
4781
|
if (fileCount > 0) imports.push({ name, fileCount });
|
|
4704
4782
|
}
|
|
4705
4783
|
if (imports.length === 0) {
|
|
@@ -4729,7 +4807,7 @@ dataCommand.addCommand(dataListCommand);
|
|
|
4729
4807
|
|
|
4730
4808
|
// src/commands/deploy.ts
|
|
4731
4809
|
import { execSync as execSync4 } from "child_process";
|
|
4732
|
-
import { existsSync as
|
|
4810
|
+
import { existsSync as existsSync17, readFileSync as readFileSync11 } from "fs";
|
|
4733
4811
|
import { resolve as resolve7 } from "path";
|
|
4734
4812
|
import chalk8 from "chalk";
|
|
4735
4813
|
import { Command as Command9 } from "commander";
|
|
@@ -5086,7 +5164,7 @@ var deployCommand = new Command9("deploy").description("Deploy infrastructure an
|
|
|
5086
5164
|
);
|
|
5087
5165
|
async function resolveConfig2(options) {
|
|
5088
5166
|
if (options.config) {
|
|
5089
|
-
const raw = JSON.parse(
|
|
5167
|
+
const raw = JSON.parse(readFileSync11(resolve7(options.config), "utf8"));
|
|
5090
5168
|
const result = BiffoConfigSchema.safeParse(raw);
|
|
5091
5169
|
if (!result.success) {
|
|
5092
5170
|
log.error(`Invalid config at ${options.config}:`);
|
|
@@ -5106,8 +5184,8 @@ async function resolveConfig2(options) {
|
|
|
5106
5184
|
return cfg;
|
|
5107
5185
|
}
|
|
5108
5186
|
const localConfigPath = resolve7(process.cwd(), "biffo.config.json");
|
|
5109
|
-
if (
|
|
5110
|
-
const raw = JSON.parse(
|
|
5187
|
+
if (existsSync17(localConfigPath)) {
|
|
5188
|
+
const raw = JSON.parse(readFileSync11(localConfigPath, "utf8"));
|
|
5111
5189
|
const result = BiffoConfigSchema.safeParse(raw);
|
|
5112
5190
|
if (result.success) return result.data;
|
|
5113
5191
|
if (isTemplatePlaceholderConfig(raw)) {
|
|
@@ -5503,7 +5581,7 @@ function resolveGithubToken() {
|
|
|
5503
5581
|
|
|
5504
5582
|
// src/commands/destroy.ts
|
|
5505
5583
|
import { execSync as execSync5 } from "child_process";
|
|
5506
|
-
import { readFileSync as
|
|
5584
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
5507
5585
|
import { resolve as resolve8 } from "path";
|
|
5508
5586
|
import chalk9 from "chalk";
|
|
5509
5587
|
import { Command as Command10 } from "commander";
|
|
@@ -5593,7 +5671,7 @@ var destroyCommand = new Command10("destroy").description("Destroy infrastructur
|
|
|
5593
5671
|
});
|
|
5594
5672
|
async function resolveConfig3(options) {
|
|
5595
5673
|
if (options.config) {
|
|
5596
|
-
const raw = JSON.parse(
|
|
5674
|
+
const raw = JSON.parse(readFileSync12(resolve8(options.config), "utf8"));
|
|
5597
5675
|
const result = BiffoConfigSchema.safeParse(raw);
|
|
5598
5676
|
if (!result.success) {
|
|
5599
5677
|
log.error(`Invalid config at ${options.config}:`);
|
|
@@ -5613,7 +5691,7 @@ async function resolveConfig3(options) {
|
|
|
5613
5691
|
return cfg;
|
|
5614
5692
|
}
|
|
5615
5693
|
try {
|
|
5616
|
-
const raw = JSON.parse(
|
|
5694
|
+
const raw = JSON.parse(readFileSync12(resolve8(process.cwd(), "biffo.config.json"), "utf8"));
|
|
5617
5695
|
const result = BiffoConfigSchema.safeParse(raw);
|
|
5618
5696
|
if (result.success) return result.data;
|
|
5619
5697
|
} catch {
|
|
@@ -5663,15 +5741,15 @@ function resolveGithubToken2() {
|
|
|
5663
5741
|
}
|
|
5664
5742
|
|
|
5665
5743
|
// src/commands/init.ts
|
|
5666
|
-
import { readFileSync as
|
|
5744
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
5667
5745
|
import { resolve as resolve10 } from "path";
|
|
5668
5746
|
import chalk12 from "chalk";
|
|
5669
5747
|
import { Command as Command12 } from "commander";
|
|
5670
5748
|
import inquirer5 from "inquirer";
|
|
5671
5749
|
|
|
5672
5750
|
// src/lib/build-freshness.ts
|
|
5673
|
-
import { existsSync as
|
|
5674
|
-
import { dirname as dirname5, join as
|
|
5751
|
+
import { existsSync as existsSync18, readdirSync as readdirSync7, statSync as statSync4 } from "fs";
|
|
5752
|
+
import { dirname as dirname5, join as join17, relative as relative2, sep as sep2 } from "path";
|
|
5675
5753
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
5676
5754
|
var SKIP_ENV_VAR = "BIFFO_SKIP_BUILD_FRESHNESS_CHECK";
|
|
5677
5755
|
function checkBuildFreshness(options = {}) {
|
|
@@ -5685,7 +5763,7 @@ function checkBuildFreshness(options = {}) {
|
|
|
5685
5763
|
if (!packageRoot) {
|
|
5686
5764
|
return { status: "skipped", reason: `no package.json above ${moduleDir}`, newerSources: [] };
|
|
5687
5765
|
}
|
|
5688
|
-
const distDir =
|
|
5766
|
+
const distDir = join17(packageRoot, "dist");
|
|
5689
5767
|
if (!isInside(distDir, moduleDir)) {
|
|
5690
5768
|
return {
|
|
5691
5769
|
status: "skipped",
|
|
@@ -5693,16 +5771,16 @@ function checkBuildFreshness(options = {}) {
|
|
|
5693
5771
|
newerSources: []
|
|
5694
5772
|
};
|
|
5695
5773
|
}
|
|
5696
|
-
const srcDir =
|
|
5697
|
-
if (!
|
|
5774
|
+
const srcDir = join17(packageRoot, "src");
|
|
5775
|
+
if (!existsSync18(srcDir)) {
|
|
5698
5776
|
return {
|
|
5699
5777
|
status: "skipped",
|
|
5700
5778
|
reason: "no src/ alongside dist/ \u2014 this is a shipped package",
|
|
5701
5779
|
newerSources: []
|
|
5702
5780
|
};
|
|
5703
5781
|
}
|
|
5704
|
-
const entry =
|
|
5705
|
-
if (!
|
|
5782
|
+
const entry = join17(distDir, "index.js");
|
|
5783
|
+
if (!existsSync18(entry)) {
|
|
5706
5784
|
return { status: "skipped", reason: `${entry} not found`, newerSources: [] };
|
|
5707
5785
|
}
|
|
5708
5786
|
const builtAt = statSync4(entry).mtimeMs;
|
|
@@ -5746,7 +5824,7 @@ function collectSourceFiles(srcDir) {
|
|
|
5746
5824
|
const found = [];
|
|
5747
5825
|
const walk = (dir) => {
|
|
5748
5826
|
for (const entry of readdirSync7(dir, { withFileTypes: true })) {
|
|
5749
|
-
const full =
|
|
5827
|
+
const full = join17(dir, entry.name);
|
|
5750
5828
|
if (entry.isDirectory()) {
|
|
5751
5829
|
if (entry.name === "node_modules") continue;
|
|
5752
5830
|
walk(full);
|
|
@@ -5765,7 +5843,7 @@ function collectSourceFiles(srcDir) {
|
|
|
5765
5843
|
function findPackageRoot(from) {
|
|
5766
5844
|
let dir = from;
|
|
5767
5845
|
for (; ; ) {
|
|
5768
|
-
if (
|
|
5846
|
+
if (existsSync18(join17(dir, "package.json"))) return dir;
|
|
5769
5847
|
const parent = dirname5(dir);
|
|
5770
5848
|
if (parent === dir) return null;
|
|
5771
5849
|
dir = parent;
|
|
@@ -5779,9 +5857,9 @@ function isInside(parent, child) {
|
|
|
5779
5857
|
|
|
5780
5858
|
// src/lib/credentials.ts
|
|
5781
5859
|
import { execSync as execSync6 } from "child_process";
|
|
5782
|
-
import { existsSync as
|
|
5860
|
+
import { existsSync as existsSync19, readFileSync as readFileSync13 } from "fs";
|
|
5783
5861
|
import { homedir as homedir2 } from "os";
|
|
5784
|
-
import { join as
|
|
5862
|
+
import { join as join18 } from "path";
|
|
5785
5863
|
import { GetCallerIdentityCommand as GetCallerIdentityCommand2, STSClient as STSClient2 } from "@aws-sdk/client-sts";
|
|
5786
5864
|
import chalk10 from "chalk";
|
|
5787
5865
|
import inquirer4 from "inquirer";
|
|
@@ -5960,11 +6038,11 @@ async function verifySelectedAwsCredentials(profile, region) {
|
|
|
5960
6038
|
return sts.send(new GetCallerIdentityCommand2({}));
|
|
5961
6039
|
}
|
|
5962
6040
|
function discoverAwsProfiles() {
|
|
5963
|
-
const files = [
|
|
6041
|
+
const files = [join18(homedir2(), ".aws", "credentials"), join18(homedir2(), ".aws", "config")];
|
|
5964
6042
|
const profiles = /* @__PURE__ */ new Set();
|
|
5965
6043
|
for (const file of files) {
|
|
5966
|
-
if (!
|
|
5967
|
-
const content =
|
|
6044
|
+
if (!existsSync19(file)) continue;
|
|
6045
|
+
const content = readFileSync13(file, "utf8");
|
|
5968
6046
|
for (const match of content.matchAll(/^\s*\[([^\]]+)\]\s*$/gm)) {
|
|
5969
6047
|
const section = match[1]?.trim();
|
|
5970
6048
|
if (!section) continue;
|
|
@@ -6054,34 +6132,34 @@ var SiblingConfigSchema = z6.object({
|
|
|
6054
6132
|
|
|
6055
6133
|
// src/lib/sibling-session.ts
|
|
6056
6134
|
import {
|
|
6057
|
-
existsSync as
|
|
6135
|
+
existsSync as existsSync20,
|
|
6058
6136
|
mkdirSync as mkdirSync6,
|
|
6059
6137
|
readdirSync as readdirSync8,
|
|
6060
|
-
readFileSync as
|
|
6138
|
+
readFileSync as readFileSync14,
|
|
6061
6139
|
rmSync as rmSync7,
|
|
6062
6140
|
statSync as statSync5,
|
|
6063
6141
|
writeFileSync as writeFileSync6
|
|
6064
6142
|
} from "fs";
|
|
6065
6143
|
import { homedir as homedir3 } from "os";
|
|
6066
|
-
import { join as
|
|
6144
|
+
import { join as join19 } from "path";
|
|
6067
6145
|
function sessionsDir2() {
|
|
6068
|
-
return process.env["BIFFO_SIBLING_SESSIONS_DIR"] ??
|
|
6146
|
+
return process.env["BIFFO_SIBLING_SESSIONS_DIR"] ?? join19(homedir3(), ".biffo", "sibling-sessions");
|
|
6069
6147
|
}
|
|
6070
6148
|
function sessionPath2(projectName) {
|
|
6071
|
-
return
|
|
6149
|
+
return join19(sessionsDir2(), `${projectName}.json`);
|
|
6072
6150
|
}
|
|
6073
6151
|
function loadSiblingSession(projectName) {
|
|
6074
6152
|
const path = sessionPath2(projectName);
|
|
6075
|
-
if (!
|
|
6153
|
+
if (!existsSync20(path)) return null;
|
|
6076
6154
|
try {
|
|
6077
|
-
return JSON.parse(
|
|
6155
|
+
return JSON.parse(readFileSync14(path, "utf8"));
|
|
6078
6156
|
} catch {
|
|
6079
6157
|
return null;
|
|
6080
6158
|
}
|
|
6081
6159
|
}
|
|
6082
6160
|
function saveSiblingSession(session) {
|
|
6083
6161
|
const dir = sessionsDir2();
|
|
6084
|
-
if (!
|
|
6162
|
+
if (!existsSync20(dir)) mkdirSync6(dir, { recursive: true });
|
|
6085
6163
|
const name = session.config.project?.name ?? "unknown";
|
|
6086
6164
|
const prior = loadSiblingSession(name);
|
|
6087
6165
|
if (prior) {
|
|
@@ -6103,30 +6181,30 @@ function markSiblingStepComplete(session, step) {
|
|
|
6103
6181
|
}
|
|
6104
6182
|
function deleteSiblingSession(projectName) {
|
|
6105
6183
|
const path = sessionPath2(projectName);
|
|
6106
|
-
if (
|
|
6184
|
+
if (existsSync20(path)) rmSync7(path);
|
|
6107
6185
|
}
|
|
6108
6186
|
|
|
6109
6187
|
// src/commands/sibling-create.ts
|
|
6110
|
-
import { cpSync as cpSync2, existsSync as
|
|
6188
|
+
import { cpSync as cpSync2, existsSync as existsSync21, mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync4, readFileSync as readFileSync15, writeFileSync as writeFileSync7 } from "fs";
|
|
6111
6189
|
import { tmpdir as tmpdir4 } from "os";
|
|
6112
|
-
import { dirname as dirname6, join as
|
|
6190
|
+
import { dirname as dirname6, join as join21, resolve as resolve9 } from "path";
|
|
6113
6191
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
6114
6192
|
import chalk11 from "chalk";
|
|
6115
6193
|
import { Command as Command11 } from "commander";
|
|
6116
6194
|
|
|
6117
6195
|
// src/lib/skeleton-dotfiles.ts
|
|
6118
6196
|
import { readdirSync as readdirSync9, renameSync } from "fs";
|
|
6119
|
-
import { join as
|
|
6197
|
+
import { join as join20 } from "path";
|
|
6120
6198
|
var PACKAGED_GITIGNORE = "_gitignore";
|
|
6121
6199
|
var REAL_GITIGNORE = ".gitignore";
|
|
6122
6200
|
function restorePackagedDotfiles(dir) {
|
|
6123
6201
|
const restored = [];
|
|
6124
6202
|
for (const entry of readdirSync9(dir, { withFileTypes: true })) {
|
|
6125
|
-
const full =
|
|
6203
|
+
const full = join20(dir, entry.name);
|
|
6126
6204
|
if (entry.isDirectory()) {
|
|
6127
6205
|
restored.push(...restorePackagedDotfiles(full));
|
|
6128
6206
|
} else if (entry.name === PACKAGED_GITIGNORE) {
|
|
6129
|
-
const target =
|
|
6207
|
+
const target = join20(dir, REAL_GITIGNORE);
|
|
6130
6208
|
renameSync(full, target);
|
|
6131
6209
|
restored.push(target);
|
|
6132
6210
|
}
|
|
@@ -6171,7 +6249,7 @@ async function runSiblingCreateCommand(name, options) {
|
|
|
6171
6249
|
printDryRun2(config, coreConfig, options.templateRoot);
|
|
6172
6250
|
return;
|
|
6173
6251
|
}
|
|
6174
|
-
if (!
|
|
6252
|
+
if (!existsSync21(options.templateRoot)) {
|
|
6175
6253
|
throw new Error(`Sibling template not found at ${options.templateRoot}`);
|
|
6176
6254
|
}
|
|
6177
6255
|
let session = null;
|
|
@@ -6388,7 +6466,7 @@ function assertPathPrefixIsAllowed(pathPrefix) {
|
|
|
6388
6466
|
}
|
|
6389
6467
|
}
|
|
6390
6468
|
function readSiblingConfig(path, root = false) {
|
|
6391
|
-
const raw = JSON.parse(
|
|
6469
|
+
const raw = JSON.parse(readFileSync15(path, "utf8"));
|
|
6392
6470
|
const withDefaults = raw && typeof raw === "object" && "project" in raw && "core" in raw ? {
|
|
6393
6471
|
...raw,
|
|
6394
6472
|
core: {
|
|
@@ -6422,7 +6500,7 @@ function resolveCoreConfig(config, configPath) {
|
|
|
6422
6500
|
throw new Error("Either core.project_name or core.config_path is required.");
|
|
6423
6501
|
}
|
|
6424
6502
|
function parseCoreConfig(path) {
|
|
6425
|
-
const result = BiffoConfigSchema.safeParse(JSON.parse(
|
|
6503
|
+
const result = BiffoConfigSchema.safeParse(JSON.parse(readFileSync15(path, "utf8")));
|
|
6426
6504
|
if (!result.success) {
|
|
6427
6505
|
throw new Error(
|
|
6428
6506
|
`Invalid core configuration at ${path}:
|
|
@@ -6461,7 +6539,7 @@ async function resolveCoreIdentity(coreAws, coreConfig, environments) {
|
|
|
6461
6539
|
return coreIdentity;
|
|
6462
6540
|
}
|
|
6463
6541
|
async function pushSkeleton(git, skeletonRoot, cloneUrl, config, coreConfig, githubToken) {
|
|
6464
|
-
const workDir = mkdtempSync4(
|
|
6542
|
+
const workDir = mkdtempSync4(join21(tmpdir4(), `biffo-sibling-${config.project.name}-`));
|
|
6465
6543
|
try {
|
|
6466
6544
|
writeSiblingTemplate(skeletonRoot, workDir, config, {
|
|
6467
6545
|
coreProjectName: coreConfig.project.name,
|
|
@@ -6486,13 +6564,13 @@ async function pushSkeleton(git, skeletonRoot, cloneUrl, config, coreConfig, git
|
|
|
6486
6564
|
}
|
|
6487
6565
|
}
|
|
6488
6566
|
function writeSiblingTemplate(templateRoot, targetDir, config, context) {
|
|
6489
|
-
if (!
|
|
6567
|
+
if (!existsSync21(templateRoot)) {
|
|
6490
6568
|
throw new Error(`Sibling template not found at ${templateRoot}`);
|
|
6491
6569
|
}
|
|
6492
6570
|
cpSync2(templateRoot, targetDir, { recursive: true });
|
|
6493
6571
|
restorePackagedDotfiles(targetDir);
|
|
6494
6572
|
writeFileSync7(
|
|
6495
|
-
|
|
6573
|
+
join21(targetDir, "biffo.sibling.json"),
|
|
6496
6574
|
JSON.stringify(
|
|
6497
6575
|
{
|
|
6498
6576
|
name: config.project.name,
|
|
@@ -6510,10 +6588,10 @@ function writeSiblingTemplate(templateRoot, targetDir, config, context) {
|
|
|
6510
6588
|
2
|
|
6511
6589
|
) + "\n"
|
|
6512
6590
|
);
|
|
6513
|
-
const envPath =
|
|
6591
|
+
const envPath = join21(targetDir, "apps", "frontend", ".env.example");
|
|
6514
6592
|
try {
|
|
6515
6593
|
const path = basePathFor(context.pathPrefix);
|
|
6516
|
-
const content =
|
|
6594
|
+
const content = readFileSync15(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}`);
|
|
6517
6595
|
writeFileSync7(envPath, content);
|
|
6518
6596
|
} catch (err) {
|
|
6519
6597
|
if (err.code !== "ENOENT") throw err;
|
|
@@ -6560,7 +6638,7 @@ async function configureSiblingGithub(github, config, coreConfig, session, coreI
|
|
|
6560
6638
|
}
|
|
6561
6639
|
function readExistingSiblingOrigins(filePath) {
|
|
6562
6640
|
try {
|
|
6563
|
-
return JSON.parse(
|
|
6641
|
+
return JSON.parse(readFileSync15(filePath, "utf8"));
|
|
6564
6642
|
} catch (err) {
|
|
6565
6643
|
if (err.code === "ENOENT") return {};
|
|
6566
6644
|
throw err;
|
|
@@ -6579,10 +6657,10 @@ function assertGitIdentity(identity) {
|
|
|
6579
6657
|
);
|
|
6580
6658
|
}
|
|
6581
6659
|
function assertCoreSupportsSiblingRouting(cloneDir, coreRepo, pathPrefix = "x") {
|
|
6582
|
-
const cdnVarsPath =
|
|
6660
|
+
const cdnVarsPath = join21(cloneDir, "modules", "cloud", "aws", "cdn", "variables.tf");
|
|
6583
6661
|
let declaresSiblingOrigins = false;
|
|
6584
6662
|
try {
|
|
6585
|
-
declaresSiblingOrigins = /variable\s+"sibling_origins"/.test(
|
|
6663
|
+
declaresSiblingOrigins = /variable\s+"sibling_origins"/.test(readFileSync15(cdnVarsPath, "utf8"));
|
|
6586
6664
|
} catch {
|
|
6587
6665
|
declaresSiblingOrigins = false;
|
|
6588
6666
|
}
|
|
@@ -6592,10 +6670,10 @@ function assertCoreSupportsSiblingRouting(cloneDir, coreRepo, pathPrefix = "x")
|
|
|
6592
6670
|
);
|
|
6593
6671
|
}
|
|
6594
6672
|
if (!isRootPathPrefix(pathPrefix)) return;
|
|
6595
|
-
const cdnMainPath =
|
|
6673
|
+
const cdnMainPath = join21(cloneDir, "modules", "cloud", "aws", "cdn", "main.tf");
|
|
6596
6674
|
let supportsRoot = false;
|
|
6597
6675
|
try {
|
|
6598
|
-
supportsRoot = /root_sibling_registered/.test(
|
|
6676
|
+
supportsRoot = /root_sibling_registered/.test(readFileSync15(cdnMainPath, "utf8"));
|
|
6599
6677
|
} catch {
|
|
6600
6678
|
supportsRoot = false;
|
|
6601
6679
|
}
|
|
@@ -6625,8 +6703,8 @@ async function registerWithCore(git, github, config, coreConfig, pathPrefix, git
|
|
|
6625
6703
|
for (const env of config.environments) {
|
|
6626
6704
|
const bucketName = siteBucketName(config.project.name, env, siblingAccountId);
|
|
6627
6705
|
const domain = bucketRegionalDomain(bucketName, coreAwsRegion);
|
|
6628
|
-
const relativePath =
|
|
6629
|
-
const filePath =
|
|
6706
|
+
const relativePath = join21("infra", "environments", env, "siblings.auto.tfvars.json");
|
|
6707
|
+
const filePath = join21(cloneDir, relativePath);
|
|
6630
6708
|
const existing = readExistingSiblingOrigins(filePath);
|
|
6631
6709
|
const siblings = upsertSiblingOrigin(existing.sibling_origins ?? [], {
|
|
6632
6710
|
name,
|
|
@@ -6706,8 +6784,8 @@ function defaultSiblingTemplateRoot() {
|
|
|
6706
6784
|
const start = dirname6(fileURLToPath4(import.meta.url));
|
|
6707
6785
|
let dir = start;
|
|
6708
6786
|
for (; ; ) {
|
|
6709
|
-
const candidate =
|
|
6710
|
-
if (
|
|
6787
|
+
const candidate = join21(dir, "_skeletons", "sibling-template");
|
|
6788
|
+
if (existsSync21(candidate)) return candidate;
|
|
6711
6789
|
const parent = dirname6(dir);
|
|
6712
6790
|
if (parent === dir) break;
|
|
6713
6791
|
dir = parent;
|
|
@@ -6731,7 +6809,7 @@ var initCommand = new Command12("init").description("Scaffold a new project from
|
|
|
6731
6809
|
let config;
|
|
6732
6810
|
let githubToken;
|
|
6733
6811
|
if (options.config) {
|
|
6734
|
-
const rawConfig = JSON.parse(
|
|
6812
|
+
const rawConfig = JSON.parse(readFileSync16(resolve10(options.config), "utf8"));
|
|
6735
6813
|
config = parseConfig(rawConfig);
|
|
6736
6814
|
const { account_id: accountId, region } = config.cloud.config;
|
|
6737
6815
|
session = resolveConfigFileSession(config, accountId, region, options.fresh === true);
|
|
@@ -7176,8 +7254,8 @@ async function promptForConfig(awsAccountId, awsRegion, awsProfile) {
|
|
|
7176
7254
|
import { Command as Command20 } from "commander";
|
|
7177
7255
|
|
|
7178
7256
|
// src/commands/plugin-create.ts
|
|
7179
|
-
import { existsSync as
|
|
7180
|
-
import { dirname as dirname8, join as
|
|
7257
|
+
import { existsSync as existsSync24, readFileSync as readFileSync18, writeFileSync as writeFileSync9 } from "fs";
|
|
7258
|
+
import { dirname as dirname8, join as join24, resolve as resolve11 } from "path";
|
|
7181
7259
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
7182
7260
|
import chalk13 from "chalk";
|
|
7183
7261
|
import { Command as Command13 } from "commander";
|
|
@@ -7241,21 +7319,21 @@ function workflowCheckContexts(workflow) {
|
|
|
7241
7319
|
}
|
|
7242
7320
|
|
|
7243
7321
|
// src/lib/plugin-locations.ts
|
|
7244
|
-
import { existsSync as
|
|
7245
|
-
import { join as
|
|
7322
|
+
import { existsSync as existsSync22, readdirSync as readdirSync10 } from "fs";
|
|
7323
|
+
import { join as join22 } from "path";
|
|
7246
7324
|
var FIRST_PARTY_PLUGINS_DIR = "_plugins";
|
|
7247
7325
|
var PLUGIN_MANIFEST_FILE = "biffo.plugin.json";
|
|
7248
7326
|
function pluginDir(name, channel) {
|
|
7249
7327
|
return channel === "first-party" ? `services/${FIRST_PARTY_PLUGINS_DIR}/${name}` : `services/${name}`;
|
|
7250
7328
|
}
|
|
7251
7329
|
function scanDir(absDir, relDir, channel) {
|
|
7252
|
-
if (!
|
|
7330
|
+
if (!existsSync22(absDir)) return [];
|
|
7253
7331
|
const found = [];
|
|
7254
7332
|
for (const entry of readdirSync10(absDir, { withFileTypes: true })) {
|
|
7255
7333
|
if (!entry.isDirectory()) continue;
|
|
7256
7334
|
if (channel === "third-party" && entry.name === FIRST_PARTY_PLUGINS_DIR) continue;
|
|
7257
|
-
const manifestPath =
|
|
7258
|
-
if (!
|
|
7335
|
+
const manifestPath = join22(absDir, entry.name, PLUGIN_MANIFEST_FILE);
|
|
7336
|
+
if (!existsSync22(manifestPath)) continue;
|
|
7259
7337
|
found.push({
|
|
7260
7338
|
dirName: entry.name,
|
|
7261
7339
|
relDir: `${relDir}/${entry.name}`,
|
|
@@ -7266,11 +7344,11 @@ function scanDir(absDir, relDir, channel) {
|
|
|
7266
7344
|
return found;
|
|
7267
7345
|
}
|
|
7268
7346
|
function findInstalledPlugins(cwd) {
|
|
7269
|
-
const servicesDir =
|
|
7347
|
+
const servicesDir = join22(cwd, "services");
|
|
7270
7348
|
return [
|
|
7271
7349
|
...scanDir(servicesDir, "services", "third-party"),
|
|
7272
7350
|
...scanDir(
|
|
7273
|
-
|
|
7351
|
+
join22(servicesDir, FIRST_PARTY_PLUGINS_DIR),
|
|
7274
7352
|
`services/${FIRST_PARTY_PLUGINS_DIR}`,
|
|
7275
7353
|
"first-party"
|
|
7276
7354
|
)
|
|
@@ -7473,13 +7551,13 @@ function validateManifest(raw) {
|
|
|
7473
7551
|
// src/lib/plugin-scaffold.ts
|
|
7474
7552
|
import {
|
|
7475
7553
|
copyFileSync,
|
|
7476
|
-
existsSync as
|
|
7554
|
+
existsSync as existsSync23,
|
|
7477
7555
|
mkdirSync as mkdirSync8,
|
|
7478
|
-
readFileSync as
|
|
7556
|
+
readFileSync as readFileSync17,
|
|
7479
7557
|
readdirSync as readdirSync11,
|
|
7480
7558
|
writeFileSync as writeFileSync8
|
|
7481
7559
|
} from "fs";
|
|
7482
|
-
import { dirname as dirname7, join as
|
|
7560
|
+
import { dirname as dirname7, join as join23 } from "path";
|
|
7483
7561
|
var STANDALONE_ONLY_ENTRIES = {
|
|
7484
7562
|
".github": "standalone-repo CI/release workflows \u2014 the host monorepo already runs lint/type/test/security over services/",
|
|
7485
7563
|
"registry-schema.json": "the plugin-registry publishing schema, used when submitting a *published* plugin to the registry repo, not by an in-tree plugin"
|
|
@@ -7533,10 +7611,10 @@ function applySubstitutions(text, names) {
|
|
|
7533
7611
|
var BINARY_EXTENSIONS = /\.(png|jpe?g|gif|ico|woff2?|ttf|zip|gz)$/i;
|
|
7534
7612
|
function scaffoldPlugin(skeletonRoot, destDir, names, options = {}) {
|
|
7535
7613
|
const layout = options.layout ?? "in-tree";
|
|
7536
|
-
if (!
|
|
7614
|
+
if (!existsSync23(skeletonRoot)) {
|
|
7537
7615
|
throw new Error(`Plugin skeleton not found at ${skeletonRoot}`);
|
|
7538
7616
|
}
|
|
7539
|
-
if (!
|
|
7617
|
+
if (!existsSync23(join23(skeletonRoot, "terraform"))) {
|
|
7540
7618
|
throw new Error(
|
|
7541
7619
|
`Plugin skeleton at ${skeletonRoot} has no terraform/ directory. Refusing to scaffold a plugin that cannot receive events (issue #194) \u2014 the skeleton is broken.`
|
|
7542
7620
|
);
|
|
@@ -7544,7 +7622,7 @@ function scaffoldPlugin(skeletonRoot, destDir, names, options = {}) {
|
|
|
7544
7622
|
const skipped = [];
|
|
7545
7623
|
const files = [];
|
|
7546
7624
|
const walk = (relDir) => {
|
|
7547
|
-
const absDir =
|
|
7625
|
+
const absDir = join23(skeletonRoot, relDir);
|
|
7548
7626
|
for (const entry of readdirSync11(absDir, { withFileTypes: true }).sort(
|
|
7549
7627
|
(a, b) => a.name.localeCompare(b.name)
|
|
7550
7628
|
)) {
|
|
@@ -7559,14 +7637,14 @@ function scaffoldPlugin(skeletonRoot, destDir, names, options = {}) {
|
|
|
7559
7637
|
continue;
|
|
7560
7638
|
}
|
|
7561
7639
|
const destRel = applySubstitutions(relPath, names);
|
|
7562
|
-
const destPath =
|
|
7640
|
+
const destPath = join23(destDir, destRel);
|
|
7563
7641
|
mkdirSync8(dirname7(destPath), { recursive: true });
|
|
7564
7642
|
if (BINARY_EXTENSIONS.test(entry.name)) {
|
|
7565
|
-
copyFileSync(
|
|
7643
|
+
copyFileSync(join23(skeletonRoot, relPath), destPath);
|
|
7566
7644
|
} else {
|
|
7567
7645
|
writeFileSync8(
|
|
7568
7646
|
destPath,
|
|
7569
|
-
applySubstitutions(
|
|
7647
|
+
applySubstitutions(readFileSync17(join23(skeletonRoot, relPath), "utf8"), names)
|
|
7570
7648
|
);
|
|
7571
7649
|
}
|
|
7572
7650
|
files.push(destRel);
|
|
@@ -7583,8 +7661,8 @@ function scaffoldPlugin(skeletonRoot, destDir, names, options = {}) {
|
|
|
7583
7661
|
function findSkeletonRoot(startDir, skeleton) {
|
|
7584
7662
|
let dir = startDir;
|
|
7585
7663
|
for (; ; ) {
|
|
7586
|
-
const candidate =
|
|
7587
|
-
if (
|
|
7664
|
+
const candidate = join23(dir, "_skeletons", skeleton);
|
|
7665
|
+
if (existsSync23(candidate)) return candidate;
|
|
7588
7666
|
const parent = dirname7(dir);
|
|
7589
7667
|
if (parent === dir) return null;
|
|
7590
7668
|
dir = parent;
|
|
@@ -7652,7 +7730,7 @@ async function runPluginCreate(name, options, deps) {
|
|
|
7652
7730
|
reportBranchProtectionSummary();
|
|
7653
7731
|
return;
|
|
7654
7732
|
}
|
|
7655
|
-
const isInstance =
|
|
7733
|
+
const isInstance = existsSync24(join24(options.cwd, INSTANCE_CORE_FILE));
|
|
7656
7734
|
if (options.firstParty && isInstance) {
|
|
7657
7735
|
throw new Error(
|
|
7658
7736
|
`--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.`
|
|
@@ -7660,19 +7738,19 @@ async function runPluginCreate(name, options, deps) {
|
|
|
7660
7738
|
}
|
|
7661
7739
|
const channel = options.firstParty ? "first-party" : "third-party";
|
|
7662
7740
|
const relDir = pluginDir(names.slug, channel);
|
|
7663
|
-
const destDir =
|
|
7664
|
-
const servicesDir =
|
|
7665
|
-
if (!
|
|
7741
|
+
const destDir = join24(options.cwd, relDir);
|
|
7742
|
+
const servicesDir = join24(options.cwd, "services");
|
|
7743
|
+
if (!existsSync24(servicesDir)) {
|
|
7666
7744
|
throw new Error(
|
|
7667
7745
|
`${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
|
|
7668
7746
|
);
|
|
7669
7747
|
}
|
|
7670
|
-
if (
|
|
7748
|
+
if (existsSync24(destDir)) {
|
|
7671
7749
|
throw new Error(`${relDir}/ already exists. Choose a different name, or remove it first.`);
|
|
7672
7750
|
}
|
|
7673
7751
|
const here = dirname8(fileURLToPath5(import.meta.url));
|
|
7674
|
-
const skeletonRoot = options.skeletonRoot ?? findSkeletonRoot(here, "plugin-template") ??
|
|
7675
|
-
if (!
|
|
7752
|
+
const skeletonRoot = options.skeletonRoot ?? findSkeletonRoot(here, "plugin-template") ?? join24(options.cwd, "_skeletons", "plugin-template");
|
|
7753
|
+
if (!existsSync24(skeletonRoot)) {
|
|
7676
7754
|
throw new Error(
|
|
7677
7755
|
`Could not find the plugin skeleton (_skeletons/plugin-template/). Pass --skeleton <path> to point at it explicitly.`
|
|
7678
7756
|
);
|
|
@@ -7687,8 +7765,8 @@ async function runPluginCreate(name, options, deps) {
|
|
|
7687
7765
|
for (const { entry, reason } of skipped) {
|
|
7688
7766
|
log.info(`Skipped ${entry} \u2014 ${reason}`);
|
|
7689
7767
|
}
|
|
7690
|
-
const manifestPath =
|
|
7691
|
-
const manifest = validateManifest(JSON.parse(
|
|
7768
|
+
const manifestPath = join24(destDir, "biffo.plugin.json");
|
|
7769
|
+
const manifest = validateManifest(JSON.parse(readFileSync18(manifestPath, "utf8")));
|
|
7692
7770
|
if (manifest.name !== names.slug) {
|
|
7693
7771
|
throw new Error(
|
|
7694
7772
|
`Scaffolded manifest declares name '${manifest.name}', expected '${names.slug}'. The skeleton's manifest name may have diverged from 'example-plugin'.`
|
|
@@ -7711,8 +7789,8 @@ async function runPluginCreate(name, options, deps) {
|
|
|
7711
7789
|
printNextSteps(names, relDir, channel);
|
|
7712
7790
|
}
|
|
7713
7791
|
async function runStandaloneCreate(names, options, deps) {
|
|
7714
|
-
const destDir =
|
|
7715
|
-
if (
|
|
7792
|
+
const destDir = join24(options.cwd, names.dist);
|
|
7793
|
+
if (existsSync24(destDir)) {
|
|
7716
7794
|
throw new Error(`${names.dist}/ already exists. Choose a different name, or remove it first.`);
|
|
7717
7795
|
}
|
|
7718
7796
|
const skeletonRoot = resolveSkeletonRoot(options);
|
|
@@ -7730,7 +7808,7 @@ async function runStandaloneCreate(names, options, deps) {
|
|
|
7730
7808
|
restorePackagedDotfiles(destDir);
|
|
7731
7809
|
log.success(`Scaffolded ${String(files.length)} file(s) into ${names.dist}/`);
|
|
7732
7810
|
const manifest = validateManifest(
|
|
7733
|
-
JSON.parse(
|
|
7811
|
+
JSON.parse(readFileSync18(join24(destDir, "biffo.plugin.json"), "utf8"))
|
|
7734
7812
|
);
|
|
7735
7813
|
if (manifest.name !== names.slug) {
|
|
7736
7814
|
throw new Error(
|
|
@@ -7769,8 +7847,8 @@ async function createAndPushStandaloneRepo(org, names, destDir, options, deps) {
|
|
|
7769
7847
|
await deps.git.push(destDir, "dev", { token });
|
|
7770
7848
|
log.success(`Pushed dev to ${org}/${names.dist}`);
|
|
7771
7849
|
await github.setDefaultBranch(org, names.dist, "dev");
|
|
7772
|
-
const ciPath =
|
|
7773
|
-
const contexts =
|
|
7850
|
+
const ciPath = join24(destDir, ".github", "workflows", "ci.yml");
|
|
7851
|
+
const contexts = existsSync24(ciPath) ? workflowCheckContexts(readFileSync18(ciPath, "utf8")) : [];
|
|
7774
7852
|
if (contexts.length === 0) {
|
|
7775
7853
|
log.warn(
|
|
7776
7854
|
`Could not determine required status checks from ${ciPath} \u2014 skipping branch protection. Configure it manually on dev once you know the CI job names.`
|
|
@@ -7796,8 +7874,8 @@ async function registerInRegistrySources(names, cloneUrl, token, deps) {
|
|
|
7796
7874
|
let dir;
|
|
7797
7875
|
try {
|
|
7798
7876
|
dir = await deps.git.cloneForEditing(REGISTRY_REPO, "biffo-registry", token);
|
|
7799
|
-
const path =
|
|
7800
|
-
const file = JSON.parse(
|
|
7877
|
+
const path = join24(dir, "sources.json");
|
|
7878
|
+
const file = JSON.parse(readFileSync18(path, "utf8"));
|
|
7801
7879
|
const next = addSource(file, {
|
|
7802
7880
|
name: names.slug,
|
|
7803
7881
|
repo: cloneUrl.replace(/\.git$/, ""),
|
|
@@ -7905,8 +7983,8 @@ function printStandaloneNextSteps(names, minor) {
|
|
|
7905
7983
|
}
|
|
7906
7984
|
function resolveSkeletonRoot(options) {
|
|
7907
7985
|
const here = dirname8(fileURLToPath5(import.meta.url));
|
|
7908
|
-
const skeletonRoot = options.skeletonRoot ?? findSkeletonRoot(here, "plugin-template") ??
|
|
7909
|
-
if (!
|
|
7986
|
+
const skeletonRoot = options.skeletonRoot ?? findSkeletonRoot(here, "plugin-template") ?? join24(options.cwd, "_skeletons", "plugin-template");
|
|
7987
|
+
if (!existsSync24(skeletonRoot)) {
|
|
7910
7988
|
throw new Error(
|
|
7911
7989
|
`Could not find the plugin skeleton (_skeletons/plugin-template/). Pass --skeleton <path> to point at it explicitly.`
|
|
7912
7990
|
);
|
|
@@ -8081,14 +8159,14 @@ function printEntry(entry) {
|
|
|
8081
8159
|
}
|
|
8082
8160
|
|
|
8083
8161
|
// src/commands/plugin-install.ts
|
|
8084
|
-
import { cpSync as cpSync3, existsSync as
|
|
8085
|
-
import { basename, join as
|
|
8162
|
+
import { cpSync as cpSync3, existsSync as existsSync26, mkdirSync as mkdirSync9, readFileSync as readFileSync20, statSync as statSync6 } from "fs";
|
|
8163
|
+
import { basename, join as join27, relative as relative3, resolve as resolve12 } from "path";
|
|
8086
8164
|
import chalk15 from "chalk";
|
|
8087
8165
|
import { Command as Command15 } from "commander";
|
|
8088
8166
|
|
|
8089
8167
|
// src/adapters/plugin-migrations/index.ts
|
|
8090
8168
|
import { execa as execa4 } from "execa";
|
|
8091
|
-
import { join as
|
|
8169
|
+
import { join as join25 } from "path";
|
|
8092
8170
|
var PluginMigrationsAdapter = class {
|
|
8093
8171
|
/**
|
|
8094
8172
|
* Generates migration file(s) for `pluginNames` (every discovered
|
|
@@ -8097,22 +8175,22 @@ var PluginMigrationsAdapter = class {
|
|
|
8097
8175
|
* or declared no tables.
|
|
8098
8176
|
*/
|
|
8099
8177
|
async generate(cwd, pluginNames) {
|
|
8100
|
-
const scriptPath =
|
|
8178
|
+
const scriptPath = join25(cwd, "services", "api", "scripts", "generate_plugin_migrations.py");
|
|
8101
8179
|
const args = [
|
|
8102
8180
|
"run",
|
|
8103
8181
|
"python",
|
|
8104
8182
|
scriptPath,
|
|
8105
8183
|
"--services-root",
|
|
8106
|
-
|
|
8184
|
+
join25(cwd, "services"),
|
|
8107
8185
|
"--versions-dir",
|
|
8108
|
-
|
|
8186
|
+
join25(cwd, "services", "api", "migrations", "versions")
|
|
8109
8187
|
];
|
|
8110
8188
|
for (const name of pluginNames ?? []) {
|
|
8111
8189
|
args.push("--plugin", name);
|
|
8112
8190
|
}
|
|
8113
8191
|
let result;
|
|
8114
8192
|
try {
|
|
8115
|
-
result = await execa4("uv", args, { cwd:
|
|
8193
|
+
result = await execa4("uv", args, { cwd: join25(cwd, "services", "api") });
|
|
8116
8194
|
} catch (err) {
|
|
8117
8195
|
const cause = err;
|
|
8118
8196
|
if (cause.code === "ENOENT") {
|
|
@@ -8129,8 +8207,8 @@ var PluginMigrationsAdapter = class {
|
|
|
8129
8207
|
};
|
|
8130
8208
|
|
|
8131
8209
|
// src/lib/plugin-workspace-sources.ts
|
|
8132
|
-
import { existsSync as
|
|
8133
|
-
import { join as
|
|
8210
|
+
import { existsSync as existsSync25, readdirSync as readdirSync12, readFileSync as readFileSync19, writeFileSync as writeFileSync10 } from "fs";
|
|
8211
|
+
import { join as join26 } from "path";
|
|
8134
8212
|
function readTomlStringArray(text, key) {
|
|
8135
8213
|
const open = new RegExp(`^${key}\\s*=\\s*\\[`, "m").exec(text);
|
|
8136
8214
|
if (!open) return [];
|
|
@@ -8174,9 +8252,9 @@ function readDependencyNames(text) {
|
|
|
8174
8252
|
return readTomlStringArray(text, "dependencies").map((dep) => /^\s*([A-Za-z0-9._-]+)/.exec(dep)?.[1] ?? "").filter(Boolean);
|
|
8175
8253
|
}
|
|
8176
8254
|
function workspaceMemberNames(instanceRoot) {
|
|
8177
|
-
const rootPyproject =
|
|
8178
|
-
if (!
|
|
8179
|
-
const text =
|
|
8255
|
+
const rootPyproject = join26(instanceRoot, "pyproject.toml");
|
|
8256
|
+
if (!existsSync25(rootPyproject)) return /* @__PURE__ */ new Set();
|
|
8257
|
+
const text = readFileSync19(rootPyproject, "utf8");
|
|
8180
8258
|
const members = readTomlStringArray(text, "members");
|
|
8181
8259
|
const excluded = new Set(readTomlStringArray(text, "exclude"));
|
|
8182
8260
|
const dirs = [];
|
|
@@ -8185,7 +8263,7 @@ function workspaceMemberNames(instanceRoot) {
|
|
|
8185
8263
|
const base = member.slice(0, -2);
|
|
8186
8264
|
let entries;
|
|
8187
8265
|
try {
|
|
8188
|
-
entries = readdirSync12(
|
|
8266
|
+
entries = readdirSync12(join26(instanceRoot, base), { withFileTypes: true });
|
|
8189
8267
|
} catch {
|
|
8190
8268
|
continue;
|
|
8191
8269
|
}
|
|
@@ -8199,9 +8277,9 @@ function workspaceMemberNames(instanceRoot) {
|
|
|
8199
8277
|
}
|
|
8200
8278
|
const names = /* @__PURE__ */ new Set();
|
|
8201
8279
|
for (const dir of dirs) {
|
|
8202
|
-
const pp =
|
|
8203
|
-
if (!
|
|
8204
|
-
const name = readProjectName(
|
|
8280
|
+
const pp = join26(instanceRoot, dir, "pyproject.toml");
|
|
8281
|
+
if (!existsSync25(pp)) continue;
|
|
8282
|
+
const name = readProjectName(readFileSync19(pp, "utf8"));
|
|
8205
8283
|
if (name) names.add(name);
|
|
8206
8284
|
}
|
|
8207
8285
|
return names;
|
|
@@ -8212,8 +8290,8 @@ function existingWorkspaceSources(text) {
|
|
|
8212
8290
|
);
|
|
8213
8291
|
}
|
|
8214
8292
|
function ensureWorkspaceSources(pluginPyprojectPath, memberNames) {
|
|
8215
|
-
if (!
|
|
8216
|
-
const text =
|
|
8293
|
+
if (!existsSync25(pluginPyprojectPath) || memberNames.size === 0) return [];
|
|
8294
|
+
const text = readFileSync19(pluginPyprojectPath, "utf8");
|
|
8217
8295
|
const already = existingWorkspaceSources(text);
|
|
8218
8296
|
const toAdd = readDependencyNames(text).filter((n) => memberNames.has(n) && !already.has(n));
|
|
8219
8297
|
if (toAdd.length === 0) return [];
|
|
@@ -8279,14 +8357,14 @@ var LOCAL_COPY_EXCLUDES = /* @__PURE__ */ new Set([
|
|
|
8279
8357
|
".terraform"
|
|
8280
8358
|
]);
|
|
8281
8359
|
function resolveLocalPlugin(localPath) {
|
|
8282
|
-
if (!
|
|
8360
|
+
if (!existsSync26(localPath)) {
|
|
8283
8361
|
throw new Error(`--local path does not exist: ${localPath}`);
|
|
8284
8362
|
}
|
|
8285
8363
|
if (!statSync6(localPath).isDirectory()) {
|
|
8286
8364
|
throw new Error(`--local path is not a directory: ${localPath}`);
|
|
8287
8365
|
}
|
|
8288
|
-
const manifestPath =
|
|
8289
|
-
if (!
|
|
8366
|
+
const manifestPath = join27(localPath, "biffo.plugin.json");
|
|
8367
|
+
if (!existsSync26(manifestPath)) {
|
|
8290
8368
|
throw new Error(
|
|
8291
8369
|
`${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>\`.)`
|
|
8292
8370
|
);
|
|
@@ -8312,8 +8390,8 @@ function parsePluginTarget(target) {
|
|
|
8312
8390
|
async function cloneAndValidatePlugin(entry, git) {
|
|
8313
8391
|
const tmpDir = await git.cloneToTemp(entry.repo, `biffo-plugin-${entry.name}`);
|
|
8314
8392
|
try {
|
|
8315
|
-
const manifestPath =
|
|
8316
|
-
if (!
|
|
8393
|
+
const manifestPath = join27(tmpDir, "biffo.plugin.json");
|
|
8394
|
+
if (!existsSync26(manifestPath)) {
|
|
8317
8395
|
throw new Error(
|
|
8318
8396
|
`Plugin repo ${entry.repo} does not contain a biffo.plugin.json manifest at its root.`
|
|
8319
8397
|
);
|
|
@@ -8341,8 +8419,8 @@ async function runPluginInstall(target, options, deps) {
|
|
|
8341
8419
|
`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\`).`
|
|
8342
8420
|
);
|
|
8343
8421
|
}
|
|
8344
|
-
const servicesDir =
|
|
8345
|
-
if (!
|
|
8422
|
+
const servicesDir = join27(options.cwd, "services");
|
|
8423
|
+
if (!existsSync26(servicesDir)) {
|
|
8346
8424
|
throw new Error(
|
|
8347
8425
|
`${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
|
|
8348
8426
|
);
|
|
@@ -8360,10 +8438,10 @@ async function runPluginInstall(target, options, deps) {
|
|
|
8360
8438
|
}
|
|
8361
8439
|
const pluginName = entry ? entry.name : source.name;
|
|
8362
8440
|
const relTargetDir = pluginDir(pluginName, "third-party");
|
|
8363
|
-
const targetDir =
|
|
8364
|
-
const modulesDir =
|
|
8441
|
+
const targetDir = join27(options.cwd, relTargetDir);
|
|
8442
|
+
const modulesDir = join27(options.cwd, "modules", "plugins", pluginName);
|
|
8365
8443
|
const inTreeSource = options.local !== void 0 && resolve12(options.local) === resolve12(targetDir);
|
|
8366
|
-
if (
|
|
8444
|
+
if (existsSync26(targetDir) && !inTreeSource) {
|
|
8367
8445
|
throw new Error(
|
|
8368
8446
|
`Plugin '${pluginName}' is already installed at ${relTargetDir}/. Remove it first, or wait for a future 'biffo plugin upgrade' command.`
|
|
8369
8447
|
);
|
|
@@ -8405,8 +8483,8 @@ async function runPluginInstall(target, options, deps) {
|
|
|
8405
8483
|
});
|
|
8406
8484
|
log.success(`Installed plugin source at ${relTargetDir}/`);
|
|
8407
8485
|
}
|
|
8408
|
-
const pluginPyproject =
|
|
8409
|
-
if (
|
|
8486
|
+
const pluginPyproject = join27(targetDir, "pyproject.toml");
|
|
8487
|
+
if (existsSync26(pluginPyproject)) {
|
|
8410
8488
|
const sourced = ensureWorkspaceSources(pluginPyproject, workspaceMemberNames(options.cwd));
|
|
8411
8489
|
if (sourced.length > 0) {
|
|
8412
8490
|
log.info(
|
|
@@ -8415,8 +8493,8 @@ async function runPluginInstall(target, options, deps) {
|
|
|
8415
8493
|
}
|
|
8416
8494
|
}
|
|
8417
8495
|
const stagePaths = [relTargetDir];
|
|
8418
|
-
const tfSourceDir =
|
|
8419
|
-
if (
|
|
8496
|
+
const tfSourceDir = join27(targetDir, "terraform");
|
|
8497
|
+
if (existsSync26(tfSourceDir)) {
|
|
8420
8498
|
mkdirSync9(modulesDir, { recursive: true });
|
|
8421
8499
|
cpSync3(tfSourceDir, modulesDir, { recursive: true });
|
|
8422
8500
|
stagePaths.push(`modules/plugins/${pluginName}`);
|
|
@@ -8473,7 +8551,7 @@ async function runPluginInstall(target, options, deps) {
|
|
|
8473
8551
|
}
|
|
8474
8552
|
function parseManifestFile(path) {
|
|
8475
8553
|
try {
|
|
8476
|
-
return JSON.parse(
|
|
8554
|
+
return JSON.parse(readFileSync20(path, "utf8"));
|
|
8477
8555
|
} catch (err) {
|
|
8478
8556
|
throw new Error(`Could not parse ${path} as JSON: ${err.message}`);
|
|
8479
8557
|
}
|
|
@@ -8510,8 +8588,8 @@ function printDryRun4(entry, source, relTargetDir, inTreeSource) {
|
|
|
8510
8588
|
}
|
|
8511
8589
|
|
|
8512
8590
|
// src/commands/plugin-list.ts
|
|
8513
|
-
import { existsSync as
|
|
8514
|
-
import { join as
|
|
8591
|
+
import { existsSync as existsSync27, readFileSync as readFileSync21 } from "fs";
|
|
8592
|
+
import { join as join28, resolve as resolve13 } from "path";
|
|
8515
8593
|
import chalk16 from "chalk";
|
|
8516
8594
|
import { Command as Command16 } from "commander";
|
|
8517
8595
|
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) => {
|
|
@@ -8524,8 +8602,8 @@ var pluginListCommand = new Command16("list").description("List plugins installe
|
|
|
8524
8602
|
}
|
|
8525
8603
|
});
|
|
8526
8604
|
async function runPluginList(options) {
|
|
8527
|
-
const servicesDir =
|
|
8528
|
-
if (!
|
|
8605
|
+
const servicesDir = join28(options.cwd, "services");
|
|
8606
|
+
if (!existsSync27(servicesDir)) {
|
|
8529
8607
|
throw new Error(
|
|
8530
8608
|
`${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
|
|
8531
8609
|
);
|
|
@@ -8533,7 +8611,7 @@ async function runPluginList(options) {
|
|
|
8533
8611
|
const plugins = [];
|
|
8534
8612
|
for (const location of findInstalledPlugins(options.cwd)) {
|
|
8535
8613
|
try {
|
|
8536
|
-
const manifest = validateManifest(JSON.parse(
|
|
8614
|
+
const manifest = validateManifest(JSON.parse(readFileSync21(location.manifestPath, "utf8")));
|
|
8537
8615
|
plugins.push({
|
|
8538
8616
|
name: manifest.name,
|
|
8539
8617
|
version: manifest.version,
|
|
@@ -8570,8 +8648,8 @@ async function runPluginList(options) {
|
|
|
8570
8648
|
}
|
|
8571
8649
|
|
|
8572
8650
|
// src/commands/plugin-sync-migrations.ts
|
|
8573
|
-
import { existsSync as
|
|
8574
|
-
import { join as
|
|
8651
|
+
import { existsSync as existsSync28 } from "fs";
|
|
8652
|
+
import { join as join29, relative as relative4, resolve as resolve14 } from "path";
|
|
8575
8653
|
import chalk17 from "chalk";
|
|
8576
8654
|
import { Command as Command17 } from "commander";
|
|
8577
8655
|
var pluginSyncMigrationsCommand = new Command17("sync-migrations").description(
|
|
@@ -8592,11 +8670,11 @@ var pluginSyncMigrationsCommand = new Command17("sync-migrations").description(
|
|
|
8592
8670
|
}
|
|
8593
8671
|
);
|
|
8594
8672
|
async function runPluginSyncMigrations(name, options, deps) {
|
|
8595
|
-
const servicesDir =
|
|
8596
|
-
if (!
|
|
8673
|
+
const servicesDir = join29(options.cwd, "services");
|
|
8674
|
+
if (!existsSync28(servicesDir)) {
|
|
8597
8675
|
throw new Error(`${servicesDir} does not exist \u2014 is ${options.cwd} a Biffo project checkout?`);
|
|
8598
8676
|
}
|
|
8599
|
-
if (name && !
|
|
8677
|
+
if (name && !existsSync28(join29(servicesDir, name, "biffo.plugin.json"))) {
|
|
8600
8678
|
throw new Error(`Plugin '${name}' is not installed at services/${name}/.`);
|
|
8601
8679
|
}
|
|
8602
8680
|
if (options.dryRun) {
|
|
@@ -8632,8 +8710,8 @@ async function runPluginSyncMigrations(name, options, deps) {
|
|
|
8632
8710
|
}
|
|
8633
8711
|
|
|
8634
8712
|
// src/commands/plugin-uninstall.ts
|
|
8635
|
-
import { existsSync as
|
|
8636
|
-
import { join as
|
|
8713
|
+
import { existsSync as existsSync29, readFileSync as readFileSync22, rmSync as rmSync8 } from "fs";
|
|
8714
|
+
import { join as join30, resolve as resolve15 } from "path";
|
|
8637
8715
|
import chalk18 from "chalk";
|
|
8638
8716
|
import { Command as Command18 } from "commander";
|
|
8639
8717
|
import inquirer6 from "inquirer";
|
|
@@ -8665,16 +8743,16 @@ async function runPluginUninstall(name, options, deps) {
|
|
|
8665
8743
|
if (!NAME_PATTERN2.test(name)) {
|
|
8666
8744
|
throw new Error(`Invalid plugin name '${name}'. Expected a lowercase kebab-case slug.`);
|
|
8667
8745
|
}
|
|
8668
|
-
const servicesDir =
|
|
8669
|
-
if (!
|
|
8746
|
+
const servicesDir = join30(options.cwd, "services");
|
|
8747
|
+
if (!existsSync29(servicesDir)) {
|
|
8670
8748
|
throw new Error(
|
|
8671
8749
|
`${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
|
|
8672
8750
|
);
|
|
8673
8751
|
}
|
|
8674
|
-
const targetDir =
|
|
8675
|
-
if (!
|
|
8676
|
-
const firstParty =
|
|
8677
|
-
if (
|
|
8752
|
+
const targetDir = join30(servicesDir, name);
|
|
8753
|
+
if (!existsSync29(targetDir)) {
|
|
8754
|
+
const firstParty = join30(servicesDir, FIRST_PARTY_PLUGINS_DIR, name);
|
|
8755
|
+
if (existsSync29(firstParty)) {
|
|
8678
8756
|
throw new Error(
|
|
8679
8757
|
`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.`
|
|
8680
8758
|
);
|
|
@@ -8682,9 +8760,9 @@ async function runPluginUninstall(name, options, deps) {
|
|
|
8682
8760
|
throw new Error(`Plugin '${name}' is not installed at services/${name}/.`);
|
|
8683
8761
|
}
|
|
8684
8762
|
const version = readInstalledVersion(targetDir);
|
|
8685
|
-
const modulesDir =
|
|
8763
|
+
const modulesDir = join30(options.cwd, "modules", "plugins", name);
|
|
8686
8764
|
const stagePaths = [`services/${name}`];
|
|
8687
|
-
if (
|
|
8765
|
+
if (existsSync29(modulesDir)) {
|
|
8688
8766
|
stagePaths.push(`modules/plugins/${name}`);
|
|
8689
8767
|
}
|
|
8690
8768
|
if (options.dryRun) {
|
|
@@ -8706,7 +8784,7 @@ async function runPluginUninstall(name, options, deps) {
|
|
|
8706
8784
|
}
|
|
8707
8785
|
rmSync8(targetDir, { recursive: true, force: true });
|
|
8708
8786
|
log.success(`Removed services/${name}/`);
|
|
8709
|
-
if (
|
|
8787
|
+
if (existsSync29(modulesDir)) {
|
|
8710
8788
|
rmSync8(modulesDir, { recursive: true, force: true });
|
|
8711
8789
|
log.success(`Removed modules/plugins/${name}/`);
|
|
8712
8790
|
const wiring = syncPluginTerraform(options.cwd);
|
|
@@ -8743,10 +8821,10 @@ async function runPluginUninstall(name, options, deps) {
|
|
|
8743
8821
|
}
|
|
8744
8822
|
}
|
|
8745
8823
|
function readInstalledVersion(targetDir) {
|
|
8746
|
-
const manifestPath =
|
|
8747
|
-
if (!
|
|
8824
|
+
const manifestPath = join30(targetDir, "biffo.plugin.json");
|
|
8825
|
+
if (!existsSync29(manifestPath)) return void 0;
|
|
8748
8826
|
try {
|
|
8749
|
-
return validateManifest(JSON.parse(
|
|
8827
|
+
return validateManifest(JSON.parse(readFileSync22(manifestPath, "utf8"))).version;
|
|
8750
8828
|
} catch {
|
|
8751
8829
|
return void 0;
|
|
8752
8830
|
}
|
|
@@ -8779,8 +8857,8 @@ function printDryRun5(name, version, stagePaths, keepData) {
|
|
|
8779
8857
|
}
|
|
8780
8858
|
|
|
8781
8859
|
// src/commands/plugin-upgrade.ts
|
|
8782
|
-
import { cpSync as cpSync4, existsSync as
|
|
8783
|
-
import { join as
|
|
8860
|
+
import { cpSync as cpSync4, existsSync as existsSync30, mkdirSync as mkdirSync10, readFileSync as readFileSync23, rmSync as rmSync9 } from "fs";
|
|
8861
|
+
import { join as join31, relative as relative5, resolve as resolve16 } from "path";
|
|
8784
8862
|
import chalk19 from "chalk";
|
|
8785
8863
|
import { Command as Command19 } from "commander";
|
|
8786
8864
|
import inquirer7 from "inquirer";
|
|
@@ -8805,14 +8883,14 @@ var pluginUpgradeCommand = new Command19("upgrade").description(
|
|
|
8805
8883
|
});
|
|
8806
8884
|
async function runPluginUpgrade(target, options, deps) {
|
|
8807
8885
|
const { name, minor } = parsePluginTarget(target);
|
|
8808
|
-
const servicesDir =
|
|
8809
|
-
if (!
|
|
8886
|
+
const servicesDir = join31(options.cwd, "services");
|
|
8887
|
+
if (!existsSync30(servicesDir)) {
|
|
8810
8888
|
throw new Error(
|
|
8811
8889
|
`${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
|
|
8812
8890
|
);
|
|
8813
8891
|
}
|
|
8814
|
-
const targetDir =
|
|
8815
|
-
if (!
|
|
8892
|
+
const targetDir = join31(servicesDir, name);
|
|
8893
|
+
if (!existsSync30(targetDir)) {
|
|
8816
8894
|
throw new Error(
|
|
8817
8895
|
`Plugin '${name}' is not installed at services/${name}/. Use 'biffo plugin install ${name}@${minor}' instead.`
|
|
8818
8896
|
);
|
|
@@ -8826,7 +8904,7 @@ async function runPluginUpgrade(target, options, deps) {
|
|
|
8826
8904
|
`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.`
|
|
8827
8905
|
);
|
|
8828
8906
|
}
|
|
8829
|
-
const modulesDir =
|
|
8907
|
+
const modulesDir = join31(options.cwd, "modules", "plugins", entry.name);
|
|
8830
8908
|
if (options.dryRun) {
|
|
8831
8909
|
printDryRun6(entry, currentVersion);
|
|
8832
8910
|
return;
|
|
@@ -8859,11 +8937,11 @@ async function runPluginUpgrade(target, options, deps) {
|
|
|
8859
8937
|
cpSync4(tmpDir, targetDir, { recursive: true });
|
|
8860
8938
|
log.success(`Upgraded plugin source at services/${entry.name}/`);
|
|
8861
8939
|
const stagePaths = [`services/${entry.name}`];
|
|
8862
|
-
if (
|
|
8940
|
+
if (existsSync30(modulesDir)) {
|
|
8863
8941
|
rmSync9(modulesDir, { recursive: true, force: true });
|
|
8864
8942
|
}
|
|
8865
|
-
const tfSourceDir =
|
|
8866
|
-
if (
|
|
8943
|
+
const tfSourceDir = join31(targetDir, "terraform");
|
|
8944
|
+
if (existsSync30(tfSourceDir)) {
|
|
8867
8945
|
mkdirSync10(modulesDir, { recursive: true });
|
|
8868
8946
|
cpSync4(tfSourceDir, modulesDir, { recursive: true });
|
|
8869
8947
|
stagePaths.push(`modules/plugins/${entry.name}`);
|
|
@@ -8897,10 +8975,10 @@ async function runPluginUpgrade(target, options, deps) {
|
|
|
8897
8975
|
}
|
|
8898
8976
|
}
|
|
8899
8977
|
function readInstalledVersion2(targetDir) {
|
|
8900
|
-
const manifestPath =
|
|
8901
|
-
if (!
|
|
8978
|
+
const manifestPath = join31(targetDir, "biffo.plugin.json");
|
|
8979
|
+
if (!existsSync30(manifestPath)) return void 0;
|
|
8902
8980
|
try {
|
|
8903
|
-
return validateManifest(JSON.parse(
|
|
8981
|
+
return validateManifest(JSON.parse(readFileSync23(manifestPath, "utf8"))).version;
|
|
8904
8982
|
} catch {
|
|
8905
8983
|
return void 0;
|
|
8906
8984
|
}
|
|
@@ -8942,7 +9020,7 @@ pluginCommand.addCommand(pluginInfoCommand);
|
|
|
8942
9020
|
import { Command as Command22 } from "commander";
|
|
8943
9021
|
|
|
8944
9022
|
// src/commands/sibling-check-identity.ts
|
|
8945
|
-
import { existsSync as
|
|
9023
|
+
import { existsSync as existsSync31, readFileSync as readFileSync24 } from "fs";
|
|
8946
9024
|
import { resolve as resolve17 } from "path";
|
|
8947
9025
|
import chalk20 from "chalk";
|
|
8948
9026
|
import { Command as Command21 } from "commander";
|
|
@@ -9136,7 +9214,7 @@ async function fetchPublishedIdentity(portalUrl) {
|
|
|
9136
9214
|
}
|
|
9137
9215
|
async function resolveConfig4(options) {
|
|
9138
9216
|
if (options.config) {
|
|
9139
|
-
const raw = JSON.parse(
|
|
9217
|
+
const raw = JSON.parse(readFileSync24(resolve17(options.config), "utf8"));
|
|
9140
9218
|
const result = BiffoConfigSchema.safeParse(raw);
|
|
9141
9219
|
if (!result.success) {
|
|
9142
9220
|
log.error(`Invalid config at ${options.config}:`);
|
|
@@ -9156,8 +9234,8 @@ async function resolveConfig4(options) {
|
|
|
9156
9234
|
return cfg;
|
|
9157
9235
|
}
|
|
9158
9236
|
const localConfigPath = resolve17(process.cwd(), "biffo.config.json");
|
|
9159
|
-
if (
|
|
9160
|
-
const raw = JSON.parse(
|
|
9237
|
+
if (existsSync31(localConfigPath)) {
|
|
9238
|
+
const raw = JSON.parse(readFileSync24(localConfigPath, "utf8"));
|
|
9161
9239
|
const result = BiffoConfigSchema.safeParse(raw);
|
|
9162
9240
|
if (result.success) return result.data;
|
|
9163
9241
|
if (isTemplatePlaceholderConfig(raw)) {
|
|
@@ -9203,21 +9281,21 @@ siblingCommand.addCommand(siblingCheckIdentityCommand);
|
|
|
9203
9281
|
import { Command as Command23 } from "commander";
|
|
9204
9282
|
|
|
9205
9283
|
// src/scripts/check-adr-numbering.ts
|
|
9206
|
-
import { existsSync as
|
|
9207
|
-
import { join as
|
|
9284
|
+
import { existsSync as existsSync33 } from "fs";
|
|
9285
|
+
import { join as join33 } from "path";
|
|
9208
9286
|
import { execa as execa5 } from "execa";
|
|
9209
9287
|
|
|
9210
9288
|
// src/lib/adr-numbering-guard.ts
|
|
9211
|
-
import { existsSync as
|
|
9212
|
-
import { join as
|
|
9289
|
+
import { existsSync as existsSync32, readdirSync as readdirSync13, readFileSync as readFileSync25 } from "fs";
|
|
9290
|
+
import { join as join32 } from "path";
|
|
9213
9291
|
var ADR_FILENAME = /^(\d{4})-.+\.md$/;
|
|
9214
9292
|
var ALLOWLIST_FILENAME = ".numbering-allowlist";
|
|
9215
9293
|
var TEMPLATE_ADR_RESERVED_UPTO = "0099";
|
|
9216
9294
|
function readAdrNumberingAllowlist(adrDir) {
|
|
9217
|
-
const path =
|
|
9218
|
-
if (!
|
|
9295
|
+
const path = join32(adrDir, ALLOWLIST_FILENAME);
|
|
9296
|
+
if (!existsSync32(path)) return /* @__PURE__ */ new Set();
|
|
9219
9297
|
const numbers = /* @__PURE__ */ new Set();
|
|
9220
|
-
for (const rawLine of
|
|
9298
|
+
for (const rawLine of readFileSync25(path, "utf8").split("\n")) {
|
|
9221
9299
|
const line = rawLine.split("#")[0].trim();
|
|
9222
9300
|
if (line) numbers.add(line);
|
|
9223
9301
|
}
|
|
@@ -9225,7 +9303,7 @@ function readAdrNumberingAllowlist(adrDir) {
|
|
|
9225
9303
|
}
|
|
9226
9304
|
function adrNumbersIn(adrDir) {
|
|
9227
9305
|
const claims = /* @__PURE__ */ new Map();
|
|
9228
|
-
if (!
|
|
9306
|
+
if (!existsSync32(adrDir)) return claims;
|
|
9229
9307
|
for (const entry of readdirSync13(adrDir).sort()) {
|
|
9230
9308
|
const match = ADR_FILENAME.exec(entry);
|
|
9231
9309
|
if (!match) continue;
|
|
@@ -9280,8 +9358,8 @@ function formatAdrReservedRangeViolations(violations, reservedUpTo = TEMPLATE_AD
|
|
|
9280
9358
|
// src/scripts/check-adr-numbering.ts
|
|
9281
9359
|
async function runAdrNumberingCheck() {
|
|
9282
9360
|
const root = (await execa5("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
9283
|
-
const adrDir =
|
|
9284
|
-
if (!
|
|
9361
|
+
const adrDir = join33(root, "docs", "ADR");
|
|
9362
|
+
if (!existsSync33(adrDir)) {
|
|
9285
9363
|
console.log("\u2713 ADR numbering guard: no docs/ADR/ directory \u2014 nothing to compare");
|
|
9286
9364
|
return;
|
|
9287
9365
|
}
|
|
@@ -9602,8 +9680,8 @@ async function runOwnershipCheck(argv) {
|
|
|
9602
9680
|
const { stdout } = await execa7("git", ["diff", "--cached", "--name-status"], { cwd: root });
|
|
9603
9681
|
({ changed: changedFiles, deleted: deletedFiles } = parseNameStatus(stdout));
|
|
9604
9682
|
if (messageFile) {
|
|
9605
|
-
const { readFileSync:
|
|
9606
|
-
if (
|
|
9683
|
+
const { readFileSync: readFileSync28, existsSync: existsSync38 } = await import("fs");
|
|
9684
|
+
if (existsSync38(messageFile)) commitMessage = readFileSync28(messageFile, "utf8");
|
|
9607
9685
|
}
|
|
9608
9686
|
} else {
|
|
9609
9687
|
const base = process.env["GITHUB_BASE_REF"] ?? args[0];
|
|
@@ -9704,33 +9782,33 @@ ${BOLD}If the divergence is deliberate${OFF}
|
|
|
9704
9782
|
}
|
|
9705
9783
|
|
|
9706
9784
|
// src/scripts/check-plugin-collisions.ts
|
|
9707
|
-
import { existsSync as
|
|
9708
|
-
import { join as
|
|
9785
|
+
import { existsSync as existsSync35 } from "fs";
|
|
9786
|
+
import { join as join35 } from "path";
|
|
9709
9787
|
import { execa as execa8 } from "execa";
|
|
9710
9788
|
|
|
9711
9789
|
// src/lib/plugin-collision-guard.ts
|
|
9712
|
-
import { existsSync as
|
|
9713
|
-
import { join as
|
|
9790
|
+
import { existsSync as existsSync34, readdirSync as readdirSync14, statSync as statSync7 } from "fs";
|
|
9791
|
+
import { join as join34 } from "path";
|
|
9714
9792
|
var PYTEST_SPECIAL = /* @__PURE__ */ new Set(["conftest.py"]);
|
|
9715
9793
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([".venv", "node_modules", "__pycache__", ".git", "dist", "build"]);
|
|
9716
9794
|
function subdirectories(dir) {
|
|
9717
|
-
if (!
|
|
9795
|
+
if (!existsSync34(dir)) return [];
|
|
9718
9796
|
return readdirSync14(dir).filter((entry) => {
|
|
9719
9797
|
if (IGNORED_DIRS.has(entry) || entry.startsWith(".")) return false;
|
|
9720
9798
|
try {
|
|
9721
|
-
return statSync7(
|
|
9799
|
+
return statSync7(join34(dir, entry)).isDirectory();
|
|
9722
9800
|
} catch {
|
|
9723
9801
|
return false;
|
|
9724
9802
|
}
|
|
9725
9803
|
});
|
|
9726
9804
|
}
|
|
9727
9805
|
function regularPackagesOf(pluginDir2) {
|
|
9728
|
-
return subdirectories(pluginDir2).filter((name) =>
|
|
9806
|
+
return subdirectories(pluginDir2).filter((name) => existsSync34(join34(pluginDir2, name, "__init__.py"))).sort();
|
|
9729
9807
|
}
|
|
9730
9808
|
function bareTestModulesOf(pluginDir2) {
|
|
9731
|
-
const testsDir =
|
|
9732
|
-
if (!
|
|
9733
|
-
if (
|
|
9809
|
+
const testsDir = join34(pluginDir2, "tests");
|
|
9810
|
+
if (!existsSync34(testsDir)) return [];
|
|
9811
|
+
if (existsSync34(join34(testsDir, "__init__.py"))) return [];
|
|
9734
9812
|
return readdirSync14(testsDir).filter((f) => f.endsWith(".py") && !PYTEST_SPECIAL.has(f)).sort();
|
|
9735
9813
|
}
|
|
9736
9814
|
function findCollisions(servicesDir, pluginDirs) {
|
|
@@ -9739,7 +9817,7 @@ function findCollisions(servicesDir, pluginDirs) {
|
|
|
9739
9817
|
const gather = (kind, namesOf) => {
|
|
9740
9818
|
const claims = /* @__PURE__ */ new Map();
|
|
9741
9819
|
for (const plugin of plugins) {
|
|
9742
|
-
for (const name of namesOf(
|
|
9820
|
+
for (const name of namesOf(join34(servicesDir, plugin))) {
|
|
9743
9821
|
claims.set(name, [...claims.get(name) ?? [], plugin]);
|
|
9744
9822
|
}
|
|
9745
9823
|
}
|
|
@@ -9777,8 +9855,8 @@ function formatCollisions(collisions) {
|
|
|
9777
9855
|
// src/scripts/check-plugin-collisions.ts
|
|
9778
9856
|
async function runPluginCollisionCheck() {
|
|
9779
9857
|
const root = (await execa8("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
9780
|
-
const servicesDir =
|
|
9781
|
-
if (!
|
|
9858
|
+
const servicesDir = join35(root, "services");
|
|
9859
|
+
if (!existsSync35(servicesDir)) {
|
|
9782
9860
|
console.log("\u2713 plugin collision guard: no services/ directory \u2014 nothing to compare");
|
|
9783
9861
|
return;
|
|
9784
9862
|
}
|
|
@@ -9798,8 +9876,8 @@ async function runPluginCollisionCheck() {
|
|
|
9798
9876
|
import { execa as execa9 } from "execa";
|
|
9799
9877
|
|
|
9800
9878
|
// src/lib/plugin-terraform-guard.ts
|
|
9801
|
-
import { existsSync as
|
|
9802
|
-
import { dirname as dirname9, join as
|
|
9879
|
+
import { existsSync as existsSync36, readFileSync as readFileSync26, readdirSync as readdirSync15 } from "fs";
|
|
9880
|
+
import { dirname as dirname9, join as join36, relative as relative6, sep as sep3 } from "path";
|
|
9803
9881
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".worktrees", "dist", ".venv", "__pycache__"]);
|
|
9804
9882
|
var PLUGIN_MANIFEST_FILE2 = "biffo.plugin.json";
|
|
9805
9883
|
function findPluginManifests(root) {
|
|
@@ -9814,9 +9892,9 @@ function findPluginManifests(root) {
|
|
|
9814
9892
|
for (const entry of entries) {
|
|
9815
9893
|
if (entry.isDirectory()) {
|
|
9816
9894
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
9817
|
-
walk(
|
|
9895
|
+
walk(join36(dir, entry.name));
|
|
9818
9896
|
} else if (entry.isFile() && entry.name === PLUGIN_MANIFEST_FILE2) {
|
|
9819
|
-
found.push(relative6(root,
|
|
9897
|
+
found.push(relative6(root, join36(dir, entry.name)).split(sep3).join("/"));
|
|
9820
9898
|
}
|
|
9821
9899
|
}
|
|
9822
9900
|
};
|
|
@@ -9826,7 +9904,7 @@ function findPluginManifests(root) {
|
|
|
9826
9904
|
function readSubscriptions(absManifestPath) {
|
|
9827
9905
|
let parsed;
|
|
9828
9906
|
try {
|
|
9829
|
-
parsed = JSON.parse(
|
|
9907
|
+
parsed = JSON.parse(readFileSync26(absManifestPath, "utf8"));
|
|
9830
9908
|
} catch {
|
|
9831
9909
|
return null;
|
|
9832
9910
|
}
|
|
@@ -9841,14 +9919,14 @@ function readSubscriptions(absManifestPath) {
|
|
|
9841
9919
|
}
|
|
9842
9920
|
function checkPluginTerraform(root) {
|
|
9843
9921
|
const violations = [];
|
|
9844
|
-
const coreManifest =
|
|
9922
|
+
const coreManifest = existsSync36(join36(root, CORE_MANIFEST_FILE)) ? readCoreManifest(root) : null;
|
|
9845
9923
|
for (const manifest of findPluginManifests(root)) {
|
|
9846
9924
|
if (coreManifest && !isTemplateOwned(manifest, coreManifest)) continue;
|
|
9847
|
-
const absManifest =
|
|
9925
|
+
const absManifest = join36(root, manifest);
|
|
9848
9926
|
const subscriptions = readSubscriptions(absManifest);
|
|
9849
9927
|
if (subscriptions === null) continue;
|
|
9850
9928
|
const pluginDir2 = dirname9(absManifest);
|
|
9851
|
-
if (
|
|
9929
|
+
if (existsSync36(join36(pluginDir2, "terraform"))) continue;
|
|
9852
9930
|
const relPluginDir = relative6(root, pluginDir2).split(sep3).join("/");
|
|
9853
9931
|
violations.push({
|
|
9854
9932
|
manifest,
|
|
@@ -10044,8 +10122,8 @@ function rawArgsAfter(subcommand) {
|
|
|
10044
10122
|
}
|
|
10045
10123
|
|
|
10046
10124
|
// src/commands/doctor.ts
|
|
10047
|
-
import { existsSync as
|
|
10048
|
-
import { join as
|
|
10125
|
+
import { existsSync as existsSync37, readFileSync as readFileSync27 } from "fs";
|
|
10126
|
+
import { join as join37, resolve as resolve18 } from "path";
|
|
10049
10127
|
import chalk21 from "chalk";
|
|
10050
10128
|
import { Command as Command24 } from "commander";
|
|
10051
10129
|
|
|
@@ -10220,10 +10298,10 @@ async function runDoctor(options, deps = { git: new GitAdapter() }) {
|
|
|
10220
10298
|
return runDoctorChecks(facts);
|
|
10221
10299
|
}
|
|
10222
10300
|
function readLocalCoreVersion(cwd) {
|
|
10223
|
-
const path =
|
|
10224
|
-
if (!
|
|
10301
|
+
const path = join37(cwd, INSTANCE_CORE_FILE);
|
|
10302
|
+
if (!existsSync37(path)) return null;
|
|
10225
10303
|
try {
|
|
10226
|
-
return parseCoreRecord(
|
|
10304
|
+
return parseCoreRecord(readFileSync27(path, "utf8"));
|
|
10227
10305
|
} catch {
|
|
10228
10306
|
return null;
|
|
10229
10307
|
}
|
|
@@ -10238,10 +10316,10 @@ function parseCoreRecord(contents) {
|
|
|
10238
10316
|
}
|
|
10239
10317
|
}
|
|
10240
10318
|
function readFossil(cwd) {
|
|
10241
|
-
const path =
|
|
10242
|
-
if (!
|
|
10319
|
+
const path = join37(cwd, CORE_VERSION_FILE);
|
|
10320
|
+
if (!existsSync37(path)) return null;
|
|
10243
10321
|
try {
|
|
10244
|
-
const value =
|
|
10322
|
+
const value = readFileSync27(path, "utf8").trim();
|
|
10245
10323
|
return value === "" ? null : value;
|
|
10246
10324
|
} catch {
|
|
10247
10325
|
return null;
|