@tablation/crew 0.111.0 → 0.111.1
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 +131 -128
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -26518,35 +26518,141 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
26518
26518
|
break;
|
|
26519
26519
|
}
|
|
26520
26520
|
case "sandbox": {
|
|
26521
|
-
|
|
26522
|
-
|
|
26523
|
-
|
|
26524
|
-
|
|
26525
|
-
|
|
26526
|
-
|
|
26527
|
-
if (wanted.length !== 1) {
|
|
26528
|
-
process.stderr.write(`crew sandbox policy: ${only ? `no route "${only}"` : "name the route with --route"} (have: ${cfg.routes.map((r2) => r2.route).join(", ")})
|
|
26521
|
+
switch (positional[1]) {
|
|
26522
|
+
case "policy": {
|
|
26523
|
+
const only = value("route");
|
|
26524
|
+
const wanted = only ? cfg.routes.filter((r2) => r2.route === only) : cfg.routes;
|
|
26525
|
+
if (wanted.length !== 1) {
|
|
26526
|
+
process.stderr.write(`crew sandbox policy: ${only ? `no route "${only}"` : "name the route with --route"} (have: ${cfg.routes.map((r2) => r2.route).join(", ")})
|
|
26529
26527
|
`);
|
|
26530
|
-
|
|
26531
|
-
|
|
26532
|
-
|
|
26533
|
-
|
|
26534
|
-
|
|
26528
|
+
process.exit(2);
|
|
26529
|
+
}
|
|
26530
|
+
const role = value("role") ?? "dev";
|
|
26531
|
+
if (!ROLE_NAMES.includes(role)) {
|
|
26532
|
+
process.stderr.write(`crew sandbox policy: --role must be one of ${ROLE_NAMES.join(", ")}
|
|
26535
26533
|
`);
|
|
26536
|
-
|
|
26537
|
-
|
|
26538
|
-
|
|
26539
|
-
|
|
26540
|
-
|
|
26541
|
-
|
|
26542
|
-
|
|
26543
|
-
|
|
26544
|
-
|
|
26534
|
+
process.exit(2);
|
|
26535
|
+
}
|
|
26536
|
+
const r = wanted[0];
|
|
26537
|
+
const repoName = value("repo");
|
|
26538
|
+
const seat = resolveSeat(cfg.ship, r, role, {}, repoName);
|
|
26539
|
+
const input = sandboxPolicyInputFor(r, seat.toolchain, { harnessBin: seat.toolchain.bin, repo: repoName, sandbox: cfg.ship.sandbox });
|
|
26540
|
+
try {
|
|
26541
|
+
const policy = generateSandboxPolicy({ mirrorDir: cfg.ship.sandbox?.mirrorDir, workDir: cfg.ship.sandbox?.workDir, ...input });
|
|
26542
|
+
process.stdout.write(flag("json") ? `${JSON.stringify({ route: r.route, role, input, groups: policy.groups, yaml: policy.yaml }, null, 2)}
|
|
26545
26543
|
` : policy.yaml);
|
|
26546
|
-
|
|
26547
|
-
|
|
26544
|
+
} catch (e) {
|
|
26545
|
+
process.stderr.write(`crew sandbox policy: ${e.message}
|
|
26548
26546
|
`);
|
|
26549
|
-
|
|
26547
|
+
process.exit(1);
|
|
26548
|
+
}
|
|
26549
|
+
break;
|
|
26550
|
+
}
|
|
26551
|
+
case "build":
|
|
26552
|
+
case "images": {
|
|
26553
|
+
const subS = positional[1];
|
|
26554
|
+
const jsonS = flag("json");
|
|
26555
|
+
let routesS;
|
|
26556
|
+
try {
|
|
26557
|
+
routesS = value("route") ? [findRoute(cfg, value("route"))] : cfg.routes.filter((r) => r.enabled);
|
|
26558
|
+
} catch (e) {
|
|
26559
|
+
if (e instanceof ConfigError) {
|
|
26560
|
+
process.stderr.write(`crew: ${e.message}
|
|
26561
|
+
`);
|
|
26562
|
+
process.exit(2);
|
|
26563
|
+
}
|
|
26564
|
+
throw e;
|
|
26565
|
+
}
|
|
26566
|
+
const declared = await sandboxReposOf(routesS);
|
|
26567
|
+
if (subS === "images") {
|
|
26568
|
+
const stateS = readSandboxState(cfg.ship.stateDir);
|
|
26569
|
+
const rows = declared.map((r) => {
|
|
26570
|
+
const plan = planSandbox(r.name, r.decl, r.warm);
|
|
26571
|
+
return { repo: r.name, wants: plan.tag, tags: stateS[r.name]?.tags ?? [], ...sandboxImageStatus(stateS, plan) };
|
|
26572
|
+
});
|
|
26573
|
+
if (jsonS) {
|
|
26574
|
+
process.stdout.write(`${JSON.stringify(rows, null, 2)}
|
|
26575
|
+
`);
|
|
26576
|
+
break;
|
|
26577
|
+
}
|
|
26578
|
+
if (rows.length === 0) process.stdout.write("no repository declares a sandbox: section in its .crew.yaml\n");
|
|
26579
|
+
for (const r of rows) {
|
|
26580
|
+
process.stdout.write(`${r.repo}: ${r.status} \u2014 ${r.detail}
|
|
26581
|
+
`);
|
|
26582
|
+
for (const t of r.tags) {
|
|
26583
|
+
const ageH = Math.max(0, Math.round((Date.now() - Date.parse(t.builtAt)) / 36e5));
|
|
26584
|
+
process.stdout.write(` ${t.tag} built ${t.builtAt} (${ageH}h ago)
|
|
26585
|
+
`);
|
|
26586
|
+
}
|
|
26587
|
+
}
|
|
26588
|
+
break;
|
|
26589
|
+
}
|
|
26590
|
+
const only = positional[2];
|
|
26591
|
+
const targets = only ? declared.filter((r) => r.name === only) : declared;
|
|
26592
|
+
if (targets.length === 0) {
|
|
26593
|
+
process.stderr.write(only ? `crew: no repository "${only}" declares a sandbox: section
|
|
26594
|
+
` : "crew: no repository declares a sandbox: section in its .crew.yaml\n");
|
|
26595
|
+
process.exit(2);
|
|
26596
|
+
}
|
|
26597
|
+
const emitS = new Emitter({ route: routesS[0].route, eventFile: eventFileFor(cfg.ship.stateDir), rotateBytes: cfg.ship.eventsRotateBytes, logFile: cfg.ship.logFile, console: (l) => process.stderr.write(`${l}
|
|
26598
|
+
`) });
|
|
26599
|
+
const depsS = sandboxTickDeps(emitS, routesS[0]);
|
|
26600
|
+
let failed = false;
|
|
26601
|
+
for (const t of targets) {
|
|
26602
|
+
await sandboxBuildTick([t], depsS, true);
|
|
26603
|
+
const after = readSandboxState(cfg.ship.stateDir);
|
|
26604
|
+
const st = sandboxImageStatus(after, planSandbox(t.name, t.decl, t.warm));
|
|
26605
|
+
if (st.status === "failed") failed = true;
|
|
26606
|
+
process.stdout.write(`${t.name}: ${st.status} \u2014 ${st.detail}
|
|
26607
|
+
`);
|
|
26608
|
+
}
|
|
26609
|
+
if (failed) process.exit(1);
|
|
26610
|
+
break;
|
|
26611
|
+
}
|
|
26612
|
+
case "cache": {
|
|
26613
|
+
const settings = cacheSettingsOf(cfg.ship.sandbox);
|
|
26614
|
+
if (!settings) {
|
|
26615
|
+
process.stderr.write("crew sandbox cache: ship.sandbox.cacheVolume is not set, so there is no cache volume\n");
|
|
26616
|
+
process.exit(2);
|
|
26617
|
+
}
|
|
26618
|
+
const cache = new SandboxCache(settings);
|
|
26619
|
+
const served = cfg.routes.flatMap((r) => sandboxCacheRepos(r, cfg.ship));
|
|
26620
|
+
try {
|
|
26621
|
+
if (flag("reset")) {
|
|
26622
|
+
await cache.reset();
|
|
26623
|
+
process.stdout.write(`recreated volume ${settings.volume}; the next daemon pass refreshes every mirror
|
|
26624
|
+
`);
|
|
26625
|
+
break;
|
|
26626
|
+
}
|
|
26627
|
+
const dropped = flag("prune") ? await cache.prune(served) : [];
|
|
26628
|
+
const mirrors = await cache.list();
|
|
26629
|
+
if (flag("json")) {
|
|
26630
|
+
process.stdout.write(`${JSON.stringify({ volume: settings.volume, mirrors, dropped }, null, 2)}
|
|
26631
|
+
`);
|
|
26632
|
+
} else {
|
|
26633
|
+
for (const d of dropped) process.stdout.write(`dropped ${d.host}/${d.path}
|
|
26634
|
+
`);
|
|
26635
|
+
if (mirrors.length === 0) process.stdout.write(`volume ${settings.volume}: no mirrors yet
|
|
26636
|
+
`);
|
|
26637
|
+
for (const m of mirrors) process.stdout.write(`${m.repo.host}/${m.repo.path} last refresh ${m.lastRefresh ?? "never"}
|
|
26638
|
+
`);
|
|
26639
|
+
}
|
|
26640
|
+
} catch (e) {
|
|
26641
|
+
process.stderr.write(`crew sandbox cache: ${e instanceof Error ? e.message : String(e)}
|
|
26642
|
+
`);
|
|
26643
|
+
process.exitCode = 1;
|
|
26644
|
+
}
|
|
26645
|
+
break;
|
|
26646
|
+
}
|
|
26647
|
+
default:
|
|
26648
|
+
process.stderr.write([
|
|
26649
|
+
"crew sandbox policy --route ROUTE --role ROLE [--repo NAME] [--json]",
|
|
26650
|
+
"crew sandbox build [REPO] [--route ROUTE] [--json]",
|
|
26651
|
+
"crew sandbox images [--route ROUTE] [--json]",
|
|
26652
|
+
"crew sandbox cache [--prune|--reset] [--json]",
|
|
26653
|
+
""
|
|
26654
|
+
].join("\n"));
|
|
26655
|
+
process.exit(2);
|
|
26550
26656
|
}
|
|
26551
26657
|
break;
|
|
26552
26658
|
}
|
|
@@ -26799,70 +26905,6 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
26799
26905
|
if (!planIsUsable(planWt)) process.exitCode = 1;
|
|
26800
26906
|
break;
|
|
26801
26907
|
}
|
|
26802
|
-
case "sandbox": {
|
|
26803
|
-
const subS = positional[1];
|
|
26804
|
-
if (subS !== "build" && subS !== "images") {
|
|
26805
|
-
process.stderr.write("crew sandbox build [REPO] [--route ROUTE] [--json]\ncrew sandbox images [--route ROUTE] [--json]\n");
|
|
26806
|
-
process.exit(2);
|
|
26807
|
-
}
|
|
26808
|
-
const jsonS = flag("json");
|
|
26809
|
-
let routesS;
|
|
26810
|
-
try {
|
|
26811
|
-
routesS = value("route") ? [findRoute(cfg, value("route"))] : cfg.routes.filter((r) => r.enabled);
|
|
26812
|
-
} catch (e) {
|
|
26813
|
-
if (e instanceof ConfigError) {
|
|
26814
|
-
process.stderr.write(`crew: ${e.message}
|
|
26815
|
-
`);
|
|
26816
|
-
process.exit(2);
|
|
26817
|
-
}
|
|
26818
|
-
throw e;
|
|
26819
|
-
}
|
|
26820
|
-
const declared = await sandboxReposOf(routesS);
|
|
26821
|
-
if (subS === "images") {
|
|
26822
|
-
const stateS = readSandboxState(cfg.ship.stateDir);
|
|
26823
|
-
const rows = declared.map((r) => {
|
|
26824
|
-
const plan = planSandbox(r.name, r.decl, r.warm);
|
|
26825
|
-
return { repo: r.name, wants: plan.tag, tags: stateS[r.name]?.tags ?? [], ...sandboxImageStatus(stateS, plan) };
|
|
26826
|
-
});
|
|
26827
|
-
if (jsonS) {
|
|
26828
|
-
process.stdout.write(`${JSON.stringify(rows, null, 2)}
|
|
26829
|
-
`);
|
|
26830
|
-
break;
|
|
26831
|
-
}
|
|
26832
|
-
if (rows.length === 0) process.stdout.write("no repository declares a sandbox: section in its .crew.yaml\n");
|
|
26833
|
-
for (const r of rows) {
|
|
26834
|
-
process.stdout.write(`${r.repo}: ${r.status} \u2014 ${r.detail}
|
|
26835
|
-
`);
|
|
26836
|
-
for (const t of r.tags) {
|
|
26837
|
-
const ageH = Math.max(0, Math.round((Date.now() - Date.parse(t.builtAt)) / 36e5));
|
|
26838
|
-
process.stdout.write(` ${t.tag} built ${t.builtAt} (${ageH}h ago)
|
|
26839
|
-
`);
|
|
26840
|
-
}
|
|
26841
|
-
}
|
|
26842
|
-
break;
|
|
26843
|
-
}
|
|
26844
|
-
const only = positional[2];
|
|
26845
|
-
const targets = only ? declared.filter((r) => r.name === only) : declared;
|
|
26846
|
-
if (targets.length === 0) {
|
|
26847
|
-
process.stderr.write(only ? `crew: no repository "${only}" declares a sandbox: section
|
|
26848
|
-
` : "crew: no repository declares a sandbox: section in its .crew.yaml\n");
|
|
26849
|
-
process.exit(2);
|
|
26850
|
-
}
|
|
26851
|
-
const emitS = new Emitter({ route: routesS[0].route, eventFile: eventFileFor(cfg.ship.stateDir), rotateBytes: cfg.ship.eventsRotateBytes, logFile: cfg.ship.logFile, console: (l) => process.stderr.write(`${l}
|
|
26852
|
-
`) });
|
|
26853
|
-
const depsS = sandboxTickDeps(emitS, routesS[0]);
|
|
26854
|
-
let failed = false;
|
|
26855
|
-
for (const t of targets) {
|
|
26856
|
-
await sandboxBuildTick([t], depsS, true);
|
|
26857
|
-
const after = readSandboxState(cfg.ship.stateDir);
|
|
26858
|
-
const st = sandboxImageStatus(after, planSandbox(t.name, t.decl, t.warm));
|
|
26859
|
-
if (st.status === "failed") failed = true;
|
|
26860
|
-
process.stdout.write(`${t.name}: ${st.status} \u2014 ${st.detail}
|
|
26861
|
-
`);
|
|
26862
|
-
}
|
|
26863
|
-
if (failed) process.exit(1);
|
|
26864
|
-
break;
|
|
26865
|
-
}
|
|
26866
26908
|
case "periscope": {
|
|
26867
26909
|
const subP = positional[1];
|
|
26868
26910
|
if (subP !== "up" && subP !== "start" && subP !== "down" && subP !== "end" && subP !== "list") {
|
|
@@ -27748,45 +27790,6 @@ ${where.join("\n")}`);
|
|
|
27748
27790
|
if (results.some((r) => !r.ok)) process.exitCode = 1;
|
|
27749
27791
|
break;
|
|
27750
27792
|
}
|
|
27751
|
-
case "sandbox": {
|
|
27752
|
-
const settings = cacheSettingsOf(cfg.ship.sandbox);
|
|
27753
|
-
if (positional[1] !== "cache") {
|
|
27754
|
-
process.stderr.write("crew sandbox cache [--prune|--reset] [--json]\n");
|
|
27755
|
-
process.exit(2);
|
|
27756
|
-
}
|
|
27757
|
-
if (!settings) {
|
|
27758
|
-
process.stderr.write("crew sandbox cache: ship.sandbox.cacheVolume is not set, so there is no cache volume\n");
|
|
27759
|
-
process.exit(2);
|
|
27760
|
-
}
|
|
27761
|
-
const cache = new SandboxCache(settings);
|
|
27762
|
-
const served = cfg.routes.flatMap((r) => sandboxCacheRepos(r, cfg.ship));
|
|
27763
|
-
try {
|
|
27764
|
-
if (flag("reset")) {
|
|
27765
|
-
await cache.reset();
|
|
27766
|
-
process.stdout.write(`recreated volume ${settings.volume}; the next daemon pass refreshes every mirror
|
|
27767
|
-
`);
|
|
27768
|
-
break;
|
|
27769
|
-
}
|
|
27770
|
-
const dropped = flag("prune") ? await cache.prune(served) : [];
|
|
27771
|
-
const mirrors = await cache.list();
|
|
27772
|
-
if (flag("json")) {
|
|
27773
|
-
process.stdout.write(`${JSON.stringify({ volume: settings.volume, mirrors, dropped }, null, 2)}
|
|
27774
|
-
`);
|
|
27775
|
-
} else {
|
|
27776
|
-
for (const d of dropped) process.stdout.write(`dropped ${d.host}/${d.path}
|
|
27777
|
-
`);
|
|
27778
|
-
if (mirrors.length === 0) process.stdout.write(`volume ${settings.volume}: no mirrors yet
|
|
27779
|
-
`);
|
|
27780
|
-
for (const m of mirrors) process.stdout.write(`${m.repo.host}/${m.repo.path} last refresh ${m.lastRefresh ?? "never"}
|
|
27781
|
-
`);
|
|
27782
|
-
}
|
|
27783
|
-
} catch (e) {
|
|
27784
|
-
process.stderr.write(`crew sandbox cache: ${e instanceof Error ? e.message : String(e)}
|
|
27785
|
-
`);
|
|
27786
|
-
process.exitCode = 1;
|
|
27787
|
-
}
|
|
27788
|
-
break;
|
|
27789
|
-
}
|
|
27790
27793
|
case "whoami": {
|
|
27791
27794
|
const only = value("route") ?? positional[1];
|
|
27792
27795
|
const wanted = only ? cfg.routes.filter((r) => r.route === only) : cfg.routes;
|
package/package.json
CHANGED