fleetfizz 0.5.0 → 0.5.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/fleetfizz.mjs +914 -583
- 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.1";
|
|
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
|
/**
|
|
@@ -36039,546 +36087,6 @@ const resolveVersions = fn("resolveVersions")(function* (packages) {
|
|
|
36039
36087
|
return Object.fromEntries(entries);
|
|
36040
36088
|
});
|
|
36041
36089
|
//#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
36090
|
//#region ../../node_modules/.pnpm/react@19.3.0/node_modules/react/cjs/react.production.js
|
|
36583
36091
|
/**
|
|
36584
36092
|
* @license React
|
|
@@ -64506,6 +64014,79 @@ const getInstance = (stdout, createInstance) => {
|
|
|
64506
64014
|
return instance;
|
|
64507
64015
|
};
|
|
64508
64016
|
//#endregion
|
|
64017
|
+
//#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
|
|
64018
|
+
/**
|
|
64019
|
+
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.
|
|
64020
|
+
|
|
64021
|
+
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.
|
|
64022
|
+
|
|
64023
|
+
**Notes:**
|
|
64024
|
+
|
|
64025
|
+
- 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.
|
|
64026
|
+
- `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.
|
|
64027
|
+
- `useLayoutEffect` callbacks fire synchronously during commit, so state updates they trigger **will** be reflected in the output.
|
|
64028
|
+
- The `<Static>` component is supported — its output is prepended to the dynamic output.
|
|
64029
|
+
- If a component throws during rendering, the error is propagated to the caller after cleanup.
|
|
64030
|
+
|
|
64031
|
+
@example
|
|
64032
|
+
```
|
|
64033
|
+
import {renderToString, Text, Box} from 'ink';
|
|
64034
|
+
|
|
64035
|
+
const output = renderToString(
|
|
64036
|
+
<Box padding={1}>
|
|
64037
|
+
<Text color="green">Hello World</Text>
|
|
64038
|
+
</Box>,
|
|
64039
|
+
{columns: 40}
|
|
64040
|
+
);
|
|
64041
|
+
|
|
64042
|
+
console.log(output);
|
|
64043
|
+
```
|
|
64044
|
+
*/
|
|
64045
|
+
const renderToString = (node, options) => {
|
|
64046
|
+
const columns = options?.columns ?? 80;
|
|
64047
|
+
const rootNode = createNode("ink-root");
|
|
64048
|
+
let capturedStaticOutput = "";
|
|
64049
|
+
rootNode.onStaticChange = () => {
|
|
64050
|
+
capturedStaticOutput = "";
|
|
64051
|
+
};
|
|
64052
|
+
rootNode.onComputeLayout = () => {
|
|
64053
|
+
rootNode.yogaNode.setWidth(columns);
|
|
64054
|
+
rootNode.yogaNode.calculateLayout(void 0, void 0, Yoga.DIRECTION_LTR);
|
|
64055
|
+
};
|
|
64056
|
+
rootNode.onImmediateRender = () => {
|
|
64057
|
+
const { staticOutput } = renderer(rootNode, false);
|
|
64058
|
+
if (staticOutput !== "") capturedStaticOutput += staticOutput;
|
|
64059
|
+
};
|
|
64060
|
+
let uncaughtError;
|
|
64061
|
+
const container = reconciler_default.createContainer(rootNode, import_constants.LegacyRoot, null, false, null, "render-to-string", (error) => {
|
|
64062
|
+
uncaughtError ??= types.isNativeError(error) ? error : new Error(String(error));
|
|
64063
|
+
}, () => {}, () => {}, () => {}, null);
|
|
64064
|
+
let didTeardownSucceed = false;
|
|
64065
|
+
try {
|
|
64066
|
+
let output;
|
|
64067
|
+
let outputHeight;
|
|
64068
|
+
let staticOutput;
|
|
64069
|
+
try {
|
|
64070
|
+
reconciler_default.updateContainerSync((0, import_react.createElement)(RootNodeContext.Provider, { value: rootNode }, node), container, null, () => {});
|
|
64071
|
+
reconciler_default.flushSyncWork();
|
|
64072
|
+
({output, outputHeight} = renderer(rootNode, false));
|
|
64073
|
+
staticOutput = capturedStaticOutput;
|
|
64074
|
+
} finally {
|
|
64075
|
+
reconciler_default.updateContainerSync(null, container, null, () => {});
|
|
64076
|
+
reconciler_default.flushSyncWork();
|
|
64077
|
+
didTeardownSucceed = true;
|
|
64078
|
+
rootNode.yogaNode.free();
|
|
64079
|
+
}
|
|
64080
|
+
if (uncaughtError !== void 0) throw uncaughtError;
|
|
64081
|
+
if (outputHeight > 0) return staticOutput + output;
|
|
64082
|
+
return staticOutput.endsWith("\n") ? staticOutput.slice(0, -1) : staticOutput;
|
|
64083
|
+
} finally {
|
|
64084
|
+
if (!didTeardownSucceed && rootNode.yogaNode) try {
|
|
64085
|
+
rootNode.yogaNode.freeRecursive();
|
|
64086
|
+
} catch {}
|
|
64087
|
+
}
|
|
64088
|
+
};
|
|
64089
|
+
//#endregion
|
|
64509
64090
|
//#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
64091
|
/**
|
|
64511
64092
|
A React hook that returns the stdin stream and stdin-related utilities.
|
|
@@ -64591,6 +64172,341 @@ const useInput = (inputHandler, options = {}) => {
|
|
|
64591
64172
|
}, [options.isActive, internal_eventEmitter]);
|
|
64592
64173
|
};
|
|
64593
64174
|
//#endregion
|
|
64175
|
+
//#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
|
|
64176
|
+
/**
|
|
64177
|
+
A React hook that returns the stdout stream where Ink renders your app.
|
|
64178
|
+
*/
|
|
64179
|
+
function useStdout() {
|
|
64180
|
+
return (0, import_react.use)(StdoutContext);
|
|
64181
|
+
}
|
|
64182
|
+
//#endregion
|
|
64183
|
+
//#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
|
|
64184
|
+
/**
|
|
64185
|
+
A React hook that returns the current terminal window dimensions and re-renders the component whenever the terminal is resized.
|
|
64186
|
+
*/
|
|
64187
|
+
const useWindowSize = () => {
|
|
64188
|
+
const { stdout } = useStdout();
|
|
64189
|
+
const [size, setSize] = (0, import_react.useState)(() => getWindowSize(stdout));
|
|
64190
|
+
(0, import_react.useEffect)(() => {
|
|
64191
|
+
const onResize = () => {
|
|
64192
|
+
setSize(getWindowSize(stdout));
|
|
64193
|
+
};
|
|
64194
|
+
stdout.on("resize", onResize);
|
|
64195
|
+
onResize();
|
|
64196
|
+
return () => {
|
|
64197
|
+
stdout.off("resize", onResize);
|
|
64198
|
+
};
|
|
64199
|
+
}, [stdout]);
|
|
64200
|
+
return size;
|
|
64201
|
+
};
|
|
64202
|
+
//#endregion
|
|
64203
|
+
//#region src/ledger.ts
|
|
64204
|
+
const Note = Struct({
|
|
64205
|
+
kind: Literals$2([
|
|
64206
|
+
"ask",
|
|
64207
|
+
"answer",
|
|
64208
|
+
"info"
|
|
64209
|
+
]),
|
|
64210
|
+
text: String$5
|
|
64211
|
+
});
|
|
64212
|
+
const Item = Struct({
|
|
64213
|
+
name: String$5,
|
|
64214
|
+
kind: Literals$2([
|
|
64215
|
+
"version",
|
|
64216
|
+
"repo",
|
|
64217
|
+
"config"
|
|
64218
|
+
]),
|
|
64219
|
+
from: optionalKey(String$5),
|
|
64220
|
+
to: optionalKey(String$5),
|
|
64221
|
+
reason: optionalKey(String$5),
|
|
64222
|
+
changes: Boolean$4,
|
|
64223
|
+
status: Literals$2([
|
|
64224
|
+
"idle",
|
|
64225
|
+
"running",
|
|
64226
|
+
"ok",
|
|
64227
|
+
"failed",
|
|
64228
|
+
"skipped"
|
|
64229
|
+
]),
|
|
64230
|
+
doing: optionalKey(String$5),
|
|
64231
|
+
detail: optionalKey(String$5),
|
|
64232
|
+
output: optionalKey(ArraySchema(String$5)),
|
|
64233
|
+
steps: ArraySchema(String$5),
|
|
64234
|
+
started: optionalKey(Number$2),
|
|
64235
|
+
ended: optionalKey(Number$2)
|
|
64236
|
+
});
|
|
64237
|
+
const MachineEntry = Struct({
|
|
64238
|
+
name: String$5,
|
|
64239
|
+
status: Literals$2([
|
|
64240
|
+
"checking",
|
|
64241
|
+
"waiting",
|
|
64242
|
+
"planned",
|
|
64243
|
+
"running",
|
|
64244
|
+
"ok",
|
|
64245
|
+
"skipped",
|
|
64246
|
+
"failed"
|
|
64247
|
+
]),
|
|
64248
|
+
detail: optionalKey(String$5),
|
|
64249
|
+
output: optionalKey(ArraySchema(String$5)),
|
|
64250
|
+
items: ArraySchema(Item),
|
|
64251
|
+
started: optionalKey(Number$2),
|
|
64252
|
+
ended: optionalKey(Number$2)
|
|
64253
|
+
});
|
|
64254
|
+
const LedgerState = Struct({
|
|
64255
|
+
from: String$5,
|
|
64256
|
+
dryRun: Boolean$4,
|
|
64257
|
+
notes: ArraySchema(Note),
|
|
64258
|
+
machines: ArraySchema(MachineEntry),
|
|
64259
|
+
started: Number$2,
|
|
64260
|
+
ended: optionalKey(Number$2)
|
|
64261
|
+
});
|
|
64262
|
+
const short = (sha) => sha === void 0 ? void 0 : sha.slice(0, 7);
|
|
64263
|
+
const FETCH = ":fetch";
|
|
64264
|
+
const doingOf = (id) => id === "config" ? "copying" : id.endsWith(FETCH) ? "fetching" : "installing";
|
|
64265
|
+
const itemsOf = (p, state) => {
|
|
64266
|
+
const items = /* @__PURE__ */ new Map();
|
|
64267
|
+
const key = (kind, name) => `${kind}:${name}`;
|
|
64268
|
+
const base = (name, from, kind = "version") => ({
|
|
64269
|
+
name,
|
|
64270
|
+
kind,
|
|
64271
|
+
...from === void 0 ? {} : { from },
|
|
64272
|
+
changes: false,
|
|
64273
|
+
status: "idle",
|
|
64274
|
+
steps: []
|
|
64275
|
+
});
|
|
64276
|
+
const set = (name, change, kind = "version") => items.set(key(kind, name), change(items.get(key(kind, name)) ?? base(name, void 0, kind)));
|
|
64277
|
+
const plans = (id, to, from, reason) => ({ from: checked, ...item }) => {
|
|
64278
|
+
const now = from === void 0 ? checked : from ?? void 0;
|
|
64279
|
+
return {
|
|
64280
|
+
...item,
|
|
64281
|
+
...now === void 0 ? {} : { from: now },
|
|
64282
|
+
to,
|
|
64283
|
+
...reason === void 0 ? {} : { reason },
|
|
64284
|
+
changes: true,
|
|
64285
|
+
steps: [...item.steps, id]
|
|
64286
|
+
};
|
|
64287
|
+
};
|
|
64288
|
+
const vp = state.vp?.version ?? void 0;
|
|
64289
|
+
items.set(key("version", "vp"), base("vp", vp));
|
|
64290
|
+
if (state.node != null) items.set(key("version", "node"), base("node", state.node));
|
|
64291
|
+
for (const g of state.globals) items.set(key("version", g.name), base(g.name, g.version));
|
|
64292
|
+
for (const step of p.vp) {
|
|
64293
|
+
const id = vpStepId(step);
|
|
64294
|
+
switch (step.kind) {
|
|
64295
|
+
case "bootstrap":
|
|
64296
|
+
set("vp", plans(id, "latest", null));
|
|
64297
|
+
break;
|
|
64298
|
+
case "vp":
|
|
64299
|
+
case "node":
|
|
64300
|
+
set(step.kind, plans(id, step.version, step.from));
|
|
64301
|
+
break;
|
|
64302
|
+
case "reinstall":
|
|
64303
|
+
set(step.name, plans(id, step.version, step.version, "reinstall"));
|
|
64304
|
+
break;
|
|
64305
|
+
case "globals": for (const pkg of step.packages) set(pkg.name, plans(id, pkg.version, pkg.from ?? null));
|
|
64306
|
+
}
|
|
64307
|
+
}
|
|
64308
|
+
for (const repo of p.repos) {
|
|
64309
|
+
const from = short(state.records[repo.name]?.commit);
|
|
64310
|
+
const to = short(repo.head);
|
|
64311
|
+
items.set(key("repo", repo.name), {
|
|
64312
|
+
name: repo.name,
|
|
64313
|
+
kind: "repo",
|
|
64314
|
+
...from === void 0 ? {} : { from },
|
|
64315
|
+
...to === void 0 ? {} : { to },
|
|
64316
|
+
...repo.decision.run ? { reason: repo.decision.reason } : {},
|
|
64317
|
+
changes: repo.decision.run,
|
|
64318
|
+
status: "idle",
|
|
64319
|
+
steps: [stepId(repo.name, "fetch"), stepId(repo.name, "install")]
|
|
64320
|
+
});
|
|
64321
|
+
}
|
|
64322
|
+
for (const s of p.skipped) set(s.what, (i) => ({
|
|
64323
|
+
...i,
|
|
64324
|
+
status: "skipped",
|
|
64325
|
+
detail: s.reason
|
|
64326
|
+
}));
|
|
64327
|
+
for (const f of p.failures) {
|
|
64328
|
+
const kind = f.section === "repos" ? "repo" : "version";
|
|
64329
|
+
set(f.what, (i) => ({
|
|
64330
|
+
...i,
|
|
64331
|
+
changes: true,
|
|
64332
|
+
status: "failed",
|
|
64333
|
+
detail: f.reason
|
|
64334
|
+
}), kind);
|
|
64335
|
+
}
|
|
64336
|
+
items.set(key("config", CONFIG_STEP), {
|
|
64337
|
+
name: CONFIG_STEP,
|
|
64338
|
+
kind: "config",
|
|
64339
|
+
changes: false,
|
|
64340
|
+
status: "idle",
|
|
64341
|
+
steps: [CONFIG_STEP]
|
|
64342
|
+
});
|
|
64343
|
+
return [...items.values()];
|
|
64344
|
+
};
|
|
64345
|
+
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;
|
|
64346
|
+
const tally = (state) => {
|
|
64347
|
+
const outcomes = state.machines.map((m) => outcomeOf(m, state.dryRun));
|
|
64348
|
+
const n = (o) => outcomes.filter((x) => x === o).length;
|
|
64349
|
+
return {
|
|
64350
|
+
ok: n("ok"),
|
|
64351
|
+
skipped: n("skipped"),
|
|
64352
|
+
failed: n("failed")
|
|
64353
|
+
};
|
|
64354
|
+
};
|
|
64355
|
+
const makeLedger = fn("makeLedger")(function* (input) {
|
|
64356
|
+
const started = yield* currentTimeMillis;
|
|
64357
|
+
let snapshot = {
|
|
64358
|
+
state: {
|
|
64359
|
+
from: input.from,
|
|
64360
|
+
dryRun: input.dryRun,
|
|
64361
|
+
notes: [],
|
|
64362
|
+
machines: input.machines.map((name) => ({
|
|
64363
|
+
name,
|
|
64364
|
+
status: "checking",
|
|
64365
|
+
items: []
|
|
64366
|
+
})),
|
|
64367
|
+
started
|
|
64368
|
+
},
|
|
64369
|
+
now: started
|
|
64370
|
+
};
|
|
64371
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
64372
|
+
const update = (change) => gen(function* () {
|
|
64373
|
+
const now = yield* currentTimeMillis;
|
|
64374
|
+
snapshot = {
|
|
64375
|
+
state: change(snapshot.state, now),
|
|
64376
|
+
now
|
|
64377
|
+
};
|
|
64378
|
+
for (const listener of listeners) listener();
|
|
64379
|
+
});
|
|
64380
|
+
const machine = (name, change) => update((state, now) => ({
|
|
64381
|
+
...state,
|
|
64382
|
+
machines: state.machines.map((m) => m.name === name ? change(m, now) : m)
|
|
64383
|
+
}));
|
|
64384
|
+
const items = (name, id, change) => machine(name, (m, now) => ({
|
|
64385
|
+
...m,
|
|
64386
|
+
items: m.items.map((i) => i.steps.includes(id) ? change(i, now) : i)
|
|
64387
|
+
}));
|
|
64388
|
+
const store = {
|
|
64389
|
+
get: () => snapshot,
|
|
64390
|
+
subscribe: (listener) => {
|
|
64391
|
+
listeners.add(listener);
|
|
64392
|
+
return () => listeners.delete(listener);
|
|
64393
|
+
},
|
|
64394
|
+
tick: update((state) => state)
|
|
64395
|
+
};
|
|
64396
|
+
return {
|
|
64397
|
+
ledger: {
|
|
64398
|
+
note: fn("ledger.note")(function* (kind, text) {
|
|
64399
|
+
yield* update((state) => ({
|
|
64400
|
+
...state,
|
|
64401
|
+
notes: [...state.notes, {
|
|
64402
|
+
kind,
|
|
64403
|
+
text
|
|
64404
|
+
}]
|
|
64405
|
+
}));
|
|
64406
|
+
yield* input.print({
|
|
64407
|
+
kind,
|
|
64408
|
+
text
|
|
64409
|
+
});
|
|
64410
|
+
}),
|
|
64411
|
+
checked: (name) => machine(name, (m) => ({
|
|
64412
|
+
...m,
|
|
64413
|
+
status: "waiting"
|
|
64414
|
+
})),
|
|
64415
|
+
skip: (name, reason) => machine(name, (m) => ({
|
|
64416
|
+
...m,
|
|
64417
|
+
status: "skipped",
|
|
64418
|
+
detail: reason
|
|
64419
|
+
})),
|
|
64420
|
+
fail: (name, [reason = "failed", ...output]) => machine(name, (m, now) => ({
|
|
64421
|
+
...m,
|
|
64422
|
+
status: "failed",
|
|
64423
|
+
detail: reason,
|
|
64424
|
+
...output.length === 0 ? {} : { output },
|
|
64425
|
+
ended: now
|
|
64426
|
+
})),
|
|
64427
|
+
plan: (name, p, state) => machine(name, (m) => ({
|
|
64428
|
+
...m,
|
|
64429
|
+
status: "planned",
|
|
64430
|
+
items: itemsOf(p, state)
|
|
64431
|
+
})),
|
|
64432
|
+
start: (name) => machine(name, (m, now) => ({
|
|
64433
|
+
...m,
|
|
64434
|
+
status: "running",
|
|
64435
|
+
started: now
|
|
64436
|
+
})),
|
|
64437
|
+
stepStarted: (name, id) => items(name, id, (item, now) => item.status === "failed" ? item : {
|
|
64438
|
+
...item,
|
|
64439
|
+
changes: item.changes || !(id === "config" || id.endsWith(FETCH)),
|
|
64440
|
+
status: "running",
|
|
64441
|
+
doing: doingOf(id),
|
|
64442
|
+
started: item.started ?? now
|
|
64443
|
+
}),
|
|
64444
|
+
stepDone: (name, id, result) => items(name, id, (item, now) => {
|
|
64445
|
+
if (item.status === "failed") return item;
|
|
64446
|
+
if (!result.ok) return {
|
|
64447
|
+
...item,
|
|
64448
|
+
changes: true,
|
|
64449
|
+
status: "failed",
|
|
64450
|
+
detail: result.detail,
|
|
64451
|
+
...result.output.length === 0 ? {} : { output: result.output },
|
|
64452
|
+
ended: now
|
|
64453
|
+
};
|
|
64454
|
+
const { doing: _, ...rest } = item;
|
|
64455
|
+
if (id.endsWith(FETCH)) return item.changes ? item : {
|
|
64456
|
+
...rest,
|
|
64457
|
+
status: "idle",
|
|
64458
|
+
ended: now
|
|
64459
|
+
};
|
|
64460
|
+
return {
|
|
64461
|
+
...rest,
|
|
64462
|
+
status: "ok",
|
|
64463
|
+
ended: now
|
|
64464
|
+
};
|
|
64465
|
+
}),
|
|
64466
|
+
fetched: (name, repo, head) => machine(name, (m) => ({
|
|
64467
|
+
...m,
|
|
64468
|
+
items: m.items.map((i) => i.kind === "repo" && i.name === repo ? {
|
|
64469
|
+
...i,
|
|
64470
|
+
to: head.slice(0, 7),
|
|
64471
|
+
changes: i.changes || head.slice(0, 7) !== i.from
|
|
64472
|
+
} : i)
|
|
64473
|
+
})),
|
|
64474
|
+
finish: (name, outcome) => machine(name, (m, now) => ({
|
|
64475
|
+
...m,
|
|
64476
|
+
status: m.status === "failed" ? "failed" : outcome,
|
|
64477
|
+
ended: m.ended ?? now
|
|
64478
|
+
})),
|
|
64479
|
+
end: gen(function* () {
|
|
64480
|
+
yield* update((state, now) => ({
|
|
64481
|
+
...state,
|
|
64482
|
+
ended: now,
|
|
64483
|
+
machines: state.machines.map((m) => m.status === "running" || m.status === "checking" ? {
|
|
64484
|
+
...m,
|
|
64485
|
+
status: "failed",
|
|
64486
|
+
detail: "stopped",
|
|
64487
|
+
ended: now,
|
|
64488
|
+
items: m.items.map((i) => i.status === "running" ? {
|
|
64489
|
+
...i,
|
|
64490
|
+
status: "failed",
|
|
64491
|
+
detail: "stopped",
|
|
64492
|
+
ended: now
|
|
64493
|
+
} : i)
|
|
64494
|
+
} : m)
|
|
64495
|
+
}));
|
|
64496
|
+
return snapshot.state;
|
|
64497
|
+
})
|
|
64498
|
+
},
|
|
64499
|
+
store
|
|
64500
|
+
};
|
|
64501
|
+
});
|
|
64502
|
+
const duration = (ms) => {
|
|
64503
|
+
const s = ms / 1e3;
|
|
64504
|
+
if (s < 60) return `${s.toFixed(1)}s`;
|
|
64505
|
+
const m = Math.floor(s / 60);
|
|
64506
|
+
if (m < 60) return `${m}m ${String(Math.floor(s % 60)).padStart(2, "0")}s`;
|
|
64507
|
+
return `${Math.floor(m / 60)}h ${String(m % 60).padStart(2, "0")}m`;
|
|
64508
|
+
};
|
|
64509
|
+
//#endregion
|
|
64594
64510
|
//#region ../../node_modules/.pnpm/react@19.3.0/node_modules/react/cjs/react-jsx-runtime.production.js
|
|
64595
64511
|
/**
|
|
64596
64512
|
* @license React
|
|
@@ -64838,12 +64754,12 @@ var require_react_jsx_runtime_development = /* @__PURE__ */ __commonJSMin(((expo
|
|
|
64838
64754
|
})();
|
|
64839
64755
|
}));
|
|
64840
64756
|
//#endregion
|
|
64841
|
-
//#region src/
|
|
64757
|
+
//#region src/view.tsx
|
|
64842
64758
|
var import_jsx_runtime = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
64843
64759
|
if (process.env.NODE_ENV === "production") module.exports = require_react_jsx_runtime_production();
|
|
64844
64760
|
else module.exports = require_react_jsx_runtime_development();
|
|
64845
64761
|
})))();
|
|
64846
|
-
const
|
|
64762
|
+
const TONE = {
|
|
64847
64763
|
dim: { dimColor: true },
|
|
64848
64764
|
bold: { bold: true },
|
|
64849
64765
|
ok: { color: "green" },
|
|
@@ -64858,6 +64774,420 @@ const STYLE = {
|
|
|
64858
64774
|
bold: true
|
|
64859
64775
|
}
|
|
64860
64776
|
};
|
|
64777
|
+
const ModeContext = (0, import_react.createContext)({
|
|
64778
|
+
color: false,
|
|
64779
|
+
live: false
|
|
64780
|
+
});
|
|
64781
|
+
const T = (props) => {
|
|
64782
|
+
const { color } = (0, import_react.useContext)(ModeContext);
|
|
64783
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
64784
|
+
...color && props.tone !== void 0 ? TONE[props.tone] : {},
|
|
64785
|
+
children: props.children
|
|
64786
|
+
});
|
|
64787
|
+
};
|
|
64788
|
+
const Line = (props) => {
|
|
64789
|
+
const { live } = (0, import_react.useContext)(ModeContext);
|
|
64790
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
64791
|
+
paddingLeft: props.indent ?? 0,
|
|
64792
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
64793
|
+
wrap: live ? "truncate-end" : "wrap",
|
|
64794
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
64795
|
+
tone: props.tone,
|
|
64796
|
+
children: props.children
|
|
64797
|
+
})
|
|
64798
|
+
})
|
|
64799
|
+
});
|
|
64800
|
+
};
|
|
64801
|
+
const Cell = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
64802
|
+
width: props.width,
|
|
64803
|
+
flexShrink: 0,
|
|
64804
|
+
justifyContent: props.right ? "flex-end" : "flex-start",
|
|
64805
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
64806
|
+
wrap: "truncate-end",
|
|
64807
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
64808
|
+
tone: props.tone,
|
|
64809
|
+
children: props.children
|
|
64810
|
+
})
|
|
64811
|
+
})
|
|
64812
|
+
});
|
|
64813
|
+
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}`;
|
|
64814
|
+
const statusOf = (i, dryRun) => {
|
|
64815
|
+
switch (i.status) {
|
|
64816
|
+
case "running": return {
|
|
64817
|
+
text: `▸ ${i.doing ?? "running"}`,
|
|
64818
|
+
tone: "run"
|
|
64819
|
+
};
|
|
64820
|
+
case "failed": return {
|
|
64821
|
+
text: "✗ failed",
|
|
64822
|
+
tone: "fail",
|
|
64823
|
+
detail: i.detail
|
|
64824
|
+
};
|
|
64825
|
+
case "skipped": return {
|
|
64826
|
+
text: "– skipped",
|
|
64827
|
+
tone: "skip",
|
|
64828
|
+
detail: i.detail
|
|
64829
|
+
};
|
|
64830
|
+
case "ok":
|
|
64831
|
+
if (i.kind === "config") return {
|
|
64832
|
+
text: "✓ copied",
|
|
64833
|
+
tone: "dim"
|
|
64834
|
+
};
|
|
64835
|
+
if (i.kind === "repo") return i.from !== void 0 && i.from === i.to ? {
|
|
64836
|
+
text: "✓ rebuilt",
|
|
64837
|
+
tone: "ok",
|
|
64838
|
+
detail: i.reason
|
|
64839
|
+
} : {
|
|
64840
|
+
text: "✓ installed",
|
|
64841
|
+
tone: "ok"
|
|
64842
|
+
};
|
|
64843
|
+
return {
|
|
64844
|
+
text: "✓ done",
|
|
64845
|
+
tone: "ok",
|
|
64846
|
+
detail: i.reason
|
|
64847
|
+
};
|
|
64848
|
+
case "idle":
|
|
64849
|
+
if (i.changes) return {
|
|
64850
|
+
text: dryRun ? "· planned" : "· queued",
|
|
64851
|
+
tone: "dim",
|
|
64852
|
+
detail: i.reason
|
|
64853
|
+
};
|
|
64854
|
+
return {
|
|
64855
|
+
text: "",
|
|
64856
|
+
detail: i.kind === "repo" ? "up to date" : void 0
|
|
64857
|
+
};
|
|
64858
|
+
}
|
|
64859
|
+
};
|
|
64860
|
+
const countsOf = (m) => {
|
|
64861
|
+
const changes = m.items.filter((i) => i.changes && i.status !== "skipped");
|
|
64862
|
+
return {
|
|
64863
|
+
changes: changes.length,
|
|
64864
|
+
done: changes.filter((i) => i.status === "ok").length,
|
|
64865
|
+
failed: m.items.filter((i) => i.status === "failed").length,
|
|
64866
|
+
skipped: m.items.filter((i) => i.status === "skipped").length
|
|
64867
|
+
};
|
|
64868
|
+
};
|
|
64869
|
+
const settled = (m, dryRun) => dryRun || m.status === "ok" || m.status === "failed";
|
|
64870
|
+
const upToDate = (m, dryRun) => {
|
|
64871
|
+
const c = countsOf(m);
|
|
64872
|
+
return c.changes === 0 && c.skipped === 0 && settled(m, dryRun);
|
|
64873
|
+
};
|
|
64874
|
+
const summaryOf = (m, dryRun) => {
|
|
64875
|
+
if (m.status === "checking" || m.status === "waiting") return [{
|
|
64876
|
+
text: m.status,
|
|
64877
|
+
tone: "dim"
|
|
64878
|
+
}];
|
|
64879
|
+
if (m.status === "skipped") return [{
|
|
64880
|
+
text: "– skipped",
|
|
64881
|
+
tone: "skip"
|
|
64882
|
+
}, { text: ` ${m.detail ?? ""}` }];
|
|
64883
|
+
if (m.status === "failed" && m.detail !== void 0) return [{
|
|
64884
|
+
text: "✗ failed",
|
|
64885
|
+
tone: "fail"
|
|
64886
|
+
}, { text: ` ${m.detail}` }];
|
|
64887
|
+
const { changes, done, failed, skipped } = countsOf(m);
|
|
64888
|
+
if (changes === 0 && skipped === 0) return [{
|
|
64889
|
+
text: "up to date",
|
|
64890
|
+
tone: settled(m, dryRun) ? "ok" : "dim"
|
|
64891
|
+
}];
|
|
64892
|
+
const finished = m.status === "ok" || m.status === "failed";
|
|
64893
|
+
const parts = [];
|
|
64894
|
+
if (dryRun && m.status === "planned") {
|
|
64895
|
+
if (changes > failed) parts.push({
|
|
64896
|
+
text: `${changes - failed} to change`,
|
|
64897
|
+
tone: "dim"
|
|
64898
|
+
});
|
|
64899
|
+
} else if (changes > 0 && (!finished || done > 0)) parts.push({
|
|
64900
|
+
text: `${done} of ${changes} done`,
|
|
64901
|
+
tone: done === changes ? "ok" : "dim"
|
|
64902
|
+
});
|
|
64903
|
+
if (failed > 0) parts.push({
|
|
64904
|
+
text: `${failed} failed`,
|
|
64905
|
+
tone: "fail"
|
|
64906
|
+
});
|
|
64907
|
+
if (skipped > 0) parts.push({
|
|
64908
|
+
text: `${skipped} skipped`,
|
|
64909
|
+
tone: "skip"
|
|
64910
|
+
});
|
|
64911
|
+
return parts.flatMap((part, n) => n === 0 ? [part] : [{
|
|
64912
|
+
text: ", ",
|
|
64913
|
+
tone: "dim"
|
|
64914
|
+
}, part]);
|
|
64915
|
+
};
|
|
64916
|
+
const elapsed = (started, ended, now) => started === void 0 ? "" : duration((ended ?? now) - started);
|
|
64917
|
+
const Parts = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Line, { children: props.parts.map((part, n) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
64918
|
+
tone: part.tone,
|
|
64919
|
+
children: part.text
|
|
64920
|
+
}, n)) });
|
|
64921
|
+
const GAP = 2;
|
|
64922
|
+
const INDENT = 2;
|
|
64923
|
+
const nameAt = (w) => w.status + GAP + INDENT;
|
|
64924
|
+
const LIVE_OUTPUT = 12;
|
|
64925
|
+
const outputLines = (key, output, indent, cut) => {
|
|
64926
|
+
const hidden = cut ? Math.max(0, output.length - LIVE_OUTPUT) : 0;
|
|
64927
|
+
return [...hidden > 0 ? [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Line, {
|
|
64928
|
+
indent,
|
|
64929
|
+
tone: "dim",
|
|
64930
|
+
children: [
|
|
64931
|
+
"(",
|
|
64932
|
+
hidden,
|
|
64933
|
+
" earlier lines)"
|
|
64934
|
+
]
|
|
64935
|
+
}, `${key}/earlier`)] : [], ...output.slice(hidden).map((line, n) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Line, {
|
|
64936
|
+
indent,
|
|
64937
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
64938
|
+
tone: "fail",
|
|
64939
|
+
children: "│ "
|
|
64940
|
+
}), line]
|
|
64941
|
+
}, `${key}/${n}`))];
|
|
64942
|
+
};
|
|
64943
|
+
const MachineLine = (props) => {
|
|
64944
|
+
const { m, w } = props;
|
|
64945
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
64946
|
+
columnGap: GAP,
|
|
64947
|
+
children: [
|
|
64948
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Cell, {
|
|
64949
|
+
width: nameAt(w) + w.item + GAP + w.value,
|
|
64950
|
+
tone: "bold",
|
|
64951
|
+
children: m.name
|
|
64952
|
+
}),
|
|
64953
|
+
w.time > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Cell, {
|
|
64954
|
+
width: w.time,
|
|
64955
|
+
right: true,
|
|
64956
|
+
tone: "dim",
|
|
64957
|
+
children: upToDate(m, props.dryRun) ? "" : elapsed(m.started, m.ended, props.now)
|
|
64958
|
+
}),
|
|
64959
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Parts, { parts: summaryOf(m, props.dryRun) })
|
|
64960
|
+
]
|
|
64961
|
+
});
|
|
64962
|
+
};
|
|
64963
|
+
const ItemLine = (props) => {
|
|
64964
|
+
const { i, w } = props;
|
|
64965
|
+
const status = statusOf(i, props.dryRun);
|
|
64966
|
+
const quiet = i.status === "idle" ? "dim" : void 0;
|
|
64967
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
64968
|
+
columnGap: GAP,
|
|
64969
|
+
children: [
|
|
64970
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Cell, {
|
|
64971
|
+
width: w.status,
|
|
64972
|
+
tone: status.tone,
|
|
64973
|
+
children: status.text
|
|
64974
|
+
}),
|
|
64975
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Cell, {
|
|
64976
|
+
width: INDENT + w.item,
|
|
64977
|
+
tone: quiet,
|
|
64978
|
+
children: `${" ".repeat(INDENT)}${i.name}`
|
|
64979
|
+
}),
|
|
64980
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Cell, {
|
|
64981
|
+
width: w.value,
|
|
64982
|
+
tone: quiet,
|
|
64983
|
+
children: value(i)
|
|
64984
|
+
}),
|
|
64985
|
+
w.time > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Cell, {
|
|
64986
|
+
width: w.time,
|
|
64987
|
+
right: true,
|
|
64988
|
+
tone: "dim",
|
|
64989
|
+
children: elapsed(i.started, i.ended, props.now)
|
|
64990
|
+
}),
|
|
64991
|
+
status.detail !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Line, {
|
|
64992
|
+
tone: quiet,
|
|
64993
|
+
children: status.detail
|
|
64994
|
+
})
|
|
64995
|
+
]
|
|
64996
|
+
});
|
|
64997
|
+
};
|
|
64998
|
+
const LedgerView = (props) => {
|
|
64999
|
+
const { state, full, now, height } = props;
|
|
65000
|
+
const { dryRun } = state;
|
|
65001
|
+
const { live } = (0, import_react.useContext)(ModeContext);
|
|
65002
|
+
const running = live && state.ended === void 0;
|
|
65003
|
+
const shownItems = (m) => m.items.filter((i) => full || i.changes || i.status === "failed" || i.status === "skipped");
|
|
65004
|
+
const visible = state.machines.flatMap(shownItems);
|
|
65005
|
+
const width = (texts) => Math.max(0, ...texts.map((t) => t.length));
|
|
65006
|
+
const item = width(visible.map((i) => i.name));
|
|
65007
|
+
const status = width(visible.map((i) => statusOf(i, dryRun).text));
|
|
65008
|
+
const w = {
|
|
65009
|
+
status,
|
|
65010
|
+
item,
|
|
65011
|
+
value: Math.max(width(visible.map(value)), width(state.machines.map((m) => m.name)) - (status + GAP + INDENT + item + GAP)),
|
|
65012
|
+
time: width([...visible.map((i) => elapsed(i.started, i.ended, now)), ...state.machines.map((m) => elapsed(m.started, m.ended, now))])
|
|
65013
|
+
};
|
|
65014
|
+
const blocks = state.machines.map((m) => ({
|
|
65015
|
+
m,
|
|
65016
|
+
head: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MachineLine, {
|
|
65017
|
+
m,
|
|
65018
|
+
w,
|
|
65019
|
+
dryRun,
|
|
65020
|
+
now
|
|
65021
|
+
}, m.name),
|
|
65022
|
+
body: [...m.output === void 0 ? [] : outputLines(m.name, m.output, nameAt(w), running), ...shownItems(m).flatMap((i) => [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ItemLine, {
|
|
65023
|
+
i,
|
|
65024
|
+
w,
|
|
65025
|
+
dryRun,
|
|
65026
|
+
now
|
|
65027
|
+
}, `${m.name}/${i.kind}:${i.name}`), ...i.output === void 0 ? [] : outputLines(`${m.name}/${i.kind}:${i.name}`, i.output, nameAt(w), running)])]
|
|
65028
|
+
}));
|
|
65029
|
+
const legend = state.ended === void 0 ? `f ${full ? "changes only" : "full list"} · ctrl-c stop` : void 0;
|
|
65030
|
+
const room = height === void 0 ? Infinity : height - (legend === void 0 ? 1 : 3);
|
|
65031
|
+
const count = (bs) => bs.reduce((n, b) => n + 1 + b.body.length, Math.max(0, bs.length - 1));
|
|
65032
|
+
const fits = count(blocks) <= room;
|
|
65033
|
+
let spare = room - count(blocks.map((b) => ({
|
|
65034
|
+
...b,
|
|
65035
|
+
body: []
|
|
65036
|
+
})));
|
|
65037
|
+
let cuts = 0;
|
|
65038
|
+
const fitted = [];
|
|
65039
|
+
for (const b of blocks) {
|
|
65040
|
+
const open = b.m.status === "running";
|
|
65041
|
+
if (fits) fitted.push(b);
|
|
65042
|
+
else if (open && b.body.length <= spare) {
|
|
65043
|
+
fitted.push(b);
|
|
65044
|
+
spare -= b.body.length;
|
|
65045
|
+
} else if (open && spare > 0) {
|
|
65046
|
+
const ellipsis = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Line, {
|
|
65047
|
+
indent: nameAt(w),
|
|
65048
|
+
tone: "dim",
|
|
65049
|
+
children: "…"
|
|
65050
|
+
}, `${b.m.name}/cut`);
|
|
65051
|
+
fitted.push({
|
|
65052
|
+
...b,
|
|
65053
|
+
body: [...b.body.slice(0, spare - 1), ellipsis]
|
|
65054
|
+
});
|
|
65055
|
+
spare = 0;
|
|
65056
|
+
cuts += 1;
|
|
65057
|
+
} else fitted.push({
|
|
65058
|
+
...b,
|
|
65059
|
+
body: []
|
|
65060
|
+
});
|
|
65061
|
+
}
|
|
65062
|
+
const lines = fitted.flatMap((b, n) => [
|
|
65063
|
+
...n === 0 ? [] : [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: " " }, `${b.m.name}/gap`)],
|
|
65064
|
+
b.head,
|
|
65065
|
+
...b.body
|
|
65066
|
+
]);
|
|
65067
|
+
const shown = lines.length <= room ? lines : lines.slice(0, Math.max(0, room - 1));
|
|
65068
|
+
const hidden = count(blocks) - (shown.length - cuts);
|
|
65069
|
+
if (room < 1) return null;
|
|
65070
|
+
const t = tally(state);
|
|
65071
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
65072
|
+
flexDirection: "column",
|
|
65073
|
+
marginTop: 1,
|
|
65074
|
+
children: [
|
|
65075
|
+
shown,
|
|
65076
|
+
shown.length < lines.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Line, {
|
|
65077
|
+
tone: "dim",
|
|
65078
|
+
children: "…"
|
|
65079
|
+
}),
|
|
65080
|
+
state.ended !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
65081
|
+
marginTop: 1,
|
|
65082
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Parts, { parts: [
|
|
65083
|
+
{
|
|
65084
|
+
text: `${dryRun ? "planned" : "ok"} ${t.ok}`,
|
|
65085
|
+
...t.ok > 0 ? { tone: "ok" } : {}
|
|
65086
|
+
},
|
|
65087
|
+
{ text: " " },
|
|
65088
|
+
{
|
|
65089
|
+
text: `skipped ${t.skipped}`,
|
|
65090
|
+
...t.skipped > 0 ? { tone: "skip" } : {}
|
|
65091
|
+
},
|
|
65092
|
+
{ text: " " },
|
|
65093
|
+
{
|
|
65094
|
+
text: `failed ${t.failed}`,
|
|
65095
|
+
...t.failed > 0 ? { tone: "fail" } : {}
|
|
65096
|
+
},
|
|
65097
|
+
...dryRun ? [] : [{
|
|
65098
|
+
text: ` in ${duration(state.ended - state.started)}`,
|
|
65099
|
+
tone: "dim"
|
|
65100
|
+
}]
|
|
65101
|
+
] })
|
|
65102
|
+
}),
|
|
65103
|
+
state.ended !== void 0 && props.hint !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Line, {
|
|
65104
|
+
tone: "dim",
|
|
65105
|
+
children: props.hint
|
|
65106
|
+
}),
|
|
65107
|
+
legend !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
65108
|
+
marginTop: 1,
|
|
65109
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Line, {
|
|
65110
|
+
tone: "dim",
|
|
65111
|
+
children: [legend, hidden > 0 && ` · ${hidden} lines hidden to fit the window`]
|
|
65112
|
+
})
|
|
65113
|
+
})
|
|
65114
|
+
]
|
|
65115
|
+
});
|
|
65116
|
+
};
|
|
65117
|
+
const NoteLine = (props) => {
|
|
65118
|
+
const { kind, text } = props.note;
|
|
65119
|
+
return kind === "ask" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
|
|
65120
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
65121
|
+
tone: "ask",
|
|
65122
|
+
children: "?"
|
|
65123
|
+
}),
|
|
65124
|
+
" ",
|
|
65125
|
+
text
|
|
65126
|
+
] }) : kind === "answer" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
65127
|
+
tone: "dim",
|
|
65128
|
+
children: ` → ${text}`
|
|
65129
|
+
}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: text });
|
|
65130
|
+
};
|
|
65131
|
+
const Styled = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ModeContext.Provider, {
|
|
65132
|
+
value: {
|
|
65133
|
+
color: props.color,
|
|
65134
|
+
live: props.live
|
|
65135
|
+
},
|
|
65136
|
+
children: props.children
|
|
65137
|
+
});
|
|
65138
|
+
const colorful = () => Boolean(process.stdout.isTTY) && process.env.NO_COLOR === void 0;
|
|
65139
|
+
const columns = () => process.stdout.columns ?? 1e4;
|
|
65140
|
+
const toText = (node, color = colorful()) => renderToString(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Styled, {
|
|
65141
|
+
color,
|
|
65142
|
+
live: false,
|
|
65143
|
+
children: node
|
|
65144
|
+
}), { columns: columns() });
|
|
65145
|
+
const ledgerText = (state, options, color) => toText(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LedgerView, {
|
|
65146
|
+
state,
|
|
65147
|
+
...options
|
|
65148
|
+
}), color);
|
|
65149
|
+
const noteText = (note, color) => toText(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(NoteLine, { note }), color);
|
|
65150
|
+
//#endregion
|
|
65151
|
+
//#region src/report.tsx
|
|
65152
|
+
const reportPath = () => `${STATE_DIR.replace(/^~/, process.env.HOME ?? "~")}/last-ship.json`;
|
|
65153
|
+
const LedgerJson = fromJsonString(LedgerState);
|
|
65154
|
+
const encode = encodeEffect(LedgerJson);
|
|
65155
|
+
const decode = decodeEffect(LedgerJson);
|
|
65156
|
+
const saveReport = fn("saveReport")(function* (state) {
|
|
65157
|
+
const fs = yield* FileSystem;
|
|
65158
|
+
const path = reportPath();
|
|
65159
|
+
yield* fs.makeDirectory(path.slice(0, path.lastIndexOf("/")), { recursive: true });
|
|
65160
|
+
yield* fs.writeFileString(path, yield* encode(state));
|
|
65161
|
+
});
|
|
65162
|
+
const report = fn("report")(function* () {
|
|
65163
|
+
const fs = yield* FileSystem;
|
|
65164
|
+
const path = reportPath();
|
|
65165
|
+
if (!(yield* fs.exists(path))) return yield* log("no Ship from this Machine yet");
|
|
65166
|
+
const decoded = yield* decode(yield* fs.readFileString(path)).pipe(option);
|
|
65167
|
+
if (isNone(decoded)) return yield* log(`the last report (${path}) is from another version of fleetfizz; the next Ship replaces it`);
|
|
65168
|
+
const state = decoded.value;
|
|
65169
|
+
const when = formatLocal(makeUnsafe(state.started), {
|
|
65170
|
+
dateStyle: "medium",
|
|
65171
|
+
timeStyle: "short"
|
|
65172
|
+
});
|
|
65173
|
+
yield* log(toText(/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
65174
|
+
flexDirection: "column",
|
|
65175
|
+
children: [
|
|
65176
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
65177
|
+
tone: "dim",
|
|
65178
|
+
children: `last Ship ${when} from ${state.from}`
|
|
65179
|
+
}),
|
|
65180
|
+
state.notes.map((note, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NoteLine, { note }, i)),
|
|
65181
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LedgerView, {
|
|
65182
|
+
state,
|
|
65183
|
+
full: true,
|
|
65184
|
+
now: state.ended ?? state.started
|
|
65185
|
+
})
|
|
65186
|
+
]
|
|
65187
|
+
})));
|
|
65188
|
+
});
|
|
65189
|
+
//#endregion
|
|
65190
|
+
//#region src/live.tsx
|
|
64861
65191
|
const cell = (initial) => {
|
|
64862
65192
|
let value = initial;
|
|
64863
65193
|
const listeners = /* @__PURE__ */ new Set();
|
|
@@ -64878,6 +65208,7 @@ const LiveLedger = (props) => {
|
|
|
64878
65208
|
const asking = (0, import_react.useSyncExternalStore)(props.asking.subscribe, props.asking.get);
|
|
64879
65209
|
const [full, setFull] = (0, import_react.useState)(false);
|
|
64880
65210
|
const [cursor, setCursor] = (0, import_react.useState)(0);
|
|
65211
|
+
const { rows } = useWindowSize();
|
|
64881
65212
|
const { isRawModeSupported } = useStdin();
|
|
64882
65213
|
useInput((input, key) => {
|
|
64883
65214
|
if (key.ctrl && input === "c") process.kill(process.pid, "SIGINT");
|
|
@@ -64889,42 +65220,36 @@ const LiveLedger = (props) => {
|
|
|
64889
65220
|
asking.answer(cursor);
|
|
64890
65221
|
} else if (input === "f") setFull((f) => !f);
|
|
64891
65222
|
}, { isActive: isRawModeSupported });
|
|
64892
|
-
const
|
|
64893
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.
|
|
64894
|
-
|
|
64895
|
-
|
|
64896
|
-
|
|
64897
|
-
|
|
64898
|
-
|
|
64899
|
-
|
|
64900
|
-
|
|
64901
|
-
|
|
64902
|
-
|
|
65223
|
+
const choices = asking?.titles ?? [];
|
|
65224
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Styled, {
|
|
65225
|
+
color: props.color,
|
|
65226
|
+
live: true,
|
|
65227
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
65228
|
+
flexDirection: "column",
|
|
65229
|
+
children: [choices.map((title, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Line, {
|
|
65230
|
+
tone: i === cursor ? void 0 : "dim",
|
|
65231
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
65232
|
+
tone: i === cursor ? "ask" : "dim",
|
|
65233
|
+
children: i === cursor ? " › " : " "
|
|
65234
|
+
}), title]
|
|
65235
|
+
}, i)), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LedgerView, {
|
|
65236
|
+
state,
|
|
65237
|
+
full,
|
|
65238
|
+
now,
|
|
65239
|
+
height: rows - 1 - choices.length
|
|
64903
65240
|
})]
|
|
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))]
|
|
65241
|
+
})
|
|
64915
65242
|
});
|
|
64916
65243
|
};
|
|
64917
65244
|
const startLive = fn("startLive")(function* (store, color) {
|
|
64918
65245
|
const asking = cell(void 0);
|
|
64919
|
-
const
|
|
65246
|
+
const instance = render(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LiveLedger, {
|
|
64920
65247
|
store,
|
|
64921
65248
|
asking,
|
|
64922
65249
|
color
|
|
64923
|
-
});
|
|
64924
|
-
const instance = render(view, { exitOnCtrlC: false });
|
|
65250
|
+
}), { exitOnCtrlC: false });
|
|
64925
65251
|
yield* addFinalizer(() => promise(async () => {
|
|
64926
|
-
instance.
|
|
64927
|
-
await instance.waitUntilRenderFlush();
|
|
65252
|
+
instance.clear();
|
|
64928
65253
|
instance.unmount();
|
|
64929
65254
|
await instance.waitUntilExit();
|
|
64930
65255
|
}));
|
|
@@ -64971,12 +65296,12 @@ const gather = fn("gather")(function* (target, script, ledger) {
|
|
|
64971
65296
|
else if (line.kind === "text" && line.text.trim() !== "") output.push(line.text.trimEnd());
|
|
64972
65297
|
}), ledger).pipe(timeoutOrElse({
|
|
64973
65298
|
duration: GATHER_TIMEOUT,
|
|
64974
|
-
orElse: () => ledger.fail(target.name, [`
|
|
65299
|
+
orElse: () => ledger.fail(target.name, [`checking took more than ${GATHER_TIMEOUT}; something on that Machine is blocking`, ...output]).pipe(as({ kind: "failed" }))
|
|
64975
65300
|
}));
|
|
64976
65301
|
if (run.kind !== "ran") return { kind: run.kind };
|
|
64977
65302
|
const state = assembleState(facts);
|
|
64978
65303
|
if (!state.complete) {
|
|
64979
|
-
yield* ledger.fail(target.name, ["
|
|
65304
|
+
yield* ledger.fail(target.name, ["checking did not finish", ...output]);
|
|
64980
65305
|
return { kind: "failed" };
|
|
64981
65306
|
}
|
|
64982
65307
|
return {
|
|
@@ -65132,24 +65457,30 @@ const ship = fn("ship")(function* (flags) {
|
|
|
65132
65457
|
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
65458
|
const count = `${targetsList.length} machine${targetsList.length === 1 ? "" : "s"}`;
|
|
65134
65459
|
yield* log(`${flags.dryRun ? "dry run, " : ""}${count} from ${tailnet.self.name}`);
|
|
65460
|
+
const color = colorful();
|
|
65135
65461
|
const { ledger, store } = yield* makeLedger({
|
|
65136
65462
|
from: tailnet.self.name,
|
|
65137
65463
|
dryRun: flags.dryRun,
|
|
65138
|
-
machines: targetsList.map((m) => m.name)
|
|
65464
|
+
machines: targetsList.map((m) => m.name),
|
|
65465
|
+
print: (note) => log(noteText(note, color))
|
|
65139
65466
|
});
|
|
65140
|
-
const
|
|
65467
|
+
const redraw = canRedraw();
|
|
65141
65468
|
let changing = false;
|
|
65142
65469
|
const finish = fn("finish")(function* () {
|
|
65143
65470
|
const state = yield* ledger.end;
|
|
65144
|
-
|
|
65145
|
-
|
|
65146
|
-
full:
|
|
65471
|
+
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)))));
|
|
65472
|
+
yield* log(ledgerText(state, redraw ? {
|
|
65473
|
+
full: false,
|
|
65147
65474
|
now: state.ended ?? 0,
|
|
65148
|
-
|
|
65149
|
-
}
|
|
65475
|
+
...saved ? { hint: "full report: fizz report" } : {}
|
|
65476
|
+
} : {
|
|
65477
|
+
full: true,
|
|
65478
|
+
now: state.ended ?? 0
|
|
65479
|
+
}, color));
|
|
65150
65480
|
if (tally(state).failed > 0) process.exitCode = 1;
|
|
65151
65481
|
});
|
|
65152
65482
|
yield* addFinalizer(() => finish());
|
|
65483
|
+
const live = redraw ? yield* startLive(store, color) : noLive;
|
|
65153
65484
|
const cloneRootOf = (machine) => config.machines[machine.name]?.clones ?? "~/.local/share/fleetfizz/clones";
|
|
65154
65485
|
const gatherRepos = (config.repos ?? []).map((r) => ({
|
|
65155
65486
|
name: repoName(r),
|
|
@@ -65174,7 +65505,7 @@ const ship = fn("ship")(function* (flags) {
|
|
|
65174
65505
|
local
|
|
65175
65506
|
}, script, ledger);
|
|
65176
65507
|
if (result.kind !== "gathered") return void 0;
|
|
65177
|
-
yield* ledger.
|
|
65508
|
+
yield* ledger.checked(machine.name);
|
|
65178
65509
|
return {
|
|
65179
65510
|
machine,
|
|
65180
65511
|
local,
|
|
@@ -65271,7 +65602,7 @@ const ship = fn("ship")(function* (flags) {
|
|
|
65271
65602
|
//#endregion
|
|
65272
65603
|
//#region src/bin.ts
|
|
65273
65604
|
const shipCommand = make("ship", {
|
|
65274
|
-
dryRun: Boolean$1("dry-run").pipe(withDefault(false), withDescription$1("
|
|
65605
|
+
dryRun: Boolean$1("dry-run").pipe(withDefault(false), withDescription$1("Check and plan only. Print every Machine's plan and change nothing.")),
|
|
65275
65606
|
only: String$1("only").pipe(atLeast(0), withDescription$1("Ship only to these Machines (repeat the flag).")),
|
|
65276
65607
|
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
65608
|
bump: Boolean$1("bump").pipe(withDefault(false), withDescription$1("Answer every above-range question by bumping the config.")),
|