fleetfizz 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fleetfizz.mjs +987 -654
- package/package.json +1 -1
package/dist/fleetfizz.mjs
CHANGED
|
@@ -6917,6 +6917,11 @@ const ignore$1 = /*#__PURE__*/ dual((args) => isEffect$1(args[0]), (self, option
|
|
|
6917
6917
|
});
|
|
6918
6918
|
});
|
|
6919
6919
|
/** @internal */
|
|
6920
|
+
const option$1 = (self) => match$1(self, {
|
|
6921
|
+
onFailure: none,
|
|
6922
|
+
onSuccess: some
|
|
6923
|
+
});
|
|
6924
|
+
/** @internal */
|
|
6920
6925
|
const result$1 = (self) => matchEager(self, {
|
|
6921
6926
|
onFailure: fail$5,
|
|
6922
6927
|
onSuccess: succeed$7
|
|
@@ -11094,6 +11099,49 @@ const tap = tap$1;
|
|
|
11094
11099
|
*/
|
|
11095
11100
|
const result = result$1;
|
|
11096
11101
|
/**
|
|
11102
|
+
* Converts success to `Option.some` and failure to `Option.none`.
|
|
11103
|
+
*
|
|
11104
|
+
* **When to use**
|
|
11105
|
+
*
|
|
11106
|
+
* Use when you only care whether an effect succeeds and want recoverable
|
|
11107
|
+
* failures represented as `Option.none`.
|
|
11108
|
+
*
|
|
11109
|
+
* **Details**
|
|
11110
|
+
*
|
|
11111
|
+
* Success values become `Option.some`, recoverable failures become
|
|
11112
|
+
* `Option.none`, and defects still fail the effect.
|
|
11113
|
+
*
|
|
11114
|
+
* **Gotchas**
|
|
11115
|
+
*
|
|
11116
|
+
* `option` only captures typed, recoverable failures as `Option.none`.
|
|
11117
|
+
* Defects and interruptions are not captured inside the `Option` and still
|
|
11118
|
+
* fail the effect.
|
|
11119
|
+
*
|
|
11120
|
+
* `option` also discards typed failure values. Use `result` if the failure
|
|
11121
|
+
* value matters.
|
|
11122
|
+
*
|
|
11123
|
+
* **Example** (Capturing success or failure as Option)
|
|
11124
|
+
*
|
|
11125
|
+
* ```ts import.meta.vitest
|
|
11126
|
+
* import { Effect, Option } from "effect"
|
|
11127
|
+
*
|
|
11128
|
+
* const program = Effect.all([
|
|
11129
|
+
* Effect.option(Effect.succeed(1)),
|
|
11130
|
+
* Effect.option(Effect.fail("missing"))
|
|
11131
|
+
* ])
|
|
11132
|
+
*
|
|
11133
|
+
* Effect.runSync(program) // => [Option.some(1), Option.none()]
|
|
11134
|
+
* ```
|
|
11135
|
+
*
|
|
11136
|
+
* @see {@link result} for a version that uses `Result` instead.
|
|
11137
|
+
* @see {@link exit} for a version that encapsulates both recoverable errors and defects in an `Exit`.
|
|
11138
|
+
*
|
|
11139
|
+
* @stability stable
|
|
11140
|
+
* @category error handling
|
|
11141
|
+
* @since 2.0.0
|
|
11142
|
+
*/
|
|
11143
|
+
const option = option$1;
|
|
11144
|
+
/**
|
|
11097
11145
|
* Transforms an effect to encapsulate both failure and success using the `Exit`
|
|
11098
11146
|
* data type.
|
|
11099
11147
|
*
|
|
@@ -29233,7 +29281,7 @@ const visualLength = (text) => {
|
|
|
29233
29281
|
* Helper function to pad strings to a specified width.
|
|
29234
29282
|
* @internal
|
|
29235
29283
|
*/
|
|
29236
|
-
const pad
|
|
29284
|
+
const pad = (s, width) => {
|
|
29237
29285
|
const actualLength = visualLength(s);
|
|
29238
29286
|
const padding = Math.max(0, width - actualLength);
|
|
29239
29287
|
return s + " ".repeat(padding);
|
|
@@ -29245,7 +29293,7 @@ const pad$1 = (s, width) => {
|
|
|
29245
29293
|
const renderTable = (rows, widthCap) => {
|
|
29246
29294
|
const maxColumn = Math.max(...rows.map((r) => visualLength(r.left))) + 4;
|
|
29247
29295
|
const col = widthCap === void 0 ? maxColumn : Math.min(maxColumn, widthCap);
|
|
29248
|
-
return rows.map(({ left, right }) => ` ${pad
|
|
29296
|
+
return rows.map(({ left, right }) => ` ${pad(left, Math.max(col, visualLength(left) + 1))}${right}`).join("\n");
|
|
29249
29297
|
};
|
|
29250
29298
|
const formatSubcommandName = (name, alias) => alias ? `${name}, ${alias}` : name;
|
|
29251
29299
|
/**
|
|
@@ -32392,7 +32440,7 @@ const runWith = (command, config) => {
|
|
|
32392
32440
|
};
|
|
32393
32441
|
//#endregion
|
|
32394
32442
|
//#region package.json
|
|
32395
|
-
var version = "0.5.
|
|
32443
|
+
var version = "0.5.2";
|
|
32396
32444
|
//#endregion
|
|
32397
32445
|
//#region ../../node_modules/.pnpm/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/scanner.js
|
|
32398
32446
|
/**
|
|
@@ -35226,7 +35274,7 @@ const stampText = (text, stamp) => {
|
|
|
35226
35274
|
const fleet = machinesOf(previous);
|
|
35227
35275
|
const shippedTo = {
|
|
35228
35276
|
...Object.fromEntries(Object.entries(shippedToOf(previous)).filter(([name]) => fleet.includes(name))),
|
|
35229
|
-
...Object.fromEntries(stamp.reached.map((name) => [name, stamp.shippedAt]))
|
|
35277
|
+
...Object.fromEntries([...stamp.reached, ...stamp.own === void 0 ? [] : [stamp.own]].map((name) => [name, stamp.shippedAt]))
|
|
35230
35278
|
};
|
|
35231
35279
|
let out = set(text, "shippedAt", stamp.shippedAt);
|
|
35232
35280
|
out = set(out, "shippedFrom", stamp.shippedFrom);
|
|
@@ -35244,6 +35292,7 @@ const shippedToOf = (config) => {
|
|
|
35244
35292
|
return Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[1] === "string"));
|
|
35245
35293
|
};
|
|
35246
35294
|
const isNewer = (remote, local) => remote !== void 0 && (local === void 0 || remote > local);
|
|
35295
|
+
const staleCopies = (config, copies, own) => copies.filter((c) => c.machine !== own && isNewer(c.config.shippedTo?.[c.machine], config.shippedTo?.[c.machine]));
|
|
35247
35296
|
//#endregion
|
|
35248
35297
|
//#region ../../packages/remote/src/shell.ts
|
|
35249
35298
|
const quote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
|
|
@@ -35271,11 +35320,11 @@ const fingerprintFn = [
|
|
|
35271
35320
|
"}"
|
|
35272
35321
|
].join("\n");
|
|
35273
35322
|
//#endregion
|
|
35274
|
-
//#region ../../packages/remote/src/
|
|
35323
|
+
//#region ../../packages/remote/src/check.ts
|
|
35275
35324
|
const STATE_DIR = "~/.local/state/fleetfizz";
|
|
35276
35325
|
const RECORDS_DIR = `${STATE_DIR}/records`;
|
|
35277
35326
|
const CONFIG_DIR_SH = "\"${XDG_CONFIG_HOME:-$HOME/.config}/fleetfizz\"";
|
|
35278
|
-
const
|
|
35327
|
+
const checkScript = (input) => {
|
|
35279
35328
|
const lines = [
|
|
35280
35329
|
prelude,
|
|
35281
35330
|
fingerprintFn,
|
|
@@ -35768,6 +35817,7 @@ const doneLine = (id, ok, code) => code === void 0 ? `printf 'ff-done {"id":"%s"
|
|
|
35768
35817
|
const jsonString = encodeSync(fromJsonString(String$5));
|
|
35769
35818
|
const jsonRecord = encodeSync(fromJsonString(Record(String$5, String$5)));
|
|
35770
35819
|
const stepId = (repo, step) => `repo:${repo}:${step}`;
|
|
35820
|
+
const isFetchStep = (id) => id.startsWith("repo:") && id.endsWith(":fetch");
|
|
35771
35821
|
const vpStepId = (step) => step.kind === "reinstall" ? `vp:reinstall:${step.name}` : step.kind === "globals" ? "vp:globals" : `vp:${step.kind}`;
|
|
35772
35822
|
const CONFIG_STEP = "config";
|
|
35773
35823
|
const VP_INSTALL = [
|
|
@@ -35775,6 +35825,15 @@ const VP_INSTALL = [
|
|
|
35775
35825
|
...vpEnv.split("\n"),
|
|
35776
35826
|
"{ hash -r 2>/dev/null || true; }"
|
|
35777
35827
|
].join(" && ");
|
|
35828
|
+
const marked = (id, run, then = []) => [
|
|
35829
|
+
stepLine(id),
|
|
35830
|
+
...run,
|
|
35831
|
+
`ff_code=$?`,
|
|
35832
|
+
`if [ "$ff_code" -ne 0 ]; then ${doneLine(id, false, "$ff_code")}; else`,
|
|
35833
|
+
doneLine(id, true),
|
|
35834
|
+
...then,
|
|
35835
|
+
"fi"
|
|
35836
|
+
];
|
|
35778
35837
|
const vpStep = (step) => {
|
|
35779
35838
|
const id = vpStepId(step);
|
|
35780
35839
|
const command = (() => {
|
|
@@ -35786,22 +35845,12 @@ const vpStep = (step) => {
|
|
|
35786
35845
|
case "globals": return `command vp install -g ${step.packages.map((p) => quote(`${p.name}@${p.version}`)).join(" ")}`;
|
|
35787
35846
|
}
|
|
35788
35847
|
})();
|
|
35789
|
-
return [
|
|
35790
|
-
stepLine(id),
|
|
35791
|
-
step.kind === "bootstrap" ? command : `( ${command} )`,
|
|
35792
|
-
`ff_code=$?`,
|
|
35793
|
-
`if [ "$ff_code" -ne 0 ]; then ${doneLine(id, false, "$ff_code")}; else ${doneLine(id, true)}; fi`
|
|
35794
|
-
];
|
|
35848
|
+
return marked(id, [step.kind === "bootstrap" ? command : `( ${command} )`]);
|
|
35795
35849
|
};
|
|
35796
35850
|
const configStep = () => {
|
|
35797
35851
|
const dir = CONFIG_DIR_SH;
|
|
35798
35852
|
const write = (variable, file) => `printf '%s' "$${variable}" | base64 -d > ${dir}/${quote(`${file}.tmp`)} && mv -f ${dir}/${quote(`${file}.tmp`)} ${dir}/${quote(file)}`;
|
|
35799
|
-
return [
|
|
35800
|
-
stepLine(CONFIG_STEP),
|
|
35801
|
-
`( mkdir -p ${dir} && ${write("ff_config", CONFIG_FILE)} && ${write("ff_schema", SCHEMA_FILE)} )`,
|
|
35802
|
-
`ff_code=$?`,
|
|
35803
|
-
`if [ "$ff_code" -ne 0 ]; then ${doneLine(CONFIG_STEP, false, "$ff_code")}; else ${doneLine(CONFIG_STEP, true)}; fi`
|
|
35804
|
-
];
|
|
35853
|
+
return marked(CONFIG_STEP, [`( mkdir -p ${dir} && ${write("ff_config", CONFIG_FILE)} && ${write("ff_schema", SCHEMA_FILE)} )`]);
|
|
35805
35854
|
};
|
|
35806
35855
|
const writeRecord = (repo) => {
|
|
35807
35856
|
return [
|
|
@@ -35826,34 +35875,32 @@ const repoSteps = (repo, input) => {
|
|
|
35826
35875
|
return [
|
|
35827
35876
|
`ff_d="$ff_root"/${quote(repo.name)}`,
|
|
35828
35877
|
`ff_before=$(git -C "$ff_d" rev-parse HEAD 2>/dev/null || true)`,
|
|
35829
|
-
|
|
35830
|
-
|
|
35831
|
-
|
|
35832
|
-
|
|
35833
|
-
|
|
35834
|
-
|
|
35835
|
-
|
|
35836
|
-
|
|
35837
|
-
|
|
35838
|
-
|
|
35839
|
-
|
|
35840
|
-
|
|
35841
|
-
|
|
35842
|
-
|
|
35843
|
-
|
|
35844
|
-
|
|
35845
|
-
|
|
35846
|
-
|
|
35847
|
-
|
|
35848
|
-
|
|
35849
|
-
|
|
35850
|
-
|
|
35851
|
-
|
|
35852
|
-
|
|
35853
|
-
|
|
35854
|
-
|
|
35855
|
-
"fi",
|
|
35856
|
-
"fi"
|
|
35878
|
+
...marked(fetch, [
|
|
35879
|
+
"(",
|
|
35880
|
+
" set -e",
|
|
35881
|
+
` if [ ! -d "$ff_d/.git" ]; then ff_git clone --quiet ${quote(url)} "$ff_d"; fi`,
|
|
35882
|
+
` if [ "$(git -C "$ff_d" remote get-url origin)" != ${quote(url)} ]; then echo "origin is $(git -C "$ff_d" remote get-url origin), expected ${url}"; exit 1; fi`,
|
|
35883
|
+
...sync.map((l) => ` ${l}`),
|
|
35884
|
+
` git -C "$ff_d" clean --quiet -fd`,
|
|
35885
|
+
...writeFiles(repo).map((l) => ` ${l}`),
|
|
35886
|
+
")"
|
|
35887
|
+
], [
|
|
35888
|
+
`ff_after=$(git -C "$ff_d" rev-parse HEAD)`,
|
|
35889
|
+
`ff_state "{\\"fact\\":\\"repo\\",\\"name\\":\\"$(ff_json ${quote(repo.name)})\\",\\"present\\":true,\\"head\\":\\"$(ff_json "$ff_after")\\"}"`,
|
|
35890
|
+
`if [ ${repo.decision.run ? "true" : "false"} = true ] || [ "$ff_after" != "$ff_before" ]; then`,
|
|
35891
|
+
` ${stepLine(install)}`,
|
|
35892
|
+
` (cd "$ff_d" && ${envPrefix} "\${SHELL:-/bin/sh}" -lc ${quote(repo.install)})`,
|
|
35893
|
+
` ff_code=$?`,
|
|
35894
|
+
` if [ "$ff_code" -ne 0 ]; then ${doneLine(install, false, "$ff_code")}; else`,
|
|
35895
|
+
" (",
|
|
35896
|
+
" set -e",
|
|
35897
|
+
...writeRecord(repo).map((l) => ` ${l}`),
|
|
35898
|
+
" )",
|
|
35899
|
+
` ff_code=$?`,
|
|
35900
|
+
` if [ "$ff_code" -ne 0 ]; then echo "could not write the install record"; ${doneLine(install, false, "$ff_code")}; else ${doneLine(install, true)}; fi`,
|
|
35901
|
+
" fi",
|
|
35902
|
+
"fi"
|
|
35903
|
+
])
|
|
35857
35904
|
];
|
|
35858
35905
|
};
|
|
35859
35906
|
const applyStdin = (token, config, schema) => [
|
|
@@ -36039,546 +36086,6 @@ const resolveVersions = fn("resolveVersions")(function* (packages) {
|
|
|
36039
36086
|
return Object.fromEntries(entries);
|
|
36040
36087
|
});
|
|
36041
36088
|
//#endregion
|
|
36042
|
-
//#region src/ledger.ts
|
|
36043
|
-
const Note = Struct({
|
|
36044
|
-
kind: Literals$2([
|
|
36045
|
-
"ask",
|
|
36046
|
-
"answer",
|
|
36047
|
-
"info"
|
|
36048
|
-
]),
|
|
36049
|
-
text: String$5
|
|
36050
|
-
});
|
|
36051
|
-
const Item = Struct({
|
|
36052
|
-
name: String$5,
|
|
36053
|
-
kind: Literals$2([
|
|
36054
|
-
"version",
|
|
36055
|
-
"repo",
|
|
36056
|
-
"config"
|
|
36057
|
-
]),
|
|
36058
|
-
from: optionalKey(String$5),
|
|
36059
|
-
to: optionalKey(String$5),
|
|
36060
|
-
reason: optionalKey(String$5),
|
|
36061
|
-
changes: Boolean$4,
|
|
36062
|
-
status: Literals$2([
|
|
36063
|
-
"idle",
|
|
36064
|
-
"running",
|
|
36065
|
-
"ok",
|
|
36066
|
-
"failed",
|
|
36067
|
-
"skipped"
|
|
36068
|
-
]),
|
|
36069
|
-
doing: optionalKey(String$5),
|
|
36070
|
-
detail: optionalKey(String$5),
|
|
36071
|
-
output: optionalKey(ArraySchema(String$5)),
|
|
36072
|
-
steps: ArraySchema(String$5),
|
|
36073
|
-
started: optionalKey(Number$2),
|
|
36074
|
-
ended: optionalKey(Number$2)
|
|
36075
|
-
});
|
|
36076
|
-
const MachineEntry = Struct({
|
|
36077
|
-
name: String$5,
|
|
36078
|
-
status: Literals$2([
|
|
36079
|
-
"gathering",
|
|
36080
|
-
"gathered",
|
|
36081
|
-
"planned",
|
|
36082
|
-
"running",
|
|
36083
|
-
"ok",
|
|
36084
|
-
"skipped",
|
|
36085
|
-
"failed"
|
|
36086
|
-
]),
|
|
36087
|
-
detail: optionalKey(String$5),
|
|
36088
|
-
output: optionalKey(ArraySchema(String$5)),
|
|
36089
|
-
items: ArraySchema(Item),
|
|
36090
|
-
started: optionalKey(Number$2),
|
|
36091
|
-
ended: optionalKey(Number$2)
|
|
36092
|
-
});
|
|
36093
|
-
const LedgerState = Struct({
|
|
36094
|
-
from: String$5,
|
|
36095
|
-
dryRun: Boolean$4,
|
|
36096
|
-
notes: ArraySchema(Note),
|
|
36097
|
-
machines: ArraySchema(MachineEntry),
|
|
36098
|
-
started: Number$2,
|
|
36099
|
-
ended: optionalKey(Number$2)
|
|
36100
|
-
});
|
|
36101
|
-
const short = (sha) => sha === void 0 ? void 0 : sha.slice(0, 7);
|
|
36102
|
-
const FETCH = ":fetch";
|
|
36103
|
-
const doingOf = (id) => id === "config" ? "copying" : id.endsWith(FETCH) ? "fetching" : "installing";
|
|
36104
|
-
const itemsOf = (p, state) => {
|
|
36105
|
-
const items = /* @__PURE__ */ new Map();
|
|
36106
|
-
const key = (kind, name) => `${kind}:${name}`;
|
|
36107
|
-
const base = (name, from, kind = "version") => ({
|
|
36108
|
-
name,
|
|
36109
|
-
kind,
|
|
36110
|
-
...from === void 0 ? {} : { from },
|
|
36111
|
-
changes: false,
|
|
36112
|
-
status: "idle",
|
|
36113
|
-
steps: []
|
|
36114
|
-
});
|
|
36115
|
-
const set = (name, change, kind = "version") => items.set(key(kind, name), change(items.get(key(kind, name)) ?? base(name, void 0, kind)));
|
|
36116
|
-
const plans = (id, to, from, reason) => ({ from: gathered, ...item }) => {
|
|
36117
|
-
const now = from === void 0 ? gathered : from ?? void 0;
|
|
36118
|
-
return {
|
|
36119
|
-
...item,
|
|
36120
|
-
...now === void 0 ? {} : { from: now },
|
|
36121
|
-
to,
|
|
36122
|
-
...reason === void 0 ? {} : { reason },
|
|
36123
|
-
changes: true,
|
|
36124
|
-
steps: [...item.steps, id]
|
|
36125
|
-
};
|
|
36126
|
-
};
|
|
36127
|
-
const vp = state.vp?.version ?? void 0;
|
|
36128
|
-
items.set(key("version", "vp"), base("vp", vp));
|
|
36129
|
-
if (state.node != null) items.set(key("version", "node"), base("node", state.node));
|
|
36130
|
-
for (const g of state.globals) items.set(key("version", g.name), base(g.name, g.version));
|
|
36131
|
-
for (const step of p.vp) {
|
|
36132
|
-
const id = vpStepId(step);
|
|
36133
|
-
switch (step.kind) {
|
|
36134
|
-
case "bootstrap":
|
|
36135
|
-
set("vp", plans(id, "latest", null));
|
|
36136
|
-
break;
|
|
36137
|
-
case "vp":
|
|
36138
|
-
case "node":
|
|
36139
|
-
set(step.kind, plans(id, step.version, step.from));
|
|
36140
|
-
break;
|
|
36141
|
-
case "reinstall":
|
|
36142
|
-
set(step.name, plans(id, step.version, step.version, "reinstall"));
|
|
36143
|
-
break;
|
|
36144
|
-
case "globals": for (const pkg of step.packages) set(pkg.name, plans(id, pkg.version, pkg.from ?? null));
|
|
36145
|
-
}
|
|
36146
|
-
}
|
|
36147
|
-
for (const repo of p.repos) {
|
|
36148
|
-
const from = short(state.records[repo.name]?.commit);
|
|
36149
|
-
const to = short(repo.head);
|
|
36150
|
-
items.set(key("repo", repo.name), {
|
|
36151
|
-
name: repo.name,
|
|
36152
|
-
kind: "repo",
|
|
36153
|
-
...from === void 0 ? {} : { from },
|
|
36154
|
-
...to === void 0 ? {} : { to },
|
|
36155
|
-
...repo.decision.run ? { reason: repo.decision.reason } : {},
|
|
36156
|
-
changes: repo.decision.run,
|
|
36157
|
-
status: "idle",
|
|
36158
|
-
steps: [stepId(repo.name, "fetch"), stepId(repo.name, "install")]
|
|
36159
|
-
});
|
|
36160
|
-
}
|
|
36161
|
-
for (const s of p.skipped) set(s.what, (i) => ({
|
|
36162
|
-
...i,
|
|
36163
|
-
status: "skipped",
|
|
36164
|
-
detail: s.reason
|
|
36165
|
-
}));
|
|
36166
|
-
for (const f of p.failures) {
|
|
36167
|
-
const kind = f.section === "repos" ? "repo" : "version";
|
|
36168
|
-
set(f.what, (i) => ({
|
|
36169
|
-
...i,
|
|
36170
|
-
changes: true,
|
|
36171
|
-
status: "failed",
|
|
36172
|
-
detail: f.reason
|
|
36173
|
-
}), kind);
|
|
36174
|
-
}
|
|
36175
|
-
items.set(key("config", CONFIG_STEP), {
|
|
36176
|
-
name: CONFIG_STEP,
|
|
36177
|
-
kind: "config",
|
|
36178
|
-
changes: false,
|
|
36179
|
-
status: "idle",
|
|
36180
|
-
steps: [CONFIG_STEP]
|
|
36181
|
-
});
|
|
36182
|
-
return [...items.values()];
|
|
36183
|
-
};
|
|
36184
|
-
const outcomeOf = (m, dryRun) => m.status === "skipped" ? "skipped" : m.status === "failed" || m.items.some((i) => i.status === "failed") ? "failed" : m.status === "ok" || m.status === "planned" && dryRun ? "ok" : void 0;
|
|
36185
|
-
const tally = (state) => {
|
|
36186
|
-
const outcomes = state.machines.map((m) => outcomeOf(m, state.dryRun));
|
|
36187
|
-
const n = (o) => outcomes.filter((x) => x === o).length;
|
|
36188
|
-
return {
|
|
36189
|
-
ok: n("ok"),
|
|
36190
|
-
skipped: n("skipped"),
|
|
36191
|
-
failed: n("failed")
|
|
36192
|
-
};
|
|
36193
|
-
};
|
|
36194
|
-
const makeLedger = fn("makeLedger")(function* (input) {
|
|
36195
|
-
const started = yield* currentTimeMillis;
|
|
36196
|
-
let snapshot = {
|
|
36197
|
-
state: {
|
|
36198
|
-
from: input.from,
|
|
36199
|
-
dryRun: input.dryRun,
|
|
36200
|
-
notes: [],
|
|
36201
|
-
machines: input.machines.map((name) => ({
|
|
36202
|
-
name,
|
|
36203
|
-
status: "gathering",
|
|
36204
|
-
items: []
|
|
36205
|
-
})),
|
|
36206
|
-
started
|
|
36207
|
-
},
|
|
36208
|
-
now: started
|
|
36209
|
-
};
|
|
36210
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
36211
|
-
const update = (change) => gen(function* () {
|
|
36212
|
-
const now = yield* currentTimeMillis;
|
|
36213
|
-
snapshot = {
|
|
36214
|
-
state: change(snapshot.state, now),
|
|
36215
|
-
now
|
|
36216
|
-
};
|
|
36217
|
-
for (const listener of listeners) listener();
|
|
36218
|
-
});
|
|
36219
|
-
const machine = (name, change) => update((state, now) => ({
|
|
36220
|
-
...state,
|
|
36221
|
-
machines: state.machines.map((m) => m.name === name ? change(m, now) : m)
|
|
36222
|
-
}));
|
|
36223
|
-
const items = (name, id, change) => machine(name, (m, now) => ({
|
|
36224
|
-
...m,
|
|
36225
|
-
items: m.items.map((i) => i.steps.includes(id) ? change(i, now) : i)
|
|
36226
|
-
}));
|
|
36227
|
-
const store = {
|
|
36228
|
-
get: () => snapshot,
|
|
36229
|
-
subscribe: (listener) => {
|
|
36230
|
-
listeners.add(listener);
|
|
36231
|
-
return () => listeners.delete(listener);
|
|
36232
|
-
},
|
|
36233
|
-
tick: update((state) => state)
|
|
36234
|
-
};
|
|
36235
|
-
return {
|
|
36236
|
-
ledger: {
|
|
36237
|
-
note: fn("ledger.note")(function* (kind, text) {
|
|
36238
|
-
yield* update((state) => ({
|
|
36239
|
-
...state,
|
|
36240
|
-
notes: [...state.notes, {
|
|
36241
|
-
kind,
|
|
36242
|
-
text
|
|
36243
|
-
}]
|
|
36244
|
-
}));
|
|
36245
|
-
yield* log(noteText({
|
|
36246
|
-
kind,
|
|
36247
|
-
text
|
|
36248
|
-
}, styled()));
|
|
36249
|
-
}),
|
|
36250
|
-
gathered: (name) => machine(name, (m) => ({
|
|
36251
|
-
...m,
|
|
36252
|
-
status: "gathered"
|
|
36253
|
-
})),
|
|
36254
|
-
skip: (name, reason) => machine(name, (m) => ({
|
|
36255
|
-
...m,
|
|
36256
|
-
status: "skipped",
|
|
36257
|
-
detail: reason
|
|
36258
|
-
})),
|
|
36259
|
-
fail: (name, [reason = "failed", ...output]) => machine(name, (m, now) => ({
|
|
36260
|
-
...m,
|
|
36261
|
-
status: "failed",
|
|
36262
|
-
detail: reason,
|
|
36263
|
-
...output.length === 0 ? {} : { output },
|
|
36264
|
-
ended: now
|
|
36265
|
-
})),
|
|
36266
|
-
plan: (name, p, state) => machine(name, (m) => ({
|
|
36267
|
-
...m,
|
|
36268
|
-
status: "planned",
|
|
36269
|
-
items: itemsOf(p, state)
|
|
36270
|
-
})),
|
|
36271
|
-
start: (name) => machine(name, (m, now) => ({
|
|
36272
|
-
...m,
|
|
36273
|
-
status: "running",
|
|
36274
|
-
started: now
|
|
36275
|
-
})),
|
|
36276
|
-
stepStarted: (name, id) => items(name, id, (item, now) => item.status === "failed" ? item : {
|
|
36277
|
-
...item,
|
|
36278
|
-
changes: item.changes || !(id === "config" || id.endsWith(FETCH)),
|
|
36279
|
-
status: "running",
|
|
36280
|
-
doing: doingOf(id),
|
|
36281
|
-
started: item.started ?? now
|
|
36282
|
-
}),
|
|
36283
|
-
stepDone: (name, id, result) => items(name, id, (item, now) => {
|
|
36284
|
-
if (item.status === "failed") return item;
|
|
36285
|
-
if (!result.ok) return {
|
|
36286
|
-
...item,
|
|
36287
|
-
changes: true,
|
|
36288
|
-
status: "failed",
|
|
36289
|
-
detail: result.detail,
|
|
36290
|
-
...result.output.length === 0 ? {} : { output: result.output },
|
|
36291
|
-
ended: now
|
|
36292
|
-
};
|
|
36293
|
-
const { doing: _, ...rest } = item;
|
|
36294
|
-
if (id.endsWith(FETCH)) return item.changes ? item : {
|
|
36295
|
-
...rest,
|
|
36296
|
-
status: "idle",
|
|
36297
|
-
ended: now
|
|
36298
|
-
};
|
|
36299
|
-
return {
|
|
36300
|
-
...rest,
|
|
36301
|
-
status: "ok",
|
|
36302
|
-
ended: now
|
|
36303
|
-
};
|
|
36304
|
-
}),
|
|
36305
|
-
fetched: (name, repo, head) => machine(name, (m) => ({
|
|
36306
|
-
...m,
|
|
36307
|
-
items: m.items.map((i) => i.kind === "repo" && i.name === repo ? {
|
|
36308
|
-
...i,
|
|
36309
|
-
to: head.slice(0, 7),
|
|
36310
|
-
changes: i.changes || head.slice(0, 7) !== i.from
|
|
36311
|
-
} : i)
|
|
36312
|
-
})),
|
|
36313
|
-
finish: (name, outcome) => machine(name, (m, now) => ({
|
|
36314
|
-
...m,
|
|
36315
|
-
status: m.status === "failed" ? "failed" : outcome,
|
|
36316
|
-
ended: m.ended ?? now
|
|
36317
|
-
})),
|
|
36318
|
-
end: gen(function* () {
|
|
36319
|
-
yield* update((state, now) => ({
|
|
36320
|
-
...state,
|
|
36321
|
-
ended: now,
|
|
36322
|
-
machines: state.machines.map((m) => m.status === "running" || m.status === "gathering" ? {
|
|
36323
|
-
...m,
|
|
36324
|
-
status: "failed",
|
|
36325
|
-
detail: "stopped",
|
|
36326
|
-
ended: now,
|
|
36327
|
-
items: m.items.map((i) => i.status === "running" ? {
|
|
36328
|
-
...i,
|
|
36329
|
-
status: "failed",
|
|
36330
|
-
detail: "stopped",
|
|
36331
|
-
ended: now
|
|
36332
|
-
} : i)
|
|
36333
|
-
} : m)
|
|
36334
|
-
}));
|
|
36335
|
-
return snapshot.state;
|
|
36336
|
-
})
|
|
36337
|
-
},
|
|
36338
|
-
store
|
|
36339
|
-
};
|
|
36340
|
-
});
|
|
36341
|
-
const duration = (ms) => {
|
|
36342
|
-
const s = ms / 1e3;
|
|
36343
|
-
if (s < 60) return `${s.toFixed(1)}s`;
|
|
36344
|
-
const m = Math.floor(s / 60);
|
|
36345
|
-
if (m < 60) return `${m}m ${String(Math.floor(s % 60)).padStart(2, "0")}s`;
|
|
36346
|
-
return `${Math.floor(m / 60)}h ${String(m % 60).padStart(2, "0")}m`;
|
|
36347
|
-
};
|
|
36348
|
-
const value = (i) => i.to === void 0 ? i.from ?? "" : i.from === void 0 ? `+ ${i.to}` : i.from === i.to ? i.to : `${i.from} -> ${i.to}`;
|
|
36349
|
-
const note = (i) => {
|
|
36350
|
-
switch (i.status) {
|
|
36351
|
-
case "running": return {
|
|
36352
|
-
text: i.doing ?? "running",
|
|
36353
|
-
style: "run"
|
|
36354
|
-
};
|
|
36355
|
-
case "failed": return {
|
|
36356
|
-
text: "FAIL",
|
|
36357
|
-
style: "fail"
|
|
36358
|
-
};
|
|
36359
|
-
case "skipped": return {
|
|
36360
|
-
text: "SKIP",
|
|
36361
|
-
style: "skip"
|
|
36362
|
-
};
|
|
36363
|
-
case "ok":
|
|
36364
|
-
if (i.kind === "config") return {
|
|
36365
|
-
text: "copied",
|
|
36366
|
-
style: "dim"
|
|
36367
|
-
};
|
|
36368
|
-
if (i.kind === "repo") return i.from !== void 0 && i.from === i.to ? {
|
|
36369
|
-
text: `rebuilt${i.reason === void 0 ? "" : `: ${i.reason}`}`,
|
|
36370
|
-
style: "ok"
|
|
36371
|
-
} : {
|
|
36372
|
-
text: "installed",
|
|
36373
|
-
style: "ok"
|
|
36374
|
-
};
|
|
36375
|
-
return {
|
|
36376
|
-
text: i.reason ?? "done",
|
|
36377
|
-
style: "ok"
|
|
36378
|
-
};
|
|
36379
|
-
case "idle":
|
|
36380
|
-
if (i.changes) return {
|
|
36381
|
-
text: i.reason ?? "",
|
|
36382
|
-
style: "dim"
|
|
36383
|
-
};
|
|
36384
|
-
return {
|
|
36385
|
-
text: i.kind === "repo" ? "up to date" : "",
|
|
36386
|
-
style: "dim"
|
|
36387
|
-
};
|
|
36388
|
-
}
|
|
36389
|
-
};
|
|
36390
|
-
const elapsed = (started, ended, now) => started === void 0 ? "" : duration((ended ?? now) - started);
|
|
36391
|
-
const LIVE_OUTPUT = 12;
|
|
36392
|
-
const outputRows = (output, indent, running) => {
|
|
36393
|
-
const cut = running && output.length > LIVE_OUTPUT;
|
|
36394
|
-
const shown = cut ? output.slice(-12) : output;
|
|
36395
|
-
return [...cut ? [[{
|
|
36396
|
-
text: `${indent}(${output.length - LIVE_OUTPUT} earlier lines)`,
|
|
36397
|
-
style: "dim"
|
|
36398
|
-
}]] : [], ...shown.map((line) => [{ text: `${indent}${line}` }])];
|
|
36399
|
-
};
|
|
36400
|
-
const pad = (text, width) => text.padEnd(width);
|
|
36401
|
-
const rows = (state, options) => {
|
|
36402
|
-
const { full, now, live } = options;
|
|
36403
|
-
const running = live && state.ended === void 0;
|
|
36404
|
-
const shownItems = (m) => m.items.filter((i) => full || i.changes || i.status === "failed" || i.status === "skipped");
|
|
36405
|
-
const visible = state.machines.flatMap(shownItems);
|
|
36406
|
-
const nameW = Math.max(0, ...state.machines.map((m) => m.name.length));
|
|
36407
|
-
const itemW = Math.max(0, ...visible.map((i) => i.name.length));
|
|
36408
|
-
const valueW = Math.max(0, ...visible.map((i) => value(i).length));
|
|
36409
|
-
const noteW = Math.max(0, ...visible.map((i) => note(i).text.length));
|
|
36410
|
-
const out = [[]];
|
|
36411
|
-
for (const m of state.machines) {
|
|
36412
|
-
const items = shownItems(m);
|
|
36413
|
-
const upToDate = !m.items.some((i) => i.changes);
|
|
36414
|
-
const head = [{
|
|
36415
|
-
text: pad(m.name, nameW),
|
|
36416
|
-
style: "bold"
|
|
36417
|
-
}, { text: " " }];
|
|
36418
|
-
const time = {
|
|
36419
|
-
text: elapsed(m.started, m.ended, now),
|
|
36420
|
-
style: "dim"
|
|
36421
|
-
};
|
|
36422
|
-
switch (m.status) {
|
|
36423
|
-
case "gathering":
|
|
36424
|
-
case "gathered":
|
|
36425
|
-
head.push({ text: " " }, {
|
|
36426
|
-
text: m.status,
|
|
36427
|
-
style: "dim"
|
|
36428
|
-
});
|
|
36429
|
-
break;
|
|
36430
|
-
case "planned":
|
|
36431
|
-
if (m.items.some((i) => i.status === "failed")) head.push({
|
|
36432
|
-
text: "FAIL",
|
|
36433
|
-
style: "fail"
|
|
36434
|
-
});
|
|
36435
|
-
else head.push({ text: " " }, {
|
|
36436
|
-
text: upToDate ? "up to date" : "",
|
|
36437
|
-
style: "dim"
|
|
36438
|
-
});
|
|
36439
|
-
break;
|
|
36440
|
-
case "running":
|
|
36441
|
-
head.push({
|
|
36442
|
-
text: "RUN ",
|
|
36443
|
-
style: "run"
|
|
36444
|
-
}, { text: " " }, time);
|
|
36445
|
-
break;
|
|
36446
|
-
case "ok":
|
|
36447
|
-
head.push({
|
|
36448
|
-
text: "OK ",
|
|
36449
|
-
style: "ok"
|
|
36450
|
-
}, { text: " " });
|
|
36451
|
-
head.push(upToDate ? {
|
|
36452
|
-
text: "up to date",
|
|
36453
|
-
style: "dim"
|
|
36454
|
-
} : time);
|
|
36455
|
-
break;
|
|
36456
|
-
case "skipped":
|
|
36457
|
-
head.push({
|
|
36458
|
-
text: "SKIP",
|
|
36459
|
-
style: "skip"
|
|
36460
|
-
}, { text: " " }, { text: m.detail ?? "" });
|
|
36461
|
-
break;
|
|
36462
|
-
case "failed":
|
|
36463
|
-
head.push({
|
|
36464
|
-
text: "FAIL",
|
|
36465
|
-
style: "fail"
|
|
36466
|
-
}, { text: " " });
|
|
36467
|
-
head.push(m.detail === void 0 ? time : { text: m.detail });
|
|
36468
|
-
}
|
|
36469
|
-
out.push(head);
|
|
36470
|
-
for (const i of items) {
|
|
36471
|
-
const n = note(i);
|
|
36472
|
-
const t = elapsed(i.started, i.ended, now);
|
|
36473
|
-
const idle = i.status === "idle" && !i.changes;
|
|
36474
|
-
out.push([
|
|
36475
|
-
{
|
|
36476
|
-
text: ` ${pad(i.name, itemW)} `,
|
|
36477
|
-
...idle ? { style: "dim" } : {}
|
|
36478
|
-
},
|
|
36479
|
-
{
|
|
36480
|
-
text: pad(value(i), valueW),
|
|
36481
|
-
...idle ? { style: "dim" } : {}
|
|
36482
|
-
},
|
|
36483
|
-
{ text: " " },
|
|
36484
|
-
{
|
|
36485
|
-
text: pad(n.text, noteW),
|
|
36486
|
-
...n.style === void 0 ? {} : { style: n.style }
|
|
36487
|
-
},
|
|
36488
|
-
{
|
|
36489
|
-
text: t === "" ? "" : ` ${t.padStart(6)}`,
|
|
36490
|
-
style: "dim"
|
|
36491
|
-
},
|
|
36492
|
-
...i.detail === void 0 ? [] : [{ text: ` ${i.detail}` }]
|
|
36493
|
-
]);
|
|
36494
|
-
if (i.output !== void 0) out.push(...outputRows(i.output, " ", running));
|
|
36495
|
-
}
|
|
36496
|
-
if (m.output !== void 0) out.push(...outputRows(m.output, " ", running));
|
|
36497
|
-
}
|
|
36498
|
-
out.push([]);
|
|
36499
|
-
if (state.ended !== void 0) {
|
|
36500
|
-
const t = tally(state);
|
|
36501
|
-
out.push([
|
|
36502
|
-
{
|
|
36503
|
-
text: `${state.dryRun ? "planned" : "ok"} ${t.ok}`,
|
|
36504
|
-
...t.failed === 0 ? { style: "ok" } : {}
|
|
36505
|
-
},
|
|
36506
|
-
{ text: ` skipped ${t.skipped} ` },
|
|
36507
|
-
{
|
|
36508
|
-
text: `failed ${t.failed}`,
|
|
36509
|
-
...t.failed > 0 ? { style: "fail" } : {}
|
|
36510
|
-
},
|
|
36511
|
-
...state.dryRun ? [] : [{
|
|
36512
|
-
text: ` in ${duration(state.ended - state.started)}`,
|
|
36513
|
-
style: "dim"
|
|
36514
|
-
}]
|
|
36515
|
-
]);
|
|
36516
|
-
}
|
|
36517
|
-
if (live) {
|
|
36518
|
-
const legend = state.ended === void 0 ? `f ${full ? "changes only" : "full list"} · ctrl-c stop` : state.dryRun ? void 0 : "full report: fizz report";
|
|
36519
|
-
if (legend !== void 0) out.push([{
|
|
36520
|
-
text: legend,
|
|
36521
|
-
style: "dim"
|
|
36522
|
-
}]);
|
|
36523
|
-
}
|
|
36524
|
-
while (out.length > 0 && out[out.length - 1]?.length === 0) out.pop();
|
|
36525
|
-
return out;
|
|
36526
|
-
};
|
|
36527
|
-
const noteRow = (n) => n.kind === "ask" ? [{
|
|
36528
|
-
text: "?",
|
|
36529
|
-
style: "ask"
|
|
36530
|
-
}, { text: ` ${n.text}` }] : n.kind === "answer" ? [{
|
|
36531
|
-
text: ` -> ${n.text}`,
|
|
36532
|
-
style: "dim"
|
|
36533
|
-
}] : [{ text: n.text }];
|
|
36534
|
-
const ANSI = {
|
|
36535
|
-
dim: "\x1B[2m",
|
|
36536
|
-
bold: "\x1B[1m",
|
|
36537
|
-
ok: "\x1B[32m",
|
|
36538
|
-
fail: "\x1B[1;31m",
|
|
36539
|
-
skip: "\x1B[33m",
|
|
36540
|
-
run: "\x1B[34m",
|
|
36541
|
-
ask: "\x1B[1;35m"
|
|
36542
|
-
};
|
|
36543
|
-
const styled = () => Boolean(process.stdout.isTTY) && process.env.NO_COLOR === void 0;
|
|
36544
|
-
const rowText = (row, color) => row.map((s) => color && s.style !== void 0 && s.text !== "" ? `${ANSI[s.style]}${s.text}\u001b[0m` : s.text).join("").trimEnd();
|
|
36545
|
-
const noteText = (n, color) => rowText(noteRow(n), color);
|
|
36546
|
-
const printRows = (list, color = styled()) => log(list.map((row) => rowText(row, color)).join("\n"));
|
|
36547
|
-
//#endregion
|
|
36548
|
-
//#region src/report.ts
|
|
36549
|
-
const reportPath = () => `${STATE_DIR.replace(/^~/, process.env.HOME ?? "~")}/last-ship.json`;
|
|
36550
|
-
const LedgerJson = fromJsonString(LedgerState);
|
|
36551
|
-
const encode = encodeEffect(LedgerJson);
|
|
36552
|
-
const decode = decodeEffect(LedgerJson);
|
|
36553
|
-
const saveReport = fn("saveReport")(function* (state) {
|
|
36554
|
-
const fs = yield* FileSystem;
|
|
36555
|
-
const path = reportPath();
|
|
36556
|
-
yield* fs.makeDirectory(path.slice(0, path.lastIndexOf("/")), { recursive: true });
|
|
36557
|
-
yield* fs.writeFileString(path, yield* encode(state));
|
|
36558
|
-
});
|
|
36559
|
-
const report = fn("report")(function* () {
|
|
36560
|
-
const fs = yield* FileSystem;
|
|
36561
|
-
const path = reportPath();
|
|
36562
|
-
if (!(yield* fs.exists(path))) return yield* log("no Ship from this Machine yet");
|
|
36563
|
-
const state = yield* decode(yield* fs.readFileString(path));
|
|
36564
|
-
const when = formatLocal(makeUnsafe(state.started), {
|
|
36565
|
-
dateStyle: "medium",
|
|
36566
|
-
timeStyle: "short"
|
|
36567
|
-
});
|
|
36568
|
-
yield* printRows([
|
|
36569
|
-
[{
|
|
36570
|
-
text: `last Ship ${when} from ${state.from}`,
|
|
36571
|
-
style: "dim"
|
|
36572
|
-
}],
|
|
36573
|
-
...state.notes.map(noteRow),
|
|
36574
|
-
...rows(state, {
|
|
36575
|
-
full: true,
|
|
36576
|
-
now: state.ended ?? state.started,
|
|
36577
|
-
live: false
|
|
36578
|
-
})
|
|
36579
|
-
]);
|
|
36580
|
-
});
|
|
36581
|
-
//#endregion
|
|
36582
36089
|
//#region ../../node_modules/.pnpm/react@19.3.0/node_modules/react/cjs/react.production.js
|
|
36583
36090
|
/**
|
|
36584
36091
|
* @license React
|
|
@@ -37981,8 +37488,8 @@ const exitAlternativeScreen = "\x1B[?1049l";
|
|
|
37981
37488
|
//#endregion
|
|
37982
37489
|
//#region ../../node_modules/.pnpm/is-in-ci@2.0.0/node_modules/is-in-ci/index.js
|
|
37983
37490
|
var import_react = /* @__PURE__ */ __toESM(require_react(), 1);
|
|
37984
|
-
const check = (key) => key in env && env[key] !== "0" && env[key] !== "false";
|
|
37985
|
-
const isInCi = check("CI") || check("CONTINUOUS_INTEGRATION");
|
|
37491
|
+
const check$1 = (key) => key in env && env[key] !== "0" && env[key] !== "false";
|
|
37492
|
+
const isInCi = check$1("CI") || check$1("CONTINUOUS_INTEGRATION");
|
|
37986
37493
|
//#endregion
|
|
37987
37494
|
//#region ../../node_modules/.pnpm/auto-bind@5.0.1/node_modules/auto-bind/index.js
|
|
37988
37495
|
const getAllProperties = (object) => {
|
|
@@ -64506,6 +64013,79 @@ const getInstance = (stdout, createInstance) => {
|
|
|
64506
64013
|
return instance;
|
|
64507
64014
|
};
|
|
64508
64015
|
//#endregion
|
|
64016
|
+
//#region ../../node_modules/.pnpm/ink@8.0.0_@types+react@19.3.0_react@19.3.0/node_modules/ink/build/render-to-string.js
|
|
64017
|
+
/**
|
|
64018
|
+
Render a React element to a string synchronously. Unlike `render()`, this function does not write to stdout, does not set up any terminal event listeners, and returns the rendered output as a string.
|
|
64019
|
+
|
|
64020
|
+
Useful for generating documentation, writing output to files, testing, or any scenario where you need the rendered output as a string without starting a persistent terminal application.
|
|
64021
|
+
|
|
64022
|
+
**Notes:**
|
|
64023
|
+
|
|
64024
|
+
- Terminal-specific hooks (`useInput`, `useStdin`, `useStdout`, `useStderr`, `useApp`, `useFocus`, `useFocusManager`) return default no-op values since there is no terminal session. They will not throw, but they will not function as in a live terminal.
|
|
64025
|
+
- `useEffect` callbacks will execute during rendering (due to synchronous rendering mode), but state updates they trigger will not affect the returned output, which reflects the initial render.
|
|
64026
|
+
- `useLayoutEffect` callbacks fire synchronously during commit, so state updates they trigger **will** be reflected in the output.
|
|
64027
|
+
- The `<Static>` component is supported — its output is prepended to the dynamic output.
|
|
64028
|
+
- If a component throws during rendering, the error is propagated to the caller after cleanup.
|
|
64029
|
+
|
|
64030
|
+
@example
|
|
64031
|
+
```
|
|
64032
|
+
import {renderToString, Text, Box} from 'ink';
|
|
64033
|
+
|
|
64034
|
+
const output = renderToString(
|
|
64035
|
+
<Box padding={1}>
|
|
64036
|
+
<Text color="green">Hello World</Text>
|
|
64037
|
+
</Box>,
|
|
64038
|
+
{columns: 40}
|
|
64039
|
+
);
|
|
64040
|
+
|
|
64041
|
+
console.log(output);
|
|
64042
|
+
```
|
|
64043
|
+
*/
|
|
64044
|
+
const renderToString = (node, options) => {
|
|
64045
|
+
const columns = options?.columns ?? 80;
|
|
64046
|
+
const rootNode = createNode("ink-root");
|
|
64047
|
+
let capturedStaticOutput = "";
|
|
64048
|
+
rootNode.onStaticChange = () => {
|
|
64049
|
+
capturedStaticOutput = "";
|
|
64050
|
+
};
|
|
64051
|
+
rootNode.onComputeLayout = () => {
|
|
64052
|
+
rootNode.yogaNode.setWidth(columns);
|
|
64053
|
+
rootNode.yogaNode.calculateLayout(void 0, void 0, Yoga.DIRECTION_LTR);
|
|
64054
|
+
};
|
|
64055
|
+
rootNode.onImmediateRender = () => {
|
|
64056
|
+
const { staticOutput } = renderer(rootNode, false);
|
|
64057
|
+
if (staticOutput !== "") capturedStaticOutput += staticOutput;
|
|
64058
|
+
};
|
|
64059
|
+
let uncaughtError;
|
|
64060
|
+
const container = reconciler_default.createContainer(rootNode, import_constants.LegacyRoot, null, false, null, "render-to-string", (error) => {
|
|
64061
|
+
uncaughtError ??= types.isNativeError(error) ? error : new Error(String(error));
|
|
64062
|
+
}, () => {}, () => {}, () => {}, null);
|
|
64063
|
+
let didTeardownSucceed = false;
|
|
64064
|
+
try {
|
|
64065
|
+
let output;
|
|
64066
|
+
let outputHeight;
|
|
64067
|
+
let staticOutput;
|
|
64068
|
+
try {
|
|
64069
|
+
reconciler_default.updateContainerSync((0, import_react.createElement)(RootNodeContext.Provider, { value: rootNode }, node), container, null, () => {});
|
|
64070
|
+
reconciler_default.flushSyncWork();
|
|
64071
|
+
({output, outputHeight} = renderer(rootNode, false));
|
|
64072
|
+
staticOutput = capturedStaticOutput;
|
|
64073
|
+
} finally {
|
|
64074
|
+
reconciler_default.updateContainerSync(null, container, null, () => {});
|
|
64075
|
+
reconciler_default.flushSyncWork();
|
|
64076
|
+
didTeardownSucceed = true;
|
|
64077
|
+
rootNode.yogaNode.free();
|
|
64078
|
+
}
|
|
64079
|
+
if (uncaughtError !== void 0) throw uncaughtError;
|
|
64080
|
+
if (outputHeight > 0) return staticOutput + output;
|
|
64081
|
+
return staticOutput.endsWith("\n") ? staticOutput.slice(0, -1) : staticOutput;
|
|
64082
|
+
} finally {
|
|
64083
|
+
if (!didTeardownSucceed && rootNode.yogaNode) try {
|
|
64084
|
+
rootNode.yogaNode.freeRecursive();
|
|
64085
|
+
} catch {}
|
|
64086
|
+
}
|
|
64087
|
+
};
|
|
64088
|
+
//#endregion
|
|
64509
64089
|
//#region ../../node_modules/.pnpm/ink@8.0.0_@types+react@19.3.0_react@19.3.0/node_modules/ink/build/hooks/use-stdin.js
|
|
64510
64090
|
/**
|
|
64511
64091
|
A React hook that returns the stdin stream and stdin-related utilities.
|
|
@@ -64591,6 +64171,332 @@ const useInput = (inputHandler, options = {}) => {
|
|
|
64591
64171
|
}, [options.isActive, internal_eventEmitter]);
|
|
64592
64172
|
};
|
|
64593
64173
|
//#endregion
|
|
64174
|
+
//#region ../../node_modules/.pnpm/ink@8.0.0_@types+react@19.3.0_react@19.3.0/node_modules/ink/build/hooks/use-stdout.js
|
|
64175
|
+
/**
|
|
64176
|
+
A React hook that returns the stdout stream where Ink renders your app.
|
|
64177
|
+
*/
|
|
64178
|
+
function useStdout() {
|
|
64179
|
+
return (0, import_react.use)(StdoutContext);
|
|
64180
|
+
}
|
|
64181
|
+
//#endregion
|
|
64182
|
+
//#region ../../node_modules/.pnpm/ink@8.0.0_@types+react@19.3.0_react@19.3.0/node_modules/ink/build/hooks/use-window-size.js
|
|
64183
|
+
/**
|
|
64184
|
+
A React hook that returns the current terminal window dimensions and re-renders the component whenever the terminal is resized.
|
|
64185
|
+
*/
|
|
64186
|
+
const useWindowSize = () => {
|
|
64187
|
+
const { stdout } = useStdout();
|
|
64188
|
+
const [size, setSize] = (0, import_react.useState)(() => getWindowSize(stdout));
|
|
64189
|
+
(0, import_react.useEffect)(() => {
|
|
64190
|
+
const onResize = () => {
|
|
64191
|
+
setSize(getWindowSize(stdout));
|
|
64192
|
+
};
|
|
64193
|
+
stdout.on("resize", onResize);
|
|
64194
|
+
onResize();
|
|
64195
|
+
return () => {
|
|
64196
|
+
stdout.off("resize", onResize);
|
|
64197
|
+
};
|
|
64198
|
+
}, [stdout]);
|
|
64199
|
+
return size;
|
|
64200
|
+
};
|
|
64201
|
+
//#endregion
|
|
64202
|
+
//#region src/ledger.ts
|
|
64203
|
+
const Note = Struct({
|
|
64204
|
+
kind: Literals$2([
|
|
64205
|
+
"ask",
|
|
64206
|
+
"answer",
|
|
64207
|
+
"info"
|
|
64208
|
+
]),
|
|
64209
|
+
text: String$5
|
|
64210
|
+
});
|
|
64211
|
+
const Item = Struct({
|
|
64212
|
+
name: String$5,
|
|
64213
|
+
kind: Literals$2([
|
|
64214
|
+
"version",
|
|
64215
|
+
"repo",
|
|
64216
|
+
"config"
|
|
64217
|
+
]),
|
|
64218
|
+
from: optionalKey(String$5),
|
|
64219
|
+
to: optionalKey(String$5),
|
|
64220
|
+
reason: optionalKey(String$5),
|
|
64221
|
+
changes: Boolean$4,
|
|
64222
|
+
status: Literals$2([
|
|
64223
|
+
"idle",
|
|
64224
|
+
"running",
|
|
64225
|
+
"ok",
|
|
64226
|
+
"failed",
|
|
64227
|
+
"skipped"
|
|
64228
|
+
]),
|
|
64229
|
+
doing: optionalKey(String$5),
|
|
64230
|
+
detail: optionalKey(String$5),
|
|
64231
|
+
output: optionalKey(ArraySchema(String$5)),
|
|
64232
|
+
steps: ArraySchema(String$5),
|
|
64233
|
+
started: optionalKey(Number$2),
|
|
64234
|
+
ended: optionalKey(Number$2)
|
|
64235
|
+
});
|
|
64236
|
+
const MachineEntry = Struct({
|
|
64237
|
+
name: String$5,
|
|
64238
|
+
status: Literals$2([
|
|
64239
|
+
"checking",
|
|
64240
|
+
"waiting",
|
|
64241
|
+
"planned",
|
|
64242
|
+
"running",
|
|
64243
|
+
"ok",
|
|
64244
|
+
"skipped",
|
|
64245
|
+
"failed"
|
|
64246
|
+
]),
|
|
64247
|
+
detail: optionalKey(String$5),
|
|
64248
|
+
output: optionalKey(ArraySchema(String$5)),
|
|
64249
|
+
items: ArraySchema(Item),
|
|
64250
|
+
started: optionalKey(Number$2),
|
|
64251
|
+
ended: optionalKey(Number$2)
|
|
64252
|
+
});
|
|
64253
|
+
const LedgerState = Struct({
|
|
64254
|
+
from: String$5,
|
|
64255
|
+
dryRun: Boolean$4,
|
|
64256
|
+
notes: ArraySchema(Note),
|
|
64257
|
+
machines: ArraySchema(MachineEntry),
|
|
64258
|
+
started: Number$2,
|
|
64259
|
+
ended: optionalKey(Number$2)
|
|
64260
|
+
});
|
|
64261
|
+
const short = (sha) => sha === void 0 ? void 0 : sha.slice(0, 7);
|
|
64262
|
+
const doingOf = (id) => id === "config" ? "copying" : isFetchStep(id) ? "fetching" : "installing";
|
|
64263
|
+
const itemsOf = (p, state) => {
|
|
64264
|
+
const items = /* @__PURE__ */ new Map();
|
|
64265
|
+
const key = (kind, name) => `${kind}:${name}`;
|
|
64266
|
+
const base = (name, from, kind = "version") => ({
|
|
64267
|
+
name,
|
|
64268
|
+
kind,
|
|
64269
|
+
...from === void 0 ? {} : { from },
|
|
64270
|
+
changes: false,
|
|
64271
|
+
status: "idle",
|
|
64272
|
+
steps: []
|
|
64273
|
+
});
|
|
64274
|
+
const set = (name, change, kind = "version") => items.set(key(kind, name), change(items.get(key(kind, name)) ?? base(name, void 0, kind)));
|
|
64275
|
+
const plans = (id, to, from, reason) => ({ from: checked, ...item }) => {
|
|
64276
|
+
const now = from === void 0 ? checked : from ?? void 0;
|
|
64277
|
+
return {
|
|
64278
|
+
...item,
|
|
64279
|
+
...now === void 0 ? {} : { from: now },
|
|
64280
|
+
to,
|
|
64281
|
+
...reason === void 0 ? {} : { reason },
|
|
64282
|
+
changes: true,
|
|
64283
|
+
steps: [...item.steps, id]
|
|
64284
|
+
};
|
|
64285
|
+
};
|
|
64286
|
+
const vp = state.vp?.version ?? void 0;
|
|
64287
|
+
items.set(key("version", "vp"), base("vp", vp));
|
|
64288
|
+
if (state.node != null) items.set(key("version", "node"), base("node", state.node));
|
|
64289
|
+
for (const g of state.globals) items.set(key("version", g.name), base(g.name, g.version));
|
|
64290
|
+
for (const step of p.vp) {
|
|
64291
|
+
const id = vpStepId(step);
|
|
64292
|
+
switch (step.kind) {
|
|
64293
|
+
case "bootstrap":
|
|
64294
|
+
set("vp", plans(id, "latest", null));
|
|
64295
|
+
break;
|
|
64296
|
+
case "vp":
|
|
64297
|
+
case "node":
|
|
64298
|
+
set(step.kind, plans(id, step.version, step.from));
|
|
64299
|
+
break;
|
|
64300
|
+
case "reinstall":
|
|
64301
|
+
set(step.name, plans(id, step.version, step.version, "reinstall"));
|
|
64302
|
+
break;
|
|
64303
|
+
case "globals": for (const pkg of step.packages) set(pkg.name, plans(id, pkg.version, pkg.from ?? null));
|
|
64304
|
+
}
|
|
64305
|
+
}
|
|
64306
|
+
for (const repo of p.repos) {
|
|
64307
|
+
const from = short(state.records[repo.name]?.commit);
|
|
64308
|
+
const to = short(repo.head);
|
|
64309
|
+
items.set(key("repo", repo.name), {
|
|
64310
|
+
name: repo.name,
|
|
64311
|
+
kind: "repo",
|
|
64312
|
+
...from === void 0 ? {} : { from },
|
|
64313
|
+
...to === void 0 ? {} : { to },
|
|
64314
|
+
...repo.decision.run ? { reason: repo.decision.reason } : {},
|
|
64315
|
+
changes: repo.decision.run,
|
|
64316
|
+
status: "idle",
|
|
64317
|
+
steps: [stepId(repo.name, "fetch"), stepId(repo.name, "install")]
|
|
64318
|
+
});
|
|
64319
|
+
}
|
|
64320
|
+
for (const s of p.skipped) set(s.what, (i) => ({
|
|
64321
|
+
...i,
|
|
64322
|
+
status: "skipped",
|
|
64323
|
+
detail: s.reason
|
|
64324
|
+
}));
|
|
64325
|
+
for (const f of p.failures) {
|
|
64326
|
+
const kind = f.section === "repos" ? "repo" : "version";
|
|
64327
|
+
set(f.what, (i) => ({
|
|
64328
|
+
...i,
|
|
64329
|
+
changes: true,
|
|
64330
|
+
status: "failed",
|
|
64331
|
+
detail: f.reason
|
|
64332
|
+
}), kind);
|
|
64333
|
+
}
|
|
64334
|
+
items.set(key("config", CONFIG_STEP), {
|
|
64335
|
+
name: CONFIG_STEP,
|
|
64336
|
+
kind: "config",
|
|
64337
|
+
changes: false,
|
|
64338
|
+
status: "idle",
|
|
64339
|
+
steps: [CONFIG_STEP]
|
|
64340
|
+
});
|
|
64341
|
+
return [...items.values()];
|
|
64342
|
+
};
|
|
64343
|
+
const outcomeOf = (m, dryRun) => m.status === "skipped" ? "skipped" : m.status === "failed" || m.items.some((i) => i.status === "failed") ? "failed" : m.status === "ok" || m.status === "planned" && dryRun ? "ok" : void 0;
|
|
64344
|
+
const anyFailed = (state) => state.machines.some((m) => outcomeOf(m, state.dryRun) === "failed");
|
|
64345
|
+
const makeLedger = fn("makeLedger")(function* (input) {
|
|
64346
|
+
const started = yield* currentTimeMillis;
|
|
64347
|
+
let snapshot = {
|
|
64348
|
+
state: {
|
|
64349
|
+
from: input.from,
|
|
64350
|
+
dryRun: input.dryRun,
|
|
64351
|
+
notes: [],
|
|
64352
|
+
machines: input.machines.map((name) => ({
|
|
64353
|
+
name,
|
|
64354
|
+
status: "checking",
|
|
64355
|
+
items: []
|
|
64356
|
+
})),
|
|
64357
|
+
started
|
|
64358
|
+
},
|
|
64359
|
+
now: started
|
|
64360
|
+
};
|
|
64361
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
64362
|
+
const update = (change) => gen(function* () {
|
|
64363
|
+
const now = yield* currentTimeMillis;
|
|
64364
|
+
snapshot = {
|
|
64365
|
+
state: change(snapshot.state, now),
|
|
64366
|
+
now
|
|
64367
|
+
};
|
|
64368
|
+
for (const listener of listeners) listener();
|
|
64369
|
+
});
|
|
64370
|
+
const machine = (name, change) => update((state, now) => ({
|
|
64371
|
+
...state,
|
|
64372
|
+
machines: state.machines.map((m) => m.name === name ? change(m, now) : m)
|
|
64373
|
+
}));
|
|
64374
|
+
const items = (name, id, change) => machine(name, (m, now) => ({
|
|
64375
|
+
...m,
|
|
64376
|
+
items: m.items.map((i) => i.steps.includes(id) ? change(i, now) : i)
|
|
64377
|
+
}));
|
|
64378
|
+
const store = {
|
|
64379
|
+
get: () => snapshot,
|
|
64380
|
+
subscribe: (listener) => {
|
|
64381
|
+
listeners.add(listener);
|
|
64382
|
+
return () => listeners.delete(listener);
|
|
64383
|
+
},
|
|
64384
|
+
tick: update((state) => state)
|
|
64385
|
+
};
|
|
64386
|
+
return {
|
|
64387
|
+
ledger: {
|
|
64388
|
+
note: fn("ledger.note")(function* (kind, text) {
|
|
64389
|
+
yield* update((state) => ({
|
|
64390
|
+
...state,
|
|
64391
|
+
notes: [...state.notes, {
|
|
64392
|
+
kind,
|
|
64393
|
+
text
|
|
64394
|
+
}]
|
|
64395
|
+
}));
|
|
64396
|
+
yield* input.print({
|
|
64397
|
+
kind,
|
|
64398
|
+
text
|
|
64399
|
+
});
|
|
64400
|
+
}),
|
|
64401
|
+
checked: (name) => machine(name, (m) => ({
|
|
64402
|
+
...m,
|
|
64403
|
+
status: "waiting"
|
|
64404
|
+
})),
|
|
64405
|
+
skip: (name, reason) => machine(name, (m) => ({
|
|
64406
|
+
...m,
|
|
64407
|
+
status: "skipped",
|
|
64408
|
+
detail: reason
|
|
64409
|
+
})),
|
|
64410
|
+
fail: (name, [reason = "failed", ...output]) => machine(name, (m, now) => ({
|
|
64411
|
+
...m,
|
|
64412
|
+
status: "failed",
|
|
64413
|
+
detail: reason,
|
|
64414
|
+
...output.length === 0 ? {} : { output },
|
|
64415
|
+
ended: now
|
|
64416
|
+
})),
|
|
64417
|
+
plan: (name, p, state) => machine(name, (m) => ({
|
|
64418
|
+
...m,
|
|
64419
|
+
status: "planned",
|
|
64420
|
+
items: itemsOf(p, state)
|
|
64421
|
+
})),
|
|
64422
|
+
start: (name) => machine(name, (m, now) => ({
|
|
64423
|
+
...m,
|
|
64424
|
+
status: "running",
|
|
64425
|
+
started: now
|
|
64426
|
+
})),
|
|
64427
|
+
stepStarted: (name, id) => items(name, id, (item, now) => item.status === "failed" ? item : {
|
|
64428
|
+
...item,
|
|
64429
|
+
changes: item.changes || !(id === "config" || isFetchStep(id)),
|
|
64430
|
+
status: "running",
|
|
64431
|
+
doing: doingOf(id),
|
|
64432
|
+
started: item.started ?? now
|
|
64433
|
+
}),
|
|
64434
|
+
stepDone: (name, id, result) => items(name, id, (item, now) => {
|
|
64435
|
+
if (item.status === "failed") return item;
|
|
64436
|
+
if (!result.ok) return {
|
|
64437
|
+
...item,
|
|
64438
|
+
changes: true,
|
|
64439
|
+
status: "failed",
|
|
64440
|
+
detail: result.detail,
|
|
64441
|
+
...result.output.length === 0 ? {} : { output: result.output },
|
|
64442
|
+
ended: now
|
|
64443
|
+
};
|
|
64444
|
+
const { doing: _, ...rest } = item;
|
|
64445
|
+
if (isFetchStep(id)) return item.changes ? item : {
|
|
64446
|
+
...rest,
|
|
64447
|
+
status: "idle",
|
|
64448
|
+
ended: now
|
|
64449
|
+
};
|
|
64450
|
+
return {
|
|
64451
|
+
...rest,
|
|
64452
|
+
status: "ok",
|
|
64453
|
+
ended: now
|
|
64454
|
+
};
|
|
64455
|
+
}),
|
|
64456
|
+
fetched: (name, repo, head) => machine(name, (m) => ({
|
|
64457
|
+
...m,
|
|
64458
|
+
items: m.items.map((i) => i.kind === "repo" && i.name === repo ? {
|
|
64459
|
+
...i,
|
|
64460
|
+
to: head.slice(0, 7),
|
|
64461
|
+
changes: i.changes || head.slice(0, 7) !== i.from
|
|
64462
|
+
} : i)
|
|
64463
|
+
})),
|
|
64464
|
+
finish: (name, outcome) => machine(name, (m, now) => ({
|
|
64465
|
+
...m,
|
|
64466
|
+
status: m.status === "failed" ? "failed" : outcome,
|
|
64467
|
+
ended: m.ended ?? now
|
|
64468
|
+
})),
|
|
64469
|
+
end: gen(function* () {
|
|
64470
|
+
yield* update((state, now) => ({
|
|
64471
|
+
...state,
|
|
64472
|
+
ended: now,
|
|
64473
|
+
machines: state.machines.map((m) => m.status === "running" || m.status === "checking" ? {
|
|
64474
|
+
...m,
|
|
64475
|
+
status: "failed",
|
|
64476
|
+
detail: "stopped",
|
|
64477
|
+
ended: now,
|
|
64478
|
+
items: m.items.map((i) => i.status === "running" ? {
|
|
64479
|
+
...i,
|
|
64480
|
+
status: "failed",
|
|
64481
|
+
detail: "stopped",
|
|
64482
|
+
ended: now
|
|
64483
|
+
} : i)
|
|
64484
|
+
} : m)
|
|
64485
|
+
}));
|
|
64486
|
+
return snapshot.state;
|
|
64487
|
+
})
|
|
64488
|
+
},
|
|
64489
|
+
store
|
|
64490
|
+
};
|
|
64491
|
+
});
|
|
64492
|
+
const duration = (ms) => {
|
|
64493
|
+
const s = ms / 1e3;
|
|
64494
|
+
if (s < 60) return `${s.toFixed(1)}s`;
|
|
64495
|
+
const m = Math.floor(s / 60);
|
|
64496
|
+
if (m < 60) return `${m}m ${String(Math.floor(s % 60)).padStart(2, "0")}s`;
|
|
64497
|
+
return `${Math.floor(m / 60)}h ${String(m % 60).padStart(2, "0")}m`;
|
|
64498
|
+
};
|
|
64499
|
+
//#endregion
|
|
64594
64500
|
//#region ../../node_modules/.pnpm/react@19.3.0/node_modules/react/cjs/react-jsx-runtime.production.js
|
|
64595
64501
|
/**
|
|
64596
64502
|
* @license React
|
|
@@ -64838,12 +64744,12 @@ var require_react_jsx_runtime_development = /* @__PURE__ */ __commonJSMin(((expo
|
|
|
64838
64744
|
})();
|
|
64839
64745
|
}));
|
|
64840
64746
|
//#endregion
|
|
64841
|
-
//#region src/
|
|
64747
|
+
//#region src/view.tsx
|
|
64842
64748
|
var import_jsx_runtime = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
64843
64749
|
if (process.env.NODE_ENV === "production") module.exports = require_react_jsx_runtime_production();
|
|
64844
64750
|
else module.exports = require_react_jsx_runtime_development();
|
|
64845
64751
|
})))();
|
|
64846
|
-
const
|
|
64752
|
+
const TONE = {
|
|
64847
64753
|
dim: { dimColor: true },
|
|
64848
64754
|
bold: { bold: true },
|
|
64849
64755
|
ok: { color: "green" },
|
|
@@ -64858,6 +64764,431 @@ const STYLE = {
|
|
|
64858
64764
|
bold: true
|
|
64859
64765
|
}
|
|
64860
64766
|
};
|
|
64767
|
+
const ModeContext = (0, import_react.createContext)({
|
|
64768
|
+
color: false,
|
|
64769
|
+
live: false
|
|
64770
|
+
});
|
|
64771
|
+
const T = (props) => {
|
|
64772
|
+
const { color } = (0, import_react.useContext)(ModeContext);
|
|
64773
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
64774
|
+
...color && props.tone !== void 0 ? TONE[props.tone] : {},
|
|
64775
|
+
children: props.children
|
|
64776
|
+
});
|
|
64777
|
+
};
|
|
64778
|
+
const Line = (props) => {
|
|
64779
|
+
const { live } = (0, import_react.useContext)(ModeContext);
|
|
64780
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
64781
|
+
paddingLeft: props.indent ?? 0,
|
|
64782
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
64783
|
+
wrap: live ? "truncate-end" : "wrap",
|
|
64784
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
64785
|
+
tone: props.tone,
|
|
64786
|
+
children: props.children
|
|
64787
|
+
})
|
|
64788
|
+
})
|
|
64789
|
+
});
|
|
64790
|
+
};
|
|
64791
|
+
const Cell = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
64792
|
+
width: props.width,
|
|
64793
|
+
flexShrink: 0,
|
|
64794
|
+
justifyContent: props.right ? "flex-end" : "flex-start",
|
|
64795
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
64796
|
+
wrap: "truncate-end",
|
|
64797
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
64798
|
+
tone: props.tone,
|
|
64799
|
+
children: props.children
|
|
64800
|
+
})
|
|
64801
|
+
})
|
|
64802
|
+
});
|
|
64803
|
+
const value = (i) => i.to === void 0 ? i.from ?? "" : i.from === void 0 ? `+ ${i.to}` : i.from === i.to ? i.to : `${i.from} → ${i.to}`;
|
|
64804
|
+
const statusOf = (i, dryRun) => {
|
|
64805
|
+
switch (i.status) {
|
|
64806
|
+
case "running": return {
|
|
64807
|
+
text: `▸ ${i.doing ?? "running"}`,
|
|
64808
|
+
tone: "run"
|
|
64809
|
+
};
|
|
64810
|
+
case "failed": return {
|
|
64811
|
+
text: "✗ failed",
|
|
64812
|
+
tone: "fail",
|
|
64813
|
+
detail: i.detail
|
|
64814
|
+
};
|
|
64815
|
+
case "skipped": return {
|
|
64816
|
+
text: "– skipped",
|
|
64817
|
+
tone: "skip",
|
|
64818
|
+
detail: i.detail
|
|
64819
|
+
};
|
|
64820
|
+
case "ok":
|
|
64821
|
+
if (i.kind === "config") return {
|
|
64822
|
+
text: "✓ copied",
|
|
64823
|
+
tone: "dim"
|
|
64824
|
+
};
|
|
64825
|
+
if (i.kind === "repo") return i.from !== void 0 && i.from === i.to ? {
|
|
64826
|
+
text: "✓ rebuilt",
|
|
64827
|
+
tone: "ok",
|
|
64828
|
+
detail: i.reason
|
|
64829
|
+
} : {
|
|
64830
|
+
text: "✓ installed",
|
|
64831
|
+
tone: "ok"
|
|
64832
|
+
};
|
|
64833
|
+
return {
|
|
64834
|
+
text: "✓ done",
|
|
64835
|
+
tone: "ok",
|
|
64836
|
+
detail: i.reason
|
|
64837
|
+
};
|
|
64838
|
+
case "idle":
|
|
64839
|
+
if (i.changes) return {
|
|
64840
|
+
text: dryRun ? "· planned" : "· queued",
|
|
64841
|
+
tone: "dim",
|
|
64842
|
+
detail: i.reason
|
|
64843
|
+
};
|
|
64844
|
+
return {
|
|
64845
|
+
text: "",
|
|
64846
|
+
detail: i.kind === "repo" ? "up to date" : void 0
|
|
64847
|
+
};
|
|
64848
|
+
}
|
|
64849
|
+
};
|
|
64850
|
+
const countsOf = (m) => {
|
|
64851
|
+
const changes = m.items.filter((i) => i.changes && i.status !== "skipped");
|
|
64852
|
+
return {
|
|
64853
|
+
changes: changes.length,
|
|
64854
|
+
done: changes.filter((i) => i.status === "ok").length,
|
|
64855
|
+
failed: m.items.filter((i) => i.status === "failed").length,
|
|
64856
|
+
skipped: m.items.filter((i) => i.status === "skipped").length
|
|
64857
|
+
};
|
|
64858
|
+
};
|
|
64859
|
+
const settled = (m, dryRun) => dryRun || m.status === "ok" || m.status === "failed";
|
|
64860
|
+
const upToDate = (m, dryRun) => {
|
|
64861
|
+
const c = countsOf(m);
|
|
64862
|
+
return c.changes === 0 && c.skipped === 0 && settled(m, dryRun);
|
|
64863
|
+
};
|
|
64864
|
+
const summaryOf = (m, dryRun) => {
|
|
64865
|
+
if (m.status === "checking" || m.status === "waiting") return [{
|
|
64866
|
+
text: m.status,
|
|
64867
|
+
tone: "dim"
|
|
64868
|
+
}];
|
|
64869
|
+
if (m.status === "skipped") return [{
|
|
64870
|
+
text: "– skipped",
|
|
64871
|
+
tone: "skip"
|
|
64872
|
+
}, { text: ` ${m.detail ?? ""}` }];
|
|
64873
|
+
if (m.status === "failed" && m.detail !== void 0) return [{
|
|
64874
|
+
text: "✗ failed",
|
|
64875
|
+
tone: "fail"
|
|
64876
|
+
}, { text: ` ${m.detail}` }];
|
|
64877
|
+
const { changes, done, failed, skipped } = countsOf(m);
|
|
64878
|
+
if (changes === 0 && skipped === 0) return [{
|
|
64879
|
+
text: "up to date",
|
|
64880
|
+
tone: settled(m, dryRun) ? "ok" : "dim"
|
|
64881
|
+
}];
|
|
64882
|
+
const finished = m.status === "ok" || m.status === "failed";
|
|
64883
|
+
const parts = [];
|
|
64884
|
+
if (dryRun && m.status === "planned") {
|
|
64885
|
+
if (changes > failed) parts.push({
|
|
64886
|
+
text: `${changes - failed} to change`,
|
|
64887
|
+
tone: "dim"
|
|
64888
|
+
});
|
|
64889
|
+
} else if (changes > 0 && (!finished || done > 0)) parts.push({
|
|
64890
|
+
text: `${done} of ${changes} done`,
|
|
64891
|
+
tone: done === changes ? "ok" : "dim"
|
|
64892
|
+
});
|
|
64893
|
+
if (failed > 0) parts.push({
|
|
64894
|
+
text: `${failed} failed`,
|
|
64895
|
+
tone: "fail"
|
|
64896
|
+
});
|
|
64897
|
+
if (skipped > 0) parts.push({
|
|
64898
|
+
text: `${skipped} skipped`,
|
|
64899
|
+
tone: "skip"
|
|
64900
|
+
});
|
|
64901
|
+
return joined(parts);
|
|
64902
|
+
};
|
|
64903
|
+
const joined = (parts) => parts.flatMap((part, n) => n === 0 ? [part] : [{
|
|
64904
|
+
text: ", ",
|
|
64905
|
+
tone: "dim"
|
|
64906
|
+
}, part]);
|
|
64907
|
+
const shipSummary = (state) => {
|
|
64908
|
+
const outcomes = state.machines.map((m) => {
|
|
64909
|
+
const outcome = outcomeOf(m, state.dryRun);
|
|
64910
|
+
if (outcome !== "ok") return outcome;
|
|
64911
|
+
const { changes, skipped } = countsOf(m);
|
|
64912
|
+
if (changes > 0) return state.dryRun ? "to change" : "updated";
|
|
64913
|
+
return skipped > 0 ? "skipped" : "up to date";
|
|
64914
|
+
});
|
|
64915
|
+
const counted = (outcome, tone) => {
|
|
64916
|
+
const n = outcomes.filter((o) => o === outcome).length;
|
|
64917
|
+
return n === 0 ? [] : [{
|
|
64918
|
+
text: `${n} ${outcome}`,
|
|
64919
|
+
tone
|
|
64920
|
+
}];
|
|
64921
|
+
};
|
|
64922
|
+
const parts = joined([
|
|
64923
|
+
...counted("updated", "ok"),
|
|
64924
|
+
...counted("to change", "dim"),
|
|
64925
|
+
...counted("up to date", "ok"),
|
|
64926
|
+
...counted("failed", "fail"),
|
|
64927
|
+
...counted("skipped", "skip")
|
|
64928
|
+
]);
|
|
64929
|
+
return parts.length === 0 ? [{
|
|
64930
|
+
text: "nothing changed",
|
|
64931
|
+
tone: "dim"
|
|
64932
|
+
}] : parts;
|
|
64933
|
+
};
|
|
64934
|
+
const elapsed = (started, ended, now) => started === void 0 ? "" : duration((ended ?? now) - started);
|
|
64935
|
+
const Parts = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Line, { children: props.parts.map((part, n) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
64936
|
+
tone: part.tone,
|
|
64937
|
+
children: part.text
|
|
64938
|
+
}, n)) });
|
|
64939
|
+
const GAP = 2;
|
|
64940
|
+
const INDENT = 2;
|
|
64941
|
+
const nameAt = (w) => w.status + GAP + INDENT;
|
|
64942
|
+
const LIVE_OUTPUT = 12;
|
|
64943
|
+
const outputLines = (key, output, indent, cut) => {
|
|
64944
|
+
const hidden = cut ? Math.max(0, output.length - LIVE_OUTPUT) : 0;
|
|
64945
|
+
return [...hidden > 0 ? [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Line, {
|
|
64946
|
+
indent,
|
|
64947
|
+
tone: "dim",
|
|
64948
|
+
children: [
|
|
64949
|
+
"(",
|
|
64950
|
+
hidden,
|
|
64951
|
+
" earlier lines)"
|
|
64952
|
+
]
|
|
64953
|
+
}, `${key}/earlier`)] : [], ...output.slice(hidden).map((line, n) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Line, {
|
|
64954
|
+
indent,
|
|
64955
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
64956
|
+
tone: "fail",
|
|
64957
|
+
children: "│ "
|
|
64958
|
+
}), line]
|
|
64959
|
+
}, `${key}/${n}`))];
|
|
64960
|
+
};
|
|
64961
|
+
const MachineLine = (props) => {
|
|
64962
|
+
const { m, w } = props;
|
|
64963
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
64964
|
+
columnGap: GAP,
|
|
64965
|
+
children: [
|
|
64966
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Cell, {
|
|
64967
|
+
width: nameAt(w) + w.item + GAP + w.value,
|
|
64968
|
+
tone: "bold",
|
|
64969
|
+
children: m.name
|
|
64970
|
+
}),
|
|
64971
|
+
w.time > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Cell, {
|
|
64972
|
+
width: w.time,
|
|
64973
|
+
right: true,
|
|
64974
|
+
tone: "dim",
|
|
64975
|
+
children: upToDate(m, props.dryRun) ? "" : elapsed(m.started, m.ended, props.now)
|
|
64976
|
+
}),
|
|
64977
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Parts, { parts: summaryOf(m, props.dryRun) })
|
|
64978
|
+
]
|
|
64979
|
+
});
|
|
64980
|
+
};
|
|
64981
|
+
const ItemLine = (props) => {
|
|
64982
|
+
const { i, w } = props;
|
|
64983
|
+
const status = statusOf(i, props.dryRun);
|
|
64984
|
+
const quiet = i.status === "idle" ? "dim" : void 0;
|
|
64985
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
64986
|
+
columnGap: GAP,
|
|
64987
|
+
children: [
|
|
64988
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Cell, {
|
|
64989
|
+
width: w.status,
|
|
64990
|
+
tone: status.tone,
|
|
64991
|
+
children: status.text
|
|
64992
|
+
}),
|
|
64993
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Cell, {
|
|
64994
|
+
width: INDENT + w.item,
|
|
64995
|
+
tone: quiet,
|
|
64996
|
+
children: `${" ".repeat(INDENT)}${i.name}`
|
|
64997
|
+
}),
|
|
64998
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Cell, {
|
|
64999
|
+
width: w.value,
|
|
65000
|
+
tone: quiet,
|
|
65001
|
+
children: value(i)
|
|
65002
|
+
}),
|
|
65003
|
+
w.time > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Cell, {
|
|
65004
|
+
width: w.time,
|
|
65005
|
+
right: true,
|
|
65006
|
+
tone: "dim",
|
|
65007
|
+
children: elapsed(i.started, i.ended, props.now)
|
|
65008
|
+
}),
|
|
65009
|
+
status.detail !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Line, {
|
|
65010
|
+
tone: quiet,
|
|
65011
|
+
children: status.detail
|
|
65012
|
+
})
|
|
65013
|
+
]
|
|
65014
|
+
});
|
|
65015
|
+
};
|
|
65016
|
+
const LedgerView = (props) => {
|
|
65017
|
+
const { state, full, now, height } = props;
|
|
65018
|
+
const { dryRun } = state;
|
|
65019
|
+
const { live } = (0, import_react.useContext)(ModeContext);
|
|
65020
|
+
const running = live && state.ended === void 0;
|
|
65021
|
+
const shownItems = (m) => m.items.filter((i) => full || i.changes || i.status === "failed" || i.status === "skipped");
|
|
65022
|
+
const visible = state.machines.flatMap(shownItems);
|
|
65023
|
+
const width = (texts) => Math.max(0, ...texts.map((t) => t.length));
|
|
65024
|
+
const item = width(visible.map((i) => i.name));
|
|
65025
|
+
const status = width(visible.map((i) => statusOf(i, dryRun).text));
|
|
65026
|
+
const w = {
|
|
65027
|
+
status,
|
|
65028
|
+
item,
|
|
65029
|
+
value: Math.max(width(visible.map(value)), width(state.machines.map((m) => m.name)) - (status + GAP + INDENT + item + GAP)),
|
|
65030
|
+
time: width([...visible.map((i) => elapsed(i.started, i.ended, now)), ...state.machines.map((m) => elapsed(m.started, m.ended, now))])
|
|
65031
|
+
};
|
|
65032
|
+
const blocks = state.machines.map((m) => ({
|
|
65033
|
+
m,
|
|
65034
|
+
head: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MachineLine, {
|
|
65035
|
+
m,
|
|
65036
|
+
w,
|
|
65037
|
+
dryRun,
|
|
65038
|
+
now
|
|
65039
|
+
}, m.name),
|
|
65040
|
+
body: [...m.output === void 0 ? [] : outputLines(m.name, m.output, nameAt(w), running), ...shownItems(m).flatMap((i) => [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ItemLine, {
|
|
65041
|
+
i,
|
|
65042
|
+
w,
|
|
65043
|
+
dryRun,
|
|
65044
|
+
now
|
|
65045
|
+
}, `${m.name}/${i.kind}:${i.name}`), ...i.output === void 0 ? [] : outputLines(`${m.name}/${i.kind}:${i.name}`, i.output, nameAt(w), running)])]
|
|
65046
|
+
}));
|
|
65047
|
+
const legend = state.ended === void 0 ? `f ${full ? "changes only" : "full list"} · ctrl-c stop` : void 0;
|
|
65048
|
+
const room = height === void 0 ? Infinity : height - (legend === void 0 ? 1 : 3);
|
|
65049
|
+
const count = (bs) => bs.reduce((n, b) => n + 1 + b.body.length, Math.max(0, bs.length - 1));
|
|
65050
|
+
const fits = count(blocks) <= room;
|
|
65051
|
+
let spare = room - count(blocks.map((b) => ({
|
|
65052
|
+
...b,
|
|
65053
|
+
body: []
|
|
65054
|
+
})));
|
|
65055
|
+
let cuts = 0;
|
|
65056
|
+
const fitted = [];
|
|
65057
|
+
for (const b of blocks) {
|
|
65058
|
+
const open = b.m.status === "running";
|
|
65059
|
+
if (fits) fitted.push(b);
|
|
65060
|
+
else if (open && b.body.length <= spare) {
|
|
65061
|
+
fitted.push(b);
|
|
65062
|
+
spare -= b.body.length;
|
|
65063
|
+
} else if (open && spare > 0) {
|
|
65064
|
+
const ellipsis = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Line, {
|
|
65065
|
+
indent: nameAt(w),
|
|
65066
|
+
tone: "dim",
|
|
65067
|
+
children: "…"
|
|
65068
|
+
}, `${b.m.name}/cut`);
|
|
65069
|
+
fitted.push({
|
|
65070
|
+
...b,
|
|
65071
|
+
body: [...b.body.slice(0, spare - 1), ellipsis]
|
|
65072
|
+
});
|
|
65073
|
+
spare = 0;
|
|
65074
|
+
cuts += 1;
|
|
65075
|
+
} else fitted.push({
|
|
65076
|
+
...b,
|
|
65077
|
+
body: []
|
|
65078
|
+
});
|
|
65079
|
+
}
|
|
65080
|
+
const lines = fitted.flatMap((b, n) => [
|
|
65081
|
+
...n === 0 ? [] : [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: " " }, `${b.m.name}/gap`)],
|
|
65082
|
+
b.head,
|
|
65083
|
+
...b.body
|
|
65084
|
+
]);
|
|
65085
|
+
const shown = lines.length <= room ? lines : lines.slice(0, Math.max(0, room - 1));
|
|
65086
|
+
const hidden = count(blocks) - (shown.length - cuts);
|
|
65087
|
+
if (room < 1) return null;
|
|
65088
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
65089
|
+
flexDirection: "column",
|
|
65090
|
+
marginTop: 1,
|
|
65091
|
+
children: [
|
|
65092
|
+
shown,
|
|
65093
|
+
shown.length < lines.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Line, {
|
|
65094
|
+
tone: "dim",
|
|
65095
|
+
children: "…"
|
|
65096
|
+
}),
|
|
65097
|
+
state.ended !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
65098
|
+
marginTop: 1,
|
|
65099
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Parts, { parts: [...shipSummary(state), ...dryRun ? [] : [{
|
|
65100
|
+
text: ` in ${duration(state.ended - state.started)}`,
|
|
65101
|
+
tone: "dim"
|
|
65102
|
+
}]] })
|
|
65103
|
+
}),
|
|
65104
|
+
state.ended !== void 0 && props.hint !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Line, {
|
|
65105
|
+
tone: "dim",
|
|
65106
|
+
children: props.hint
|
|
65107
|
+
}),
|
|
65108
|
+
legend !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
65109
|
+
marginTop: 1,
|
|
65110
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Line, {
|
|
65111
|
+
tone: "dim",
|
|
65112
|
+
children: [legend, hidden > 0 && ` · ${hidden} lines hidden to fit the window`]
|
|
65113
|
+
})
|
|
65114
|
+
})
|
|
65115
|
+
]
|
|
65116
|
+
});
|
|
65117
|
+
};
|
|
65118
|
+
const NoteLine = (props) => {
|
|
65119
|
+
const { kind, text } = props.note;
|
|
65120
|
+
return kind === "ask" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
|
|
65121
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
65122
|
+
tone: "ask",
|
|
65123
|
+
children: "?"
|
|
65124
|
+
}),
|
|
65125
|
+
" ",
|
|
65126
|
+
text
|
|
65127
|
+
] }) : kind === "answer" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
65128
|
+
tone: "dim",
|
|
65129
|
+
children: ` → ${text}`
|
|
65130
|
+
}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: text });
|
|
65131
|
+
};
|
|
65132
|
+
const Styled = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ModeContext.Provider, {
|
|
65133
|
+
value: {
|
|
65134
|
+
color: props.color,
|
|
65135
|
+
live: props.live
|
|
65136
|
+
},
|
|
65137
|
+
children: props.children
|
|
65138
|
+
});
|
|
65139
|
+
const colorful = () => Boolean(process.stdout.isTTY) && process.env.NO_COLOR === void 0;
|
|
65140
|
+
const columns = () => process.stdout.columns ?? 1e4;
|
|
65141
|
+
const toText = (node, color = colorful()) => renderToString(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Styled, {
|
|
65142
|
+
color,
|
|
65143
|
+
live: false,
|
|
65144
|
+
children: node
|
|
65145
|
+
}), { columns: columns() });
|
|
65146
|
+
const ledgerText = (state, options, color) => toText(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LedgerView, {
|
|
65147
|
+
state,
|
|
65148
|
+
...options
|
|
65149
|
+
}), color);
|
|
65150
|
+
const noteText = (note, color) => toText(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(NoteLine, { note }), color);
|
|
65151
|
+
//#endregion
|
|
65152
|
+
//#region src/report.tsx
|
|
65153
|
+
const reportPath = () => `${STATE_DIR.replace(/^~/, process.env.HOME ?? "~")}/last-ship.json`;
|
|
65154
|
+
const LedgerJson = fromJsonString(LedgerState);
|
|
65155
|
+
const encode = encodeEffect(LedgerJson);
|
|
65156
|
+
const decode = decodeEffect(LedgerJson);
|
|
65157
|
+
const saveReport = fn("saveReport")(function* (state) {
|
|
65158
|
+
const fs = yield* FileSystem;
|
|
65159
|
+
const path = reportPath();
|
|
65160
|
+
yield* fs.makeDirectory(path.slice(0, path.lastIndexOf("/")), { recursive: true });
|
|
65161
|
+
yield* fs.writeFileString(path, yield* encode(state));
|
|
65162
|
+
});
|
|
65163
|
+
const report = fn("report")(function* () {
|
|
65164
|
+
const fs = yield* FileSystem;
|
|
65165
|
+
const path = reportPath();
|
|
65166
|
+
if (!(yield* fs.exists(path))) return yield* log("no Ship from this Machine yet");
|
|
65167
|
+
const decoded = yield* decode(yield* fs.readFileString(path)).pipe(option);
|
|
65168
|
+
if (isNone(decoded)) return yield* log(`the last report (${path}) is from another version of fleetfizz; the next Ship replaces it`);
|
|
65169
|
+
const state = decoded.value;
|
|
65170
|
+
const when = formatLocal(makeUnsafe(state.started), {
|
|
65171
|
+
dateStyle: "medium",
|
|
65172
|
+
timeStyle: "short"
|
|
65173
|
+
});
|
|
65174
|
+
yield* log(toText(/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
65175
|
+
flexDirection: "column",
|
|
65176
|
+
children: [
|
|
65177
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
65178
|
+
tone: "dim",
|
|
65179
|
+
children: `last Ship ${when} from ${state.from}`
|
|
65180
|
+
}),
|
|
65181
|
+
state.notes.map((note, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NoteLine, { note }, i)),
|
|
65182
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LedgerView, {
|
|
65183
|
+
state,
|
|
65184
|
+
full: true,
|
|
65185
|
+
now: state.ended ?? state.started
|
|
65186
|
+
})
|
|
65187
|
+
]
|
|
65188
|
+
})));
|
|
65189
|
+
});
|
|
65190
|
+
//#endregion
|
|
65191
|
+
//#region src/live.tsx
|
|
64861
65192
|
const cell = (initial) => {
|
|
64862
65193
|
let value = initial;
|
|
64863
65194
|
const listeners = /* @__PURE__ */ new Set();
|
|
@@ -64878,6 +65209,7 @@ const LiveLedger = (props) => {
|
|
|
64878
65209
|
const asking = (0, import_react.useSyncExternalStore)(props.asking.subscribe, props.asking.get);
|
|
64879
65210
|
const [full, setFull] = (0, import_react.useState)(false);
|
|
64880
65211
|
const [cursor, setCursor] = (0, import_react.useState)(0);
|
|
65212
|
+
const { rows } = useWindowSize();
|
|
64881
65213
|
const { isRawModeSupported } = useStdin();
|
|
64882
65214
|
useInput((input, key) => {
|
|
64883
65215
|
if (key.ctrl && input === "c") process.kill(process.pid, "SIGINT");
|
|
@@ -64889,42 +65221,36 @@ const LiveLedger = (props) => {
|
|
|
64889
65221
|
asking.answer(cursor);
|
|
64890
65222
|
} else if (input === "f") setFull((f) => !f);
|
|
64891
65223
|
}, { isActive: isRawModeSupported });
|
|
64892
|
-
const
|
|
64893
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.
|
|
64894
|
-
|
|
64895
|
-
|
|
64896
|
-
|
|
64897
|
-
|
|
64898
|
-
|
|
64899
|
-
|
|
64900
|
-
|
|
64901
|
-
|
|
64902
|
-
|
|
65224
|
+
const choices = asking?.titles ?? [];
|
|
65225
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Styled, {
|
|
65226
|
+
color: props.color,
|
|
65227
|
+
live: true,
|
|
65228
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
65229
|
+
flexDirection: "column",
|
|
65230
|
+
children: [choices.map((title, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Line, {
|
|
65231
|
+
tone: i === cursor ? void 0 : "dim",
|
|
65232
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
65233
|
+
tone: i === cursor ? "ask" : "dim",
|
|
65234
|
+
children: i === cursor ? " › " : " "
|
|
65235
|
+
}), title]
|
|
65236
|
+
}, i)), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LedgerView, {
|
|
65237
|
+
state,
|
|
65238
|
+
full,
|
|
65239
|
+
now,
|
|
65240
|
+
height: rows - 1 - choices.length
|
|
64903
65241
|
})]
|
|
64904
|
-
}
|
|
64905
|
-
full,
|
|
64906
|
-
now,
|
|
64907
|
-
live: true
|
|
64908
|
-
}).map((row, r) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
64909
|
-
wrap: "truncate-end",
|
|
64910
|
-
children: row.length === 0 ? " " : row.map((segment, s) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
64911
|
-
...style(segment.style),
|
|
64912
|
-
children: segment.text
|
|
64913
|
-
}, s))
|
|
64914
|
-
}, r))]
|
|
65242
|
+
})
|
|
64915
65243
|
});
|
|
64916
65244
|
};
|
|
64917
65245
|
const startLive = fn("startLive")(function* (store, color) {
|
|
64918
65246
|
const asking = cell(void 0);
|
|
64919
|
-
const
|
|
65247
|
+
const instance = render(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LiveLedger, {
|
|
64920
65248
|
store,
|
|
64921
65249
|
asking,
|
|
64922
65250
|
color
|
|
64923
|
-
});
|
|
64924
|
-
const instance = render(view, { exitOnCtrlC: false });
|
|
65251
|
+
}), { exitOnCtrlC: false });
|
|
64925
65252
|
yield* addFinalizer(() => promise(async () => {
|
|
64926
|
-
instance.
|
|
64927
|
-
await instance.waitUntilRenderFlush();
|
|
65253
|
+
instance.clear();
|
|
64928
65254
|
instance.unmount();
|
|
64929
65255
|
await instance.waitUntilExit();
|
|
64930
65256
|
}));
|
|
@@ -64962,25 +65288,25 @@ const runOn = fn("runOn")(function* (target, script, secrets, onLine, ledger) {
|
|
|
64962
65288
|
}
|
|
64963
65289
|
return run;
|
|
64964
65290
|
});
|
|
64965
|
-
const
|
|
64966
|
-
const
|
|
65291
|
+
const CHECK_TIMEOUT = "2 minutes";
|
|
65292
|
+
const check = fn("check")(function* (target, script, ledger) {
|
|
64967
65293
|
const facts = [];
|
|
64968
65294
|
const output = [];
|
|
64969
65295
|
const run = yield* runOn(target, script, [], (line) => sync(() => {
|
|
64970
65296
|
if (line.kind === "state") facts.push(line.fact);
|
|
64971
65297
|
else if (line.kind === "text" && line.text.trim() !== "") output.push(line.text.trimEnd());
|
|
64972
65298
|
}), ledger).pipe(timeoutOrElse({
|
|
64973
|
-
duration:
|
|
64974
|
-
orElse: () => ledger.fail(target.name, [`
|
|
65299
|
+
duration: CHECK_TIMEOUT,
|
|
65300
|
+
orElse: () => ledger.fail(target.name, [`checking took more than ${CHECK_TIMEOUT}; something on that Machine is blocking`, ...output]).pipe(as({ kind: "failed" }))
|
|
64975
65301
|
}));
|
|
64976
65302
|
if (run.kind !== "ran") return { kind: run.kind };
|
|
64977
65303
|
const state = assembleState(facts);
|
|
64978
65304
|
if (!state.complete) {
|
|
64979
|
-
yield* ledger.fail(target.name, ["
|
|
65305
|
+
yield* ledger.fail(target.name, ["checking did not finish", ...output]);
|
|
64980
65306
|
return { kind: "failed" };
|
|
64981
65307
|
}
|
|
64982
65308
|
return {
|
|
64983
|
-
kind: "
|
|
65309
|
+
kind: "checked",
|
|
64984
65310
|
state
|
|
64985
65311
|
};
|
|
64986
65312
|
});
|
|
@@ -65064,7 +65390,7 @@ const askQuestions = fn("askQuestions")(function* (questions, flags, ledger, ask
|
|
|
65064
65390
|
value: bump
|
|
65065
65391
|
},
|
|
65066
65392
|
{
|
|
65067
|
-
title: "skip on these Machines (leave as is,
|
|
65393
|
+
title: "skip on these Machines (leave as is, shown as skipped)",
|
|
65068
65394
|
value: { kind: "skip" }
|
|
65069
65395
|
},
|
|
65070
65396
|
{
|
|
@@ -65102,19 +65428,18 @@ const packagesToResolve = (config) => {
|
|
|
65102
65428
|
];
|
|
65103
65429
|
};
|
|
65104
65430
|
const rebuildOf = (flags) => flags.rebuild.length === 0 ? "none" : flags.rebuild.includes("all") ? "all" : flags.rebuild;
|
|
65105
|
-
const staleConfigs = fn("staleConfigs")(function* (
|
|
65106
|
-
const
|
|
65107
|
-
for (const { machine,
|
|
65108
|
-
if (
|
|
65431
|
+
const staleConfigs = fn("staleConfigs")(function* (config, own, checked, ledger) {
|
|
65432
|
+
const copies = [];
|
|
65433
|
+
for (const { machine, state } of checked) {
|
|
65434
|
+
if (state.config === void 0) continue;
|
|
65109
65435
|
const parsed = yield* result(parseConfig(machine.name, state.config));
|
|
65110
|
-
if (parsed._tag === "Failure") {
|
|
65111
|
-
|
|
65112
|
-
|
|
65113
|
-
|
|
65114
|
-
|
|
65115
|
-
if (isNewer(remote.shippedTo?.[machine.name], local.shippedTo?.[machine.name])) stale.push(`${machine.name} (shipped ${remote.shippedAt} from ${remote.shippedFrom ?? "unknown"})`);
|
|
65436
|
+
if (parsed._tag === "Failure") yield* ledger.note("info", `the config on ${machine.name} does not parse; it will be overwritten`);
|
|
65437
|
+
else copies.push({
|
|
65438
|
+
machine: machine.name,
|
|
65439
|
+
config: parsed.success
|
|
65440
|
+
});
|
|
65116
65441
|
}
|
|
65117
|
-
return
|
|
65442
|
+
return staleCopies(config, copies, own).map((c) => `${c.machine} (shipped ${c.config.shippedAt} from ${c.config.shippedFrom ?? "unknown"})`);
|
|
65118
65443
|
});
|
|
65119
65444
|
const schemaJson = encodeSync(fromJsonString(Unknown, { space: 2 }));
|
|
65120
65445
|
const ship = fn("ship")(function* (flags) {
|
|
@@ -65125,6 +65450,7 @@ const ship = fn("ship")(function* (flags) {
|
|
|
65125
65450
|
const unknown = unknownMachines(config, tailnet, flags.only);
|
|
65126
65451
|
if (unknown.length > 0) return yield* fail$1(new ShipError(`unknown Machine: ${unknown.join(", ")} (the tailnet has: ${tailnet.fleet.map((m) => m.name).join(", ")})`));
|
|
65127
65452
|
const fleet = new Set(Object.keys(config.machines));
|
|
65453
|
+
const own = flags.config === void 0 ? tailnet.self.name : void 0;
|
|
65128
65454
|
if (!fleet.has(tailnet.self.name)) return yield* fail$1(new ShipError(`this Machine (${tailnet.self.name}) is not in machines in ${path}`));
|
|
65129
65455
|
const outsideFleet = flags.only.filter((name) => !fleet.has(name));
|
|
65130
65456
|
if (outsideFleet.length > 0) return yield* fail$1(new ShipError(`not in machines in ${path}: ${outsideFleet.join(", ")}`));
|
|
@@ -65132,30 +65458,36 @@ const ship = fn("ship")(function* (flags) {
|
|
|
65132
65458
|
const targetsList = tailnet.fleet.filter((m) => fleet.has(m.name) && (flags.only.length === 0 || flags.only.includes(m.name))).toSorted((a, b) => order.indexOf(a.name) - order.indexOf(b.name));
|
|
65133
65459
|
const count = `${targetsList.length} machine${targetsList.length === 1 ? "" : "s"}`;
|
|
65134
65460
|
yield* log(`${flags.dryRun ? "dry run, " : ""}${count} from ${tailnet.self.name}`);
|
|
65461
|
+
const color = colorful();
|
|
65135
65462
|
const { ledger, store } = yield* makeLedger({
|
|
65136
65463
|
from: tailnet.self.name,
|
|
65137
65464
|
dryRun: flags.dryRun,
|
|
65138
|
-
machines: targetsList.map((m) => m.name)
|
|
65465
|
+
machines: targetsList.map((m) => m.name),
|
|
65466
|
+
print: (note) => log(noteText(note, color))
|
|
65139
65467
|
});
|
|
65140
|
-
const
|
|
65468
|
+
const redraw = canRedraw();
|
|
65141
65469
|
let changing = false;
|
|
65142
65470
|
const finish = fn("finish")(function* () {
|
|
65143
65471
|
const state = yield* ledger.end;
|
|
65144
|
-
|
|
65145
|
-
|
|
65146
|
-
full:
|
|
65472
|
+
const saved = changing && (yield* saveReport(state).pipe(as(true), catch_((e) => error(`could not save the report for fizz report: ${e.message}`).pipe(as(false)))));
|
|
65473
|
+
yield* log(ledgerText(state, redraw ? {
|
|
65474
|
+
full: false,
|
|
65147
65475
|
now: state.ended ?? 0,
|
|
65148
|
-
|
|
65149
|
-
}
|
|
65150
|
-
|
|
65476
|
+
...saved ? { hint: "full report: fizz report" } : {}
|
|
65477
|
+
} : {
|
|
65478
|
+
full: true,
|
|
65479
|
+
now: state.ended ?? 0
|
|
65480
|
+
}, color));
|
|
65481
|
+
if (anyFailed(state)) process.exitCode = 1;
|
|
65151
65482
|
});
|
|
65152
65483
|
yield* addFinalizer(() => finish());
|
|
65484
|
+
const live = redraw ? yield* startLive(store, color) : noLive;
|
|
65153
65485
|
const cloneRootOf = (machine) => config.machines[machine.name]?.clones ?? "~/.local/share/fleetfizz/clones";
|
|
65154
|
-
const
|
|
65486
|
+
const checkRepos = (config.repos ?? []).map((r) => ({
|
|
65155
65487
|
name: repoName(r),
|
|
65156
65488
|
outputs: r.outputs ?? []
|
|
65157
65489
|
}));
|
|
65158
|
-
const
|
|
65490
|
+
const checked = yield* forEach$1(targetsList, fn("checkOne")(function* (machine) {
|
|
65159
65491
|
const local = machine.name === tailnet.self.name;
|
|
65160
65492
|
if (!machine.online) {
|
|
65161
65493
|
yield* ledger.skip(machine.name, "offline");
|
|
@@ -65165,23 +65497,23 @@ const ship = fn("ship")(function* (flags) {
|
|
|
65165
65497
|
yield* ledger.skip(machine.name, "no Tailscale SSH server");
|
|
65166
65498
|
return;
|
|
65167
65499
|
}
|
|
65168
|
-
const script =
|
|
65500
|
+
const script = checkScript({
|
|
65169
65501
|
cloneRoot: cloneRootOf(machine),
|
|
65170
|
-
repos:
|
|
65502
|
+
repos: checkRepos
|
|
65171
65503
|
});
|
|
65172
|
-
const result = yield*
|
|
65504
|
+
const result = yield* check({
|
|
65173
65505
|
name: machine.name,
|
|
65174
65506
|
local
|
|
65175
65507
|
}, script, ledger);
|
|
65176
|
-
if (result.kind !== "
|
|
65177
|
-
yield* ledger.
|
|
65508
|
+
if (result.kind !== "checked") return void 0;
|
|
65509
|
+
yield* ledger.checked(machine.name);
|
|
65178
65510
|
return {
|
|
65179
65511
|
machine,
|
|
65180
65512
|
local,
|
|
65181
65513
|
state: result.state
|
|
65182
65514
|
};
|
|
65183
65515
|
}), { concurrency: "unbounded" }).pipe(map$5((list) => list.filter((g) => g !== void 0)));
|
|
65184
|
-
const stale = yield* staleConfigs(config,
|
|
65516
|
+
const stale = yield* staleConfigs(config, own, checked, ledger);
|
|
65185
65517
|
if (stale.length > 0) {
|
|
65186
65518
|
const what = `newer config on ${stale.join(", ")}`;
|
|
65187
65519
|
if (flags.dryRun) yield* ledger.note("info", `${what}; a Ship would ask before overwriting it`);
|
|
@@ -65201,7 +65533,7 @@ const ship = fn("ship")(function* (flags) {
|
|
|
65201
65533
|
}
|
|
65202
65534
|
}
|
|
65203
65535
|
const versions = yield* resolveVersions(packagesToResolve(config));
|
|
65204
|
-
const planAll = (cfg, answers) =>
|
|
65536
|
+
const planAll = (cfg, answers) => checked.map((g) => ({
|
|
65205
65537
|
...g,
|
|
65206
65538
|
plan: plan({
|
|
65207
65539
|
config: cfg,
|
|
@@ -65242,7 +65574,8 @@ const ship = fn("ship")(function* (flags) {
|
|
|
65242
65574
|
const stampWith = (reached) => stampText(original, {
|
|
65243
65575
|
shippedAt,
|
|
65244
65576
|
shippedFrom: tailnet.self.name,
|
|
65245
|
-
reached
|
|
65577
|
+
reached,
|
|
65578
|
+
own
|
|
65246
65579
|
});
|
|
65247
65580
|
const stamped = stampWith(planned.map((g) => g.machine.name));
|
|
65248
65581
|
yield* fs.writeFileString(path, stamped);
|
|
@@ -65271,7 +65604,7 @@ const ship = fn("ship")(function* (flags) {
|
|
|
65271
65604
|
//#endregion
|
|
65272
65605
|
//#region src/bin.ts
|
|
65273
65606
|
const shipCommand = make("ship", {
|
|
65274
|
-
dryRun: Boolean$1("dry-run").pipe(withDefault(false), withDescription$1("
|
|
65607
|
+
dryRun: Boolean$1("dry-run").pipe(withDefault(false), withDescription$1("Check and plan only. Print every Machine's plan and change nothing.")),
|
|
65275
65608
|
only: String$1("only").pipe(atLeast(0), withDescription$1("Ship only to these Machines (repeat the flag).")),
|
|
65276
65609
|
rebuild: String$1("rebuild").pipe(atLeast(0), withDescription$1("Run a repo's Install Command even when its record matches; `all` for every repo.")),
|
|
65277
65610
|
bump: Boolean$1("bump").pipe(withDefault(false), withDescription$1("Answer every above-range question by bumping the config.")),
|