@biffo/cli 0.258.1 → 0.258.3
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 +422 -295
- package/package.json +1 -1
- package/scripts/pg-test-db.sh +79 -3
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 existsSync14, rmSync as rmSync5 } from "fs";
|
|
574
|
+
import { join as join15, 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";
|
|
@@ -2783,17 +2783,110 @@ function materializeTemplateAtTag(repo, version, git = defaultGit2) {
|
|
|
2783
2783
|
return { dir, cleanup: () => rmSync3(dir, { recursive: true, force: true }) };
|
|
2784
2784
|
}
|
|
2785
2785
|
|
|
2786
|
-
// src/lib/
|
|
2786
|
+
// src/lib/core-upgrade-target-fidelity.ts
|
|
2787
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
2788
|
+
import { createHash as createHash2 } from "crypto";
|
|
2787
2789
|
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
|
|
2788
2790
|
import { join as join9 } from "path";
|
|
2791
|
+
var VERBATIM_STATUSES = /* @__PURE__ */ new Set(["take-theirs", "added", "add-conflict", "restored"]);
|
|
2792
|
+
var DERIVED_STATUSES = /* @__PURE__ */ new Set(["merged", "conflict"]);
|
|
2793
|
+
var defaultGit3 = (args) => execFileSync4("git", args, { encoding: "utf8" });
|
|
2794
|
+
function blobId(content) {
|
|
2795
|
+
const bytes = Buffer.from(content, "utf8");
|
|
2796
|
+
return createHash2("sha1").update(`blob ${String(bytes.length)}\0`).update(bytes).digest("hex");
|
|
2797
|
+
}
|
|
2798
|
+
function tagBlobIds(repo, tag, git) {
|
|
2799
|
+
const out = git(["-C", repo, "ls-tree", "-r", "-z", tag]);
|
|
2800
|
+
const blobs = /* @__PURE__ */ new Map();
|
|
2801
|
+
for (const record of out.split("\0")) {
|
|
2802
|
+
if (record === "") continue;
|
|
2803
|
+
const tab = record.indexOf(" ");
|
|
2804
|
+
if (tab === -1) continue;
|
|
2805
|
+
const meta = record.slice(0, tab).split(" ");
|
|
2806
|
+
const id = meta[2];
|
|
2807
|
+
if (meta[1] !== "blob" || id === void 0) continue;
|
|
2808
|
+
blobs.set(record.slice(tab + 1), id);
|
|
2809
|
+
}
|
|
2810
|
+
return blobs;
|
|
2811
|
+
}
|
|
2812
|
+
function assertTargetFidelity(options) {
|
|
2813
|
+
const git = options.git ?? defaultGit3;
|
|
2814
|
+
if (options.explicitTargetTree === true) {
|
|
2815
|
+
return {
|
|
2816
|
+
checked: 0,
|
|
2817
|
+
findings: [],
|
|
2818
|
+
unverifiable: "the target tree was supplied explicitly (--to-template), so there is no tag to compare against. Re-run with --to <version> to have the upgrade verify its own output."
|
|
2819
|
+
};
|
|
2820
|
+
}
|
|
2821
|
+
const tag = coreTag(options.toVersion);
|
|
2822
|
+
let blobs;
|
|
2823
|
+
try {
|
|
2824
|
+
blobs = tagBlobIds(options.templateRepo, tag, git);
|
|
2825
|
+
} catch (err) {
|
|
2826
|
+
return {
|
|
2827
|
+
checked: 0,
|
|
2828
|
+
findings: [],
|
|
2829
|
+
unverifiable: `could not read ${tag} from ${options.templateRepo} to verify the upgrade's own output: ${err.message}`
|
|
2830
|
+
};
|
|
2831
|
+
}
|
|
2832
|
+
const findings = [];
|
|
2833
|
+
let checked = 0;
|
|
2834
|
+
for (const entry of options.entries) {
|
|
2835
|
+
let content;
|
|
2836
|
+
let source;
|
|
2837
|
+
if (VERBATIM_STATUSES.has(entry.status)) {
|
|
2838
|
+
content = entry.content;
|
|
2839
|
+
source = "output";
|
|
2840
|
+
} else if (DERIVED_STATUSES.has(entry.status)) {
|
|
2841
|
+
const abs = join9(options.theirsDir, entry.path);
|
|
2842
|
+
content = existsSync8(abs) ? readFileSync6(abs, "utf8") : void 0;
|
|
2843
|
+
source = "merge-input";
|
|
2844
|
+
} else {
|
|
2845
|
+
continue;
|
|
2846
|
+
}
|
|
2847
|
+
if (content === void 0) continue;
|
|
2848
|
+
checked++;
|
|
2849
|
+
const expected = blobs.get(entry.path);
|
|
2850
|
+
if (expected === void 0) {
|
|
2851
|
+
findings.push({ path: entry.path, reason: "absent-from-tag", source });
|
|
2852
|
+
continue;
|
|
2853
|
+
}
|
|
2854
|
+
if (blobId(content) === expected) continue;
|
|
2855
|
+
let tagText;
|
|
2856
|
+
try {
|
|
2857
|
+
tagText = git(["-C", options.templateRepo, "show", `${tag}:${entry.path}`]);
|
|
2858
|
+
} catch {
|
|
2859
|
+
findings.push({ path: entry.path, reason: "content-differs", source });
|
|
2860
|
+
continue;
|
|
2861
|
+
}
|
|
2862
|
+
if (tagText !== content) {
|
|
2863
|
+
findings.push({ path: entry.path, reason: "content-differs", source });
|
|
2864
|
+
}
|
|
2865
|
+
}
|
|
2866
|
+
return { checked, findings, unverifiable: null };
|
|
2867
|
+
}
|
|
2868
|
+
function fidelityFailure(report, toVersion) {
|
|
2869
|
+
const lines = report.findings.map((f) => {
|
|
2870
|
+
const why = f.reason === "absent-from-tag" ? "resolved content for a path the tag does not contain" : "content differs from the tag";
|
|
2871
|
+
return ` ${f.path} \u2014 ${why} (${f.source})`;
|
|
2872
|
+
});
|
|
2873
|
+
return `This upgrade resolved content that is not what core-v${toVersion} ships, for ${String(report.findings.length)} of ${String(report.checked)} checked path(s):
|
|
2874
|
+
${lines.join("\n")}
|
|
2875
|
+
|
|
2876
|
+
An upgrade must be reproducible from a version number, so this is refused rather than distributed (#1399). The content came from somewhere other than the tag \u2014 a stale or wrong template checkout, or an unmerged branch. Verify the template repo is at the target tag and re-run; do not commit the plan as-is.`;
|
|
2877
|
+
}
|
|
2878
|
+
|
|
2879
|
+
// src/lib/instance-seams.ts
|
|
2880
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
|
|
2881
|
+
import { join as join10 } from "path";
|
|
2789
2882
|
var INSTANCE_SEAM_PREFIX = "@/instance-";
|
|
2790
2883
|
function readSeams(templateDir, portalRelDir) {
|
|
2791
2884
|
const seams = /* @__PURE__ */ new Map();
|
|
2792
|
-
const tsconfigPath =
|
|
2793
|
-
if (!
|
|
2885
|
+
const tsconfigPath = join10(templateDir, portalRelDir, "tsconfig.json");
|
|
2886
|
+
if (!existsSync9(tsconfigPath)) return seams;
|
|
2794
2887
|
let parsed;
|
|
2795
2888
|
try {
|
|
2796
|
-
parsed = JSON.parse(
|
|
2889
|
+
parsed = JSON.parse(readFileSync7(tsconfigPath, "utf8"));
|
|
2797
2890
|
} catch {
|
|
2798
2891
|
return seams;
|
|
2799
2892
|
}
|
|
@@ -2816,15 +2909,15 @@ function findNewUndeclaredSeams(baseDir, theirsDir, oursDir, portalRelDir = "app
|
|
|
2816
2909
|
const undeclared = [];
|
|
2817
2910
|
for (const [specifier, seam] of theirsSeams) {
|
|
2818
2911
|
if (baseSeams.has(specifier)) continue;
|
|
2819
|
-
if (
|
|
2912
|
+
if (existsSync9(join10(oursDir, seam.instanceFile))) continue;
|
|
2820
2913
|
undeclared.push(seam);
|
|
2821
2914
|
}
|
|
2822
2915
|
return undeclared.sort((a, b) => a.specifier.localeCompare(b.specifier));
|
|
2823
2916
|
}
|
|
2824
2917
|
|
|
2825
2918
|
// src/lib/breaking-changes.ts
|
|
2826
|
-
import { existsSync as
|
|
2827
|
-
import { join as
|
|
2919
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
|
|
2920
|
+
import { join as join11 } from "path";
|
|
2828
2921
|
var UPGRADE_GUIDE_PATH = "docs/guides/core-upgrade.md";
|
|
2829
2922
|
var SECTION_HEADING = "## Breaking changes by version";
|
|
2830
2923
|
var ENTRY_HEADING = /^###\s+(\d+\.\d+\.\d+)\s*[—-]\s*(.+?)\s*$/;
|
|
@@ -2849,9 +2942,9 @@ function parseBreakingChanges(guide) {
|
|
|
2849
2942
|
return entries;
|
|
2850
2943
|
}
|
|
2851
2944
|
function readBreakingChanges(templateRoot) {
|
|
2852
|
-
const path =
|
|
2853
|
-
if (!
|
|
2854
|
-
return parseBreakingChanges(
|
|
2945
|
+
const path = join11(templateRoot, UPGRADE_GUIDE_PATH);
|
|
2946
|
+
if (!existsSync10(path)) return [];
|
|
2947
|
+
return parseBreakingChanges(readFileSync8(path, "utf8"));
|
|
2855
2948
|
}
|
|
2856
2949
|
function breakingChangesBetween(from, to, entries) {
|
|
2857
2950
|
parseCoreVersion(from);
|
|
@@ -2868,8 +2961,8 @@ var GLOBAL_DISPATCH_WORKFLOW_PATHS = [
|
|
|
2868
2961
|
];
|
|
2869
2962
|
|
|
2870
2963
|
// src/lib/plugin-terraform-wiring.ts
|
|
2871
|
-
import { existsSync as
|
|
2872
|
-
import { join as
|
|
2964
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync3, readFileSync as readFileSync9, readdirSync as readdirSync3, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
2965
|
+
import { join as join12 } from "path";
|
|
2873
2966
|
var TEMPLATE_MODULE_DIR = "_template";
|
|
2874
2967
|
var DEFAULT_PLUGIN_HANDLER = "src.lambda.main.handler";
|
|
2875
2968
|
var GENERATED_TF_FILE = "plugins.generated.tf";
|
|
@@ -2894,7 +2987,7 @@ function standardArguments(pluginName, handler) {
|
|
|
2894
2987
|
];
|
|
2895
2988
|
}
|
|
2896
2989
|
function listPluginModules(cwd) {
|
|
2897
|
-
const dir =
|
|
2990
|
+
const dir = join12(cwd, "modules", "plugins");
|
|
2898
2991
|
let entries;
|
|
2899
2992
|
try {
|
|
2900
2993
|
entries = readdirSync3(dir, { withFileTypes: true });
|
|
@@ -2906,7 +2999,7 @@ function listPluginModules(cwd) {
|
|
|
2906
2999
|
var FIRST_PARTY_TERRAFORM = (name) => `../../../services/_plugins/${name}/terraform`;
|
|
2907
3000
|
var THIRD_PARTY_TERRAFORM = (name) => `../../../modules/plugins/${name}`;
|
|
2908
3001
|
function isFirstPartyPlugin(cwd, name) {
|
|
2909
|
-
return
|
|
3002
|
+
return existsSync11(join12(cwd, "services", "_plugins", name, "terraform", "main.tf"));
|
|
2910
3003
|
}
|
|
2911
3004
|
function pluginModuleSource(cwd, name) {
|
|
2912
3005
|
return isFirstPartyPlugin(cwd, name) ? FIRST_PARTY_TERRAFORM(name) : THIRD_PARTY_TERRAFORM(name);
|
|
@@ -2915,7 +3008,7 @@ function listWireablePlugins(cwd) {
|
|
|
2915
3008
|
return listPluginModules(cwd).filter((name) => !isFirstPartyPlugin(cwd, name)).sort();
|
|
2916
3009
|
}
|
|
2917
3010
|
function firstPartyPluginNames(cwd) {
|
|
2918
|
-
const dir =
|
|
3011
|
+
const dir = join12(cwd, "services", "_plugins");
|
|
2919
3012
|
let entries;
|
|
2920
3013
|
try {
|
|
2921
3014
|
entries = readdirSync3(dir, { withFileTypes: true });
|
|
@@ -2929,7 +3022,7 @@ function staleFirstPartyCopies(cwd) {
|
|
|
2929
3022
|
return firstPartyPluginNames(cwd).filter((name) => copied.has(name));
|
|
2930
3023
|
}
|
|
2931
3024
|
function listEnvironments(cwd) {
|
|
2932
|
-
const dir =
|
|
3025
|
+
const dir = join12(cwd, "infra", "environments");
|
|
2933
3026
|
let entries;
|
|
2934
3027
|
try {
|
|
2935
3028
|
entries = readdirSync3(dir, { withFileTypes: true });
|
|
@@ -2937,12 +3030,12 @@ function listEnvironments(cwd) {
|
|
|
2937
3030
|
return [];
|
|
2938
3031
|
}
|
|
2939
3032
|
return entries.filter((e) => {
|
|
2940
|
-
if (!e.isDirectory() || !
|
|
2941
|
-
return declaredVariables(
|
|
3033
|
+
if (!e.isDirectory() || !existsSync11(join12(dir, e.name, "main.tf"))) return false;
|
|
3034
|
+
return declaredVariables(join12(dir, e.name)).has("enabled_plugins");
|
|
2942
3035
|
}).map((e) => e.name).sort();
|
|
2943
3036
|
}
|
|
2944
3037
|
function listUnwirableEnvironments(cwd) {
|
|
2945
|
-
const dir =
|
|
3038
|
+
const dir = join12(cwd, "infra", "environments");
|
|
2946
3039
|
let entries;
|
|
2947
3040
|
try {
|
|
2948
3041
|
entries = readdirSync3(dir, { withFileTypes: true });
|
|
@@ -2950,7 +3043,7 @@ function listUnwirableEnvironments(cwd) {
|
|
|
2950
3043
|
return [];
|
|
2951
3044
|
}
|
|
2952
3045
|
return entries.filter(
|
|
2953
|
-
(e) => e.isDirectory() &&
|
|
3046
|
+
(e) => e.isDirectory() && existsSync11(join12(dir, e.name, "main.tf")) && !declaredVariables(join12(dir, e.name)).has("enabled_plugins")
|
|
2954
3047
|
).map((e) => e.name).sort();
|
|
2955
3048
|
}
|
|
2956
3049
|
function declaredVariables(moduleDir) {
|
|
@@ -2965,7 +3058,7 @@ function declaredVariables(moduleDir) {
|
|
|
2965
3058
|
if (!entry.isFile() || !entry.name.endsWith(".tf")) continue;
|
|
2966
3059
|
let contents;
|
|
2967
3060
|
try {
|
|
2968
|
-
contents =
|
|
3061
|
+
contents = readFileSync9(join12(moduleDir, entry.name), "utf8");
|
|
2969
3062
|
} catch {
|
|
2970
3063
|
continue;
|
|
2971
3064
|
}
|
|
@@ -2987,7 +3080,7 @@ function declaredOutputs(moduleDir) {
|
|
|
2987
3080
|
if (!entry.isFile() || !entry.name.endsWith(".tf")) continue;
|
|
2988
3081
|
let contents;
|
|
2989
3082
|
try {
|
|
2990
|
-
contents =
|
|
3083
|
+
contents = readFileSync9(join12(moduleDir, entry.name), "utf8");
|
|
2991
3084
|
} catch {
|
|
2992
3085
|
continue;
|
|
2993
3086
|
}
|
|
@@ -3069,7 +3162,7 @@ function syncPluginTerraform(cwd) {
|
|
|
3069
3162
|
const skippedEnvironments = listUnwirableEnvironments(cwd);
|
|
3070
3163
|
const changedPaths = [];
|
|
3071
3164
|
const rendered = plugins.map((name) => {
|
|
3072
|
-
const moduleDir =
|
|
3165
|
+
const moduleDir = join12(cwd, "modules", "plugins", name);
|
|
3073
3166
|
return {
|
|
3074
3167
|
name,
|
|
3075
3168
|
declaredVariables: declaredVariables(moduleDir),
|
|
@@ -3078,16 +3171,16 @@ function syncPluginTerraform(cwd) {
|
|
|
3078
3171
|
};
|
|
3079
3172
|
});
|
|
3080
3173
|
for (const env of environments) {
|
|
3081
|
-
const envDir =
|
|
3082
|
-
const tfPath =
|
|
3083
|
-
const tfvarsPath =
|
|
3174
|
+
const envDir = join12(cwd, "infra", "environments", env);
|
|
3175
|
+
const tfPath = join12(envDir, GENERATED_TF_FILE);
|
|
3176
|
+
const tfvarsPath = join12(envDir, GENERATED_TFVARS_FILE);
|
|
3084
3177
|
const relBase = `infra/environments/${env}`;
|
|
3085
3178
|
if (plugins.length === 0) {
|
|
3086
3179
|
for (const [abs, rel] of [
|
|
3087
3180
|
[tfPath, `${relBase}/${GENERATED_TF_FILE}`],
|
|
3088
3181
|
[tfvarsPath, `${relBase}/${GENERATED_TFVARS_FILE}`]
|
|
3089
3182
|
]) {
|
|
3090
|
-
if (
|
|
3183
|
+
if (existsSync11(abs)) {
|
|
3091
3184
|
rmSync4(abs);
|
|
3092
3185
|
changedPaths.push(rel);
|
|
3093
3186
|
}
|
|
@@ -3103,8 +3196,8 @@ function syncPluginTerraform(cwd) {
|
|
|
3103
3196
|
}
|
|
3104
3197
|
|
|
3105
3198
|
// src/lib/lockfile-refresh.ts
|
|
3106
|
-
import { existsSync as
|
|
3107
|
-
import { join as
|
|
3199
|
+
import { existsSync as existsSync12 } from "fs";
|
|
3200
|
+
import { join as join13 } from "path";
|
|
3108
3201
|
var LOCKFILE_TRIGGERS = [
|
|
3109
3202
|
{
|
|
3110
3203
|
manifest: "package.json",
|
|
@@ -3127,7 +3220,7 @@ function lockfilesNeedingRefresh(changedPaths, instanceDir, triggers = LOCKFILE_
|
|
|
3127
3220
|
const locked = changedPaths.filter((p) => !isForeignManifest(p));
|
|
3128
3221
|
return triggers.filter((t) => {
|
|
3129
3222
|
const touched = locked.some((p) => p === t.manifest || p.endsWith(`/${t.manifest}`));
|
|
3130
|
-
return touched &&
|
|
3223
|
+
return touched && existsSync12(join13(instanceDir, t.lockfile));
|
|
3131
3224
|
});
|
|
3132
3225
|
}
|
|
3133
3226
|
async function refreshLockfiles(instanceDir, triggers, run) {
|
|
@@ -3147,11 +3240,11 @@ function describeFailures(outcomes) {
|
|
|
3147
3240
|
}
|
|
3148
3241
|
|
|
3149
3242
|
// src/lib/instance-dependency-install.ts
|
|
3150
|
-
import { existsSync as
|
|
3151
|
-
import { join as
|
|
3243
|
+
import { existsSync as existsSync13 } from "fs";
|
|
3244
|
+
import { join as join14 } from "path";
|
|
3152
3245
|
function dependencyInstallSteps(instanceDir) {
|
|
3153
3246
|
const steps = [{ ecosystem: "pnpm", command: ["pnpm", "install"] }];
|
|
3154
|
-
if (
|
|
3247
|
+
if (existsSync13(join14(instanceDir, "pyproject.toml"))) {
|
|
3155
3248
|
steps.push({ ecosystem: "uv", command: ["uv", "sync"] });
|
|
3156
3249
|
}
|
|
3157
3250
|
return steps;
|
|
@@ -3363,6 +3456,13 @@ async function runCoreUpgradeResolved(options, deps, cleanups) {
|
|
|
3363
3456
|
});
|
|
3364
3457
|
const orphanRatchet = checkOrphanRatchet(plan.orphaned.length, readOrphanBaseline(options.cwd));
|
|
3365
3458
|
const newSeams = findNewUndeclaredSeams(baseDir, theirsDir, options.cwd);
|
|
3459
|
+
const fidelity = assertTargetFidelity({
|
|
3460
|
+
entries: plan.entries,
|
|
3461
|
+
templateRepo,
|
|
3462
|
+
toVersion,
|
|
3463
|
+
theirsDir,
|
|
3464
|
+
explicitTargetTree: options.theirsDir !== void 0
|
|
3465
|
+
});
|
|
3366
3466
|
const heading = options.apply ? "Biffo core upgrade" : "Biffo core upgrade (dry run)";
|
|
3367
3467
|
console.log(chalk4.bold(`
|
|
3368
3468
|
${heading}
|
|
@@ -3371,6 +3471,10 @@ async function runCoreUpgradeResolved(options, deps, cleanups) {
|
|
|
3371
3471
|
console.log(` merge base: ${fromVersion}`);
|
|
3372
3472
|
console.log(` target: ${toVersion}
|
|
3373
3473
|
`);
|
|
3474
|
+
printTargetFidelity(fidelity, toVersion);
|
|
3475
|
+
if (fidelity.findings.length > 0) {
|
|
3476
|
+
throw new Error(fidelityFailure(fidelity, toVersion));
|
|
3477
|
+
}
|
|
3374
3478
|
printNewInstanceSeams(newSeams);
|
|
3375
3479
|
printOrphanReport(plan.orphaned, orphanRatchet);
|
|
3376
3480
|
if (orphanRatchet.increased) {
|
|
@@ -3518,7 +3622,7 @@ async function buildCommitAndOpenPr(options, deps, plan, migrations, fromVersion
|
|
|
3518
3622
|
);
|
|
3519
3623
|
}
|
|
3520
3624
|
const cleanedCoreVersion = coreVersionCleanup?.action === "delete";
|
|
3521
|
-
if (cleanedCoreVersion &&
|
|
3625
|
+
if (cleanedCoreVersion && existsSync14(coreVersionCleanup.path)) {
|
|
3522
3626
|
rmSync5(coreVersionCleanup.path);
|
|
3523
3627
|
log.info(
|
|
3524
3628
|
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).`
|
|
@@ -3863,6 +3967,29 @@ function printNewInstanceSeams(seams) {
|
|
|
3863
3967
|
}
|
|
3864
3968
|
console.log();
|
|
3865
3969
|
}
|
|
3970
|
+
function printTargetFidelity(report, toVersion) {
|
|
3971
|
+
if (report.unverifiable !== null) {
|
|
3972
|
+
console.log(chalk4.yellow(` Target fidelity: NOT VERIFIED \u2014 ${report.unverifiable}`));
|
|
3973
|
+
console.log();
|
|
3974
|
+
return;
|
|
3975
|
+
}
|
|
3976
|
+
if (report.findings.length === 0) {
|
|
3977
|
+
console.log(
|
|
3978
|
+
chalk4.dim(
|
|
3979
|
+
` Target fidelity: ${String(report.checked)} path(s) verified byte-identical to core-v${toVersion}.`
|
|
3980
|
+
)
|
|
3981
|
+
);
|
|
3982
|
+
console.log();
|
|
3983
|
+
return;
|
|
3984
|
+
}
|
|
3985
|
+
console.log(
|
|
3986
|
+
chalk4.red(
|
|
3987
|
+
` Target fidelity: ${String(report.findings.length)} of ${String(report.checked)} checked path(s) are NOT core-v${toVersion} content (#1399):`
|
|
3988
|
+
)
|
|
3989
|
+
);
|
|
3990
|
+
for (const f of report.findings) console.log(` ${chalk4.red(f.path)} \u2014 ${f.reason}`);
|
|
3991
|
+
console.log();
|
|
3992
|
+
}
|
|
3866
3993
|
function printOrphanReport(orphaned, ratchet) {
|
|
3867
3994
|
if (orphaned.length === 0 && ratchet.baseline === null) return;
|
|
3868
3995
|
console.log(
|
|
@@ -3956,8 +4083,8 @@ function printBreakingChanges(breaking, applying) {
|
|
|
3956
4083
|
}
|
|
3957
4084
|
function versionOfCheckout(dir, explicit) {
|
|
3958
4085
|
if (explicit) return explicit;
|
|
3959
|
-
const file =
|
|
3960
|
-
if (
|
|
4086
|
+
const file = join15(dir, CORE_VERSION_FILE);
|
|
4087
|
+
if (existsSync14(file)) return readCoreVersionFile(file);
|
|
3961
4088
|
throw new Error(
|
|
3962
4089
|
`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.`
|
|
3963
4090
|
);
|
|
@@ -3965,8 +4092,8 @@ function versionOfCheckout(dir, explicit) {
|
|
|
3965
4092
|
function latestCoreVersion(repo) {
|
|
3966
4093
|
const fromTags = latestCoreVersionFromTags(repo);
|
|
3967
4094
|
if (fromTags) return fromTags;
|
|
3968
|
-
const file =
|
|
3969
|
-
if (
|
|
4095
|
+
const file = join15(repo, CORE_VERSION_FILE);
|
|
4096
|
+
if (existsSync14(file)) return readCoreVersionFile(file);
|
|
3970
4097
|
throw new Error(
|
|
3971
4098
|
`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.`
|
|
3972
4099
|
);
|
|
@@ -3984,7 +4111,7 @@ coreCommand.addCommand(coreUpgradeCommand);
|
|
|
3984
4111
|
import { Command as Command8 } from "commander";
|
|
3985
4112
|
|
|
3986
4113
|
// src/commands/data-apply.ts
|
|
3987
|
-
import { existsSync as
|
|
4114
|
+
import { existsSync as existsSync16, readFileSync as readFileSync11 } from "fs";
|
|
3988
4115
|
import { resolve as resolve4 } from "path";
|
|
3989
4116
|
import chalk5 from "chalk";
|
|
3990
4117
|
import { Command as Command5 } from "commander";
|
|
@@ -4435,16 +4562,16 @@ function isTemplatePlaceholderConfig(raw) {
|
|
|
4435
4562
|
|
|
4436
4563
|
// src/lib/session.ts
|
|
4437
4564
|
import {
|
|
4438
|
-
existsSync as
|
|
4565
|
+
existsSync as existsSync15,
|
|
4439
4566
|
mkdirSync as mkdirSync4,
|
|
4440
4567
|
readdirSync as readdirSync4,
|
|
4441
|
-
readFileSync as
|
|
4568
|
+
readFileSync as readFileSync10,
|
|
4442
4569
|
rmSync as rmSync6,
|
|
4443
4570
|
statSync as statSync2,
|
|
4444
4571
|
writeFileSync as writeFileSync5
|
|
4445
4572
|
} from "fs";
|
|
4446
4573
|
import { homedir } from "os";
|
|
4447
|
-
import { join as
|
|
4574
|
+
import { join as join16 } from "path";
|
|
4448
4575
|
var LEGACY_STEP_ALIASES = {
|
|
4449
4576
|
github_config: ["github_branches", "github_instance_files", "github_settings"]
|
|
4450
4577
|
};
|
|
@@ -4453,39 +4580,39 @@ function hasCompleted(session, step) {
|
|
|
4453
4580
|
return session.completedSteps.some((done) => LEGACY_STEP_ALIASES[done]?.includes(step) ?? false);
|
|
4454
4581
|
}
|
|
4455
4582
|
function sessionsDir() {
|
|
4456
|
-
return process.env["BIFFO_SESSIONS_DIR"] ??
|
|
4583
|
+
return process.env["BIFFO_SESSIONS_DIR"] ?? join16(homedir(), ".biffo", "sessions");
|
|
4457
4584
|
}
|
|
4458
4585
|
function sessionPath(projectName) {
|
|
4459
|
-
return
|
|
4586
|
+
return join16(sessionsDir(), `${projectName}.json`);
|
|
4460
4587
|
}
|
|
4461
4588
|
function loadSession(projectName) {
|
|
4462
4589
|
const path = sessionPath(projectName);
|
|
4463
|
-
if (!
|
|
4590
|
+
if (!existsSync15(path)) return null;
|
|
4464
4591
|
try {
|
|
4465
|
-
return JSON.parse(
|
|
4592
|
+
return JSON.parse(readFileSync10(path, "utf8"));
|
|
4466
4593
|
} catch {
|
|
4467
4594
|
return null;
|
|
4468
4595
|
}
|
|
4469
4596
|
}
|
|
4470
4597
|
function findLatestSession() {
|
|
4471
4598
|
const dir = sessionsDir();
|
|
4472
|
-
if (!
|
|
4599
|
+
if (!existsSync15(dir)) return null;
|
|
4473
4600
|
const files = readdirSync4(dir).filter((f) => f.endsWith(".json"));
|
|
4474
4601
|
if (files.length === 0) return null;
|
|
4475
4602
|
const sorted = files.map((f) => {
|
|
4476
|
-
const fullPath =
|
|
4477
|
-
const mtime =
|
|
4603
|
+
const fullPath = join16(dir, f);
|
|
4604
|
+
const mtime = existsSync15(fullPath) ? statSync2(fullPath).mtimeMs : -1;
|
|
4478
4605
|
return { f, mtime };
|
|
4479
4606
|
}).sort((a, b) => b.mtime - a.mtime);
|
|
4480
4607
|
try {
|
|
4481
|
-
return JSON.parse(
|
|
4608
|
+
return JSON.parse(readFileSync10(join16(dir, sorted[0].f), "utf8"));
|
|
4482
4609
|
} catch {
|
|
4483
4610
|
return null;
|
|
4484
4611
|
}
|
|
4485
4612
|
}
|
|
4486
4613
|
function saveSession(session) {
|
|
4487
4614
|
const dir = sessionsDir();
|
|
4488
|
-
if (!
|
|
4615
|
+
if (!existsSync15(dir)) mkdirSync4(dir, { recursive: true });
|
|
4489
4616
|
const name = session.config.project?.name ?? "unknown";
|
|
4490
4617
|
const prior = loadSession(name);
|
|
4491
4618
|
if (prior) {
|
|
@@ -4507,36 +4634,36 @@ function markStepComplete(session, step) {
|
|
|
4507
4634
|
}
|
|
4508
4635
|
function deleteSession(projectName) {
|
|
4509
4636
|
const path = sessionPath(projectName);
|
|
4510
|
-
if (
|
|
4637
|
+
if (existsSync15(path)) rmSync6(path);
|
|
4511
4638
|
}
|
|
4512
4639
|
function projectsDir() {
|
|
4513
|
-
return process.env["BIFFO_PROJECTS_DIR"] ??
|
|
4640
|
+
return process.env["BIFFO_PROJECTS_DIR"] ?? join16(homedir(), ".biffo", "projects");
|
|
4514
4641
|
}
|
|
4515
4642
|
function saveProjectConfig(config) {
|
|
4516
4643
|
const dir = projectsDir();
|
|
4517
|
-
if (!
|
|
4518
|
-
writeFileSync5(
|
|
4644
|
+
if (!existsSync15(dir)) mkdirSync4(dir, { recursive: true });
|
|
4645
|
+
writeFileSync5(join16(dir, `${config.project.name}.json`), JSON.stringify(config, null, 2));
|
|
4519
4646
|
}
|
|
4520
4647
|
function loadProjectConfig(name) {
|
|
4521
|
-
const path =
|
|
4522
|
-
if (!
|
|
4648
|
+
const path = join16(projectsDir(), `${name}.json`);
|
|
4649
|
+
if (!existsSync15(path)) return null;
|
|
4523
4650
|
try {
|
|
4524
|
-
const result = BiffoConfigSchema.safeParse(JSON.parse(
|
|
4651
|
+
const result = BiffoConfigSchema.safeParse(JSON.parse(readFileSync10(path, "utf8")));
|
|
4525
4652
|
return result.success ? result.data : null;
|
|
4526
4653
|
} catch {
|
|
4527
4654
|
return null;
|
|
4528
4655
|
}
|
|
4529
4656
|
}
|
|
4530
4657
|
function deleteProjectConfig(name) {
|
|
4531
|
-
const path =
|
|
4532
|
-
if (
|
|
4658
|
+
const path = join16(projectsDir(), `${name}.json`);
|
|
4659
|
+
if (existsSync15(path)) rmSync6(path);
|
|
4533
4660
|
}
|
|
4534
4661
|
function listProjectConfigs() {
|
|
4535
4662
|
const dir = projectsDir();
|
|
4536
|
-
if (!
|
|
4663
|
+
if (!existsSync15(dir)) return [];
|
|
4537
4664
|
return readdirSync4(dir).filter((f) => f.endsWith(".json")).flatMap((f) => {
|
|
4538
4665
|
try {
|
|
4539
|
-
const result = BiffoConfigSchema.safeParse(JSON.parse(
|
|
4666
|
+
const result = BiffoConfigSchema.safeParse(JSON.parse(readFileSync10(join16(dir, f), "utf8")));
|
|
4540
4667
|
return result.success ? [result.data] : [];
|
|
4541
4668
|
} catch {
|
|
4542
4669
|
return [];
|
|
@@ -4605,7 +4732,7 @@ async function runDataApply(name, environment, config, aws) {
|
|
|
4605
4732
|
}
|
|
4606
4733
|
async function resolveConfig(options) {
|
|
4607
4734
|
if (options.config) {
|
|
4608
|
-
const raw = JSON.parse(
|
|
4735
|
+
const raw = JSON.parse(readFileSync11(resolve4(options.config), "utf8"));
|
|
4609
4736
|
const result = BiffoConfigSchema.safeParse(raw);
|
|
4610
4737
|
if (!result.success) {
|
|
4611
4738
|
log.error(`Invalid config at ${options.config}:`);
|
|
@@ -4625,8 +4752,8 @@ async function resolveConfig(options) {
|
|
|
4625
4752
|
return cfg;
|
|
4626
4753
|
}
|
|
4627
4754
|
const localConfigPath = resolve4(process.cwd(), "biffo.config.json");
|
|
4628
|
-
if (
|
|
4629
|
-
const raw = JSON.parse(
|
|
4755
|
+
if (existsSync16(localConfigPath)) {
|
|
4756
|
+
const raw = JSON.parse(readFileSync11(localConfigPath, "utf8"));
|
|
4630
4757
|
const result = BiffoConfigSchema.safeParse(raw);
|
|
4631
4758
|
if (result.success) return result.data;
|
|
4632
4759
|
if (isTemplatePlaceholderConfig(raw)) {
|
|
@@ -4672,8 +4799,8 @@ async function resolveConfig(options) {
|
|
|
4672
4799
|
|
|
4673
4800
|
// src/commands/data-import.ts
|
|
4674
4801
|
import { execSync as execSync3 } from "child_process";
|
|
4675
|
-
import { cpSync, existsSync as
|
|
4676
|
-
import { join as
|
|
4802
|
+
import { cpSync, existsSync as existsSync17, mkdirSync as mkdirSync5, readdirSync as readdirSync5, statSync as statSync3 } from "fs";
|
|
4803
|
+
import { join as join17, resolve as resolve5 } from "path";
|
|
4677
4804
|
import chalk6 from "chalk";
|
|
4678
4805
|
import { Command as Command6 } from "commander";
|
|
4679
4806
|
import inquirer2 from "inquirer";
|
|
@@ -4713,23 +4840,23 @@ async function runDataImport(name, options, deps) {
|
|
|
4713
4840
|
`Invalid import name '${name}'. Use lowercase letters, numbers, and hyphens, starting with a letter.`
|
|
4714
4841
|
);
|
|
4715
4842
|
}
|
|
4716
|
-
const servicesDir =
|
|
4717
|
-
if (!
|
|
4843
|
+
const servicesDir = join17(options.cwd, "services");
|
|
4844
|
+
if (!existsSync17(servicesDir)) {
|
|
4718
4845
|
throw new Error(
|
|
4719
4846
|
`${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
|
|
4720
4847
|
);
|
|
4721
4848
|
}
|
|
4722
|
-
const targetDir =
|
|
4723
|
-
if (
|
|
4849
|
+
const targetDir = join17(options.cwd, "db", "imports", name);
|
|
4850
|
+
if (existsSync17(targetDir)) {
|
|
4724
4851
|
throw new Error(
|
|
4725
4852
|
`DDL import '${name}' is already present at db/imports/${name}/. Remove it first to re-import.`
|
|
4726
4853
|
);
|
|
4727
4854
|
}
|
|
4728
|
-
const isLocalDir =
|
|
4855
|
+
const isLocalDir = existsSync17(options.source) && statSync3(options.source).isDirectory();
|
|
4729
4856
|
let sourceDir;
|
|
4730
4857
|
let cleanupClone = null;
|
|
4731
4858
|
if (isLocalDir) {
|
|
4732
|
-
sourceDir = options.path ?
|
|
4859
|
+
sourceDir = options.path ? join17(options.source, options.path) : options.source;
|
|
4733
4860
|
} else {
|
|
4734
4861
|
const token = options.token ?? await resolveDdlImportToken();
|
|
4735
4862
|
log.info(`Cloning ${options.source}...`);
|
|
@@ -4737,10 +4864,10 @@ async function runDataImport(name, options, deps) {
|
|
|
4737
4864
|
cleanupClone = () => {
|
|
4738
4865
|
deps.git.cleanup(tmpDir);
|
|
4739
4866
|
};
|
|
4740
|
-
sourceDir = options.path ?
|
|
4867
|
+
sourceDir = options.path ? join17(tmpDir, options.path) : tmpDir;
|
|
4741
4868
|
}
|
|
4742
4869
|
try {
|
|
4743
|
-
if (!
|
|
4870
|
+
if (!existsSync17(sourceDir)) {
|
|
4744
4871
|
throw new Error(`Source directory does not exist: ${sourceDir}`);
|
|
4745
4872
|
}
|
|
4746
4873
|
const sqlFiles = readdirSync5(sourceDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".sql")).map((entry) => entry.name).sort();
|
|
@@ -4765,7 +4892,7 @@ async function runDataImport(name, options, deps) {
|
|
|
4765
4892
|
}
|
|
4766
4893
|
mkdirSync5(targetDir, { recursive: true });
|
|
4767
4894
|
for (const file of sqlFiles) {
|
|
4768
|
-
cpSync(
|
|
4895
|
+
cpSync(join17(sourceDir, file), join17(targetDir, file));
|
|
4769
4896
|
}
|
|
4770
4897
|
log.success(`Imported ${String(sqlFiles.length)} .sql file(s) to db/imports/${name}/`);
|
|
4771
4898
|
const commitMessage = `feat(data): import ${name} (${String(sqlFiles.length)} SQL file(s))`;
|
|
@@ -4817,8 +4944,8 @@ function printDryRun(name, sqlFiles) {
|
|
|
4817
4944
|
}
|
|
4818
4945
|
|
|
4819
4946
|
// src/commands/data-list.ts
|
|
4820
|
-
import { existsSync as
|
|
4821
|
-
import { join as
|
|
4947
|
+
import { existsSync as existsSync18, readdirSync as readdirSync6 } from "fs";
|
|
4948
|
+
import { join as join18, resolve as resolve6 } from "path";
|
|
4822
4949
|
import chalk7 from "chalk";
|
|
4823
4950
|
import { Command as Command7 } from "commander";
|
|
4824
4951
|
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) => {
|
|
@@ -4831,15 +4958,15 @@ var dataListCommand = new Command7("list").description("List DDL imports vendore
|
|
|
4831
4958
|
}
|
|
4832
4959
|
});
|
|
4833
4960
|
async function runDataList(options) {
|
|
4834
|
-
const importsDir =
|
|
4835
|
-
if (!
|
|
4961
|
+
const importsDir = join18(options.cwd, "db", "imports");
|
|
4962
|
+
if (!existsSync18(importsDir)) {
|
|
4836
4963
|
console.log(chalk7.dim("\n No DDL imports in this checkout.\n"));
|
|
4837
4964
|
return;
|
|
4838
4965
|
}
|
|
4839
4966
|
const candidates = readdirSync6(importsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
|
|
4840
4967
|
const imports = [];
|
|
4841
4968
|
for (const name of candidates) {
|
|
4842
|
-
const fileCount = readdirSync6(
|
|
4969
|
+
const fileCount = readdirSync6(join18(importsDir, name)).filter((f) => f.endsWith(".sql")).length;
|
|
4843
4970
|
if (fileCount > 0) imports.push({ name, fileCount });
|
|
4844
4971
|
}
|
|
4845
4972
|
if (imports.length === 0) {
|
|
@@ -4869,7 +4996,7 @@ dataCommand.addCommand(dataListCommand);
|
|
|
4869
4996
|
|
|
4870
4997
|
// src/commands/deploy.ts
|
|
4871
4998
|
import { execSync as execSync4 } from "child_process";
|
|
4872
|
-
import { existsSync as
|
|
4999
|
+
import { existsSync as existsSync19, readFileSync as readFileSync12 } from "fs";
|
|
4873
5000
|
import { resolve as resolve7 } from "path";
|
|
4874
5001
|
import chalk8 from "chalk";
|
|
4875
5002
|
import { Command as Command9 } from "commander";
|
|
@@ -5226,7 +5353,7 @@ var deployCommand = new Command9("deploy").description("Deploy infrastructure an
|
|
|
5226
5353
|
);
|
|
5227
5354
|
async function resolveConfig2(options) {
|
|
5228
5355
|
if (options.config) {
|
|
5229
|
-
const raw = JSON.parse(
|
|
5356
|
+
const raw = JSON.parse(readFileSync12(resolve7(options.config), "utf8"));
|
|
5230
5357
|
const result = BiffoConfigSchema.safeParse(raw);
|
|
5231
5358
|
if (!result.success) {
|
|
5232
5359
|
log.error(`Invalid config at ${options.config}:`);
|
|
@@ -5246,8 +5373,8 @@ async function resolveConfig2(options) {
|
|
|
5246
5373
|
return cfg;
|
|
5247
5374
|
}
|
|
5248
5375
|
const localConfigPath = resolve7(process.cwd(), "biffo.config.json");
|
|
5249
|
-
if (
|
|
5250
|
-
const raw = JSON.parse(
|
|
5376
|
+
if (existsSync19(localConfigPath)) {
|
|
5377
|
+
const raw = JSON.parse(readFileSync12(localConfigPath, "utf8"));
|
|
5251
5378
|
const result = BiffoConfigSchema.safeParse(raw);
|
|
5252
5379
|
if (result.success) return result.data;
|
|
5253
5380
|
if (isTemplatePlaceholderConfig(raw)) {
|
|
@@ -5643,7 +5770,7 @@ function resolveGithubToken() {
|
|
|
5643
5770
|
|
|
5644
5771
|
// src/commands/destroy.ts
|
|
5645
5772
|
import { execSync as execSync5 } from "child_process";
|
|
5646
|
-
import { readFileSync as
|
|
5773
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
5647
5774
|
import { resolve as resolve8 } from "path";
|
|
5648
5775
|
import chalk9 from "chalk";
|
|
5649
5776
|
import { Command as Command10 } from "commander";
|
|
@@ -5733,7 +5860,7 @@ var destroyCommand = new Command10("destroy").description("Destroy infrastructur
|
|
|
5733
5860
|
});
|
|
5734
5861
|
async function resolveConfig3(options) {
|
|
5735
5862
|
if (options.config) {
|
|
5736
|
-
const raw = JSON.parse(
|
|
5863
|
+
const raw = JSON.parse(readFileSync13(resolve8(options.config), "utf8"));
|
|
5737
5864
|
const result = BiffoConfigSchema.safeParse(raw);
|
|
5738
5865
|
if (!result.success) {
|
|
5739
5866
|
log.error(`Invalid config at ${options.config}:`);
|
|
@@ -5753,7 +5880,7 @@ async function resolveConfig3(options) {
|
|
|
5753
5880
|
return cfg;
|
|
5754
5881
|
}
|
|
5755
5882
|
try {
|
|
5756
|
-
const raw = JSON.parse(
|
|
5883
|
+
const raw = JSON.parse(readFileSync13(resolve8(process.cwd(), "biffo.config.json"), "utf8"));
|
|
5757
5884
|
const result = BiffoConfigSchema.safeParse(raw);
|
|
5758
5885
|
if (result.success) return result.data;
|
|
5759
5886
|
} catch {
|
|
@@ -5803,15 +5930,15 @@ function resolveGithubToken2() {
|
|
|
5803
5930
|
}
|
|
5804
5931
|
|
|
5805
5932
|
// src/commands/init.ts
|
|
5806
|
-
import { readFileSync as
|
|
5933
|
+
import { readFileSync as readFileSync17 } from "fs";
|
|
5807
5934
|
import { resolve as resolve10 } from "path";
|
|
5808
5935
|
import chalk12 from "chalk";
|
|
5809
5936
|
import { Command as Command12 } from "commander";
|
|
5810
5937
|
import inquirer5 from "inquirer";
|
|
5811
5938
|
|
|
5812
5939
|
// src/lib/build-freshness.ts
|
|
5813
|
-
import { existsSync as
|
|
5814
|
-
import { dirname as dirname5, join as
|
|
5940
|
+
import { existsSync as existsSync20, readdirSync as readdirSync7, statSync as statSync4 } from "fs";
|
|
5941
|
+
import { dirname as dirname5, join as join19, relative as relative2, sep as sep2 } from "path";
|
|
5815
5942
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
5816
5943
|
var SKIP_ENV_VAR = "BIFFO_SKIP_BUILD_FRESHNESS_CHECK";
|
|
5817
5944
|
function checkBuildFreshness(options = {}) {
|
|
@@ -5825,7 +5952,7 @@ function checkBuildFreshness(options = {}) {
|
|
|
5825
5952
|
if (!packageRoot) {
|
|
5826
5953
|
return { status: "skipped", reason: `no package.json above ${moduleDir}`, newerSources: [] };
|
|
5827
5954
|
}
|
|
5828
|
-
const distDir =
|
|
5955
|
+
const distDir = join19(packageRoot, "dist");
|
|
5829
5956
|
if (!isInside(distDir, moduleDir)) {
|
|
5830
5957
|
return {
|
|
5831
5958
|
status: "skipped",
|
|
@@ -5833,16 +5960,16 @@ function checkBuildFreshness(options = {}) {
|
|
|
5833
5960
|
newerSources: []
|
|
5834
5961
|
};
|
|
5835
5962
|
}
|
|
5836
|
-
const srcDir =
|
|
5837
|
-
if (!
|
|
5963
|
+
const srcDir = join19(packageRoot, "src");
|
|
5964
|
+
if (!existsSync20(srcDir)) {
|
|
5838
5965
|
return {
|
|
5839
5966
|
status: "skipped",
|
|
5840
5967
|
reason: "no src/ alongside dist/ \u2014 this is a shipped package",
|
|
5841
5968
|
newerSources: []
|
|
5842
5969
|
};
|
|
5843
5970
|
}
|
|
5844
|
-
const entry =
|
|
5845
|
-
if (!
|
|
5971
|
+
const entry = join19(distDir, "index.js");
|
|
5972
|
+
if (!existsSync20(entry)) {
|
|
5846
5973
|
return { status: "skipped", reason: `${entry} not found`, newerSources: [] };
|
|
5847
5974
|
}
|
|
5848
5975
|
const builtAt = statSync4(entry).mtimeMs;
|
|
@@ -5886,7 +6013,7 @@ function collectSourceFiles(srcDir) {
|
|
|
5886
6013
|
const found = [];
|
|
5887
6014
|
const walk = (dir) => {
|
|
5888
6015
|
for (const entry of readdirSync7(dir, { withFileTypes: true })) {
|
|
5889
|
-
const full =
|
|
6016
|
+
const full = join19(dir, entry.name);
|
|
5890
6017
|
if (entry.isDirectory()) {
|
|
5891
6018
|
if (entry.name === "node_modules") continue;
|
|
5892
6019
|
walk(full);
|
|
@@ -5905,7 +6032,7 @@ function collectSourceFiles(srcDir) {
|
|
|
5905
6032
|
function findPackageRoot(from) {
|
|
5906
6033
|
let dir = from;
|
|
5907
6034
|
for (; ; ) {
|
|
5908
|
-
if (
|
|
6035
|
+
if (existsSync20(join19(dir, "package.json"))) return dir;
|
|
5909
6036
|
const parent = dirname5(dir);
|
|
5910
6037
|
if (parent === dir) return null;
|
|
5911
6038
|
dir = parent;
|
|
@@ -5919,9 +6046,9 @@ function isInside(parent, child) {
|
|
|
5919
6046
|
|
|
5920
6047
|
// src/lib/credentials.ts
|
|
5921
6048
|
import { execSync as execSync6 } from "child_process";
|
|
5922
|
-
import { existsSync as
|
|
6049
|
+
import { existsSync as existsSync21, readFileSync as readFileSync14 } from "fs";
|
|
5923
6050
|
import { homedir as homedir2 } from "os";
|
|
5924
|
-
import { join as
|
|
6051
|
+
import { join as join20 } from "path";
|
|
5925
6052
|
import { GetCallerIdentityCommand as GetCallerIdentityCommand2, STSClient as STSClient2 } from "@aws-sdk/client-sts";
|
|
5926
6053
|
import chalk10 from "chalk";
|
|
5927
6054
|
import inquirer4 from "inquirer";
|
|
@@ -6100,11 +6227,11 @@ async function verifySelectedAwsCredentials(profile, region) {
|
|
|
6100
6227
|
return sts.send(new GetCallerIdentityCommand2({}));
|
|
6101
6228
|
}
|
|
6102
6229
|
function discoverAwsProfiles() {
|
|
6103
|
-
const files = [
|
|
6230
|
+
const files = [join20(homedir2(), ".aws", "credentials"), join20(homedir2(), ".aws", "config")];
|
|
6104
6231
|
const profiles = /* @__PURE__ */ new Set();
|
|
6105
6232
|
for (const file of files) {
|
|
6106
|
-
if (!
|
|
6107
|
-
const content =
|
|
6233
|
+
if (!existsSync21(file)) continue;
|
|
6234
|
+
const content = readFileSync14(file, "utf8");
|
|
6108
6235
|
for (const match of content.matchAll(/^\s*\[([^\]]+)\]\s*$/gm)) {
|
|
6109
6236
|
const section = match[1]?.trim();
|
|
6110
6237
|
if (!section) continue;
|
|
@@ -6194,34 +6321,34 @@ var SiblingConfigSchema = z6.object({
|
|
|
6194
6321
|
|
|
6195
6322
|
// src/lib/sibling-session.ts
|
|
6196
6323
|
import {
|
|
6197
|
-
existsSync as
|
|
6324
|
+
existsSync as existsSync22,
|
|
6198
6325
|
mkdirSync as mkdirSync6,
|
|
6199
6326
|
readdirSync as readdirSync8,
|
|
6200
|
-
readFileSync as
|
|
6327
|
+
readFileSync as readFileSync15,
|
|
6201
6328
|
rmSync as rmSync7,
|
|
6202
6329
|
statSync as statSync5,
|
|
6203
6330
|
writeFileSync as writeFileSync6
|
|
6204
6331
|
} from "fs";
|
|
6205
6332
|
import { homedir as homedir3 } from "os";
|
|
6206
|
-
import { join as
|
|
6333
|
+
import { join as join21 } from "path";
|
|
6207
6334
|
function sessionsDir2() {
|
|
6208
|
-
return process.env["BIFFO_SIBLING_SESSIONS_DIR"] ??
|
|
6335
|
+
return process.env["BIFFO_SIBLING_SESSIONS_DIR"] ?? join21(homedir3(), ".biffo", "sibling-sessions");
|
|
6209
6336
|
}
|
|
6210
6337
|
function sessionPath2(projectName) {
|
|
6211
|
-
return
|
|
6338
|
+
return join21(sessionsDir2(), `${projectName}.json`);
|
|
6212
6339
|
}
|
|
6213
6340
|
function loadSiblingSession(projectName) {
|
|
6214
6341
|
const path = sessionPath2(projectName);
|
|
6215
|
-
if (!
|
|
6342
|
+
if (!existsSync22(path)) return null;
|
|
6216
6343
|
try {
|
|
6217
|
-
return JSON.parse(
|
|
6344
|
+
return JSON.parse(readFileSync15(path, "utf8"));
|
|
6218
6345
|
} catch {
|
|
6219
6346
|
return null;
|
|
6220
6347
|
}
|
|
6221
6348
|
}
|
|
6222
6349
|
function saveSiblingSession(session) {
|
|
6223
6350
|
const dir = sessionsDir2();
|
|
6224
|
-
if (!
|
|
6351
|
+
if (!existsSync22(dir)) mkdirSync6(dir, { recursive: true });
|
|
6225
6352
|
const name = session.config.project?.name ?? "unknown";
|
|
6226
6353
|
const prior = loadSiblingSession(name);
|
|
6227
6354
|
if (prior) {
|
|
@@ -6243,30 +6370,30 @@ function markSiblingStepComplete(session, step) {
|
|
|
6243
6370
|
}
|
|
6244
6371
|
function deleteSiblingSession(projectName) {
|
|
6245
6372
|
const path = sessionPath2(projectName);
|
|
6246
|
-
if (
|
|
6373
|
+
if (existsSync22(path)) rmSync7(path);
|
|
6247
6374
|
}
|
|
6248
6375
|
|
|
6249
6376
|
// src/commands/sibling-create.ts
|
|
6250
|
-
import { cpSync as cpSync2, existsSync as
|
|
6377
|
+
import { cpSync as cpSync2, existsSync as existsSync23, mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync4, readFileSync as readFileSync16, writeFileSync as writeFileSync7 } from "fs";
|
|
6251
6378
|
import { tmpdir as tmpdir4 } from "os";
|
|
6252
|
-
import { dirname as dirname6, join as
|
|
6379
|
+
import { dirname as dirname6, join as join23, resolve as resolve9 } from "path";
|
|
6253
6380
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
6254
6381
|
import chalk11 from "chalk";
|
|
6255
6382
|
import { Command as Command11 } from "commander";
|
|
6256
6383
|
|
|
6257
6384
|
// src/lib/skeleton-dotfiles.ts
|
|
6258
6385
|
import { readdirSync as readdirSync9, renameSync } from "fs";
|
|
6259
|
-
import { join as
|
|
6386
|
+
import { join as join22 } from "path";
|
|
6260
6387
|
var PACKAGED_GITIGNORE = "_gitignore";
|
|
6261
6388
|
var REAL_GITIGNORE = ".gitignore";
|
|
6262
6389
|
function restorePackagedDotfiles(dir) {
|
|
6263
6390
|
const restored = [];
|
|
6264
6391
|
for (const entry of readdirSync9(dir, { withFileTypes: true })) {
|
|
6265
|
-
const full =
|
|
6392
|
+
const full = join22(dir, entry.name);
|
|
6266
6393
|
if (entry.isDirectory()) {
|
|
6267
6394
|
restored.push(...restorePackagedDotfiles(full));
|
|
6268
6395
|
} else if (entry.name === PACKAGED_GITIGNORE) {
|
|
6269
|
-
const target =
|
|
6396
|
+
const target = join22(dir, REAL_GITIGNORE);
|
|
6270
6397
|
renameSync(full, target);
|
|
6271
6398
|
restored.push(target);
|
|
6272
6399
|
}
|
|
@@ -6311,7 +6438,7 @@ async function runSiblingCreateCommand(name, options) {
|
|
|
6311
6438
|
printDryRun2(config, coreConfig, options.templateRoot);
|
|
6312
6439
|
return;
|
|
6313
6440
|
}
|
|
6314
|
-
if (!
|
|
6441
|
+
if (!existsSync23(options.templateRoot)) {
|
|
6315
6442
|
throw new Error(`Sibling template not found at ${options.templateRoot}`);
|
|
6316
6443
|
}
|
|
6317
6444
|
let session = null;
|
|
@@ -6528,7 +6655,7 @@ function assertPathPrefixIsAllowed(pathPrefix) {
|
|
|
6528
6655
|
}
|
|
6529
6656
|
}
|
|
6530
6657
|
function readSiblingConfig(path, root = false) {
|
|
6531
|
-
const raw = JSON.parse(
|
|
6658
|
+
const raw = JSON.parse(readFileSync16(path, "utf8"));
|
|
6532
6659
|
const withDefaults = raw && typeof raw === "object" && "project" in raw && "core" in raw ? {
|
|
6533
6660
|
...raw,
|
|
6534
6661
|
core: {
|
|
@@ -6562,7 +6689,7 @@ function resolveCoreConfig(config, configPath) {
|
|
|
6562
6689
|
throw new Error("Either core.project_name or core.config_path is required.");
|
|
6563
6690
|
}
|
|
6564
6691
|
function parseCoreConfig(path) {
|
|
6565
|
-
const result = BiffoConfigSchema.safeParse(JSON.parse(
|
|
6692
|
+
const result = BiffoConfigSchema.safeParse(JSON.parse(readFileSync16(path, "utf8")));
|
|
6566
6693
|
if (!result.success) {
|
|
6567
6694
|
throw new Error(
|
|
6568
6695
|
`Invalid core configuration at ${path}:
|
|
@@ -6601,7 +6728,7 @@ async function resolveCoreIdentity(coreAws, coreConfig, environments) {
|
|
|
6601
6728
|
return coreIdentity;
|
|
6602
6729
|
}
|
|
6603
6730
|
async function pushSkeleton(git, skeletonRoot, cloneUrl, config, coreConfig, githubToken) {
|
|
6604
|
-
const workDir = mkdtempSync4(
|
|
6731
|
+
const workDir = mkdtempSync4(join23(tmpdir4(), `biffo-sibling-${config.project.name}-`));
|
|
6605
6732
|
try {
|
|
6606
6733
|
writeSiblingTemplate(skeletonRoot, workDir, config, {
|
|
6607
6734
|
coreProjectName: coreConfig.project.name,
|
|
@@ -6626,13 +6753,13 @@ async function pushSkeleton(git, skeletonRoot, cloneUrl, config, coreConfig, git
|
|
|
6626
6753
|
}
|
|
6627
6754
|
}
|
|
6628
6755
|
function writeSiblingTemplate(templateRoot, targetDir, config, context) {
|
|
6629
|
-
if (!
|
|
6756
|
+
if (!existsSync23(templateRoot)) {
|
|
6630
6757
|
throw new Error(`Sibling template not found at ${templateRoot}`);
|
|
6631
6758
|
}
|
|
6632
6759
|
cpSync2(templateRoot, targetDir, { recursive: true });
|
|
6633
6760
|
restorePackagedDotfiles(targetDir);
|
|
6634
6761
|
writeFileSync7(
|
|
6635
|
-
|
|
6762
|
+
join23(targetDir, "biffo.sibling.json"),
|
|
6636
6763
|
JSON.stringify(
|
|
6637
6764
|
{
|
|
6638
6765
|
name: config.project.name,
|
|
@@ -6651,14 +6778,14 @@ function writeSiblingTemplate(templateRoot, targetDir, config, context) {
|
|
|
6651
6778
|
) + "\n"
|
|
6652
6779
|
);
|
|
6653
6780
|
writeFileSync7(
|
|
6654
|
-
|
|
6781
|
+
join23(targetDir, ".biffo-shared-version"),
|
|
6655
6782
|
`core-v${context.templateVersion.replace(/^core-v/, "")}
|
|
6656
6783
|
`
|
|
6657
6784
|
);
|
|
6658
|
-
const envPath =
|
|
6785
|
+
const envPath = join23(targetDir, "apps", "frontend", ".env.example");
|
|
6659
6786
|
try {
|
|
6660
6787
|
const path = basePathFor(context.pathPrefix);
|
|
6661
|
-
const content =
|
|
6788
|
+
const content = readFileSync16(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}`);
|
|
6662
6789
|
writeFileSync7(envPath, content);
|
|
6663
6790
|
} catch (err) {
|
|
6664
6791
|
if (err.code !== "ENOENT") throw err;
|
|
@@ -6705,7 +6832,7 @@ async function configureSiblingGithub(github, config, coreConfig, session, coreI
|
|
|
6705
6832
|
}
|
|
6706
6833
|
function readExistingSiblingOrigins(filePath) {
|
|
6707
6834
|
try {
|
|
6708
|
-
return JSON.parse(
|
|
6835
|
+
return JSON.parse(readFileSync16(filePath, "utf8"));
|
|
6709
6836
|
} catch (err) {
|
|
6710
6837
|
if (err.code === "ENOENT") return {};
|
|
6711
6838
|
throw err;
|
|
@@ -6724,10 +6851,10 @@ function assertGitIdentity(identity) {
|
|
|
6724
6851
|
);
|
|
6725
6852
|
}
|
|
6726
6853
|
function assertCoreSupportsSiblingRouting(cloneDir, coreRepo, pathPrefix = "x") {
|
|
6727
|
-
const cdnVarsPath =
|
|
6854
|
+
const cdnVarsPath = join23(cloneDir, "modules", "cloud", "aws", "cdn", "variables.tf");
|
|
6728
6855
|
let declaresSiblingOrigins = false;
|
|
6729
6856
|
try {
|
|
6730
|
-
declaresSiblingOrigins = /variable\s+"sibling_origins"/.test(
|
|
6857
|
+
declaresSiblingOrigins = /variable\s+"sibling_origins"/.test(readFileSync16(cdnVarsPath, "utf8"));
|
|
6731
6858
|
} catch {
|
|
6732
6859
|
declaresSiblingOrigins = false;
|
|
6733
6860
|
}
|
|
@@ -6737,10 +6864,10 @@ function assertCoreSupportsSiblingRouting(cloneDir, coreRepo, pathPrefix = "x")
|
|
|
6737
6864
|
);
|
|
6738
6865
|
}
|
|
6739
6866
|
if (!isRootPathPrefix(pathPrefix)) return;
|
|
6740
|
-
const cdnMainPath =
|
|
6867
|
+
const cdnMainPath = join23(cloneDir, "modules", "cloud", "aws", "cdn", "main.tf");
|
|
6741
6868
|
let supportsRoot = false;
|
|
6742
6869
|
try {
|
|
6743
|
-
supportsRoot = /root_sibling_registered/.test(
|
|
6870
|
+
supportsRoot = /root_sibling_registered/.test(readFileSync16(cdnMainPath, "utf8"));
|
|
6744
6871
|
} catch {
|
|
6745
6872
|
supportsRoot = false;
|
|
6746
6873
|
}
|
|
@@ -6770,8 +6897,8 @@ async function registerWithCore(git, github, config, coreConfig, pathPrefix, git
|
|
|
6770
6897
|
for (const env of config.environments) {
|
|
6771
6898
|
const bucketName = siteBucketName(config.project.name, env, siblingAccountId);
|
|
6772
6899
|
const domain = bucketRegionalDomain(bucketName, coreAwsRegion);
|
|
6773
|
-
const relativePath =
|
|
6774
|
-
const filePath =
|
|
6900
|
+
const relativePath = join23("infra", "environments", env, "siblings.auto.tfvars.json");
|
|
6901
|
+
const filePath = join23(cloneDir, relativePath);
|
|
6775
6902
|
const existing = readExistingSiblingOrigins(filePath);
|
|
6776
6903
|
const siblings = upsertSiblingOrigin(existing.sibling_origins ?? [], {
|
|
6777
6904
|
name,
|
|
@@ -6851,8 +6978,8 @@ function defaultSiblingTemplateRoot() {
|
|
|
6851
6978
|
const start = dirname6(fileURLToPath4(import.meta.url));
|
|
6852
6979
|
let dir = start;
|
|
6853
6980
|
for (; ; ) {
|
|
6854
|
-
const candidate =
|
|
6855
|
-
if (
|
|
6981
|
+
const candidate = join23(dir, "_skeletons", "sibling-template");
|
|
6982
|
+
if (existsSync23(candidate)) return candidate;
|
|
6856
6983
|
const parent = dirname6(dir);
|
|
6857
6984
|
if (parent === dir) break;
|
|
6858
6985
|
dir = parent;
|
|
@@ -6876,7 +7003,7 @@ var initCommand = new Command12("init").description("Scaffold a new project from
|
|
|
6876
7003
|
let config;
|
|
6877
7004
|
let githubToken;
|
|
6878
7005
|
if (options.config) {
|
|
6879
|
-
const rawConfig = JSON.parse(
|
|
7006
|
+
const rawConfig = JSON.parse(readFileSync17(resolve10(options.config), "utf8"));
|
|
6880
7007
|
config = parseConfig(rawConfig);
|
|
6881
7008
|
const { account_id: accountId, region } = config.cloud.config;
|
|
6882
7009
|
session = resolveConfigFileSession(config, accountId, region, options.fresh === true);
|
|
@@ -7321,8 +7448,8 @@ async function promptForConfig(awsAccountId, awsRegion, awsProfile) {
|
|
|
7321
7448
|
import { Command as Command20 } from "commander";
|
|
7322
7449
|
|
|
7323
7450
|
// src/commands/plugin-create.ts
|
|
7324
|
-
import { existsSync as
|
|
7325
|
-
import { dirname as dirname8, join as
|
|
7451
|
+
import { existsSync as existsSync26, readFileSync as readFileSync19, writeFileSync as writeFileSync9 } from "fs";
|
|
7452
|
+
import { dirname as dirname8, join as join26, resolve as resolve11 } from "path";
|
|
7326
7453
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
7327
7454
|
import chalk13 from "chalk";
|
|
7328
7455
|
import { Command as Command13 } from "commander";
|
|
@@ -7386,21 +7513,21 @@ function workflowCheckContexts(workflow) {
|
|
|
7386
7513
|
}
|
|
7387
7514
|
|
|
7388
7515
|
// src/lib/plugin-locations.ts
|
|
7389
|
-
import { existsSync as
|
|
7390
|
-
import { join as
|
|
7516
|
+
import { existsSync as existsSync24, readdirSync as readdirSync10 } from "fs";
|
|
7517
|
+
import { join as join24 } from "path";
|
|
7391
7518
|
var FIRST_PARTY_PLUGINS_DIR = "_plugins";
|
|
7392
7519
|
var PLUGIN_MANIFEST_FILE = "biffo.plugin.json";
|
|
7393
7520
|
function pluginDir(name, channel) {
|
|
7394
7521
|
return channel === "first-party" ? `services/${FIRST_PARTY_PLUGINS_DIR}/${name}` : `services/${name}`;
|
|
7395
7522
|
}
|
|
7396
7523
|
function scanDir(absDir, relDir, channel) {
|
|
7397
|
-
if (!
|
|
7524
|
+
if (!existsSync24(absDir)) return [];
|
|
7398
7525
|
const found = [];
|
|
7399
7526
|
for (const entry of readdirSync10(absDir, { withFileTypes: true })) {
|
|
7400
7527
|
if (!entry.isDirectory()) continue;
|
|
7401
7528
|
if (channel === "third-party" && entry.name === FIRST_PARTY_PLUGINS_DIR) continue;
|
|
7402
|
-
const manifestPath =
|
|
7403
|
-
if (!
|
|
7529
|
+
const manifestPath = join24(absDir, entry.name, PLUGIN_MANIFEST_FILE);
|
|
7530
|
+
if (!existsSync24(manifestPath)) continue;
|
|
7404
7531
|
found.push({
|
|
7405
7532
|
dirName: entry.name,
|
|
7406
7533
|
relDir: `${relDir}/${entry.name}`,
|
|
@@ -7411,11 +7538,11 @@ function scanDir(absDir, relDir, channel) {
|
|
|
7411
7538
|
return found;
|
|
7412
7539
|
}
|
|
7413
7540
|
function findInstalledPlugins(cwd) {
|
|
7414
|
-
const servicesDir =
|
|
7541
|
+
const servicesDir = join24(cwd, "services");
|
|
7415
7542
|
return [
|
|
7416
7543
|
...scanDir(servicesDir, "services", "third-party"),
|
|
7417
7544
|
...scanDir(
|
|
7418
|
-
|
|
7545
|
+
join24(servicesDir, FIRST_PARTY_PLUGINS_DIR),
|
|
7419
7546
|
`services/${FIRST_PARTY_PLUGINS_DIR}`,
|
|
7420
7547
|
"first-party"
|
|
7421
7548
|
)
|
|
@@ -7618,13 +7745,13 @@ function validateManifest(raw) {
|
|
|
7618
7745
|
// src/lib/plugin-scaffold.ts
|
|
7619
7746
|
import {
|
|
7620
7747
|
copyFileSync,
|
|
7621
|
-
existsSync as
|
|
7748
|
+
existsSync as existsSync25,
|
|
7622
7749
|
mkdirSync as mkdirSync8,
|
|
7623
|
-
readFileSync as
|
|
7750
|
+
readFileSync as readFileSync18,
|
|
7624
7751
|
readdirSync as readdirSync11,
|
|
7625
7752
|
writeFileSync as writeFileSync8
|
|
7626
7753
|
} from "fs";
|
|
7627
|
-
import { dirname as dirname7, join as
|
|
7754
|
+
import { dirname as dirname7, join as join25 } from "path";
|
|
7628
7755
|
var STANDALONE_ONLY_ENTRIES = {
|
|
7629
7756
|
".github": "standalone-repo CI/release workflows \u2014 the host monorepo already runs lint/type/test/security over services/",
|
|
7630
7757
|
"registry-schema.json": "the plugin-registry publishing schema, used when submitting a *published* plugin to the registry repo, not by an in-tree plugin"
|
|
@@ -7678,10 +7805,10 @@ function applySubstitutions(text, names) {
|
|
|
7678
7805
|
var BINARY_EXTENSIONS = /\.(png|jpe?g|gif|ico|woff2?|ttf|zip|gz)$/i;
|
|
7679
7806
|
function scaffoldPlugin(skeletonRoot, destDir, names, options = {}) {
|
|
7680
7807
|
const layout = options.layout ?? "in-tree";
|
|
7681
|
-
if (!
|
|
7808
|
+
if (!existsSync25(skeletonRoot)) {
|
|
7682
7809
|
throw new Error(`Plugin skeleton not found at ${skeletonRoot}`);
|
|
7683
7810
|
}
|
|
7684
|
-
if (!
|
|
7811
|
+
if (!existsSync25(join25(skeletonRoot, "terraform"))) {
|
|
7685
7812
|
throw new Error(
|
|
7686
7813
|
`Plugin skeleton at ${skeletonRoot} has no terraform/ directory. Refusing to scaffold a plugin that cannot receive events (issue #194) \u2014 the skeleton is broken.`
|
|
7687
7814
|
);
|
|
@@ -7689,7 +7816,7 @@ function scaffoldPlugin(skeletonRoot, destDir, names, options = {}) {
|
|
|
7689
7816
|
const skipped = [];
|
|
7690
7817
|
const files = [];
|
|
7691
7818
|
const walk = (relDir) => {
|
|
7692
|
-
const absDir =
|
|
7819
|
+
const absDir = join25(skeletonRoot, relDir);
|
|
7693
7820
|
for (const entry of readdirSync11(absDir, { withFileTypes: true }).sort(
|
|
7694
7821
|
(a, b) => a.name.localeCompare(b.name)
|
|
7695
7822
|
)) {
|
|
@@ -7704,14 +7831,14 @@ function scaffoldPlugin(skeletonRoot, destDir, names, options = {}) {
|
|
|
7704
7831
|
continue;
|
|
7705
7832
|
}
|
|
7706
7833
|
const destRel = applySubstitutions(relPath, names);
|
|
7707
|
-
const destPath =
|
|
7834
|
+
const destPath = join25(destDir, destRel);
|
|
7708
7835
|
mkdirSync8(dirname7(destPath), { recursive: true });
|
|
7709
7836
|
if (BINARY_EXTENSIONS.test(entry.name)) {
|
|
7710
|
-
copyFileSync(
|
|
7837
|
+
copyFileSync(join25(skeletonRoot, relPath), destPath);
|
|
7711
7838
|
} else {
|
|
7712
7839
|
writeFileSync8(
|
|
7713
7840
|
destPath,
|
|
7714
|
-
applySubstitutions(
|
|
7841
|
+
applySubstitutions(readFileSync18(join25(skeletonRoot, relPath), "utf8"), names)
|
|
7715
7842
|
);
|
|
7716
7843
|
}
|
|
7717
7844
|
files.push(destRel);
|
|
@@ -7728,8 +7855,8 @@ function scaffoldPlugin(skeletonRoot, destDir, names, options = {}) {
|
|
|
7728
7855
|
function findSkeletonRoot(startDir, skeleton) {
|
|
7729
7856
|
let dir = startDir;
|
|
7730
7857
|
for (; ; ) {
|
|
7731
|
-
const candidate =
|
|
7732
|
-
if (
|
|
7858
|
+
const candidate = join25(dir, "_skeletons", skeleton);
|
|
7859
|
+
if (existsSync25(candidate)) return candidate;
|
|
7733
7860
|
const parent = dirname7(dir);
|
|
7734
7861
|
if (parent === dir) return null;
|
|
7735
7862
|
dir = parent;
|
|
@@ -7797,7 +7924,7 @@ async function runPluginCreate(name, options, deps) {
|
|
|
7797
7924
|
reportBranchProtectionSummary();
|
|
7798
7925
|
return;
|
|
7799
7926
|
}
|
|
7800
|
-
const isInstance =
|
|
7927
|
+
const isInstance = existsSync26(join26(options.cwd, INSTANCE_CORE_FILE));
|
|
7801
7928
|
if (options.firstParty && isInstance) {
|
|
7802
7929
|
throw new Error(
|
|
7803
7930
|
`--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.`
|
|
@@ -7805,19 +7932,19 @@ async function runPluginCreate(name, options, deps) {
|
|
|
7805
7932
|
}
|
|
7806
7933
|
const channel = options.firstParty ? "first-party" : "third-party";
|
|
7807
7934
|
const relDir = pluginDir(names.slug, channel);
|
|
7808
|
-
const destDir =
|
|
7809
|
-
const servicesDir =
|
|
7810
|
-
if (!
|
|
7935
|
+
const destDir = join26(options.cwd, relDir);
|
|
7936
|
+
const servicesDir = join26(options.cwd, "services");
|
|
7937
|
+
if (!existsSync26(servicesDir)) {
|
|
7811
7938
|
throw new Error(
|
|
7812
7939
|
`${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
|
|
7813
7940
|
);
|
|
7814
7941
|
}
|
|
7815
|
-
if (
|
|
7942
|
+
if (existsSync26(destDir)) {
|
|
7816
7943
|
throw new Error(`${relDir}/ already exists. Choose a different name, or remove it first.`);
|
|
7817
7944
|
}
|
|
7818
7945
|
const here = dirname8(fileURLToPath5(import.meta.url));
|
|
7819
|
-
const skeletonRoot = options.skeletonRoot ?? findSkeletonRoot(here, "plugin-template") ??
|
|
7820
|
-
if (!
|
|
7946
|
+
const skeletonRoot = options.skeletonRoot ?? findSkeletonRoot(here, "plugin-template") ?? join26(options.cwd, "_skeletons", "plugin-template");
|
|
7947
|
+
if (!existsSync26(skeletonRoot)) {
|
|
7821
7948
|
throw new Error(
|
|
7822
7949
|
`Could not find the plugin skeleton (_skeletons/plugin-template/). Pass --skeleton <path> to point at it explicitly.`
|
|
7823
7950
|
);
|
|
@@ -7832,8 +7959,8 @@ async function runPluginCreate(name, options, deps) {
|
|
|
7832
7959
|
for (const { entry, reason } of skipped) {
|
|
7833
7960
|
log.info(`Skipped ${entry} \u2014 ${reason}`);
|
|
7834
7961
|
}
|
|
7835
|
-
const manifestPath =
|
|
7836
|
-
const manifest = validateManifest(JSON.parse(
|
|
7962
|
+
const manifestPath = join26(destDir, "biffo.plugin.json");
|
|
7963
|
+
const manifest = validateManifest(JSON.parse(readFileSync19(manifestPath, "utf8")));
|
|
7837
7964
|
if (manifest.name !== names.slug) {
|
|
7838
7965
|
throw new Error(
|
|
7839
7966
|
`Scaffolded manifest declares name '${manifest.name}', expected '${names.slug}'. The skeleton's manifest name may have diverged from 'example-plugin'.`
|
|
@@ -7856,8 +7983,8 @@ async function runPluginCreate(name, options, deps) {
|
|
|
7856
7983
|
printNextSteps(names, relDir, channel);
|
|
7857
7984
|
}
|
|
7858
7985
|
async function runStandaloneCreate(names, options, deps) {
|
|
7859
|
-
const destDir =
|
|
7860
|
-
if (
|
|
7986
|
+
const destDir = join26(options.cwd, names.dist);
|
|
7987
|
+
if (existsSync26(destDir)) {
|
|
7861
7988
|
throw new Error(`${names.dist}/ already exists. Choose a different name, or remove it first.`);
|
|
7862
7989
|
}
|
|
7863
7990
|
const skeletonRoot = resolveSkeletonRoot(options);
|
|
@@ -7875,7 +8002,7 @@ async function runStandaloneCreate(names, options, deps) {
|
|
|
7875
8002
|
restorePackagedDotfiles(destDir);
|
|
7876
8003
|
log.success(`Scaffolded ${String(files.length)} file(s) into ${names.dist}/`);
|
|
7877
8004
|
const manifest = validateManifest(
|
|
7878
|
-
JSON.parse(
|
|
8005
|
+
JSON.parse(readFileSync19(join26(destDir, "biffo.plugin.json"), "utf8"))
|
|
7879
8006
|
);
|
|
7880
8007
|
if (manifest.name !== names.slug) {
|
|
7881
8008
|
throw new Error(
|
|
@@ -7914,8 +8041,8 @@ async function createAndPushStandaloneRepo(org, names, destDir, options, deps) {
|
|
|
7914
8041
|
await deps.git.push(destDir, "dev", { token });
|
|
7915
8042
|
log.success(`Pushed dev to ${org}/${names.dist}`);
|
|
7916
8043
|
await github.setDefaultBranch(org, names.dist, "dev");
|
|
7917
|
-
const ciPath =
|
|
7918
|
-
const contexts =
|
|
8044
|
+
const ciPath = join26(destDir, ".github", "workflows", "ci.yml");
|
|
8045
|
+
const contexts = existsSync26(ciPath) ? workflowCheckContexts(readFileSync19(ciPath, "utf8")) : [];
|
|
7919
8046
|
if (contexts.length === 0) {
|
|
7920
8047
|
log.warn(
|
|
7921
8048
|
`Could not determine required status checks from ${ciPath} \u2014 skipping branch protection. Configure it manually on dev once you know the CI job names.`
|
|
@@ -7944,8 +8071,8 @@ async function registerInRegistrySources(names, cloneUrl, token, deps) {
|
|
|
7944
8071
|
let dir;
|
|
7945
8072
|
try {
|
|
7946
8073
|
dir = await deps.git.cloneForEditing(REGISTRY_REPO, "biffo-registry", token);
|
|
7947
|
-
const path =
|
|
7948
|
-
const file = JSON.parse(
|
|
8074
|
+
const path = join26(dir, "sources.json");
|
|
8075
|
+
const file = JSON.parse(readFileSync19(path, "utf8"));
|
|
7949
8076
|
const next = addSource(file, {
|
|
7950
8077
|
name: names.slug,
|
|
7951
8078
|
repo: cloneUrl.replace(/\.git$/, ""),
|
|
@@ -8053,8 +8180,8 @@ function printStandaloneNextSteps(names, minor) {
|
|
|
8053
8180
|
}
|
|
8054
8181
|
function resolveSkeletonRoot(options) {
|
|
8055
8182
|
const here = dirname8(fileURLToPath5(import.meta.url));
|
|
8056
|
-
const skeletonRoot = options.skeletonRoot ?? findSkeletonRoot(here, "plugin-template") ??
|
|
8057
|
-
if (!
|
|
8183
|
+
const skeletonRoot = options.skeletonRoot ?? findSkeletonRoot(here, "plugin-template") ?? join26(options.cwd, "_skeletons", "plugin-template");
|
|
8184
|
+
if (!existsSync26(skeletonRoot)) {
|
|
8058
8185
|
throw new Error(
|
|
8059
8186
|
`Could not find the plugin skeleton (_skeletons/plugin-template/). Pass --skeleton <path> to point at it explicitly.`
|
|
8060
8187
|
);
|
|
@@ -8229,14 +8356,14 @@ function printEntry(entry) {
|
|
|
8229
8356
|
}
|
|
8230
8357
|
|
|
8231
8358
|
// src/commands/plugin-install.ts
|
|
8232
|
-
import { cpSync as cpSync3, existsSync as
|
|
8233
|
-
import { basename, join as
|
|
8359
|
+
import { cpSync as cpSync3, existsSync as existsSync28, mkdirSync as mkdirSync9, readFileSync as readFileSync21, statSync as statSync6 } from "fs";
|
|
8360
|
+
import { basename, join as join29, relative as relative3, resolve as resolve12 } from "path";
|
|
8234
8361
|
import chalk15 from "chalk";
|
|
8235
8362
|
import { Command as Command15 } from "commander";
|
|
8236
8363
|
|
|
8237
8364
|
// src/adapters/plugin-migrations/index.ts
|
|
8238
8365
|
import { execa as execa4 } from "execa";
|
|
8239
|
-
import { join as
|
|
8366
|
+
import { join as join27 } from "path";
|
|
8240
8367
|
var PluginMigrationsAdapter = class {
|
|
8241
8368
|
/**
|
|
8242
8369
|
* Generates migration file(s) for `pluginNames` (every discovered
|
|
@@ -8245,22 +8372,22 @@ var PluginMigrationsAdapter = class {
|
|
|
8245
8372
|
* or declared no tables.
|
|
8246
8373
|
*/
|
|
8247
8374
|
async generate(cwd, pluginNames) {
|
|
8248
|
-
const scriptPath =
|
|
8375
|
+
const scriptPath = join27(cwd, "services", "api", "scripts", "generate_plugin_migrations.py");
|
|
8249
8376
|
const args = [
|
|
8250
8377
|
"run",
|
|
8251
8378
|
"python",
|
|
8252
8379
|
scriptPath,
|
|
8253
8380
|
"--services-root",
|
|
8254
|
-
|
|
8381
|
+
join27(cwd, "services"),
|
|
8255
8382
|
"--versions-dir",
|
|
8256
|
-
|
|
8383
|
+
join27(cwd, "services", "api", "migrations", "versions")
|
|
8257
8384
|
];
|
|
8258
8385
|
for (const name of pluginNames ?? []) {
|
|
8259
8386
|
args.push("--plugin", name);
|
|
8260
8387
|
}
|
|
8261
8388
|
let result;
|
|
8262
8389
|
try {
|
|
8263
|
-
result = await execa4("uv", args, { cwd:
|
|
8390
|
+
result = await execa4("uv", args, { cwd: join27(cwd, "services", "api") });
|
|
8264
8391
|
} catch (err) {
|
|
8265
8392
|
const cause = err;
|
|
8266
8393
|
if (cause.code === "ENOENT") {
|
|
@@ -8277,8 +8404,8 @@ var PluginMigrationsAdapter = class {
|
|
|
8277
8404
|
};
|
|
8278
8405
|
|
|
8279
8406
|
// src/lib/plugin-workspace-sources.ts
|
|
8280
|
-
import { existsSync as
|
|
8281
|
-
import { join as
|
|
8407
|
+
import { existsSync as existsSync27, readdirSync as readdirSync12, readFileSync as readFileSync20, writeFileSync as writeFileSync10 } from "fs";
|
|
8408
|
+
import { join as join28 } from "path";
|
|
8282
8409
|
function readTomlStringArray(text, key) {
|
|
8283
8410
|
const open = new RegExp(`^${key}\\s*=\\s*\\[`, "m").exec(text);
|
|
8284
8411
|
if (!open) return [];
|
|
@@ -8322,9 +8449,9 @@ function readDependencyNames(text) {
|
|
|
8322
8449
|
return readTomlStringArray(text, "dependencies").map((dep) => /^\s*([A-Za-z0-9._-]+)/.exec(dep)?.[1] ?? "").filter(Boolean);
|
|
8323
8450
|
}
|
|
8324
8451
|
function workspaceMemberNames(instanceRoot) {
|
|
8325
|
-
const rootPyproject =
|
|
8326
|
-
if (!
|
|
8327
|
-
const text =
|
|
8452
|
+
const rootPyproject = join28(instanceRoot, "pyproject.toml");
|
|
8453
|
+
if (!existsSync27(rootPyproject)) return /* @__PURE__ */ new Set();
|
|
8454
|
+
const text = readFileSync20(rootPyproject, "utf8");
|
|
8328
8455
|
const members = readTomlStringArray(text, "members");
|
|
8329
8456
|
const excluded = new Set(readTomlStringArray(text, "exclude"));
|
|
8330
8457
|
const dirs = [];
|
|
@@ -8333,7 +8460,7 @@ function workspaceMemberNames(instanceRoot) {
|
|
|
8333
8460
|
const base = member.slice(0, -2);
|
|
8334
8461
|
let entries;
|
|
8335
8462
|
try {
|
|
8336
|
-
entries = readdirSync12(
|
|
8463
|
+
entries = readdirSync12(join28(instanceRoot, base), { withFileTypes: true });
|
|
8337
8464
|
} catch {
|
|
8338
8465
|
continue;
|
|
8339
8466
|
}
|
|
@@ -8347,9 +8474,9 @@ function workspaceMemberNames(instanceRoot) {
|
|
|
8347
8474
|
}
|
|
8348
8475
|
const names = /* @__PURE__ */ new Set();
|
|
8349
8476
|
for (const dir of dirs) {
|
|
8350
|
-
const pp =
|
|
8351
|
-
if (!
|
|
8352
|
-
const name = readProjectName(
|
|
8477
|
+
const pp = join28(instanceRoot, dir, "pyproject.toml");
|
|
8478
|
+
if (!existsSync27(pp)) continue;
|
|
8479
|
+
const name = readProjectName(readFileSync20(pp, "utf8"));
|
|
8353
8480
|
if (name) names.add(name);
|
|
8354
8481
|
}
|
|
8355
8482
|
return names;
|
|
@@ -8360,8 +8487,8 @@ function existingWorkspaceSources(text) {
|
|
|
8360
8487
|
);
|
|
8361
8488
|
}
|
|
8362
8489
|
function ensureWorkspaceSources(pluginPyprojectPath, memberNames) {
|
|
8363
|
-
if (!
|
|
8364
|
-
const text =
|
|
8490
|
+
if (!existsSync27(pluginPyprojectPath) || memberNames.size === 0) return [];
|
|
8491
|
+
const text = readFileSync20(pluginPyprojectPath, "utf8");
|
|
8365
8492
|
const already = existingWorkspaceSources(text);
|
|
8366
8493
|
const toAdd = readDependencyNames(text).filter((n) => memberNames.has(n) && !already.has(n));
|
|
8367
8494
|
if (toAdd.length === 0) return [];
|
|
@@ -8427,14 +8554,14 @@ var LOCAL_COPY_EXCLUDES = /* @__PURE__ */ new Set([
|
|
|
8427
8554
|
".terraform"
|
|
8428
8555
|
]);
|
|
8429
8556
|
function resolveLocalPlugin(localPath) {
|
|
8430
|
-
if (!
|
|
8557
|
+
if (!existsSync28(localPath)) {
|
|
8431
8558
|
throw new Error(`--local path does not exist: ${localPath}`);
|
|
8432
8559
|
}
|
|
8433
8560
|
if (!statSync6(localPath).isDirectory()) {
|
|
8434
8561
|
throw new Error(`--local path is not a directory: ${localPath}`);
|
|
8435
8562
|
}
|
|
8436
|
-
const manifestPath =
|
|
8437
|
-
if (!
|
|
8563
|
+
const manifestPath = join29(localPath, "biffo.plugin.json");
|
|
8564
|
+
if (!existsSync28(manifestPath)) {
|
|
8438
8565
|
throw new Error(
|
|
8439
8566
|
`${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>\`.)`
|
|
8440
8567
|
);
|
|
@@ -8460,8 +8587,8 @@ function parsePluginTarget(target) {
|
|
|
8460
8587
|
async function cloneAndValidatePlugin(entry, git) {
|
|
8461
8588
|
const tmpDir = await git.cloneToTemp(entry.repo, `biffo-plugin-${entry.name}`);
|
|
8462
8589
|
try {
|
|
8463
|
-
const manifestPath =
|
|
8464
|
-
if (!
|
|
8590
|
+
const manifestPath = join29(tmpDir, "biffo.plugin.json");
|
|
8591
|
+
if (!existsSync28(manifestPath)) {
|
|
8465
8592
|
throw new Error(
|
|
8466
8593
|
`Plugin repo ${entry.repo} does not contain a biffo.plugin.json manifest at its root.`
|
|
8467
8594
|
);
|
|
@@ -8489,8 +8616,8 @@ async function runPluginInstall(target, options, deps) {
|
|
|
8489
8616
|
`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\`).`
|
|
8490
8617
|
);
|
|
8491
8618
|
}
|
|
8492
|
-
const servicesDir =
|
|
8493
|
-
if (!
|
|
8619
|
+
const servicesDir = join29(options.cwd, "services");
|
|
8620
|
+
if (!existsSync28(servicesDir)) {
|
|
8494
8621
|
throw new Error(
|
|
8495
8622
|
`${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
|
|
8496
8623
|
);
|
|
@@ -8508,10 +8635,10 @@ async function runPluginInstall(target, options, deps) {
|
|
|
8508
8635
|
}
|
|
8509
8636
|
const pluginName = entry ? entry.name : source.name;
|
|
8510
8637
|
const relTargetDir = pluginDir(pluginName, "third-party");
|
|
8511
|
-
const targetDir =
|
|
8512
|
-
const modulesDir =
|
|
8638
|
+
const targetDir = join29(options.cwd, relTargetDir);
|
|
8639
|
+
const modulesDir = join29(options.cwd, "modules", "plugins", pluginName);
|
|
8513
8640
|
const inTreeSource = options.local !== void 0 && resolve12(options.local) === resolve12(targetDir);
|
|
8514
|
-
if (
|
|
8641
|
+
if (existsSync28(targetDir) && !inTreeSource) {
|
|
8515
8642
|
throw new Error(
|
|
8516
8643
|
`Plugin '${pluginName}' is already installed at ${relTargetDir}/. Remove it first, or wait for a future 'biffo plugin upgrade' command.`
|
|
8517
8644
|
);
|
|
@@ -8553,8 +8680,8 @@ async function runPluginInstall(target, options, deps) {
|
|
|
8553
8680
|
});
|
|
8554
8681
|
log.success(`Installed plugin source at ${relTargetDir}/`);
|
|
8555
8682
|
}
|
|
8556
|
-
const pluginPyproject =
|
|
8557
|
-
if (
|
|
8683
|
+
const pluginPyproject = join29(targetDir, "pyproject.toml");
|
|
8684
|
+
if (existsSync28(pluginPyproject)) {
|
|
8558
8685
|
const sourced = ensureWorkspaceSources(pluginPyproject, workspaceMemberNames(options.cwd));
|
|
8559
8686
|
if (sourced.length > 0) {
|
|
8560
8687
|
log.info(
|
|
@@ -8563,8 +8690,8 @@ async function runPluginInstall(target, options, deps) {
|
|
|
8563
8690
|
}
|
|
8564
8691
|
}
|
|
8565
8692
|
const stagePaths = [relTargetDir];
|
|
8566
|
-
const tfSourceDir =
|
|
8567
|
-
if (
|
|
8693
|
+
const tfSourceDir = join29(targetDir, "terraform");
|
|
8694
|
+
if (existsSync28(tfSourceDir)) {
|
|
8568
8695
|
mkdirSync9(modulesDir, { recursive: true });
|
|
8569
8696
|
cpSync3(tfSourceDir, modulesDir, { recursive: true });
|
|
8570
8697
|
stagePaths.push(`modules/plugins/${pluginName}`);
|
|
@@ -8621,7 +8748,7 @@ async function runPluginInstall(target, options, deps) {
|
|
|
8621
8748
|
}
|
|
8622
8749
|
function parseManifestFile(path) {
|
|
8623
8750
|
try {
|
|
8624
|
-
return JSON.parse(
|
|
8751
|
+
return JSON.parse(readFileSync21(path, "utf8"));
|
|
8625
8752
|
} catch (err) {
|
|
8626
8753
|
throw new Error(`Could not parse ${path} as JSON: ${err.message}`);
|
|
8627
8754
|
}
|
|
@@ -8658,8 +8785,8 @@ function printDryRun4(entry, source, relTargetDir, inTreeSource) {
|
|
|
8658
8785
|
}
|
|
8659
8786
|
|
|
8660
8787
|
// src/commands/plugin-list.ts
|
|
8661
|
-
import { existsSync as
|
|
8662
|
-
import { join as
|
|
8788
|
+
import { existsSync as existsSync29, readFileSync as readFileSync22 } from "fs";
|
|
8789
|
+
import { join as join30, resolve as resolve13 } from "path";
|
|
8663
8790
|
import chalk16 from "chalk";
|
|
8664
8791
|
import { Command as Command16 } from "commander";
|
|
8665
8792
|
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) => {
|
|
@@ -8672,8 +8799,8 @@ var pluginListCommand = new Command16("list").description("List plugins installe
|
|
|
8672
8799
|
}
|
|
8673
8800
|
});
|
|
8674
8801
|
async function runPluginList(options) {
|
|
8675
|
-
const servicesDir =
|
|
8676
|
-
if (!
|
|
8802
|
+
const servicesDir = join30(options.cwd, "services");
|
|
8803
|
+
if (!existsSync29(servicesDir)) {
|
|
8677
8804
|
throw new Error(
|
|
8678
8805
|
`${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
|
|
8679
8806
|
);
|
|
@@ -8681,7 +8808,7 @@ async function runPluginList(options) {
|
|
|
8681
8808
|
const plugins = [];
|
|
8682
8809
|
for (const location of findInstalledPlugins(options.cwd)) {
|
|
8683
8810
|
try {
|
|
8684
|
-
const manifest = validateManifest(JSON.parse(
|
|
8811
|
+
const manifest = validateManifest(JSON.parse(readFileSync22(location.manifestPath, "utf8")));
|
|
8685
8812
|
plugins.push({
|
|
8686
8813
|
name: manifest.name,
|
|
8687
8814
|
version: manifest.version,
|
|
@@ -8718,8 +8845,8 @@ async function runPluginList(options) {
|
|
|
8718
8845
|
}
|
|
8719
8846
|
|
|
8720
8847
|
// src/commands/plugin-sync-migrations.ts
|
|
8721
|
-
import { existsSync as
|
|
8722
|
-
import { join as
|
|
8848
|
+
import { existsSync as existsSync30 } from "fs";
|
|
8849
|
+
import { join as join31, relative as relative4, resolve as resolve14 } from "path";
|
|
8723
8850
|
import chalk17 from "chalk";
|
|
8724
8851
|
import { Command as Command17 } from "commander";
|
|
8725
8852
|
var pluginSyncMigrationsCommand = new Command17("sync-migrations").description(
|
|
@@ -8740,11 +8867,11 @@ var pluginSyncMigrationsCommand = new Command17("sync-migrations").description(
|
|
|
8740
8867
|
}
|
|
8741
8868
|
);
|
|
8742
8869
|
async function runPluginSyncMigrations(name, options, deps) {
|
|
8743
|
-
const servicesDir =
|
|
8744
|
-
if (!
|
|
8870
|
+
const servicesDir = join31(options.cwd, "services");
|
|
8871
|
+
if (!existsSync30(servicesDir)) {
|
|
8745
8872
|
throw new Error(`${servicesDir} does not exist \u2014 is ${options.cwd} a Biffo project checkout?`);
|
|
8746
8873
|
}
|
|
8747
|
-
if (name && !
|
|
8874
|
+
if (name && !existsSync30(join31(servicesDir, name, "biffo.plugin.json"))) {
|
|
8748
8875
|
throw new Error(`Plugin '${name}' is not installed at services/${name}/.`);
|
|
8749
8876
|
}
|
|
8750
8877
|
if (options.dryRun) {
|
|
@@ -8780,8 +8907,8 @@ async function runPluginSyncMigrations(name, options, deps) {
|
|
|
8780
8907
|
}
|
|
8781
8908
|
|
|
8782
8909
|
// src/commands/plugin-uninstall.ts
|
|
8783
|
-
import { existsSync as
|
|
8784
|
-
import { join as
|
|
8910
|
+
import { existsSync as existsSync31, readFileSync as readFileSync23, rmSync as rmSync8 } from "fs";
|
|
8911
|
+
import { join as join32, resolve as resolve15 } from "path";
|
|
8785
8912
|
import chalk18 from "chalk";
|
|
8786
8913
|
import { Command as Command18 } from "commander";
|
|
8787
8914
|
import inquirer6 from "inquirer";
|
|
@@ -8813,16 +8940,16 @@ async function runPluginUninstall(name, options, deps) {
|
|
|
8813
8940
|
if (!NAME_PATTERN2.test(name)) {
|
|
8814
8941
|
throw new Error(`Invalid plugin name '${name}'. Expected a lowercase kebab-case slug.`);
|
|
8815
8942
|
}
|
|
8816
|
-
const servicesDir =
|
|
8817
|
-
if (!
|
|
8943
|
+
const servicesDir = join32(options.cwd, "services");
|
|
8944
|
+
if (!existsSync31(servicesDir)) {
|
|
8818
8945
|
throw new Error(
|
|
8819
8946
|
`${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
|
|
8820
8947
|
);
|
|
8821
8948
|
}
|
|
8822
|
-
const targetDir =
|
|
8823
|
-
if (!
|
|
8824
|
-
const firstParty =
|
|
8825
|
-
if (
|
|
8949
|
+
const targetDir = join32(servicesDir, name);
|
|
8950
|
+
if (!existsSync31(targetDir)) {
|
|
8951
|
+
const firstParty = join32(servicesDir, FIRST_PARTY_PLUGINS_DIR, name);
|
|
8952
|
+
if (existsSync31(firstParty)) {
|
|
8826
8953
|
throw new Error(
|
|
8827
8954
|
`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.`
|
|
8828
8955
|
);
|
|
@@ -8830,9 +8957,9 @@ async function runPluginUninstall(name, options, deps) {
|
|
|
8830
8957
|
throw new Error(`Plugin '${name}' is not installed at services/${name}/.`);
|
|
8831
8958
|
}
|
|
8832
8959
|
const version = readInstalledVersion(targetDir);
|
|
8833
|
-
const modulesDir =
|
|
8960
|
+
const modulesDir = join32(options.cwd, "modules", "plugins", name);
|
|
8834
8961
|
const stagePaths = [`services/${name}`];
|
|
8835
|
-
if (
|
|
8962
|
+
if (existsSync31(modulesDir)) {
|
|
8836
8963
|
stagePaths.push(`modules/plugins/${name}`);
|
|
8837
8964
|
}
|
|
8838
8965
|
if (options.dryRun) {
|
|
@@ -8854,7 +8981,7 @@ async function runPluginUninstall(name, options, deps) {
|
|
|
8854
8981
|
}
|
|
8855
8982
|
rmSync8(targetDir, { recursive: true, force: true });
|
|
8856
8983
|
log.success(`Removed services/${name}/`);
|
|
8857
|
-
if (
|
|
8984
|
+
if (existsSync31(modulesDir)) {
|
|
8858
8985
|
rmSync8(modulesDir, { recursive: true, force: true });
|
|
8859
8986
|
log.success(`Removed modules/plugins/${name}/`);
|
|
8860
8987
|
const wiring = syncPluginTerraform(options.cwd);
|
|
@@ -8891,10 +9018,10 @@ async function runPluginUninstall(name, options, deps) {
|
|
|
8891
9018
|
}
|
|
8892
9019
|
}
|
|
8893
9020
|
function readInstalledVersion(targetDir) {
|
|
8894
|
-
const manifestPath =
|
|
8895
|
-
if (!
|
|
9021
|
+
const manifestPath = join32(targetDir, "biffo.plugin.json");
|
|
9022
|
+
if (!existsSync31(manifestPath)) return void 0;
|
|
8896
9023
|
try {
|
|
8897
|
-
return validateManifest(JSON.parse(
|
|
9024
|
+
return validateManifest(JSON.parse(readFileSync23(manifestPath, "utf8"))).version;
|
|
8898
9025
|
} catch {
|
|
8899
9026
|
return void 0;
|
|
8900
9027
|
}
|
|
@@ -8927,8 +9054,8 @@ function printDryRun5(name, version, stagePaths, keepData) {
|
|
|
8927
9054
|
}
|
|
8928
9055
|
|
|
8929
9056
|
// src/commands/plugin-upgrade.ts
|
|
8930
|
-
import { cpSync as cpSync4, existsSync as
|
|
8931
|
-
import { join as
|
|
9057
|
+
import { cpSync as cpSync4, existsSync as existsSync32, mkdirSync as mkdirSync10, readFileSync as readFileSync24, rmSync as rmSync9 } from "fs";
|
|
9058
|
+
import { join as join33, relative as relative5, resolve as resolve16 } from "path";
|
|
8932
9059
|
import chalk19 from "chalk";
|
|
8933
9060
|
import { Command as Command19 } from "commander";
|
|
8934
9061
|
import inquirer7 from "inquirer";
|
|
@@ -8953,14 +9080,14 @@ var pluginUpgradeCommand = new Command19("upgrade").description(
|
|
|
8953
9080
|
});
|
|
8954
9081
|
async function runPluginUpgrade(target, options, deps) {
|
|
8955
9082
|
const { name, minor } = parsePluginTarget(target);
|
|
8956
|
-
const servicesDir =
|
|
8957
|
-
if (!
|
|
9083
|
+
const servicesDir = join33(options.cwd, "services");
|
|
9084
|
+
if (!existsSync32(servicesDir)) {
|
|
8958
9085
|
throw new Error(
|
|
8959
9086
|
`${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
|
|
8960
9087
|
);
|
|
8961
9088
|
}
|
|
8962
|
-
const targetDir =
|
|
8963
|
-
if (!
|
|
9089
|
+
const targetDir = join33(servicesDir, name);
|
|
9090
|
+
if (!existsSync32(targetDir)) {
|
|
8964
9091
|
throw new Error(
|
|
8965
9092
|
`Plugin '${name}' is not installed at services/${name}/. Use 'biffo plugin install ${name}@${minor}' instead.`
|
|
8966
9093
|
);
|
|
@@ -8974,7 +9101,7 @@ async function runPluginUpgrade(target, options, deps) {
|
|
|
8974
9101
|
`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.`
|
|
8975
9102
|
);
|
|
8976
9103
|
}
|
|
8977
|
-
const modulesDir =
|
|
9104
|
+
const modulesDir = join33(options.cwd, "modules", "plugins", entry.name);
|
|
8978
9105
|
if (options.dryRun) {
|
|
8979
9106
|
printDryRun6(entry, currentVersion);
|
|
8980
9107
|
return;
|
|
@@ -9007,11 +9134,11 @@ async function runPluginUpgrade(target, options, deps) {
|
|
|
9007
9134
|
cpSync4(tmpDir, targetDir, { recursive: true });
|
|
9008
9135
|
log.success(`Upgraded plugin source at services/${entry.name}/`);
|
|
9009
9136
|
const stagePaths = [`services/${entry.name}`];
|
|
9010
|
-
if (
|
|
9137
|
+
if (existsSync32(modulesDir)) {
|
|
9011
9138
|
rmSync9(modulesDir, { recursive: true, force: true });
|
|
9012
9139
|
}
|
|
9013
|
-
const tfSourceDir =
|
|
9014
|
-
if (
|
|
9140
|
+
const tfSourceDir = join33(targetDir, "terraform");
|
|
9141
|
+
if (existsSync32(tfSourceDir)) {
|
|
9015
9142
|
mkdirSync10(modulesDir, { recursive: true });
|
|
9016
9143
|
cpSync4(tfSourceDir, modulesDir, { recursive: true });
|
|
9017
9144
|
stagePaths.push(`modules/plugins/${entry.name}`);
|
|
@@ -9045,10 +9172,10 @@ async function runPluginUpgrade(target, options, deps) {
|
|
|
9045
9172
|
}
|
|
9046
9173
|
}
|
|
9047
9174
|
function readInstalledVersion2(targetDir) {
|
|
9048
|
-
const manifestPath =
|
|
9049
|
-
if (!
|
|
9175
|
+
const manifestPath = join33(targetDir, "biffo.plugin.json");
|
|
9176
|
+
if (!existsSync32(manifestPath)) return void 0;
|
|
9050
9177
|
try {
|
|
9051
|
-
return validateManifest(JSON.parse(
|
|
9178
|
+
return validateManifest(JSON.parse(readFileSync24(manifestPath, "utf8"))).version;
|
|
9052
9179
|
} catch {
|
|
9053
9180
|
return void 0;
|
|
9054
9181
|
}
|
|
@@ -9090,7 +9217,7 @@ pluginCommand.addCommand(pluginInfoCommand);
|
|
|
9090
9217
|
import { Command as Command22 } from "commander";
|
|
9091
9218
|
|
|
9092
9219
|
// src/commands/sibling-check-identity.ts
|
|
9093
|
-
import { existsSync as
|
|
9220
|
+
import { existsSync as existsSync33, readFileSync as readFileSync25 } from "fs";
|
|
9094
9221
|
import { resolve as resolve17 } from "path";
|
|
9095
9222
|
import chalk20 from "chalk";
|
|
9096
9223
|
import { Command as Command21 } from "commander";
|
|
@@ -9284,7 +9411,7 @@ async function fetchPublishedIdentity(portalUrl) {
|
|
|
9284
9411
|
}
|
|
9285
9412
|
async function resolveConfig4(options) {
|
|
9286
9413
|
if (options.config) {
|
|
9287
|
-
const raw = JSON.parse(
|
|
9414
|
+
const raw = JSON.parse(readFileSync25(resolve17(options.config), "utf8"));
|
|
9288
9415
|
const result = BiffoConfigSchema.safeParse(raw);
|
|
9289
9416
|
if (!result.success) {
|
|
9290
9417
|
log.error(`Invalid config at ${options.config}:`);
|
|
@@ -9304,8 +9431,8 @@ async function resolveConfig4(options) {
|
|
|
9304
9431
|
return cfg;
|
|
9305
9432
|
}
|
|
9306
9433
|
const localConfigPath = resolve17(process.cwd(), "biffo.config.json");
|
|
9307
|
-
if (
|
|
9308
|
-
const raw = JSON.parse(
|
|
9434
|
+
if (existsSync33(localConfigPath)) {
|
|
9435
|
+
const raw = JSON.parse(readFileSync25(localConfigPath, "utf8"));
|
|
9309
9436
|
const result = BiffoConfigSchema.safeParse(raw);
|
|
9310
9437
|
if (result.success) return result.data;
|
|
9311
9438
|
if (isTemplatePlaceholderConfig(raw)) {
|
|
@@ -9351,21 +9478,21 @@ siblingCommand.addCommand(siblingCheckIdentityCommand);
|
|
|
9351
9478
|
import { Command as Command23 } from "commander";
|
|
9352
9479
|
|
|
9353
9480
|
// src/scripts/check-adr-numbering.ts
|
|
9354
|
-
import { existsSync as
|
|
9355
|
-
import { join as
|
|
9481
|
+
import { existsSync as existsSync35 } from "fs";
|
|
9482
|
+
import { join as join35 } from "path";
|
|
9356
9483
|
import { execa as execa5 } from "execa";
|
|
9357
9484
|
|
|
9358
9485
|
// src/lib/adr-numbering-guard.ts
|
|
9359
|
-
import { existsSync as
|
|
9360
|
-
import { join as
|
|
9486
|
+
import { existsSync as existsSync34, readdirSync as readdirSync13, readFileSync as readFileSync26 } from "fs";
|
|
9487
|
+
import { join as join34 } from "path";
|
|
9361
9488
|
var ADR_FILENAME = /^(\d{4})-.+\.md$/;
|
|
9362
9489
|
var ALLOWLIST_FILENAME = ".numbering-allowlist";
|
|
9363
9490
|
var TEMPLATE_ADR_RESERVED_UPTO = "0099";
|
|
9364
9491
|
function readAdrNumberingAllowlist(adrDir) {
|
|
9365
|
-
const path =
|
|
9366
|
-
if (!
|
|
9492
|
+
const path = join34(adrDir, ALLOWLIST_FILENAME);
|
|
9493
|
+
if (!existsSync34(path)) return /* @__PURE__ */ new Set();
|
|
9367
9494
|
const numbers = /* @__PURE__ */ new Set();
|
|
9368
|
-
for (const rawLine of
|
|
9495
|
+
for (const rawLine of readFileSync26(path, "utf8").split("\n")) {
|
|
9369
9496
|
const line = rawLine.split("#")[0].trim();
|
|
9370
9497
|
if (line) numbers.add(line);
|
|
9371
9498
|
}
|
|
@@ -9373,7 +9500,7 @@ function readAdrNumberingAllowlist(adrDir) {
|
|
|
9373
9500
|
}
|
|
9374
9501
|
function adrNumbersIn(adrDir) {
|
|
9375
9502
|
const claims = /* @__PURE__ */ new Map();
|
|
9376
|
-
if (!
|
|
9503
|
+
if (!existsSync34(adrDir)) return claims;
|
|
9377
9504
|
for (const entry of readdirSync13(adrDir).sort()) {
|
|
9378
9505
|
const match = ADR_FILENAME.exec(entry);
|
|
9379
9506
|
if (!match) continue;
|
|
@@ -9428,8 +9555,8 @@ function formatAdrReservedRangeViolations(violations, reservedUpTo = TEMPLATE_AD
|
|
|
9428
9555
|
// src/scripts/check-adr-numbering.ts
|
|
9429
9556
|
async function runAdrNumberingCheck() {
|
|
9430
9557
|
const root = (await execa5("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
9431
|
-
const adrDir =
|
|
9432
|
-
if (!
|
|
9558
|
+
const adrDir = join35(root, "docs", "ADR");
|
|
9559
|
+
if (!existsSync35(adrDir)) {
|
|
9433
9560
|
console.log("\u2713 ADR numbering guard: no docs/ADR/ directory \u2014 nothing to compare");
|
|
9434
9561
|
return;
|
|
9435
9562
|
}
|
|
@@ -9757,8 +9884,8 @@ async function runOwnershipCheck(argv) {
|
|
|
9757
9884
|
const { stdout } = await execa7("git", ["diff", "--cached", "--name-status"], { cwd: root });
|
|
9758
9885
|
({ changed: changedFiles, deleted: deletedFiles } = parseNameStatus(stdout));
|
|
9759
9886
|
if (messageFile) {
|
|
9760
|
-
const { readFileSync:
|
|
9761
|
-
if (
|
|
9887
|
+
const { readFileSync: readFileSync29, existsSync: existsSync41 } = await import("fs");
|
|
9888
|
+
if (existsSync41(messageFile)) commitMessage = readFileSync29(messageFile, "utf8");
|
|
9762
9889
|
}
|
|
9763
9890
|
} else {
|
|
9764
9891
|
const base = process.env["GITHUB_BASE_REF"] ?? args[0];
|
|
@@ -9859,33 +9986,33 @@ ${BOLD}If the divergence is deliberate${OFF}
|
|
|
9859
9986
|
}
|
|
9860
9987
|
|
|
9861
9988
|
// src/scripts/check-plugin-collisions.ts
|
|
9862
|
-
import { existsSync as
|
|
9863
|
-
import { join as
|
|
9989
|
+
import { existsSync as existsSync37 } from "fs";
|
|
9990
|
+
import { join as join37 } from "path";
|
|
9864
9991
|
import { execa as execa8 } from "execa";
|
|
9865
9992
|
|
|
9866
9993
|
// src/lib/plugin-collision-guard.ts
|
|
9867
|
-
import { existsSync as
|
|
9868
|
-
import { join as
|
|
9994
|
+
import { existsSync as existsSync36, readdirSync as readdirSync14, statSync as statSync7 } from "fs";
|
|
9995
|
+
import { join as join36 } from "path";
|
|
9869
9996
|
var PYTEST_SPECIAL = /* @__PURE__ */ new Set(["conftest.py"]);
|
|
9870
9997
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([".venv", "node_modules", "__pycache__", ".git", "dist", "build"]);
|
|
9871
9998
|
function subdirectories(dir) {
|
|
9872
|
-
if (!
|
|
9999
|
+
if (!existsSync36(dir)) return [];
|
|
9873
10000
|
return readdirSync14(dir).filter((entry) => {
|
|
9874
10001
|
if (IGNORED_DIRS.has(entry) || entry.startsWith(".")) return false;
|
|
9875
10002
|
try {
|
|
9876
|
-
return statSync7(
|
|
10003
|
+
return statSync7(join36(dir, entry)).isDirectory();
|
|
9877
10004
|
} catch {
|
|
9878
10005
|
return false;
|
|
9879
10006
|
}
|
|
9880
10007
|
});
|
|
9881
10008
|
}
|
|
9882
10009
|
function regularPackagesOf(pluginDir2) {
|
|
9883
|
-
return subdirectories(pluginDir2).filter((name) =>
|
|
10010
|
+
return subdirectories(pluginDir2).filter((name) => existsSync36(join36(pluginDir2, name, "__init__.py"))).sort();
|
|
9884
10011
|
}
|
|
9885
10012
|
function bareTestModulesOf(pluginDir2) {
|
|
9886
|
-
const testsDir =
|
|
9887
|
-
if (!
|
|
9888
|
-
if (
|
|
10013
|
+
const testsDir = join36(pluginDir2, "tests");
|
|
10014
|
+
if (!existsSync36(testsDir)) return [];
|
|
10015
|
+
if (existsSync36(join36(testsDir, "__init__.py"))) return [];
|
|
9889
10016
|
return readdirSync14(testsDir).filter((f) => f.endsWith(".py") && !PYTEST_SPECIAL.has(f)).sort();
|
|
9890
10017
|
}
|
|
9891
10018
|
function findCollisions(servicesDir, pluginDirs) {
|
|
@@ -9894,7 +10021,7 @@ function findCollisions(servicesDir, pluginDirs) {
|
|
|
9894
10021
|
const gather = (kind, namesOf) => {
|
|
9895
10022
|
const claims = /* @__PURE__ */ new Map();
|
|
9896
10023
|
for (const plugin of plugins) {
|
|
9897
|
-
for (const name of namesOf(
|
|
10024
|
+
for (const name of namesOf(join36(servicesDir, plugin))) {
|
|
9898
10025
|
claims.set(name, [...claims.get(name) ?? [], plugin]);
|
|
9899
10026
|
}
|
|
9900
10027
|
}
|
|
@@ -9932,8 +10059,8 @@ function formatCollisions(collisions) {
|
|
|
9932
10059
|
// src/scripts/check-plugin-collisions.ts
|
|
9933
10060
|
async function runPluginCollisionCheck() {
|
|
9934
10061
|
const root = (await execa8("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
9935
|
-
const servicesDir =
|
|
9936
|
-
if (!
|
|
10062
|
+
const servicesDir = join37(root, "services");
|
|
10063
|
+
if (!existsSync37(servicesDir)) {
|
|
9937
10064
|
console.log("\u2713 plugin collision guard: no services/ directory \u2014 nothing to compare");
|
|
9938
10065
|
return;
|
|
9939
10066
|
}
|
|
@@ -9953,8 +10080,8 @@ async function runPluginCollisionCheck() {
|
|
|
9953
10080
|
import { execa as execa9 } from "execa";
|
|
9954
10081
|
|
|
9955
10082
|
// src/lib/plugin-terraform-guard.ts
|
|
9956
|
-
import { existsSync as
|
|
9957
|
-
import { dirname as dirname9, join as
|
|
10083
|
+
import { existsSync as existsSync38, readFileSync as readFileSync27, readdirSync as readdirSync15 } from "fs";
|
|
10084
|
+
import { dirname as dirname9, join as join38, relative as relative6, sep as sep3 } from "path";
|
|
9958
10085
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".worktrees", "dist", ".venv", "__pycache__"]);
|
|
9959
10086
|
var PLUGIN_MANIFEST_FILE2 = "biffo.plugin.json";
|
|
9960
10087
|
function findPluginManifests(root) {
|
|
@@ -9969,9 +10096,9 @@ function findPluginManifests(root) {
|
|
|
9969
10096
|
for (const entry of entries) {
|
|
9970
10097
|
if (entry.isDirectory()) {
|
|
9971
10098
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
9972
|
-
walk(
|
|
10099
|
+
walk(join38(dir, entry.name));
|
|
9973
10100
|
} else if (entry.isFile() && entry.name === PLUGIN_MANIFEST_FILE2) {
|
|
9974
|
-
found.push(relative6(root,
|
|
10101
|
+
found.push(relative6(root, join38(dir, entry.name)).split(sep3).join("/"));
|
|
9975
10102
|
}
|
|
9976
10103
|
}
|
|
9977
10104
|
};
|
|
@@ -9981,7 +10108,7 @@ function findPluginManifests(root) {
|
|
|
9981
10108
|
function readSubscriptions(absManifestPath) {
|
|
9982
10109
|
let parsed;
|
|
9983
10110
|
try {
|
|
9984
|
-
parsed = JSON.parse(
|
|
10111
|
+
parsed = JSON.parse(readFileSync27(absManifestPath, "utf8"));
|
|
9985
10112
|
} catch {
|
|
9986
10113
|
return null;
|
|
9987
10114
|
}
|
|
@@ -9996,14 +10123,14 @@ function readSubscriptions(absManifestPath) {
|
|
|
9996
10123
|
}
|
|
9997
10124
|
function checkPluginTerraform(root) {
|
|
9998
10125
|
const violations = [];
|
|
9999
|
-
const coreManifest =
|
|
10126
|
+
const coreManifest = existsSync38(join38(root, CORE_MANIFEST_FILE)) ? readCoreManifest(root) : null;
|
|
10000
10127
|
for (const manifest of findPluginManifests(root)) {
|
|
10001
10128
|
if (coreManifest && !isTemplateOwned(manifest, coreManifest)) continue;
|
|
10002
|
-
const absManifest =
|
|
10129
|
+
const absManifest = join38(root, manifest);
|
|
10003
10130
|
const subscriptions = readSubscriptions(absManifest);
|
|
10004
10131
|
if (subscriptions === null) continue;
|
|
10005
10132
|
const pluginDir2 = dirname9(absManifest);
|
|
10006
|
-
if (
|
|
10133
|
+
if (existsSync38(join38(pluginDir2, "terraform"))) continue;
|
|
10007
10134
|
const relPluginDir = relative6(root, pluginDir2).split(sep3).join("/");
|
|
10008
10135
|
violations.push({
|
|
10009
10136
|
manifest,
|
|
@@ -10199,8 +10326,8 @@ function rawArgsAfter(subcommand) {
|
|
|
10199
10326
|
}
|
|
10200
10327
|
|
|
10201
10328
|
// src/commands/doctor.ts
|
|
10202
|
-
import { existsSync as
|
|
10203
|
-
import { join as
|
|
10329
|
+
import { existsSync as existsSync39, readFileSync as readFileSync28 } from "fs";
|
|
10330
|
+
import { join as join39, resolve as resolve18 } from "path";
|
|
10204
10331
|
import chalk21 from "chalk";
|
|
10205
10332
|
import { Command as Command24 } from "commander";
|
|
10206
10333
|
|
|
@@ -10375,10 +10502,10 @@ async function runDoctor(options, deps = { git: new GitAdapter() }) {
|
|
|
10375
10502
|
return runDoctorChecks(facts);
|
|
10376
10503
|
}
|
|
10377
10504
|
function readLocalCoreVersion(cwd) {
|
|
10378
|
-
const path =
|
|
10379
|
-
if (!
|
|
10505
|
+
const path = join39(cwd, INSTANCE_CORE_FILE);
|
|
10506
|
+
if (!existsSync39(path)) return null;
|
|
10380
10507
|
try {
|
|
10381
|
-
return parseCoreRecord(
|
|
10508
|
+
return parseCoreRecord(readFileSync28(path, "utf8"));
|
|
10382
10509
|
} catch {
|
|
10383
10510
|
return null;
|
|
10384
10511
|
}
|
|
@@ -10393,10 +10520,10 @@ function parseCoreRecord(contents) {
|
|
|
10393
10520
|
}
|
|
10394
10521
|
}
|
|
10395
10522
|
function readFossil(cwd) {
|
|
10396
|
-
const path =
|
|
10397
|
-
if (!
|
|
10523
|
+
const path = join39(cwd, CORE_VERSION_FILE);
|
|
10524
|
+
if (!existsSync39(path)) return null;
|
|
10398
10525
|
try {
|
|
10399
|
-
const value =
|
|
10526
|
+
const value = readFileSync28(path, "utf8").trim();
|
|
10400
10527
|
return value === "" ? null : value;
|
|
10401
10528
|
} catch {
|
|
10402
10529
|
return null;
|
|
@@ -10845,13 +10972,13 @@ import { fileURLToPath as fileURLToPath6 } from "url";
|
|
|
10845
10972
|
import { Command as Command26 } from "commander";
|
|
10846
10973
|
|
|
10847
10974
|
// src/lib/packaged-scripts.ts
|
|
10848
|
-
import { existsSync as
|
|
10849
|
-
import { dirname as dirname10, join as
|
|
10975
|
+
import { existsSync as existsSync40 } from "fs";
|
|
10976
|
+
import { dirname as dirname10, join as join40 } from "path";
|
|
10850
10977
|
function findPackagedScript(startDir, relativePath) {
|
|
10851
10978
|
let dir = startDir;
|
|
10852
10979
|
for (; ; ) {
|
|
10853
|
-
const candidate =
|
|
10854
|
-
if (
|
|
10980
|
+
const candidate = join40(dir, relativePath);
|
|
10981
|
+
if (existsSync40(candidate)) return candidate;
|
|
10855
10982
|
const parent = dirname10(dir);
|
|
10856
10983
|
if (parent === dir) return null;
|
|
10857
10984
|
dir = parent;
|