@m8t-stack/cli 0.2.23 → 0.2.24
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/cli.js +82 -32
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1231,7 +1231,7 @@ var init_enable_hosted_brain = __esm({
|
|
|
1231
1231
|
import { Builtins, Cli } from "clipanion";
|
|
1232
1232
|
|
|
1233
1233
|
// src/lib/package-version.ts
|
|
1234
|
-
var CLI_VERSION = "0.2.
|
|
1234
|
+
var CLI_VERSION = "0.2.24";
|
|
1235
1235
|
|
|
1236
1236
|
// src/lib/render-error.ts
|
|
1237
1237
|
init_errors();
|
|
@@ -14297,6 +14297,14 @@ function run(cmd, cmdArgs, cwd, env) {
|
|
|
14297
14297
|
c.stderr.on("data", (d) => {
|
|
14298
14298
|
err += d.toString();
|
|
14299
14299
|
});
|
|
14300
|
+
c.on("error", (e) => {
|
|
14301
|
+
reject(
|
|
14302
|
+
new LocalCliError({
|
|
14303
|
+
code: "GH_APP_PUSH_FAILED",
|
|
14304
|
+
message: `${cmd} ${cmdArgs.join(" ")} could not run: ${e.message}`
|
|
14305
|
+
})
|
|
14306
|
+
);
|
|
14307
|
+
});
|
|
14300
14308
|
c.on("close", (code) => {
|
|
14301
14309
|
if (code === 0) {
|
|
14302
14310
|
resolve3();
|
|
@@ -14627,6 +14635,19 @@ async function promptCollision(ctx, info) {
|
|
|
14627
14635
|
rl.close();
|
|
14628
14636
|
}
|
|
14629
14637
|
}
|
|
14638
|
+
function resolveScaffoldCommit(repoRoot, env) {
|
|
14639
|
+
const baked = env.M8T_BUILD_SHA?.trim();
|
|
14640
|
+
if (baked) return baked;
|
|
14641
|
+
try {
|
|
14642
|
+
return execFileSync("git", ["rev-parse", "HEAD"], {
|
|
14643
|
+
cwd: repoRoot,
|
|
14644
|
+
encoding: "utf8",
|
|
14645
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
14646
|
+
}).trim() || "(scaffold)";
|
|
14647
|
+
} catch {
|
|
14648
|
+
return "(scaffold)";
|
|
14649
|
+
}
|
|
14650
|
+
}
|
|
14630
14651
|
function stageAsGitRepo(dir) {
|
|
14631
14652
|
const steps = [
|
|
14632
14653
|
{ args: ["init", "-b", "main"], label: "git init" },
|
|
@@ -14770,7 +14791,7 @@ var BrainCreateCommand = class extends M8tCommand {
|
|
|
14770
14791
|
if (needsPush) {
|
|
14771
14792
|
materializeBrainTree({ templateSrc, destDir: tmpDir, seedDir });
|
|
14772
14793
|
if (seedName && seedDir) {
|
|
14773
|
-
const headSha =
|
|
14794
|
+
const headSha = resolveScaffoldCommit(repoRoot, env);
|
|
14774
14795
|
writeSeedMarker({ destDir: tmpDir, seedName, seedDir, refreshedToCommit: headSha, version: "(scaffold)" });
|
|
14775
14796
|
}
|
|
14776
14797
|
this.context.stdout.write(
|
|
@@ -25388,6 +25409,7 @@ init_errors();
|
|
|
25388
25409
|
|
|
25389
25410
|
// src/lib/bootstrap-mi.ts
|
|
25390
25411
|
var INSTALLER_GRANT_DESCRIPTION = "m8t-installer auto-reap (bootstrap)";
|
|
25412
|
+
var GATEWAY_SUBSCOPE_GRANT_DESCRIPTION = "m8t-gateway auto-reap (bootstrap)";
|
|
25391
25413
|
async function ensureResourceGroup2(opts) {
|
|
25392
25414
|
const existing = (await runAz(["group", "show", "--name", opts.name, "--subscription", opts.subscriptionId, "-o", "json"]).catch(() => "")).trim();
|
|
25393
25415
|
if (existing) return;
|
|
@@ -25631,7 +25653,7 @@ async function writeBootstrapState(state, home = os13.homedir()) {
|
|
|
25631
25653
|
// src/commands/bootstrap/launch.ts
|
|
25632
25654
|
var DEFAULT_RG = "rg-m8t-stack";
|
|
25633
25655
|
var DEFAULT_INSTALLER = "ghcr.io/m8t-labs/m8t-installer";
|
|
25634
|
-
var DEFAULT_INSTALLER_TAG = "v0.1.
|
|
25656
|
+
var DEFAULT_INSTALLER_TAG = "v0.1.31";
|
|
25635
25657
|
var ACI_NAME = "m8t-installer";
|
|
25636
25658
|
var MI_NAME = "m8t-installer-mi";
|
|
25637
25659
|
var BootstrapLaunchCommand = class extends M8tCommand {
|
|
@@ -25642,8 +25664,8 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
25642
25664
|
examples: [
|
|
25643
25665
|
["Launch in eastus2", "$0 bootstrap launch --location eastus2"],
|
|
25644
25666
|
["BYO app registration", "$0 bootstrap launch --location eastus2 --client-id <appId>"],
|
|
25645
|
-
["Pin a specific installer tag", "$0 bootstrap launch --location eastus2 --installer-tag v0.1.
|
|
25646
|
-
["Override the full installer image ref", "$0 bootstrap launch --location eastus2 --installer-image ghcr.io/m8t-labs/m8t-installer:v0.1.
|
|
25667
|
+
["Pin a specific installer tag", "$0 bootstrap launch --location eastus2 --installer-tag v0.1.31"],
|
|
25668
|
+
["Override the full installer image ref", "$0 bootstrap launch --location eastus2 --installer-image ghcr.io/m8t-labs/m8t-installer:v0.1.31"]
|
|
25647
25669
|
]
|
|
25648
25670
|
});
|
|
25649
25671
|
location = Option46.String("--location");
|
|
@@ -25869,35 +25891,53 @@ init_errors();
|
|
|
25869
25891
|
|
|
25870
25892
|
// src/lib/bootstrap-reap.ts
|
|
25871
25893
|
var ROLE_ASSIGNMENT_API = "2022-04-01";
|
|
25872
|
-
|
|
25873
|
-
|
|
25874
|
-
|
|
25894
|
+
var GATEWAY_SUBSCOPE_ROLES = ["Cost Management Reader", "Monitoring Reader"];
|
|
25895
|
+
async function listSubscriptionAssignments(subscriptionId) {
|
|
25896
|
+
return JSON.parse(
|
|
25875
25897
|
await runAz([
|
|
25876
25898
|
"role",
|
|
25877
25899
|
"assignment",
|
|
25878
25900
|
"list",
|
|
25879
25901
|
"--all",
|
|
25880
25902
|
"--subscription",
|
|
25881
|
-
|
|
25903
|
+
subscriptionId,
|
|
25882
25904
|
"-o",
|
|
25883
25905
|
"json"
|
|
25884
25906
|
]).catch(() => "[]") || "[]"
|
|
25885
25907
|
);
|
|
25908
|
+
}
|
|
25909
|
+
async function deleteAssignmentsById(ids) {
|
|
25910
|
+
for (const id of ids) {
|
|
25911
|
+
await runAz([
|
|
25912
|
+
"rest",
|
|
25913
|
+
"--method",
|
|
25914
|
+
"delete",
|
|
25915
|
+
"--url",
|
|
25916
|
+
`https://management.azure.com${id}?api-version=${ROLE_ASSIGNMENT_API}`
|
|
25917
|
+
]).catch(() => void 0);
|
|
25918
|
+
}
|
|
25919
|
+
}
|
|
25920
|
+
async function sweepOrphanGatewaySubRoles(opts) {
|
|
25921
|
+
const subScope = `/subscriptions/${opts.subscriptionId}`;
|
|
25922
|
+
const all = await listSubscriptionAssignments(opts.subscriptionId);
|
|
25923
|
+
const matched = all.filter((r) => {
|
|
25924
|
+
if (r.principalName) return false;
|
|
25925
|
+
if (r.principalType !== "ServicePrincipal") return false;
|
|
25926
|
+
if (r.scope !== subScope) return false;
|
|
25927
|
+
if (r.description === GATEWAY_SUBSCOPE_GRANT_DESCRIPTION) return true;
|
|
25928
|
+
return GATEWAY_SUBSCOPE_ROLES.includes(r.roleDefinitionName ?? "");
|
|
25929
|
+
});
|
|
25930
|
+
if (opts.execute) await deleteAssignmentsById(matched.map((r) => r.id));
|
|
25931
|
+
return matched.map((r) => ({ id: r.id, description: r.description, roleDefinitionName: r.roleDefinitionName, createdOn: r.createdOn }));
|
|
25932
|
+
}
|
|
25933
|
+
async function sweepOrphanOwnerAssignments(opts) {
|
|
25934
|
+
const subScope = `/subscriptions/${opts.subscriptionId}`;
|
|
25935
|
+
const all = await listSubscriptionAssignments(opts.subscriptionId);
|
|
25886
25936
|
const matched = all.filter((r) => {
|
|
25887
25937
|
if (r.description === INSTALLER_GRANT_DESCRIPTION) return true;
|
|
25888
25938
|
return !r.principalName && r.principalType === "ServicePrincipal" && r.roleDefinitionName === "Owner" && r.scope === subScope;
|
|
25889
25939
|
});
|
|
25890
|
-
if (opts.execute)
|
|
25891
|
-
for (const r of matched) {
|
|
25892
|
-
await runAz([
|
|
25893
|
-
"rest",
|
|
25894
|
-
"--method",
|
|
25895
|
-
"delete",
|
|
25896
|
-
"--url",
|
|
25897
|
-
`https://management.azure.com${r.id}?api-version=${ROLE_ASSIGNMENT_API}`
|
|
25898
|
-
]).catch(() => void 0);
|
|
25899
|
-
}
|
|
25900
|
-
}
|
|
25940
|
+
if (opts.execute) await deleteAssignmentsById(matched.map((r) => r.id));
|
|
25901
25941
|
return matched.map((r) => ({ id: r.id, description: r.description, createdOn: r.createdOn }));
|
|
25902
25942
|
}
|
|
25903
25943
|
async function reapInstaller(opts) {
|
|
@@ -25954,22 +25994,30 @@ var BootstrapReapCommand = class extends M8tCommand {
|
|
|
25954
25994
|
if (!sub) {
|
|
25955
25995
|
throw new LocalCliError({ code: "BOOTSTRAP_SWEEP_NO_SUB", message: "No active subscription.", hint: "Run 'az account set --subscription <id>'." });
|
|
25956
25996
|
}
|
|
25957
|
-
const
|
|
25958
|
-
|
|
25959
|
-
|
|
25997
|
+
const execute = this.yes === true;
|
|
25998
|
+
const owner = await sweepOrphanOwnerAssignments({ subscriptionId: sub, execute });
|
|
25999
|
+
const gateway = await sweepOrphanGatewaySubRoles({ subscriptionId: sub, execute });
|
|
26000
|
+
const total = owner.length + gateway.length;
|
|
26001
|
+
if (total === 0) {
|
|
26002
|
+
this.context.stdout.write(`${colors.success("\u2713")} No orphaned installer Owner@sub or gateway sub-scope assignments found.
|
|
25960
26003
|
`);
|
|
25961
26004
|
return 0;
|
|
25962
26005
|
}
|
|
25963
|
-
for (const f of
|
|
25964
|
-
this.context.stdout.write(` ${f.id}${f.createdOn ? colors.dim(` (created ${f.createdOn})`) : ""}${f.description ? colors.dim(" [tagged]") : ""}
|
|
26006
|
+
for (const f of owner) {
|
|
26007
|
+
this.context.stdout.write(` ${colors.dim("Owner@sub")} ${f.id}${f.createdOn ? colors.dim(` (created ${f.createdOn})`) : ""}${f.description ? colors.dim(" [tagged]") : ""}
|
|
26008
|
+
`);
|
|
26009
|
+
}
|
|
26010
|
+
for (const f of gateway) {
|
|
26011
|
+
this.context.stdout.write(` ${colors.dim(f.roleDefinitionName ?? "gateway sub-scope")} ${f.id}${f.createdOn ? colors.dim(` (created ${f.createdOn})`) : ""}${f.description ? colors.dim(" [tagged]") : ""}
|
|
25965
26012
|
`);
|
|
25966
26013
|
}
|
|
25967
|
-
|
|
25968
|
-
|
|
26014
|
+
const breakdown = `${String(owner.length)} Owner@sub, ${String(gateway.length)} gateway sub-scope`;
|
|
26015
|
+
if (execute) {
|
|
26016
|
+
this.context.stdout.write(`${colors.success("\u2713")} Removed ${String(total)} orphaned assignment(s) (${breakdown}).
|
|
25969
26017
|
`);
|
|
25970
26018
|
} else {
|
|
25971
26019
|
this.context.stdout.write(`
|
|
25972
|
-
Found ${String(
|
|
26020
|
+
Found ${String(total)} orphaned assignment(s) (${breakdown}) (dry-run). ${colors.hint("Re-run with --sweep-orphans --yes to delete them.")}
|
|
25973
26021
|
`);
|
|
25974
26022
|
}
|
|
25975
26023
|
return 0;
|
|
@@ -25981,10 +26029,12 @@ Found ${String(found.length)} orphaned Owner@sub assignment(s) (dry-run). ${colo
|
|
|
25981
26029
|
if (!sub) {
|
|
25982
26030
|
throw new LocalCliError({ code: "BOOTSTRAP_SWEEP_NO_SUB", message: "No active subscription.", hint: "Run 'az account set --subscription <id>'." });
|
|
25983
26031
|
}
|
|
25984
|
-
const
|
|
26032
|
+
const owner = await sweepOrphanOwnerAssignments({ subscriptionId: sub, execute: true });
|
|
26033
|
+
const gateway = await sweepOrphanGatewaySubRoles({ subscriptionId: sub, execute: true });
|
|
26034
|
+
const total = owner.length + gateway.length;
|
|
25985
26035
|
this.context.stdout.write(
|
|
25986
|
-
|
|
25987
|
-
` : `${colors.success("\u2713")} No bootstrap state found; swept ${String(
|
|
26036
|
+
total === 0 ? `${colors.success("\u2713")} No bootstrap state and no orphaned installer Owner@sub or gateway sub-scope assignments. Nothing to reap.
|
|
26037
|
+
` : `${colors.success("\u2713")} No bootstrap state found; swept ${String(total)} orphaned assignment(s) (${String(owner.length)} Owner@sub, ${String(gateway.length)} gateway sub-scope).
|
|
25988
26038
|
`
|
|
25989
26039
|
);
|
|
25990
26040
|
return 0;
|
|
@@ -25992,7 +26042,7 @@ Found ${String(found.length)} orphaned Owner@sub assignment(s) (dry-run). ${colo
|
|
|
25992
26042
|
throw new LocalCliError({
|
|
25993
26043
|
code: "BOOTSTRAP_NO_STATE",
|
|
25994
26044
|
message: "No bootstrap state to reap.",
|
|
25995
|
-
hint: "Nothing to do. If a launch failed
|
|
26045
|
+
hint: "Nothing to do. If a launch failed and its RG was deleted it may have orphaned an installer Owner@sub grant or the gateway's sub-scope reader roles \u2014 run 'm8t bootstrap reap --force' (or 'm8t bootstrap reap --sweep-orphans --yes') to clean them."
|
|
25996
26046
|
});
|
|
25997
26047
|
}
|
|
25998
26048
|
if (this.force !== true) {
|