fleetfizz 0.5.2 → 0.6.0
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 +1688 -923
- package/package.json +2 -1
- package/schema.json +467 -17
package/dist/fleetfizz.mjs
CHANGED
|
@@ -22876,6 +22876,10 @@ function decodeUnknownEffect$1(schema, options) {
|
|
|
22876
22876
|
const parser = run$3(schema.ast);
|
|
22877
22877
|
return options === void 0 ? parser : (input, overrideOptions) => parser(input, mergeParseOptions(options, overrideOptions));
|
|
22878
22878
|
}
|
|
22879
|
+
/** @internal */
|
|
22880
|
+
function decodeUnknownOption$1(schema, options) {
|
|
22881
|
+
return asOption(decodeUnknownEffect$1(schema, options));
|
|
22882
|
+
}
|
|
22879
22883
|
/**
|
|
22880
22884
|
* Creates a decoder for `unknown` input that reports failure safely as a
|
|
22881
22885
|
* `Result`.
|
|
@@ -22961,6 +22965,16 @@ function runWithCompiler(compiler, ast) {
|
|
|
22961
22965
|
function asExit(parser) {
|
|
22962
22966
|
return (input, options) => runSyncExit(parser(input, options));
|
|
22963
22967
|
}
|
|
22968
|
+
/** @internal */
|
|
22969
|
+
function asOption(parser) {
|
|
22970
|
+
const parserExit = asExit(parser);
|
|
22971
|
+
return (input, options) => {
|
|
22972
|
+
const exit = parserExit(input, options);
|
|
22973
|
+
if (isSuccess(exit)) return some(exit.value);
|
|
22974
|
+
getSchemaIssueOrThrow(exit.cause, "Option adapter can only return none for schema issues");
|
|
22975
|
+
return none();
|
|
22976
|
+
};
|
|
22977
|
+
}
|
|
22964
22978
|
function asResult(parser) {
|
|
22965
22979
|
const parserExit = asExit(parser);
|
|
22966
22980
|
return (input, options) => {
|
|
@@ -24468,6 +24482,34 @@ function decodeUnknownEffect(schema, options) {
|
|
|
24468
24482
|
*/
|
|
24469
24483
|
const decodeEffect = decodeUnknownEffect;
|
|
24470
24484
|
/**
|
|
24485
|
+
* Decodes an `unknown` input against a schema, returning an `Option` that is
|
|
24486
|
+
* `Some` with the decoded value on success or `None` for schema mismatches.
|
|
24487
|
+
*
|
|
24488
|
+
* **When to use**
|
|
24489
|
+
*
|
|
24490
|
+
* Use when you do not know the input type statically and only need to know
|
|
24491
|
+
* whether decoding succeeded.
|
|
24492
|
+
*
|
|
24493
|
+
* **Details**
|
|
24494
|
+
*
|
|
24495
|
+
* Prefer this over {@link decodeUnknownExit} or {@link decodeUnknownEffect}
|
|
24496
|
+
* when you don't need error details. For input already typed as the schema's
|
|
24497
|
+
* `Encoded` type use {@link decodeOption}.
|
|
24498
|
+
* Options may be provided either when creating the decoder or when applying it;
|
|
24499
|
+
* application options override creation options.
|
|
24500
|
+
*
|
|
24501
|
+
* **Gotchas**
|
|
24502
|
+
*
|
|
24503
|
+
* Only causes made entirely of schema issues are converted to `None`. Causes
|
|
24504
|
+
* that contain defects, interruptions, or other non-schema reasons throw
|
|
24505
|
+
* instead.
|
|
24506
|
+
*
|
|
24507
|
+
* @stability stable
|
|
24508
|
+
* @category decoding
|
|
24509
|
+
* @since 3.10.0
|
|
24510
|
+
*/
|
|
24511
|
+
const decodeUnknownOption = decodeUnknownOption$1;
|
|
24512
|
+
/**
|
|
24471
24513
|
* Decodes an `unknown` input against a schema, returning a `Result` that
|
|
24472
24514
|
* succeeds with the decoded value or fails with a {@link SchemaError} for schema
|
|
24473
24515
|
* mismatches.
|
|
@@ -25893,145 +25935,6 @@ function defaultShouldQuit(input) {
|
|
|
25893
25935
|
*/
|
|
25894
25936
|
const layer = /*#__PURE__*/ provideMerge(layer$11, /*#__PURE__*/ mergeAll(layer$7, layer$9, layer$5, layer$3, layer$2));
|
|
25895
25937
|
//#endregion
|
|
25896
|
-
//#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/Console.js
|
|
25897
|
-
/**
|
|
25898
|
-
* Context reference for the current console service in the Effect system, allowing access to the active console implementation from within the Effect context.
|
|
25899
|
-
*
|
|
25900
|
-
* **When to use**
|
|
25901
|
-
*
|
|
25902
|
-
* Use when you need an effect to run against a provided console implementation,
|
|
25903
|
-
* such as tests or alternate runtimes, rather than the default console.
|
|
25904
|
-
*
|
|
25905
|
-
* **Details**
|
|
25906
|
-
*
|
|
25907
|
-
* When no override is provided, the reference resolves to `globalThis.console`.
|
|
25908
|
-
*
|
|
25909
|
-
* **Example** (Accessing the current console)
|
|
25910
|
-
*
|
|
25911
|
-
* ```ts import.meta.vitest
|
|
25912
|
-
* import { Console, Effect } from "effect"
|
|
25913
|
-
*
|
|
25914
|
-
* const messages: Array<unknown> = []
|
|
25915
|
-
* const testConsole: Console.Console = Object.assign(Object.create(console), {
|
|
25916
|
-
* log: (...args: ReadonlyArray<unknown>) => messages.push(...args)
|
|
25917
|
-
* })
|
|
25918
|
-
* const program = Console.consoleWith((console) =>
|
|
25919
|
-
* Effect.sync(() => {
|
|
25920
|
-
* console.log("Hello from current console!")
|
|
25921
|
-
* })
|
|
25922
|
-
* )
|
|
25923
|
-
*
|
|
25924
|
-
* Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
|
|
25925
|
-
* messages // => ["Hello from current console!"]
|
|
25926
|
-
* ```
|
|
25927
|
-
*
|
|
25928
|
-
* @see {@link consoleWith} for using the current console service inside an effect
|
|
25929
|
-
*
|
|
25930
|
-
* @stability stable
|
|
25931
|
-
* @category services
|
|
25932
|
-
* @since 2.0.0
|
|
25933
|
-
*/
|
|
25934
|
-
const Console = ConsoleRef;
|
|
25935
|
-
/**
|
|
25936
|
-
* Creates an Effect that provides access to the current console service and lets you perform operations with it within an Effect context.
|
|
25937
|
-
*
|
|
25938
|
-
* **Example** (Accessing the current console service)
|
|
25939
|
-
*
|
|
25940
|
-
* ```ts import.meta.vitest
|
|
25941
|
-
* import { Console, Effect } from "effect"
|
|
25942
|
-
*
|
|
25943
|
-
* const messages: Array<unknown> = []
|
|
25944
|
-
* const testConsole: Console.Console = Object.assign(Object.create(console), {
|
|
25945
|
-
* log: (...args: ReadonlyArray<unknown>) => messages.push(...args),
|
|
25946
|
-
* error: (...args: ReadonlyArray<unknown>) => messages.push(...args)
|
|
25947
|
-
* })
|
|
25948
|
-
* const program = Console.consoleWith((console) =>
|
|
25949
|
-
* Effect.sync(() => {
|
|
25950
|
-
* console.log("Hello, world!")
|
|
25951
|
-
* console.error("This is an error message")
|
|
25952
|
-
* })
|
|
25953
|
-
* )
|
|
25954
|
-
*
|
|
25955
|
-
* Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
|
|
25956
|
-
* messages // => ["Hello, world!", "This is an error message"]
|
|
25957
|
-
* ```
|
|
25958
|
-
*
|
|
25959
|
-
* @stability stable
|
|
25960
|
-
* @category constructors
|
|
25961
|
-
* @since 2.0.0
|
|
25962
|
-
*/
|
|
25963
|
-
const consoleWith = (f) => withFiber$1((fiber) => f(fiber.getRef(Console)));
|
|
25964
|
-
/**
|
|
25965
|
-
* Writes an error-level message to the console, typically displayed with error
|
|
25966
|
-
* styling by the active console implementation.
|
|
25967
|
-
*
|
|
25968
|
-
* **Example** (Writing error messages)
|
|
25969
|
-
*
|
|
25970
|
-
* ```ts import.meta.vitest
|
|
25971
|
-
* import { Console, Effect } from "effect"
|
|
25972
|
-
*
|
|
25973
|
-
* const messages: Array<ReadonlyArray<unknown>> = []
|
|
25974
|
-
* const testConsole: Console.Console = Object.assign(Object.create(console), {
|
|
25975
|
-
* error: (...args: ReadonlyArray<unknown>) => messages.push(args)
|
|
25976
|
-
* })
|
|
25977
|
-
* const program = Effect.gen(function*() {
|
|
25978
|
-
* yield* Console.error("Something went wrong!")
|
|
25979
|
-
* yield* Console.error("Error details:", {
|
|
25980
|
-
* code: 500,
|
|
25981
|
-
* message: "Internal Server Error"
|
|
25982
|
-
* })
|
|
25983
|
-
* })
|
|
25984
|
-
*
|
|
25985
|
-
* Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
|
|
25986
|
-
* const expected = [
|
|
25987
|
-
* ["Something went wrong!"],
|
|
25988
|
-
* ["Error details:", { code: 500, message: "Internal Server Error" }]
|
|
25989
|
-
* ]
|
|
25990
|
-
* messages // => expected
|
|
25991
|
-
* ```
|
|
25992
|
-
*
|
|
25993
|
-
* @stability stable
|
|
25994
|
-
* @category accessors
|
|
25995
|
-
* @since 2.0.0
|
|
25996
|
-
*/
|
|
25997
|
-
const error = (...args) => consoleWith((console) => sync$1(() => {
|
|
25998
|
-
console.error(...args);
|
|
25999
|
-
}));
|
|
26000
|
-
/**
|
|
26001
|
-
* Logs a general-purpose message to the console.
|
|
26002
|
-
*
|
|
26003
|
-
* **Example** (Writing log messages)
|
|
26004
|
-
*
|
|
26005
|
-
* ```ts import.meta.vitest
|
|
26006
|
-
* import { Console, Effect } from "effect"
|
|
26007
|
-
*
|
|
26008
|
-
* const messages: Array<ReadonlyArray<unknown>> = []
|
|
26009
|
-
* const testConsole: Console.Console = Object.assign(Object.create(console), {
|
|
26010
|
-
* log: (...args: ReadonlyArray<unknown>) => messages.push(args)
|
|
26011
|
-
* })
|
|
26012
|
-
* const program = Effect.gen(function*() {
|
|
26013
|
-
* yield* Console.log("Hello, world!")
|
|
26014
|
-
* yield* Console.log("User data:", { name: "John", age: 30 })
|
|
26015
|
-
* yield* Console.log("Processing", 42, "items")
|
|
26016
|
-
* })
|
|
26017
|
-
*
|
|
26018
|
-
* Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
|
|
26019
|
-
* const expected = [
|
|
26020
|
-
* ["Hello, world!"],
|
|
26021
|
-
* ["User data:", { name: "John", age: 30 }],
|
|
26022
|
-
* ["Processing", 42, "items"]
|
|
26023
|
-
* ]
|
|
26024
|
-
* messages // => expected
|
|
26025
|
-
* ```
|
|
26026
|
-
*
|
|
26027
|
-
* @stability stable
|
|
26028
|
-
* @category accessors
|
|
26029
|
-
* @since 2.0.0
|
|
26030
|
-
*/
|
|
26031
|
-
const log = (...args) => consoleWith((console) => sync$1(() => {
|
|
26032
|
-
console.log(...args);
|
|
26033
|
-
}));
|
|
26034
|
-
//#endregion
|
|
26035
25938
|
//#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/cli/CliError.js
|
|
26036
25939
|
/**
|
|
26037
25940
|
* Defines structured errors for the unstable CLI parser and runner.
|
|
@@ -29124,6 +29027,145 @@ const getParamMetadata = (param) => {
|
|
|
29124
29027
|
});
|
|
29125
29028
|
};
|
|
29126
29029
|
//#endregion
|
|
29030
|
+
//#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/Console.js
|
|
29031
|
+
/**
|
|
29032
|
+
* Context reference for the current console service in the Effect system, allowing access to the active console implementation from within the Effect context.
|
|
29033
|
+
*
|
|
29034
|
+
* **When to use**
|
|
29035
|
+
*
|
|
29036
|
+
* Use when you need an effect to run against a provided console implementation,
|
|
29037
|
+
* such as tests or alternate runtimes, rather than the default console.
|
|
29038
|
+
*
|
|
29039
|
+
* **Details**
|
|
29040
|
+
*
|
|
29041
|
+
* When no override is provided, the reference resolves to `globalThis.console`.
|
|
29042
|
+
*
|
|
29043
|
+
* **Example** (Accessing the current console)
|
|
29044
|
+
*
|
|
29045
|
+
* ```ts import.meta.vitest
|
|
29046
|
+
* import { Console, Effect } from "effect"
|
|
29047
|
+
*
|
|
29048
|
+
* const messages: Array<unknown> = []
|
|
29049
|
+
* const testConsole: Console.Console = Object.assign(Object.create(console), {
|
|
29050
|
+
* log: (...args: ReadonlyArray<unknown>) => messages.push(...args)
|
|
29051
|
+
* })
|
|
29052
|
+
* const program = Console.consoleWith((console) =>
|
|
29053
|
+
* Effect.sync(() => {
|
|
29054
|
+
* console.log("Hello from current console!")
|
|
29055
|
+
* })
|
|
29056
|
+
* )
|
|
29057
|
+
*
|
|
29058
|
+
* Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
|
|
29059
|
+
* messages // => ["Hello from current console!"]
|
|
29060
|
+
* ```
|
|
29061
|
+
*
|
|
29062
|
+
* @see {@link consoleWith} for using the current console service inside an effect
|
|
29063
|
+
*
|
|
29064
|
+
* @stability stable
|
|
29065
|
+
* @category services
|
|
29066
|
+
* @since 2.0.0
|
|
29067
|
+
*/
|
|
29068
|
+
const Console = ConsoleRef;
|
|
29069
|
+
/**
|
|
29070
|
+
* Creates an Effect that provides access to the current console service and lets you perform operations with it within an Effect context.
|
|
29071
|
+
*
|
|
29072
|
+
* **Example** (Accessing the current console service)
|
|
29073
|
+
*
|
|
29074
|
+
* ```ts import.meta.vitest
|
|
29075
|
+
* import { Console, Effect } from "effect"
|
|
29076
|
+
*
|
|
29077
|
+
* const messages: Array<unknown> = []
|
|
29078
|
+
* const testConsole: Console.Console = Object.assign(Object.create(console), {
|
|
29079
|
+
* log: (...args: ReadonlyArray<unknown>) => messages.push(...args),
|
|
29080
|
+
* error: (...args: ReadonlyArray<unknown>) => messages.push(...args)
|
|
29081
|
+
* })
|
|
29082
|
+
* const program = Console.consoleWith((console) =>
|
|
29083
|
+
* Effect.sync(() => {
|
|
29084
|
+
* console.log("Hello, world!")
|
|
29085
|
+
* console.error("This is an error message")
|
|
29086
|
+
* })
|
|
29087
|
+
* )
|
|
29088
|
+
*
|
|
29089
|
+
* Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
|
|
29090
|
+
* messages // => ["Hello, world!", "This is an error message"]
|
|
29091
|
+
* ```
|
|
29092
|
+
*
|
|
29093
|
+
* @stability stable
|
|
29094
|
+
* @category constructors
|
|
29095
|
+
* @since 2.0.0
|
|
29096
|
+
*/
|
|
29097
|
+
const consoleWith = (f) => withFiber$1((fiber) => f(fiber.getRef(Console)));
|
|
29098
|
+
/**
|
|
29099
|
+
* Writes an error-level message to the console, typically displayed with error
|
|
29100
|
+
* styling by the active console implementation.
|
|
29101
|
+
*
|
|
29102
|
+
* **Example** (Writing error messages)
|
|
29103
|
+
*
|
|
29104
|
+
* ```ts import.meta.vitest
|
|
29105
|
+
* import { Console, Effect } from "effect"
|
|
29106
|
+
*
|
|
29107
|
+
* const messages: Array<ReadonlyArray<unknown>> = []
|
|
29108
|
+
* const testConsole: Console.Console = Object.assign(Object.create(console), {
|
|
29109
|
+
* error: (...args: ReadonlyArray<unknown>) => messages.push(args)
|
|
29110
|
+
* })
|
|
29111
|
+
* const program = Effect.gen(function*() {
|
|
29112
|
+
* yield* Console.error("Something went wrong!")
|
|
29113
|
+
* yield* Console.error("Error details:", {
|
|
29114
|
+
* code: 500,
|
|
29115
|
+
* message: "Internal Server Error"
|
|
29116
|
+
* })
|
|
29117
|
+
* })
|
|
29118
|
+
*
|
|
29119
|
+
* Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
|
|
29120
|
+
* const expected = [
|
|
29121
|
+
* ["Something went wrong!"],
|
|
29122
|
+
* ["Error details:", { code: 500, message: "Internal Server Error" }]
|
|
29123
|
+
* ]
|
|
29124
|
+
* messages // => expected
|
|
29125
|
+
* ```
|
|
29126
|
+
*
|
|
29127
|
+
* @stability stable
|
|
29128
|
+
* @category accessors
|
|
29129
|
+
* @since 2.0.0
|
|
29130
|
+
*/
|
|
29131
|
+
const error = (...args) => consoleWith((console) => sync$1(() => {
|
|
29132
|
+
console.error(...args);
|
|
29133
|
+
}));
|
|
29134
|
+
/**
|
|
29135
|
+
* Logs a general-purpose message to the console.
|
|
29136
|
+
*
|
|
29137
|
+
* **Example** (Writing log messages)
|
|
29138
|
+
*
|
|
29139
|
+
* ```ts import.meta.vitest
|
|
29140
|
+
* import { Console, Effect } from "effect"
|
|
29141
|
+
*
|
|
29142
|
+
* const messages: Array<ReadonlyArray<unknown>> = []
|
|
29143
|
+
* const testConsole: Console.Console = Object.assign(Object.create(console), {
|
|
29144
|
+
* log: (...args: ReadonlyArray<unknown>) => messages.push(args)
|
|
29145
|
+
* })
|
|
29146
|
+
* const program = Effect.gen(function*() {
|
|
29147
|
+
* yield* Console.log("Hello, world!")
|
|
29148
|
+
* yield* Console.log("User data:", { name: "John", age: 30 })
|
|
29149
|
+
* yield* Console.log("Processing", 42, "items")
|
|
29150
|
+
* })
|
|
29151
|
+
*
|
|
29152
|
+
* Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
|
|
29153
|
+
* const expected = [
|
|
29154
|
+
* ["Hello, world!"],
|
|
29155
|
+
* ["User data:", { name: "John", age: 30 }],
|
|
29156
|
+
* ["Processing", 42, "items"]
|
|
29157
|
+
* ]
|
|
29158
|
+
* messages // => expected
|
|
29159
|
+
* ```
|
|
29160
|
+
*
|
|
29161
|
+
* @stability stable
|
|
29162
|
+
* @category accessors
|
|
29163
|
+
* @since 2.0.0
|
|
29164
|
+
*/
|
|
29165
|
+
const log = (...args) => consoleWith((console) => sync$1(() => {
|
|
29166
|
+
console.log(...args);
|
|
29167
|
+
}));
|
|
29168
|
+
//#endregion
|
|
29127
29169
|
//#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/cli/CliOutput.js
|
|
29128
29170
|
/**
|
|
29129
29171
|
* Formats CLI help and errors as text.
|
|
@@ -32440,7 +32482,7 @@ const runWith = (command, config) => {
|
|
|
32440
32482
|
};
|
|
32441
32483
|
//#endregion
|
|
32442
32484
|
//#region package.json
|
|
32443
|
-
var version = "0.
|
|
32485
|
+
var version = "0.6.0";
|
|
32444
32486
|
//#endregion
|
|
32445
32487
|
//#region ../../node_modules/.pnpm/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/scanner.js
|
|
32446
32488
|
/**
|
|
@@ -35097,51 +35139,45 @@ var import_semver = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exp
|
|
|
35097
35139
|
})))(), 1);
|
|
35098
35140
|
var BumpError = class extends TaggedError()("BumpError", {
|
|
35099
35141
|
path: String$5,
|
|
35100
|
-
|
|
35142
|
+
at: String$5,
|
|
35101
35143
|
reason: String$5
|
|
35102
35144
|
}) {
|
|
35103
35145
|
get message() {
|
|
35104
|
-
return `${this.path}: cannot bump ${this.
|
|
35146
|
+
return `${this.path}: cannot bump ${this.at}: ${this.reason}`;
|
|
35105
35147
|
}
|
|
35106
35148
|
};
|
|
35107
|
-
const rangePath = (pkg) => pkg === "vite-plus" ? ["vp", "version"] : pkg === "node" ? ["vp", "node"] : [
|
|
35108
|
-
"vp",
|
|
35109
|
-
"globals",
|
|
35110
|
-
pkg
|
|
35111
|
-
];
|
|
35112
35149
|
const bumpedRange = (current, installed) => import_semver.valid(current) !== null ? installed : `^${installed}`;
|
|
35113
|
-
const bumpText = (text,
|
|
35114
|
-
const path = rangePath(pkg);
|
|
35150
|
+
const bumpText = (text, path, installed) => {
|
|
35115
35151
|
const node = findNodeAtLocation(parseTree(text) ?? {
|
|
35116
35152
|
type: "null",
|
|
35117
35153
|
offset: 0,
|
|
35118
35154
|
length: 0
|
|
35119
|
-
}, path);
|
|
35155
|
+
}, [...path]);
|
|
35120
35156
|
if (node === void 0 || node.type !== "string" || typeof node.value !== "string") return { reason: `${path.join(".")} is not a string literal; change it by hand` };
|
|
35121
35157
|
const range = bumpedRange(node.value, installed);
|
|
35122
35158
|
return {
|
|
35123
|
-
text: applyEdits(text, modify(text, path, range, { formattingOptions: {
|
|
35159
|
+
text: applyEdits(text, modify(text, [...path], range, { formattingOptions: {
|
|
35124
35160
|
insertSpaces: true,
|
|
35125
35161
|
tabSize: 2
|
|
35126
35162
|
} })),
|
|
35127
35163
|
range
|
|
35128
35164
|
};
|
|
35129
35165
|
};
|
|
35130
|
-
const bumpConfig = fn("bumpConfig")(function* (configPath,
|
|
35166
|
+
const bumpConfig = fn("bumpConfig")(function* (configPath, path, installed) {
|
|
35131
35167
|
const fs = yield* FileSystem;
|
|
35132
35168
|
const text = yield* fs.readFileString(configPath);
|
|
35133
|
-
const result = bumpText(text,
|
|
35169
|
+
const result = bumpText(text, path, installed);
|
|
35134
35170
|
if ("reason" in result) return yield* new BumpError({
|
|
35135
35171
|
path: configPath,
|
|
35136
|
-
|
|
35172
|
+
at: path.join("."),
|
|
35137
35173
|
reason: result.reason
|
|
35138
35174
|
});
|
|
35139
35175
|
yield* fs.writeFileString(configPath, result.text);
|
|
35140
35176
|
return result.range;
|
|
35141
35177
|
});
|
|
35142
35178
|
const Targeting = {
|
|
35143
|
-
platforms: optionalKey(NonEmptyArray(Literals$2(["darwin", "linux"]))),
|
|
35144
|
-
machines: optionalKey(NonEmptyArray(NonEmptyString))
|
|
35179
|
+
platforms: optionalKey(NonEmptyArray(Literals$2(["darwin", "linux"]))).annotateKey({ description: "`darwin`, `linux`, or both." }),
|
|
35180
|
+
machines: optionalKey(NonEmptyArray(NonEmptyString)).annotateKey({ description: "Machine names from `machines`." })
|
|
35145
35181
|
};
|
|
35146
35182
|
const Origin = String$5.check(isPattern(/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/, {
|
|
35147
35183
|
title: "GitHub origin",
|
|
@@ -35149,26 +35185,76 @@ const Origin = String$5.check(isPattern(/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/, {
|
|
|
35149
35185
|
}));
|
|
35150
35186
|
const Ref = NonEmptyString;
|
|
35151
35187
|
const isPin = (ref) => /^[0-9a-f]{40}$/.test(ref);
|
|
35188
|
+
const ProviderName = Literals$2([
|
|
35189
|
+
"vp",
|
|
35190
|
+
"brew",
|
|
35191
|
+
"cask",
|
|
35192
|
+
"repos"
|
|
35193
|
+
]);
|
|
35152
35194
|
const Range = NonEmptyString.check(makeFilter((range) => range === "latest" || import_semver.validRange(range) !== null, {
|
|
35153
35195
|
title: "Range",
|
|
35154
|
-
description: "a semver range, \"*\", or \"latest\""
|
|
35196
|
+
description: "a semver range (leave inside it, install below it, ask above it), \"*\" (install when missing, never upgrade), or \"latest\" (upgrade to the newest on every Ship)"
|
|
35155
35197
|
}));
|
|
35156
35198
|
const TrackedRepo = Struct({
|
|
35157
35199
|
...Targeting,
|
|
35158
|
-
origin: Origin,
|
|
35159
|
-
ref: Ref,
|
|
35160
|
-
install: NonEmptyString,
|
|
35161
|
-
name: optionalKey(NonEmptyString),
|
|
35162
|
-
outputs: optionalKey(ArraySchema(NonEmptyString)),
|
|
35163
|
-
files: optionalKey(Record(NonEmptyString, String$5))
|
|
35200
|
+
origin: Origin.annotateKey({ description: "The GitHub `owner/repo`." }),
|
|
35201
|
+
ref: Ref.annotateKey({ description: "A branch, or a full 40-character commit SHA to pin it." }),
|
|
35202
|
+
install: NonEmptyString.annotateKey({ description: "The Install Command, run in a login shell inside the Managed Clone." }),
|
|
35203
|
+
name: optionalKey(NonEmptyString).annotateKey({ description: "Names the clone's directory and the repo in output. Default: `<owner>-<repo>`." }),
|
|
35204
|
+
outputs: optionalKey(ArraySchema(NonEmptyString)).annotateKey({ description: "Paths outside the clone the Install Command produces. If one changes by hand, the repo reinstalls." }),
|
|
35205
|
+
files: optionalKey(Record(NonEmptyString, String$5)).annotateKey({ description: "Gitignored files written into the clone before every install, keyed by path. Never secrets." })
|
|
35164
35206
|
});
|
|
35207
|
+
const Global = Union([Range, Struct({
|
|
35208
|
+
...Targeting,
|
|
35209
|
+
range: Range
|
|
35210
|
+
})]);
|
|
35211
|
+
const PACKAGE_MANAGERS = [
|
|
35212
|
+
"npm",
|
|
35213
|
+
"pnpm",
|
|
35214
|
+
"yarn",
|
|
35215
|
+
"bun"
|
|
35216
|
+
];
|
|
35217
|
+
const NOT_GLOBALS = {
|
|
35218
|
+
vp: "the vp Item is vp itself; set it with vp.version",
|
|
35219
|
+
mode: "the mode Item is vp's managed mode",
|
|
35220
|
+
node: "set Node with vp.node",
|
|
35221
|
+
...Object.fromEntries(PACKAGE_MANAGERS.map((pm) => [pm, `set ${pm} with vp.${pm}`])),
|
|
35222
|
+
"vite-plus": "set vp with vp.version",
|
|
35223
|
+
fleetfizz: "a Ship installs the fleetfizz that ships"
|
|
35224
|
+
};
|
|
35165
35225
|
const VpSection = Struct({
|
|
35166
35226
|
...Targeting,
|
|
35167
|
-
|
|
35168
|
-
|
|
35169
|
-
|
|
35227
|
+
dependsOn: optionalKey(ArraySchema(ProviderName)).annotateKey({ description: "Providers whose Items must succeed on a Machine before vp changes there." }),
|
|
35228
|
+
version: optionalKey(Range).annotateKey({ description: "The vp version. Pin it exactly." }),
|
|
35229
|
+
node: optionalKey(Range).annotateKey({ description: "The default Node version. Pin it exactly." }),
|
|
35230
|
+
npm: optionalKey(Range).annotateKey({ description: "The default npm version." }),
|
|
35231
|
+
pnpm: optionalKey(Range).annotateKey({ description: "The default pnpm version." }),
|
|
35232
|
+
yarn: optionalKey(Range).annotateKey({ description: "The default Yarn version." }),
|
|
35233
|
+
bun: optionalKey(Range).annotateKey({ description: "The default Bun version." }),
|
|
35234
|
+
globals: optionalKey(Record(NonEmptyString, Global).check(makeFilter((globals) => {
|
|
35235
|
+
const name = Object.keys(globals).find((n) => n in NOT_GLOBALS);
|
|
35236
|
+
return name === void 0 || `"${name}" is not a Global: ${NOT_GLOBALS[name]}`;
|
|
35237
|
+
}, {
|
|
35238
|
+
title: "Globals",
|
|
35239
|
+
description: "npm packages other than vp, node, a package manager, vite-plus, and fleetfizz"
|
|
35240
|
+
}))).annotateKey({ description: "Global packages, each with a Range, or `{ range, platforms?, machines? }` to target one." })
|
|
35170
35241
|
});
|
|
35171
|
-
const
|
|
35242
|
+
const HomebrewRange = Literals$2(["*", "latest"]).annotate({ description: "\"*\" (install when missing, never upgrade) or \"latest\" (brew update, then upgrade on every Ship)" });
|
|
35243
|
+
const HomebrewItems = Record(NonEmptyString, Union([HomebrewRange, Struct({
|
|
35244
|
+
...Targeting,
|
|
35245
|
+
range: HomebrewRange
|
|
35246
|
+
})]));
|
|
35247
|
+
const homebrewSection = (items, noun) => Struct({
|
|
35248
|
+
...Targeting,
|
|
35249
|
+
dependsOn: optionalKey(ArraySchema(ProviderName)).annotateKey({ description: "Providers whose Items must succeed on a Machine before this one changes there." }),
|
|
35250
|
+
items: optionalKey(items).annotateKey({ description: `${noun}, each \`"*"\` or \`"latest"\`, or \`{ range, platforms?, machines? }\` to target one. A tapped name (\`owner/tap/name\`) taps on install.` })
|
|
35251
|
+
});
|
|
35252
|
+
const BrewSection = homebrewSection(HomebrewItems.check(makeFilter((items) => !Object.hasOwn(items, "brew") || "\"brew\" is Homebrew itself", {
|
|
35253
|
+
title: "Formulae",
|
|
35254
|
+
description: "Formulae other than brew"
|
|
35255
|
+
})), "Formulae");
|
|
35256
|
+
const CaskSection = homebrewSection(HomebrewItems, "Casks");
|
|
35257
|
+
const MachineSettings = Struct({ clones: optionalKey(NonEmptyString).annotateKey({ description: `Where this Machine keeps its Managed Clones. \`~\` expands on that Machine. Default: \`~/.local/share/fleetfizz/clones\`.` }) });
|
|
35172
35258
|
const FleetConfig = Struct({
|
|
35173
35259
|
$schema: optionalKey(String$5),
|
|
35174
35260
|
shippedAt: optionalKey(String$5),
|
|
@@ -35177,9 +35263,11 @@ const FleetConfig = Struct({
|
|
|
35177
35263
|
machines: Record(NonEmptyString, MachineSettings).check(makeFilter((machines) => Object.keys(machines).length > 0, {
|
|
35178
35264
|
title: "Fleet",
|
|
35179
35265
|
description: "at least one Machine"
|
|
35180
|
-
})),
|
|
35181
|
-
repos: optionalKey(ArraySchema(TrackedRepo)),
|
|
35182
|
-
vp: optionalKey(VpSection)
|
|
35266
|
+
})).annotateKey({ description: "The Fleet, keyed by each Machine's tailnet name. A tailnet member that is not listed is not part of the Fleet." }),
|
|
35267
|
+
repos: optionalKey(ArraySchema(TrackedRepo)).annotateKey({ description: "Each Tracked Repo is cloned on every targeted Machine and installed from its origin ref." }),
|
|
35268
|
+
vp: optionalKey(VpSection).annotateKey({ description: "The vite-plus toolchain." }),
|
|
35269
|
+
brew: optionalKey(BrewSection).annotateKey({ description: "Homebrew Formulae. Needs Homebrew on the Machine; a Ship never installs it." }),
|
|
35270
|
+
cask: optionalKey(CaskSection).annotateKey({ description: "Homebrew Casks, macOS only. Needs Homebrew on the Machine." })
|
|
35183
35271
|
});
|
|
35184
35272
|
const repoName = (repo) => repo.name ?? repo.origin.replace("/", "-");
|
|
35185
35273
|
const SELF_PACKAGE = "fleetfizz";
|
|
@@ -35308,8 +35396,10 @@ const vpEnv = [
|
|
|
35308
35396
|
].join("\n");
|
|
35309
35397
|
const prelude = [
|
|
35310
35398
|
vpEnv,
|
|
35399
|
+
`export PATH="$PATH:/opt/homebrew/bin:/home/linuxbrew/.linuxbrew/bin"`,
|
|
35311
35400
|
`ff_json() { printf '%s' "$1" | sed -e 's/\\\\/\\\\\\\\/g' -e 's/"/\\\\"/g' | tr -d '\\n'; }`,
|
|
35312
|
-
`ff_state() { printf 'ff-state %s\\n' "$1"; }
|
|
35401
|
+
`ff_state() { printf 'ff-state %s\\n' "$1"; }`,
|
|
35402
|
+
`ff_at() { ff_state "{\\"provider\\":\\"$(ff_json "$1")\\",\\"fact\\":\\"at\\",\\"item\\":\\"$(ff_json "$2")\\",\\"at\\":\\"$(ff_json "$3")\\"}"; }`
|
|
35313
35403
|
].join("\n");
|
|
35314
35404
|
const fingerprintFn = [
|
|
35315
35405
|
"ff_fp() {",
|
|
@@ -35324,470 +35414,52 @@ const fingerprintFn = [
|
|
|
35324
35414
|
const STATE_DIR = "~/.local/state/fleetfizz";
|
|
35325
35415
|
const RECORDS_DIR = `${STATE_DIR}/records`;
|
|
35326
35416
|
const CONFIG_DIR_SH = "\"${XDG_CONFIG_HOME:-$HOME/.config}/fleetfizz\"";
|
|
35417
|
+
const recordDir = (provider) => `${quotePath(RECORDS_DIR)}/${quote(provider)}`;
|
|
35418
|
+
const recordFile = (provider, item) => `${recordDir(provider)}/${quote(`${item}.json`)}`;
|
|
35419
|
+
const LEGACY_RECORD_PROVIDER = "repos";
|
|
35420
|
+
const recordFact = (provider, item) => [
|
|
35421
|
+
`ff_r=${recordFile(provider, item)}`,
|
|
35422
|
+
...provider === LEGACY_RECORD_PROVIDER ? [`[ -f "$ff_r" ] || ff_r=${quotePath(RECORDS_DIR)}/${quote(`${item}.json`)}`] : [],
|
|
35423
|
+
`if [ -f "$ff_r" ]; then ff_state "{\\"fact\\":\\"record\\",\\"provider\\":\\"${provider}\\",\\"item\\":\\"$(ff_json ${quote(item)})\\",\\"record\\":$(tr -d '\\n' < "$ff_r")}"; fi`
|
|
35424
|
+
];
|
|
35327
35425
|
const checkScript = (input) => {
|
|
35328
35426
|
const lines = [
|
|
35329
35427
|
prelude,
|
|
35330
35428
|
fingerprintFn,
|
|
35331
|
-
`ff_state "{\\"fact\\":\\"home\\",\\"path\\":\\"$(ff_json "$HOME")\\"}"
|
|
35332
|
-
"if command -v vp >/dev/null 2>&1; then",
|
|
35333
|
-
` ff_v=$(command vp --version 2>/dev/null | sed -n 's/^vp v\\{0,1\\}\\([^ ]*\\).*/\\1/p' | head -n 1)`,
|
|
35334
|
-
` ff_state "{\\"fact\\":\\"vp\\",\\"version\\":\\"$(ff_json "$ff_v")\\",\\"path\\":\\"$(ff_json "$(command -v vp)")\\"}"`,
|
|
35335
|
-
` ff_n=$(command vp env default 2>/dev/null | sed -n 's/^Default Node.js version: //p' | head -n 1)`,
|
|
35336
|
-
` if [ -n "$ff_n" ]; then ff_state "{\\"fact\\":\\"node\\",\\"version\\":\\"$(ff_json "$ff_n")\\"}"; else ff_state '{"fact":"node","version":null}'; fi`,
|
|
35337
|
-
` ff_g=$(command vp list --global --json 2>/dev/null | tr -d '\\n')`,
|
|
35338
|
-
` ff_state "{\\"fact\\":\\"globals\\",\\"list\\":\${ff_g:-[]}}"`,
|
|
35339
|
-
"else",
|
|
35340
|
-
` ff_state '{"fact":"vp","version":null}'`,
|
|
35341
|
-
"fi",
|
|
35342
|
-
`ff_root=${quotePath(input.cloneRoot)}`,
|
|
35343
|
-
`if [ -d "$ff_root" ]; then ff_present=true; else ff_present=false; fi`,
|
|
35344
|
-
`ff_state "{\\"fact\\":\\"cloneRoot\\",\\"path\\":\\"$(ff_json "$ff_root")\\",\\"present\\":$ff_present}"`
|
|
35429
|
+
`ff_state "{\\"fact\\":\\"home\\",\\"path\\":\\"$(ff_json "$HOME")\\"}"`
|
|
35345
35430
|
];
|
|
35346
|
-
for (const
|
|
35347
|
-
|
|
35348
|
-
|
|
35349
|
-
for (const output of repo.outputs) lines.push(`ff_p=${quotePath(output)}; ff_f=$(ff_fp "$ff_p")`, `if [ -n "$ff_f" ]; then ff_state "{\\"fact\\":\\"output\\",\\"repo\\":\\"$(ff_json ${name})\\",\\"path\\":\\"$(ff_json ${quote(output)})\\",\\"fingerprint\\":\\"$(ff_json "$ff_f")\\"}"; else ff_state "{\\"fact\\":\\"output\\",\\"repo\\":\\"$(ff_json ${name})\\",\\"path\\":\\"$(ff_json ${quote(output)})\\",\\"fingerprint\\":null}"; fi`);
|
|
35350
|
-
}
|
|
35351
|
-
for (const repo of input.repos) {
|
|
35352
|
-
const name = quote(repo.name);
|
|
35353
|
-
lines.push(`ff_r=${quotePath(RECORDS_DIR)}/${quote(`${repo.name}.json`)}`, `if [ -f "$ff_r" ]; then ff_state "{\\"fact\\":\\"record\\",\\"name\\":\\"$(ff_json ${name})\\",\\"record\\":$(tr -d '\\n' < "$ff_r")}"; fi`);
|
|
35431
|
+
for (const { provider, items } of input.providers) {
|
|
35432
|
+
lines.push(provider.observe(items, input.scope));
|
|
35433
|
+
if (provider.rule === "record") for (const item of items) lines.push(...recordFact(provider.name, item.name));
|
|
35354
35434
|
}
|
|
35355
35435
|
lines.push(`ff_c=${CONFIG_DIR_SH}/${quote(CONFIG_FILE)}`, `if [ -f "$ff_c" ]; then ff_state "{\\"fact\\":\\"config\\",\\"present\\":true,\\"base64\\":\\"$(base64 < "$ff_c" | tr -d '\\n')\\"}"; else ff_state '{"fact":"config","present":false}'; fi`);
|
|
35356
35436
|
lines.push(`ff_state '{"fact":"end"}'`);
|
|
35357
35437
|
return lines.join("\n");
|
|
35358
35438
|
};
|
|
35359
35439
|
//#endregion
|
|
35360
|
-
//#region ../../packages/core/src/range.ts
|
|
35361
|
-
const newest$1 = (available) => available.filter((v) => import_semver.prerelease(v) === null).sort(import_semver.rcompare)[0];
|
|
35362
|
-
const decideRange = (range, installed, available) => {
|
|
35363
|
-
if (range === "latest") {
|
|
35364
|
-
const version = newest$1(available);
|
|
35365
|
-
if (version === void 0) return { kind: "unavailable" };
|
|
35366
|
-
return installed === version ? { kind: "leave" } : {
|
|
35367
|
-
kind: "install",
|
|
35368
|
-
version
|
|
35369
|
-
};
|
|
35370
|
-
}
|
|
35371
|
-
if (range === "*") {
|
|
35372
|
-
if (installed !== void 0) return { kind: "leave" };
|
|
35373
|
-
const version = newest$1(available);
|
|
35374
|
-
return version === void 0 ? { kind: "unavailable" } : {
|
|
35375
|
-
kind: "install",
|
|
35376
|
-
version
|
|
35377
|
-
};
|
|
35378
|
-
}
|
|
35379
|
-
if (installed !== void 0 && import_semver.satisfies(installed, range)) return { kind: "leave" };
|
|
35380
|
-
if (installed !== void 0 && import_semver.gtr(installed, range)) return {
|
|
35381
|
-
kind: "ask",
|
|
35382
|
-
installed
|
|
35383
|
-
};
|
|
35384
|
-
const version = import_semver.maxSatisfying([...available], range);
|
|
35385
|
-
return version === null ? { kind: "unavailable" } : {
|
|
35386
|
-
kind: "install",
|
|
35387
|
-
version
|
|
35388
|
-
};
|
|
35389
|
-
};
|
|
35390
|
-
//#endregion
|
|
35391
|
-
//#region ../../packages/core/src/record.ts
|
|
35392
|
-
const sameRecord = (a, b) => Object.keys(a).length === Object.keys(b).length && Object.entries(a).every(([k, v]) => b[k] === v);
|
|
35393
|
-
const decideInstall = (record, now, rebuild) => {
|
|
35394
|
-
if (rebuild) return {
|
|
35395
|
-
run: true,
|
|
35396
|
-
reason: "--rebuild"
|
|
35397
|
-
};
|
|
35398
|
-
if (record === void 0) return {
|
|
35399
|
-
run: true,
|
|
35400
|
-
reason: "never installed"
|
|
35401
|
-
};
|
|
35402
|
-
if (record.commit !== now.commit) return {
|
|
35403
|
-
run: true,
|
|
35404
|
-
reason: `new commit ${now.commit.slice(0, 7)}`
|
|
35405
|
-
};
|
|
35406
|
-
if (record.path !== now.path) return {
|
|
35407
|
-
run: true,
|
|
35408
|
-
reason: "clone moved"
|
|
35409
|
-
};
|
|
35410
|
-
if (record.install !== now.install) return {
|
|
35411
|
-
run: true,
|
|
35412
|
-
reason: "install command changed"
|
|
35413
|
-
};
|
|
35414
|
-
if (!sameRecord(record.files, now.files)) return {
|
|
35415
|
-
run: true,
|
|
35416
|
-
reason: "files changed"
|
|
35417
|
-
};
|
|
35418
|
-
for (const [path, fingerprint] of Object.entries(now.outputs)) if (fingerprint === void 0 || record.outputs[path] !== fingerprint) return {
|
|
35419
|
-
run: true,
|
|
35420
|
-
reason: `drift: ${path}`
|
|
35421
|
-
};
|
|
35422
|
-
return { run: false };
|
|
35423
|
-
};
|
|
35424
|
-
//#endregion
|
|
35425
|
-
//#region ../../packages/core/src/targeting.ts
|
|
35426
|
-
const targets = (targeting, machine) => (targeting.platforms?.includes(machine.platform) ?? true) && (targeting.machines?.includes(machine.name) ?? true);
|
|
35427
|
-
//#endregion
|
|
35428
|
-
//#region ../../packages/core/src/plan.ts
|
|
35429
|
-
const VP_PACKAGE = "vite-plus";
|
|
35430
|
-
const NODE_PACKAGE = "node";
|
|
35431
|
-
const expandHome = (path, home) => path === "~" ? home : path.startsWith("~/") ? `${home}${path.slice(1)}` : path;
|
|
35432
|
-
const isHomebrew = (path) => path !== void 0 && (path.includes("/homebrew/") || path.includes("/Cellar/"));
|
|
35433
|
-
const planVp = (input) => {
|
|
35434
|
-
const out = {
|
|
35435
|
-
steps: [],
|
|
35436
|
-
questions: [],
|
|
35437
|
-
skipped: [],
|
|
35438
|
-
failures: []
|
|
35439
|
-
};
|
|
35440
|
-
const { state, machine } = input;
|
|
35441
|
-
const vp = input.config.vp;
|
|
35442
|
-
const targeted = vp !== void 0 && targets(vp, machine);
|
|
35443
|
-
const missing = state.vp === void 0 || state.vp.version === null;
|
|
35444
|
-
if (missing) out.steps.push({ kind: "bootstrap" });
|
|
35445
|
-
const installed = missing ? void 0 : state.vp?.version ?? void 0;
|
|
35446
|
-
const decide = (pkg, range, current, shown = pkg) => {
|
|
35447
|
-
if (range === void 0) return void 0;
|
|
35448
|
-
const decision = decideRange(range, current, input.versions[pkg] ?? []);
|
|
35449
|
-
switch (decision.kind) {
|
|
35450
|
-
case "leave": return;
|
|
35451
|
-
case "install": return decision.version;
|
|
35452
|
-
case "unavailable":
|
|
35453
|
-
out.failures.push({
|
|
35454
|
-
what: shown,
|
|
35455
|
-
reason: `no published version satisfies ${range}`
|
|
35456
|
-
});
|
|
35457
|
-
return;
|
|
35458
|
-
case "ask": {
|
|
35459
|
-
const answer = input.answers[pkg];
|
|
35460
|
-
if (answer === "downgrade") {
|
|
35461
|
-
const target = decideRange(range, void 0, input.versions[pkg] ?? []);
|
|
35462
|
-
if (target.kind === "install") return target.version;
|
|
35463
|
-
out.failures.push({
|
|
35464
|
-
what: shown,
|
|
35465
|
-
reason: `no published version satisfies ${range}`
|
|
35466
|
-
});
|
|
35467
|
-
return;
|
|
35468
|
-
}
|
|
35469
|
-
if (answer === "skip") {
|
|
35470
|
-
out.skipped.push({
|
|
35471
|
-
what: shown,
|
|
35472
|
-
reason: `${decision.installed} is above ${range}`
|
|
35473
|
-
});
|
|
35474
|
-
return;
|
|
35475
|
-
}
|
|
35476
|
-
out.questions.push({
|
|
35477
|
-
machine: machine.name,
|
|
35478
|
-
package: pkg,
|
|
35479
|
-
installed: decision.installed,
|
|
35480
|
-
range
|
|
35481
|
-
});
|
|
35482
|
-
return;
|
|
35483
|
-
}
|
|
35484
|
-
}
|
|
35485
|
-
};
|
|
35486
|
-
const nodeNow = missing ? void 0 : state.node ?? void 0;
|
|
35487
|
-
let nodeTarget;
|
|
35488
|
-
if (targeted) {
|
|
35489
|
-
const vpTarget = decide(VP_PACKAGE, vp.version, installed, "vp");
|
|
35490
|
-
if (vpTarget !== void 0) {
|
|
35491
|
-
if (isHomebrew(state.vp?.path)) out.failures.push({
|
|
35492
|
-
what: "vp",
|
|
35493
|
-
reason: `vp ${installed} was installed with Homebrew; run brew upgrade vite-plus to get ${vpTarget}`
|
|
35494
|
-
});
|
|
35495
|
-
else out.steps.push({
|
|
35496
|
-
kind: "vp",
|
|
35497
|
-
from: installed ?? null,
|
|
35498
|
-
version: vpTarget
|
|
35499
|
-
});
|
|
35500
|
-
}
|
|
35501
|
-
nodeTarget = decide(NODE_PACKAGE, vp.node, nodeNow, "node");
|
|
35502
|
-
if (nodeTarget !== void 0) out.steps.push({
|
|
35503
|
-
kind: "node",
|
|
35504
|
-
from: nodeNow ?? null,
|
|
35505
|
-
version: nodeTarget
|
|
35506
|
-
});
|
|
35507
|
-
}
|
|
35508
|
-
const nodeDefault = nodeTarget ?? nodeNow;
|
|
35509
|
-
const byName = new Map((missing ? [] : state.globals).map((g) => [g.name, g]));
|
|
35510
|
-
const installs = [];
|
|
35511
|
-
const wanted = Object.entries(targeted ? vp.globals ?? {} : {});
|
|
35512
|
-
for (const [name, range] of [...wanted, [SELF_PACKAGE, input.self]]) {
|
|
35513
|
-
const current = byName.get(name);
|
|
35514
|
-
const version = name === "fleetfizz" ? decideSelf(current?.version, input.self, out) : decide(name, range, current?.version);
|
|
35515
|
-
if (version !== void 0) installs.push(current === void 0 ? {
|
|
35516
|
-
name,
|
|
35517
|
-
version
|
|
35518
|
-
} : {
|
|
35519
|
-
name,
|
|
35520
|
-
from: current.version,
|
|
35521
|
-
version
|
|
35522
|
-
});
|
|
35523
|
-
else if (current !== void 0 && nodeDefault !== void 0 && current.platform?.node !== void 0 && current.platform.node !== nodeDefault) out.steps.push({
|
|
35524
|
-
kind: "reinstall",
|
|
35525
|
-
name,
|
|
35526
|
-
version: current.version
|
|
35527
|
-
});
|
|
35528
|
-
}
|
|
35529
|
-
if (installs.length > 0) out.steps.push({
|
|
35530
|
-
kind: "globals",
|
|
35531
|
-
packages: installs
|
|
35532
|
-
});
|
|
35533
|
-
return out;
|
|
35534
|
-
};
|
|
35535
|
-
const decideSelf = (installed, self, out) => {
|
|
35536
|
-
if (installed === void 0 || import_semver.lt(installed, self)) return self;
|
|
35537
|
-
if (import_semver.gt(installed, self)) out.failures.push({
|
|
35538
|
-
what: SELF_PACKAGE,
|
|
35539
|
-
reason: `runs ${SELF_PACKAGE} ${installed}, newer than this ${self}; upgrade here first`
|
|
35540
|
-
});
|
|
35541
|
-
};
|
|
35542
|
-
const planRepos = (input) => {
|
|
35543
|
-
const repos = [];
|
|
35544
|
-
const failures = [];
|
|
35545
|
-
const { config, machine, state } = input;
|
|
35546
|
-
const home = state.home ?? "~";
|
|
35547
|
-
const root = config.machines?.[machine.name]?.clones ?? "~/.local/share/fleetfizz/clones";
|
|
35548
|
-
for (const repo of config.repos ?? []) {
|
|
35549
|
-
if (!targets(repo, machine)) continue;
|
|
35550
|
-
const name = repoName(repo);
|
|
35551
|
-
if (state.cloneRoot !== void 0 && !state.cloneRoot.present && !root.startsWith("~")) {
|
|
35552
|
-
failures.push({
|
|
35553
|
-
what: name,
|
|
35554
|
-
reason: `clone root not mounted: ${state.cloneRoot.path}`
|
|
35555
|
-
});
|
|
35556
|
-
continue;
|
|
35557
|
-
}
|
|
35558
|
-
const current = state.repos[name];
|
|
35559
|
-
const expectedOrigin = `https://github.com/${repo.origin}.git`;
|
|
35560
|
-
if (current?.present && current.origin !== void 0 && current.origin !== expectedOrigin) {
|
|
35561
|
-
failures.push({
|
|
35562
|
-
what: name,
|
|
35563
|
-
reason: `managed clone has origin ${current.origin}, expected ${expectedOrigin}`
|
|
35564
|
-
});
|
|
35565
|
-
continue;
|
|
35566
|
-
}
|
|
35567
|
-
const path = `${root}/${name}`;
|
|
35568
|
-
const expanded = expandHome(path, home);
|
|
35569
|
-
const files = repo.files ?? {};
|
|
35570
|
-
const outputs = repo.outputs ?? [];
|
|
35571
|
-
const rebuild = input.rebuild === "all" || input.rebuild !== "none" && input.rebuild.includes(name);
|
|
35572
|
-
const decision = decideInstall(state.records[name], {
|
|
35573
|
-
commit: current?.head ?? "",
|
|
35574
|
-
path: expanded,
|
|
35575
|
-
install: repo.install,
|
|
35576
|
-
files,
|
|
35577
|
-
outputs: Object.fromEntries(outputs.map((o) => [o, state.outputs[name]?.[o] ?? void 0]))
|
|
35578
|
-
}, rebuild);
|
|
35579
|
-
repos.push({
|
|
35580
|
-
name,
|
|
35581
|
-
origin: repo.origin,
|
|
35582
|
-
ref: repo.ref,
|
|
35583
|
-
pin: isPin(repo.ref),
|
|
35584
|
-
path,
|
|
35585
|
-
expanded,
|
|
35586
|
-
...current?.head === void 0 ? {} : { head: current.head },
|
|
35587
|
-
install: repo.install,
|
|
35588
|
-
files,
|
|
35589
|
-
outputs,
|
|
35590
|
-
decision
|
|
35591
|
-
});
|
|
35592
|
-
}
|
|
35593
|
-
return {
|
|
35594
|
-
repos,
|
|
35595
|
-
failures
|
|
35596
|
-
};
|
|
35597
|
-
};
|
|
35598
|
-
const plan = (input) => {
|
|
35599
|
-
const vp = planVp(input);
|
|
35600
|
-
const repos = planRepos(input);
|
|
35601
|
-
return {
|
|
35602
|
-
machine: input.machine,
|
|
35603
|
-
vp: vp.steps,
|
|
35604
|
-
repos: repos.repos,
|
|
35605
|
-
questions: vp.questions,
|
|
35606
|
-
skipped: vp.skipped,
|
|
35607
|
-
failures: [...vp.failures.map((f) => ({
|
|
35608
|
-
...f,
|
|
35609
|
-
section: "vp"
|
|
35610
|
-
})), ...repos.failures.map((f) => ({
|
|
35611
|
-
...f,
|
|
35612
|
-
section: "repos"
|
|
35613
|
-
}))]
|
|
35614
|
-
};
|
|
35615
|
-
};
|
|
35616
|
-
//#endregion
|
|
35617
|
-
//#region ../../packages/core/src/state.ts
|
|
35618
|
-
const GlobalEntry = Struct({
|
|
35619
|
-
name: String$5,
|
|
35620
|
-
version: String$5,
|
|
35621
|
-
platform: optionalKey(Struct({ node: optionalKey(String$5) }))
|
|
35622
|
-
});
|
|
35623
|
-
const InstallRecordSchema = Struct({
|
|
35624
|
-
commit: String$5,
|
|
35625
|
-
path: String$5,
|
|
35626
|
-
install: String$5,
|
|
35627
|
-
files: Record(String$5, String$5),
|
|
35628
|
-
outputs: Record(String$5, String$5)
|
|
35629
|
-
});
|
|
35630
|
-
const Fact = Union([
|
|
35631
|
-
Struct({
|
|
35632
|
-
fact: Literal("home"),
|
|
35633
|
-
path: String$5
|
|
35634
|
-
}),
|
|
35635
|
-
Struct({ fact: Literal("end") }),
|
|
35636
|
-
Struct({
|
|
35637
|
-
fact: Literal("vp"),
|
|
35638
|
-
version: NullOr(String$5),
|
|
35639
|
-
path: optionalKey(String$5)
|
|
35640
|
-
}),
|
|
35641
|
-
Struct({
|
|
35642
|
-
fact: Literal("node"),
|
|
35643
|
-
version: NullOr(String$5)
|
|
35644
|
-
}),
|
|
35645
|
-
Struct({
|
|
35646
|
-
fact: Literal("globals"),
|
|
35647
|
-
list: ArraySchema(GlobalEntry)
|
|
35648
|
-
}),
|
|
35649
|
-
Struct({
|
|
35650
|
-
fact: Literal("cloneRoot"),
|
|
35651
|
-
path: String$5,
|
|
35652
|
-
present: Boolean$4
|
|
35653
|
-
}),
|
|
35654
|
-
Struct({
|
|
35655
|
-
fact: Literal("repo"),
|
|
35656
|
-
name: String$5,
|
|
35657
|
-
present: Boolean$4,
|
|
35658
|
-
head: optionalKey(String$5),
|
|
35659
|
-
origin: optionalKey(String$5)
|
|
35660
|
-
}),
|
|
35661
|
-
Struct({
|
|
35662
|
-
fact: Literal("record"),
|
|
35663
|
-
name: String$5,
|
|
35664
|
-
record: InstallRecordSchema
|
|
35665
|
-
}),
|
|
35666
|
-
Struct({
|
|
35667
|
-
fact: Literal("config"),
|
|
35668
|
-
present: Boolean$4,
|
|
35669
|
-
base64: optionalKey(String$5)
|
|
35670
|
-
}),
|
|
35671
|
-
Struct({
|
|
35672
|
-
fact: Literal("output"),
|
|
35673
|
-
repo: String$5,
|
|
35674
|
-
path: String$5,
|
|
35675
|
-
fingerprint: NullOr(String$5)
|
|
35676
|
-
})
|
|
35677
|
-
]);
|
|
35678
|
-
const emptyState = {
|
|
35679
|
-
complete: false,
|
|
35680
|
-
globals: [],
|
|
35681
|
-
repos: {},
|
|
35682
|
-
records: {},
|
|
35683
|
-
outputs: {}
|
|
35684
|
-
};
|
|
35685
|
-
const decodeBase64 = (base64) => new TextDecoder().decode(Uint8Array.from(atob(base64), (c) => c.charCodeAt(0)));
|
|
35686
|
-
const assembleState = (facts) => {
|
|
35687
|
-
let state = emptyState;
|
|
35688
|
-
for (const fact of facts) switch (fact.fact) {
|
|
35689
|
-
case "home":
|
|
35690
|
-
state = {
|
|
35691
|
-
...state,
|
|
35692
|
-
home: fact.path
|
|
35693
|
-
};
|
|
35694
|
-
break;
|
|
35695
|
-
case "end":
|
|
35696
|
-
state = {
|
|
35697
|
-
...state,
|
|
35698
|
-
complete: true
|
|
35699
|
-
};
|
|
35700
|
-
break;
|
|
35701
|
-
case "vp":
|
|
35702
|
-
state = {
|
|
35703
|
-
...state,
|
|
35704
|
-
vp: fact.path === void 0 ? { version: fact.version } : {
|
|
35705
|
-
version: fact.version,
|
|
35706
|
-
path: fact.path
|
|
35707
|
-
}
|
|
35708
|
-
};
|
|
35709
|
-
break;
|
|
35710
|
-
case "node":
|
|
35711
|
-
state = {
|
|
35712
|
-
...state,
|
|
35713
|
-
node: fact.version
|
|
35714
|
-
};
|
|
35715
|
-
break;
|
|
35716
|
-
case "globals":
|
|
35717
|
-
state = {
|
|
35718
|
-
...state,
|
|
35719
|
-
globals: fact.list
|
|
35720
|
-
};
|
|
35721
|
-
break;
|
|
35722
|
-
case "cloneRoot":
|
|
35723
|
-
state = {
|
|
35724
|
-
...state,
|
|
35725
|
-
cloneRoot: {
|
|
35726
|
-
path: fact.path,
|
|
35727
|
-
present: fact.present
|
|
35728
|
-
}
|
|
35729
|
-
};
|
|
35730
|
-
break;
|
|
35731
|
-
case "repo": {
|
|
35732
|
-
const repo = {
|
|
35733
|
-
present: fact.present,
|
|
35734
|
-
...fact.head === void 0 ? {} : { head: fact.head },
|
|
35735
|
-
...fact.origin === void 0 ? {} : { origin: fact.origin }
|
|
35736
|
-
};
|
|
35737
|
-
state = {
|
|
35738
|
-
...state,
|
|
35739
|
-
repos: {
|
|
35740
|
-
...state.repos,
|
|
35741
|
-
[fact.name]: repo
|
|
35742
|
-
}
|
|
35743
|
-
};
|
|
35744
|
-
break;
|
|
35745
|
-
}
|
|
35746
|
-
case "record":
|
|
35747
|
-
state = {
|
|
35748
|
-
...state,
|
|
35749
|
-
records: {
|
|
35750
|
-
...state.records,
|
|
35751
|
-
[fact.name]: fact.record
|
|
35752
|
-
}
|
|
35753
|
-
};
|
|
35754
|
-
break;
|
|
35755
|
-
case "config":
|
|
35756
|
-
if (fact.present && fact.base64 !== void 0) state = {
|
|
35757
|
-
...state,
|
|
35758
|
-
config: decodeBase64(fact.base64)
|
|
35759
|
-
};
|
|
35760
|
-
break;
|
|
35761
|
-
case "output": state = {
|
|
35762
|
-
...state,
|
|
35763
|
-
outputs: {
|
|
35764
|
-
...state.outputs,
|
|
35765
|
-
[fact.repo]: {
|
|
35766
|
-
...state.outputs[fact.repo],
|
|
35767
|
-
[fact.path]: fact.fingerprint
|
|
35768
|
-
}
|
|
35769
|
-
}
|
|
35770
|
-
};
|
|
35771
|
-
}
|
|
35772
|
-
return state;
|
|
35773
|
-
};
|
|
35774
|
-
//#endregion
|
|
35775
35440
|
//#region ../../packages/remote/src/protocol.ts
|
|
35441
|
+
const Fact = Record(String$5, Unknown);
|
|
35776
35442
|
const StepStart = Struct({ id: String$5 });
|
|
35777
35443
|
const StepDone = Struct({
|
|
35778
35444
|
id: String$5,
|
|
35779
35445
|
ok: Boolean$4,
|
|
35780
35446
|
code: optionalKey(Number$2)
|
|
35781
35447
|
});
|
|
35782
|
-
const
|
|
35448
|
+
const StepSkip = Struct({
|
|
35449
|
+
id: String$5,
|
|
35450
|
+
reason: String$5
|
|
35451
|
+
});
|
|
35452
|
+
const decodeFact$3 = decodeUnknownResult(fromJsonString(Fact));
|
|
35783
35453
|
const decodeStart = decodeUnknownResult(fromJsonString(StepStart));
|
|
35784
35454
|
const decodeDone = decodeUnknownResult(fromJsonString(StepDone));
|
|
35455
|
+
const decodeSkip = decodeUnknownResult(fromJsonString(StepSkip));
|
|
35785
35456
|
const STATE = "ff-state ";
|
|
35786
35457
|
const STEP = "ff-step ";
|
|
35787
35458
|
const DONE = "ff-done ";
|
|
35459
|
+
const SKIP = "ff-skip ";
|
|
35788
35460
|
const parseLine = (line) => {
|
|
35789
35461
|
if (line.startsWith(STATE)) {
|
|
35790
|
-
const result = decodeFact(line.slice(9));
|
|
35462
|
+
const result = decodeFact$3(line.slice(9));
|
|
35791
35463
|
if (result._tag === "Success") return {
|
|
35792
35464
|
kind: "state",
|
|
35793
35465
|
fact: result.success
|
|
@@ -35804,6 +35476,12 @@ const parseLine = (line) => {
|
|
|
35804
35476
|
kind: "done",
|
|
35805
35477
|
done: result.success
|
|
35806
35478
|
};
|
|
35479
|
+
} else if (line.startsWith(SKIP)) {
|
|
35480
|
+
const result = decodeSkip(line.slice(8));
|
|
35481
|
+
if (result._tag === "Success") return {
|
|
35482
|
+
kind: "skip",
|
|
35483
|
+
skip: result.success
|
|
35484
|
+
};
|
|
35807
35485
|
}
|
|
35808
35486
|
return {
|
|
35809
35487
|
kind: "text",
|
|
@@ -35812,95 +35490,48 @@ const parseLine = (line) => {
|
|
|
35812
35490
|
};
|
|
35813
35491
|
const stepLine = (id) => `printf 'ff-step {"id":"%s"}\\n' '${id}'`;
|
|
35814
35492
|
const doneLine = (id, ok, code) => code === void 0 ? `printf 'ff-done {"id":"%s","ok":${ok}}\\n' '${id}'` : `printf 'ff-done {"id":"%s","ok":${ok},"code":%s}\\n' '${id}' "${code}"`;
|
|
35493
|
+
const skipLine = (id, reason) => `printf 'ff-skip {"id":"%s","reason":"%s"}\\n' '${id}' ${reason}`;
|
|
35815
35494
|
//#endregion
|
|
35816
35495
|
//#region ../../packages/remote/src/apply.ts
|
|
35817
|
-
const jsonString = encodeSync(fromJsonString(String$5));
|
|
35818
|
-
const jsonRecord = encodeSync(fromJsonString(Record(String$5, String$5)));
|
|
35819
|
-
const stepId = (repo, step) => `repo:${repo}:${step}`;
|
|
35820
|
-
const isFetchStep = (id) => id.startsWith("repo:") && id.endsWith(":fetch");
|
|
35821
|
-
const vpStepId = (step) => step.kind === "reinstall" ? `vp:reinstall:${step.name}` : step.kind === "globals" ? "vp:globals" : `vp:${step.kind}`;
|
|
35822
35496
|
const CONFIG_STEP = "config";
|
|
35823
|
-
const
|
|
35824
|
-
"curl -fsSL https://vite.plus | bash",
|
|
35825
|
-
...vpEnv.split("\n"),
|
|
35826
|
-
"{ hash -r 2>/dev/null || true; }"
|
|
35827
|
-
].join(" && ");
|
|
35828
|
-
const marked = (id, run, then = []) => [
|
|
35497
|
+
const marked = (id, run, failed = []) => [
|
|
35829
35498
|
stepLine(id),
|
|
35830
35499
|
...run,
|
|
35831
35500
|
`ff_code=$?`,
|
|
35832
|
-
`if [ "$ff_code" -ne 0 ]; then
|
|
35501
|
+
`if [ "$ff_code" -ne 0 ]; then`,
|
|
35502
|
+
doneLine(id, false, "$ff_code"),
|
|
35503
|
+
...failed,
|
|
35504
|
+
"else",
|
|
35833
35505
|
doneLine(id, true),
|
|
35834
|
-
...then,
|
|
35835
35506
|
"fi"
|
|
35836
35507
|
];
|
|
35837
|
-
const vpStep = (step) => {
|
|
35838
|
-
const id = vpStepId(step);
|
|
35839
|
-
const command = (() => {
|
|
35840
|
-
switch (step.kind) {
|
|
35841
|
-
case "bootstrap": return VP_INSTALL;
|
|
35842
|
-
case "vp": return `command vp upgrade ${quote(step.version)}`;
|
|
35843
|
-
case "node": return `command vp env default ${quote(step.version)}`;
|
|
35844
|
-
case "reinstall": return `command vp install -g ${quote(`${step.name}@${step.version}`)}`;
|
|
35845
|
-
case "globals": return `command vp install -g ${step.packages.map((p) => quote(`${p.name}@${p.version}`)).join(" ")}`;
|
|
35846
|
-
}
|
|
35847
|
-
})();
|
|
35848
|
-
return marked(id, [step.kind === "bootstrap" ? command : `( ${command} )`]);
|
|
35849
|
-
};
|
|
35850
35508
|
const configStep = () => {
|
|
35851
35509
|
const dir = CONFIG_DIR_SH;
|
|
35852
35510
|
const write = (variable, file) => `printf '%s' "$${variable}" | base64 -d > ${dir}/${quote(`${file}.tmp`)} && mv -f ${dir}/${quote(`${file}.tmp`)} ${dir}/${quote(file)}`;
|
|
35853
35511
|
return marked(CONFIG_STEP, [`( mkdir -p ${dir} && ${write("ff_config", CONFIG_FILE)} && ${write("ff_schema", SCHEMA_FILE)} )`]);
|
|
35854
35512
|
};
|
|
35855
|
-
const
|
|
35856
|
-
return [
|
|
35857
|
-
|
|
35858
|
-
|
|
35859
|
-
|
|
35860
|
-
|
|
35861
|
-
`
|
|
35513
|
+
const providerSteps = (plan) => {
|
|
35514
|
+
if (plan.decisions.length === 0 && plan.steps.length === 0) return [];
|
|
35515
|
+
const held = `ff_held_${plan.name}`;
|
|
35516
|
+
const items = [...new Set(plan.steps.flatMap((step) => step.items))];
|
|
35517
|
+
const flag = (item) => `ff_f_${plan.name}_${items.indexOf(item)}`;
|
|
35518
|
+
const step = (s) => [
|
|
35519
|
+
`if [ -n "${s.items.map((item) => `\${${flag(item)}:-}`).join("")}" ]; then`,
|
|
35520
|
+
skipLine(s.id, "'an earlier step failed'"),
|
|
35521
|
+
"else",
|
|
35522
|
+
...marked(s.id, s.run, [...s.items.map((item) => `${flag(item)}=1`), `${held}=1`]),
|
|
35523
|
+
"fi"
|
|
35862
35524
|
];
|
|
35863
|
-
};
|
|
35864
|
-
|
|
35865
|
-
const repoSteps = (repo, input) => {
|
|
35866
|
-
const fetch = stepId(repo.name, "fetch");
|
|
35867
|
-
const install = stepId(repo.name, "install");
|
|
35868
|
-
const url = `https://github.com/${repo.origin}.git`;
|
|
35869
|
-
const sync = repo.pin ? [
|
|
35870
|
-
`if [ "$(git -C "$ff_d" rev-parse HEAD)" != ${quote(repo.ref)} ]; then ff_git -C "$ff_d" fetch --quiet origin ${quote(repo.ref)}; fi`,
|
|
35871
|
-
`git -C "$ff_d" checkout --quiet --detach ${quote(repo.ref)}`,
|
|
35872
|
-
`git -C "$ff_d" reset --quiet --hard ${quote(repo.ref)}`
|
|
35873
|
-
] : [`ff_git -C "$ff_d" fetch --quiet origin ${quote(repo.ref)}`, `git -C "$ff_d" reset --quiet --hard ${quote(`origin/${repo.ref}`)}`];
|
|
35874
|
-
const envPrefix = `FLEETFIZZ_MACHINE=${quote(input.env.machine)} FLEETFIZZ_PLATFORM=${quote(input.env.platform)} FLEETFIZZ_MACHINES=${quote(input.env.machines.join(","))}`;
|
|
35525
|
+
const why = [`ff_why=""`, ...plan.dependsOn.map((dep) => `if [ -z "$ff_why" ] && [ -n "\${ff_held_${dep}:-}" ]; then ff_why=${quote(dep)}; fi`)];
|
|
35526
|
+
if (plan.steps.length === 0) return [...why, `if [ -n "$ff_why" ]; then ${held}=1; fi`];
|
|
35875
35527
|
return [
|
|
35876
|
-
|
|
35877
|
-
`
|
|
35878
|
-
|
|
35879
|
-
|
|
35880
|
-
|
|
35881
|
-
|
|
35882
|
-
|
|
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
|
-
])
|
|
35528
|
+
...why,
|
|
35529
|
+
`if [ -n "$ff_why" ]; then`,
|
|
35530
|
+
`${held}=1`,
|
|
35531
|
+
...plan.steps.map((s) => skipLine(s.id, `"depends on $ff_why"`)),
|
|
35532
|
+
"else",
|
|
35533
|
+
...plan.steps.flatMap(step),
|
|
35534
|
+
"fi"
|
|
35904
35535
|
];
|
|
35905
35536
|
};
|
|
35906
35537
|
const applyStdin = (token, config, schema) => [
|
|
@@ -35914,24 +35545,18 @@ const encodeBase64 = (text) => {
|
|
|
35914
35545
|
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
35915
35546
|
return btoa(binary);
|
|
35916
35547
|
};
|
|
35917
|
-
const applyScript = (
|
|
35918
|
-
|
|
35919
|
-
|
|
35920
|
-
|
|
35921
|
-
|
|
35922
|
-
|
|
35923
|
-
|
|
35924
|
-
|
|
35925
|
-
|
|
35926
|
-
|
|
35927
|
-
|
|
35928
|
-
|
|
35929
|
-
lines.push(...configStep());
|
|
35930
|
-
for (const step of input.plan.vp) lines.push(...vpStep(step));
|
|
35931
|
-
for (const repo of input.plan.repos) lines.push(...repoSteps(repo, input));
|
|
35932
|
-
lines.push(doneLine("end", true));
|
|
35933
|
-
return lines.join("\n");
|
|
35934
|
-
};
|
|
35548
|
+
const applyScript = (plan) => [
|
|
35549
|
+
prelude,
|
|
35550
|
+
fingerprintFn,
|
|
35551
|
+
`ff_lit() { printf '%s' "$1"; }`,
|
|
35552
|
+
`IFS= read -r ff_token || ff_token=""`,
|
|
35553
|
+
`IFS= read -r ff_config || ff_config=""`,
|
|
35554
|
+
`IFS= read -r ff_schema || ff_schema=""`,
|
|
35555
|
+
`ff_git() { if [ -n "$ff_token" ]; then FF_TOKEN="$ff_token" git -c credential.helper= -c 'credential.helper=!f() { echo username=x-access-token; echo "password=$FF_TOKEN"; }; f' "$@"; else git "$@"; fi; }`,
|
|
35556
|
+
...configStep(),
|
|
35557
|
+
...plan.providers.flatMap(providerSteps),
|
|
35558
|
+
doneLine("end", true)
|
|
35559
|
+
].join("\n");
|
|
35935
35560
|
//#endregion
|
|
35936
35561
|
//#region ../../packages/remote/src/tailnet.ts
|
|
35937
35562
|
const StatusNode = Struct({
|
|
@@ -64199,6 +63824,333 @@ const useWindowSize = () => {
|
|
|
64199
63824
|
return size;
|
|
64200
63825
|
};
|
|
64201
63826
|
//#endregion
|
|
63827
|
+
//#region ../../packages/core/src/targeting.ts
|
|
63828
|
+
const targets = (targeting, machine) => (targeting.platforms?.includes(machine.platform) ?? true) && (targeting.machines?.includes(machine.name) ?? true);
|
|
63829
|
+
//#endregion
|
|
63830
|
+
//#region ../../packages/core/src/engine.ts
|
|
63831
|
+
const isObjectSection = (section) => !Array.isArray(section);
|
|
63832
|
+
const sectionTargeting = (section) => isObjectSection(section) ? section : {};
|
|
63833
|
+
const dependenciesOf = (provider, config) => {
|
|
63834
|
+
const section = config[provider.name];
|
|
63835
|
+
const extra = section !== void 0 && isObjectSection(section) ? section.dependsOn ?? [] : [];
|
|
63836
|
+
return [.../* @__PURE__ */ new Set([...provider.dependsOn, ...extra])];
|
|
63837
|
+
};
|
|
63838
|
+
const targetedItems = (provider, config, machine) => {
|
|
63839
|
+
const section = config[provider.name];
|
|
63840
|
+
const scoped = section !== void 0 && targets(sectionTargeting(section), machine) ? section : void 0;
|
|
63841
|
+
return provider.items(scoped).filter((item) => targets(item, machine));
|
|
63842
|
+
};
|
|
63843
|
+
const machinesNamed = (provider, config) => {
|
|
63844
|
+
const section = config[provider.name];
|
|
63845
|
+
return [...section === void 0 ? [] : sectionTargeting(section).machines ?? [], ...provider.items(section).flatMap((item) => item.machines ?? [])];
|
|
63846
|
+
};
|
|
63847
|
+
const hold = (decisions, by) => decisions.map((d) => d.kind === "change" || d.kind === "ask" ? {
|
|
63848
|
+
kind: "skip",
|
|
63849
|
+
item: d.item,
|
|
63850
|
+
...d.kind === "change" && d.from !== void 0 ? { at: d.from } : {},
|
|
63851
|
+
...d.kind === "ask" && d.at !== void 0 ? { at: d.at } : {},
|
|
63852
|
+
reason: `depends on ${by}`
|
|
63853
|
+
} : d);
|
|
63854
|
+
const planMachine = (input) => {
|
|
63855
|
+
const ctx = {
|
|
63856
|
+
...input.ctx,
|
|
63857
|
+
home: input.state.home ?? "~"
|
|
63858
|
+
};
|
|
63859
|
+
const plans = /* @__PURE__ */ new Map();
|
|
63860
|
+
const held = /* @__PURE__ */ new Set();
|
|
63861
|
+
for (const provider of input.providers) {
|
|
63862
|
+
const dependsOn = dependenciesOf(provider, ctx.config);
|
|
63863
|
+
const items = targetedItems(provider, ctx.config, ctx.machine);
|
|
63864
|
+
const decisions = provider.decide({
|
|
63865
|
+
items,
|
|
63866
|
+
observed: provider.parse(input.state.facts[provider.name] ?? [], input.state.records[provider.name] ?? {}),
|
|
63867
|
+
resolved: input.resolved[provider.name],
|
|
63868
|
+
ctx
|
|
63869
|
+
});
|
|
63870
|
+
const by = dependsOn.find((name) => {
|
|
63871
|
+
const dep = plans.get(name);
|
|
63872
|
+
return dep !== void 0 && dep.decisions.length > 0 && (held.has(name) || dep.decisions.some((d) => d.kind === "fail"));
|
|
63873
|
+
});
|
|
63874
|
+
if (by !== void 0) held.add(provider.name);
|
|
63875
|
+
plans.set(provider.name, {
|
|
63876
|
+
name: provider.name,
|
|
63877
|
+
rule: provider.rule,
|
|
63878
|
+
dependsOn,
|
|
63879
|
+
decisions: by === void 0 ? decisions : hold(decisions, by),
|
|
63880
|
+
steps: by === void 0 ? provider.act(decisions, ctx) : []
|
|
63881
|
+
});
|
|
63882
|
+
}
|
|
63883
|
+
return {
|
|
63884
|
+
machine: ctx.machine,
|
|
63885
|
+
providers: [...plans.values()]
|
|
63886
|
+
};
|
|
63887
|
+
};
|
|
63888
|
+
//#endregion
|
|
63889
|
+
//#region ../../packages/core/src/range.ts
|
|
63890
|
+
const newest = (available) => available.filter((v) => import_semver.prerelease(v) === null).sort(import_semver.rcompare)[0];
|
|
63891
|
+
const decideRange = (range, installed, available) => {
|
|
63892
|
+
if (range === "latest") {
|
|
63893
|
+
const version = newest(available);
|
|
63894
|
+
if (version === void 0) return { kind: "unavailable" };
|
|
63895
|
+
return installed === version ? { kind: "leave" } : {
|
|
63896
|
+
kind: "install",
|
|
63897
|
+
version
|
|
63898
|
+
};
|
|
63899
|
+
}
|
|
63900
|
+
if (range === "*") {
|
|
63901
|
+
if (installed !== void 0) return { kind: "leave" };
|
|
63902
|
+
const version = newest(available);
|
|
63903
|
+
return version === void 0 ? { kind: "unavailable" } : {
|
|
63904
|
+
kind: "install",
|
|
63905
|
+
version
|
|
63906
|
+
};
|
|
63907
|
+
}
|
|
63908
|
+
if (installed !== void 0 && import_semver.satisfies(installed, range)) return { kind: "leave" };
|
|
63909
|
+
if (installed !== void 0 && import_semver.gtr(installed, range)) return {
|
|
63910
|
+
kind: "ask",
|
|
63911
|
+
installed
|
|
63912
|
+
};
|
|
63913
|
+
const version = import_semver.maxSatisfying([...available], range);
|
|
63914
|
+
return version === null ? { kind: "unavailable" } : {
|
|
63915
|
+
kind: "install",
|
|
63916
|
+
version
|
|
63917
|
+
};
|
|
63918
|
+
};
|
|
63919
|
+
//#endregion
|
|
63920
|
+
//#region ../../packages/core/src/provider.ts
|
|
63921
|
+
const address = (provider, item) => `${provider}:${item}`;
|
|
63922
|
+
const parseAddress = (text) => {
|
|
63923
|
+
const at = text.indexOf(":");
|
|
63924
|
+
return at <= 0 || at === text.length - 1 ? void 0 : {
|
|
63925
|
+
provider: text.slice(0, at),
|
|
63926
|
+
item: text.slice(at + 1)
|
|
63927
|
+
};
|
|
63928
|
+
};
|
|
63929
|
+
const QUESTION_CHOICES = {
|
|
63930
|
+
range: [
|
|
63931
|
+
"bump",
|
|
63932
|
+
"skip",
|
|
63933
|
+
"downgrade"
|
|
63934
|
+
],
|
|
63935
|
+
config: ["overwrite", "stop"],
|
|
63936
|
+
mode: ["managed", "skip"]
|
|
63937
|
+
};
|
|
63938
|
+
const isQuestionKind = (kind) => kind in QUESTION_CHOICES;
|
|
63939
|
+
const rangeQuestion = (subject, installed, range, bumpPath) => ({
|
|
63940
|
+
kind: "range",
|
|
63941
|
+
subject,
|
|
63942
|
+
found: installed,
|
|
63943
|
+
problem: `is above ${range}`,
|
|
63944
|
+
choices: [
|
|
63945
|
+
{
|
|
63946
|
+
value: "bump",
|
|
63947
|
+
title: "bump the config"
|
|
63948
|
+
},
|
|
63949
|
+
{
|
|
63950
|
+
value: "skip",
|
|
63951
|
+
title: "skip on these Machines (leave as is, shown as skipped)"
|
|
63952
|
+
},
|
|
63953
|
+
{
|
|
63954
|
+
value: "downgrade",
|
|
63955
|
+
title: "downgrade into the range"
|
|
63956
|
+
}
|
|
63957
|
+
],
|
|
63958
|
+
default: "skip",
|
|
63959
|
+
bump: {
|
|
63960
|
+
path: bumpPath,
|
|
63961
|
+
range
|
|
63962
|
+
}
|
|
63963
|
+
});
|
|
63964
|
+
const decideRangeItem = (input) => {
|
|
63965
|
+
const { item, range, installed, available } = input;
|
|
63966
|
+
const at = installed === void 0 ? {} : { at: installed };
|
|
63967
|
+
const unavailable = {
|
|
63968
|
+
kind: "fail",
|
|
63969
|
+
item,
|
|
63970
|
+
...at,
|
|
63971
|
+
reason: `no published version satisfies ${range}`
|
|
63972
|
+
};
|
|
63973
|
+
const install = (version) => ({
|
|
63974
|
+
kind: "change",
|
|
63975
|
+
item,
|
|
63976
|
+
...installed === void 0 ? {} : { from: installed },
|
|
63977
|
+
to: version,
|
|
63978
|
+
change: version
|
|
63979
|
+
});
|
|
63980
|
+
const decision = decideRange(range, installed, available);
|
|
63981
|
+
switch (decision.kind) {
|
|
63982
|
+
case "leave": return {
|
|
63983
|
+
kind: "leave",
|
|
63984
|
+
item,
|
|
63985
|
+
...at
|
|
63986
|
+
};
|
|
63987
|
+
case "install": return install(decision.version);
|
|
63988
|
+
case "unavailable": return unavailable;
|
|
63989
|
+
case "ask": {
|
|
63990
|
+
const subject = address(input.provider, item);
|
|
63991
|
+
const answer = input.ctx.answer("range", subject);
|
|
63992
|
+
if (answer === "skip") return {
|
|
63993
|
+
kind: "skip",
|
|
63994
|
+
item,
|
|
63995
|
+
...at,
|
|
63996
|
+
reason: `is above ${range}`
|
|
63997
|
+
};
|
|
63998
|
+
if (answer === "downgrade") {
|
|
63999
|
+
const target = decideRange(range, void 0, available);
|
|
64000
|
+
return target.kind === "install" ? install(target.version) : unavailable;
|
|
64001
|
+
}
|
|
64002
|
+
return {
|
|
64003
|
+
kind: "ask",
|
|
64004
|
+
item,
|
|
64005
|
+
...at,
|
|
64006
|
+
question: rangeQuestion(subject, decision.installed, range, input.bumpPath)
|
|
64007
|
+
};
|
|
64008
|
+
}
|
|
64009
|
+
}
|
|
64010
|
+
};
|
|
64011
|
+
const orderProviders = (all, dependsOn) => {
|
|
64012
|
+
const byName = new Map(all.map((p) => [p.name, p]));
|
|
64013
|
+
const order = [];
|
|
64014
|
+
const done = /* @__PURE__ */ new Set();
|
|
64015
|
+
const visit = (p, path) => {
|
|
64016
|
+
if (done.has(p.name)) return void 0;
|
|
64017
|
+
if (path.includes(p.name)) return [...path.slice(path.indexOf(p.name)), p.name];
|
|
64018
|
+
for (const name of dependsOn(p)) {
|
|
64019
|
+
const dep = byName.get(name);
|
|
64020
|
+
const cycle = dep === void 0 ? void 0 : visit(dep, [...path, p.name]);
|
|
64021
|
+
if (cycle !== void 0) return cycle;
|
|
64022
|
+
}
|
|
64023
|
+
done.add(p.name);
|
|
64024
|
+
order.push(p);
|
|
64025
|
+
};
|
|
64026
|
+
for (const p of all) {
|
|
64027
|
+
const cycle = visit(p, []);
|
|
64028
|
+
if (cycle !== void 0) return { cycle };
|
|
64029
|
+
}
|
|
64030
|
+
return { order };
|
|
64031
|
+
};
|
|
64032
|
+
//#endregion
|
|
64033
|
+
//#region ../../packages/core/src/record.ts
|
|
64034
|
+
const sameRecord = (a, b) => Object.keys(a).length === Object.keys(b).length && Object.entries(a).every(([k, v]) => b[k] === v);
|
|
64035
|
+
const decideInstall = (record, now, rebuild) => {
|
|
64036
|
+
if (rebuild) return {
|
|
64037
|
+
run: true,
|
|
64038
|
+
reason: "--rebuild"
|
|
64039
|
+
};
|
|
64040
|
+
if (record === void 0) return {
|
|
64041
|
+
run: true,
|
|
64042
|
+
reason: "never installed"
|
|
64043
|
+
};
|
|
64044
|
+
if (record.commit !== now.commit) return {
|
|
64045
|
+
run: true,
|
|
64046
|
+
reason: `new commit ${now.commit.slice(0, 7)}`
|
|
64047
|
+
};
|
|
64048
|
+
if (record.path !== now.path) return {
|
|
64049
|
+
run: true,
|
|
64050
|
+
reason: "clone moved"
|
|
64051
|
+
};
|
|
64052
|
+
if (record.install !== now.install) return {
|
|
64053
|
+
run: true,
|
|
64054
|
+
reason: "install command changed"
|
|
64055
|
+
};
|
|
64056
|
+
if (!sameRecord(record.files, now.files)) return {
|
|
64057
|
+
run: true,
|
|
64058
|
+
reason: "files changed"
|
|
64059
|
+
};
|
|
64060
|
+
for (const [path, fingerprint] of Object.entries(now.outputs)) if (fingerprint === void 0 || record.outputs[path] !== fingerprint) return {
|
|
64061
|
+
run: true,
|
|
64062
|
+
reason: `drift: ${path}`
|
|
64063
|
+
};
|
|
64064
|
+
return { run: false };
|
|
64065
|
+
};
|
|
64066
|
+
//#endregion
|
|
64067
|
+
//#region ../../packages/core/src/state.ts
|
|
64068
|
+
const EngineFact = Union([
|
|
64069
|
+
Struct({
|
|
64070
|
+
fact: Literal("home"),
|
|
64071
|
+
path: String$5
|
|
64072
|
+
}),
|
|
64073
|
+
Struct({ fact: Literal("end") }),
|
|
64074
|
+
Struct({
|
|
64075
|
+
fact: Literal("config"),
|
|
64076
|
+
present: Boolean$4,
|
|
64077
|
+
base64: optionalKey(String$5)
|
|
64078
|
+
}),
|
|
64079
|
+
Struct({
|
|
64080
|
+
fact: Literal("record"),
|
|
64081
|
+
provider: String$5,
|
|
64082
|
+
item: String$5,
|
|
64083
|
+
record: Unknown
|
|
64084
|
+
})
|
|
64085
|
+
]);
|
|
64086
|
+
const ProviderFact = Struct({ provider: String$5 });
|
|
64087
|
+
const AtFact = Struct({
|
|
64088
|
+
provider: String$5,
|
|
64089
|
+
fact: Literal("at"),
|
|
64090
|
+
item: String$5,
|
|
64091
|
+
at: String$5
|
|
64092
|
+
});
|
|
64093
|
+
const decodeEngineFact = decodeUnknownOption(EngineFact);
|
|
64094
|
+
const decodeProviderFact = decodeUnknownOption(ProviderFact);
|
|
64095
|
+
const emptyState = {
|
|
64096
|
+
complete: false,
|
|
64097
|
+
records: {},
|
|
64098
|
+
facts: {}
|
|
64099
|
+
};
|
|
64100
|
+
const decodeBase64 = (base64) => new TextDecoder().decode(Uint8Array.from(atob(base64), (c) => c.charCodeAt(0)));
|
|
64101
|
+
const assembleState = (facts) => {
|
|
64102
|
+
let state = emptyState;
|
|
64103
|
+
for (const raw of facts) {
|
|
64104
|
+
const fact = decodeEngineFact(raw);
|
|
64105
|
+
if (fact._tag === "Some") {
|
|
64106
|
+
const f = fact.value;
|
|
64107
|
+
switch (f.fact) {
|
|
64108
|
+
case "home":
|
|
64109
|
+
state = {
|
|
64110
|
+
...state,
|
|
64111
|
+
home: f.path
|
|
64112
|
+
};
|
|
64113
|
+
break;
|
|
64114
|
+
case "end":
|
|
64115
|
+
state = {
|
|
64116
|
+
...state,
|
|
64117
|
+
complete: true
|
|
64118
|
+
};
|
|
64119
|
+
break;
|
|
64120
|
+
case "config":
|
|
64121
|
+
if (f.present && f.base64 !== void 0) state = {
|
|
64122
|
+
...state,
|
|
64123
|
+
config: decodeBase64(f.base64)
|
|
64124
|
+
};
|
|
64125
|
+
break;
|
|
64126
|
+
case "record": state = {
|
|
64127
|
+
...state,
|
|
64128
|
+
records: {
|
|
64129
|
+
...state.records,
|
|
64130
|
+
[f.provider]: {
|
|
64131
|
+
...state.records[f.provider],
|
|
64132
|
+
[f.item]: f.record
|
|
64133
|
+
}
|
|
64134
|
+
}
|
|
64135
|
+
};
|
|
64136
|
+
}
|
|
64137
|
+
continue;
|
|
64138
|
+
}
|
|
64139
|
+
const owned = decodeProviderFact(raw);
|
|
64140
|
+
if (owned._tag === "Some") {
|
|
64141
|
+
const { provider } = owned.value;
|
|
64142
|
+
state = {
|
|
64143
|
+
...state,
|
|
64144
|
+
facts: {
|
|
64145
|
+
...state.facts,
|
|
64146
|
+
[provider]: [...state.facts[provider] ?? [], raw]
|
|
64147
|
+
}
|
|
64148
|
+
};
|
|
64149
|
+
}
|
|
64150
|
+
}
|
|
64151
|
+
return state;
|
|
64152
|
+
};
|
|
64153
|
+
//#endregion
|
|
64202
64154
|
//#region src/ledger.ts
|
|
64203
64155
|
const Note = Struct({
|
|
64204
64156
|
kind: Literals$2([
|
|
@@ -64211,8 +64163,8 @@ const Note = Struct({
|
|
|
64211
64163
|
const Item = Struct({
|
|
64212
64164
|
name: String$5,
|
|
64213
64165
|
kind: Literals$2([
|
|
64214
|
-
"
|
|
64215
|
-
"
|
|
64166
|
+
"range",
|
|
64167
|
+
"record",
|
|
64216
64168
|
"config"
|
|
64217
64169
|
]),
|
|
64218
64170
|
from: optionalKey(String$5),
|
|
@@ -64229,7 +64181,10 @@ const Item = Struct({
|
|
|
64229
64181
|
doing: optionalKey(String$5),
|
|
64230
64182
|
detail: optionalKey(String$5),
|
|
64231
64183
|
output: optionalKey(ArraySchema(String$5)),
|
|
64232
|
-
steps: ArraySchema(
|
|
64184
|
+
steps: ArraySchema(Struct({
|
|
64185
|
+
id: String$5,
|
|
64186
|
+
doing: String$5
|
|
64187
|
+
})),
|
|
64233
64188
|
started: optionalKey(Number$2),
|
|
64234
64189
|
ended: optionalKey(Number$2)
|
|
64235
64190
|
});
|
|
@@ -64258,88 +64213,56 @@ const LedgerState = Struct({
|
|
|
64258
64213
|
started: Number$2,
|
|
64259
64214
|
ended: optionalKey(Number$2)
|
|
64260
64215
|
});
|
|
64261
|
-
const
|
|
64262
|
-
const
|
|
64263
|
-
|
|
64264
|
-
|
|
64265
|
-
|
|
64266
|
-
|
|
64267
|
-
|
|
64268
|
-
|
|
64269
|
-
|
|
64270
|
-
|
|
64271
|
-
|
|
64272
|
-
|
|
64273
|
-
|
|
64274
|
-
|
|
64275
|
-
|
|
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 },
|
|
64216
|
+
const itemsOf = (p) => [...p.providers.flatMap((pp) => pp.decisions.map((d) => {
|
|
64217
|
+
const base = {
|
|
64218
|
+
name: address(pp.name, d.item),
|
|
64219
|
+
kind: pp.rule,
|
|
64220
|
+
steps: pp.steps.filter((step) => step.items.includes(d.item)).map(({ id, doing }) => ({
|
|
64221
|
+
id,
|
|
64222
|
+
doing
|
|
64223
|
+
}))
|
|
64224
|
+
};
|
|
64225
|
+
switch (d.kind) {
|
|
64226
|
+
case "change": return {
|
|
64227
|
+
...base,
|
|
64228
|
+
...d.from === void 0 ? {} : { from: d.from },
|
|
64229
|
+
to: d.to,
|
|
64230
|
+
...d.reason === void 0 ? {} : { reason: d.reason },
|
|
64282
64231
|
changes: true,
|
|
64283
|
-
|
|
64232
|
+
status: "idle"
|
|
64233
|
+
};
|
|
64234
|
+
case "leave": return {
|
|
64235
|
+
...base,
|
|
64236
|
+
...d.at === void 0 ? {} : { from: d.at },
|
|
64237
|
+
changes: false,
|
|
64238
|
+
status: "idle"
|
|
64239
|
+
};
|
|
64240
|
+
case "skip":
|
|
64241
|
+
case "fail": return {
|
|
64242
|
+
...base,
|
|
64243
|
+
...d.at === void 0 ? {} : { from: d.at },
|
|
64244
|
+
changes: d.kind === "fail",
|
|
64245
|
+
status: d.kind === "fail" ? "failed" : "skipped",
|
|
64246
|
+
detail: d.reason
|
|
64247
|
+
};
|
|
64248
|
+
case "ask": return {
|
|
64249
|
+
...base,
|
|
64250
|
+
...d.at === void 0 ? {} : { from: d.at },
|
|
64251
|
+
changes: false,
|
|
64252
|
+
status: "skipped",
|
|
64253
|
+
detail: d.question.problem
|
|
64284
64254
|
};
|
|
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
64255
|
}
|
|
64320
|
-
|
|
64321
|
-
|
|
64322
|
-
|
|
64323
|
-
|
|
64324
|
-
|
|
64325
|
-
|
|
64326
|
-
|
|
64327
|
-
|
|
64328
|
-
|
|
64329
|
-
|
|
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
|
-
};
|
|
64256
|
+
})), {
|
|
64257
|
+
name: CONFIG_STEP,
|
|
64258
|
+
kind: "config",
|
|
64259
|
+
changes: false,
|
|
64260
|
+
status: "idle",
|
|
64261
|
+
steps: [{
|
|
64262
|
+
id: CONFIG_STEP,
|
|
64263
|
+
doing: "copying"
|
|
64264
|
+
}]
|
|
64265
|
+
}];
|
|
64343
64266
|
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
64267
|
const anyFailed = (state) => state.machines.some((m) => outcomeOf(m, state.dryRun) === "failed");
|
|
64345
64268
|
const makeLedger = fn("makeLedger")(function* (input) {
|
|
@@ -64373,7 +64296,7 @@ const makeLedger = fn("makeLedger")(function* (input) {
|
|
|
64373
64296
|
}));
|
|
64374
64297
|
const items = (name, id, change) => machine(name, (m, now) => ({
|
|
64375
64298
|
...m,
|
|
64376
|
-
items: m.items.map((i) => i.steps.
|
|
64299
|
+
items: m.items.map((i) => i.steps.some((step) => step.id === id) ? change(i, now) : i)
|
|
64377
64300
|
}));
|
|
64378
64301
|
const store = {
|
|
64379
64302
|
get: () => snapshot,
|
|
@@ -64414,25 +64337,24 @@ const makeLedger = fn("makeLedger")(function* (input) {
|
|
|
64414
64337
|
...output.length === 0 ? {} : { output },
|
|
64415
64338
|
ended: now
|
|
64416
64339
|
})),
|
|
64417
|
-
plan: (name, p
|
|
64340
|
+
plan: (name, p) => machine(name, (m) => ({
|
|
64418
64341
|
...m,
|
|
64419
64342
|
status: "planned",
|
|
64420
|
-
items: itemsOf(p
|
|
64343
|
+
items: itemsOf(p)
|
|
64421
64344
|
})),
|
|
64422
64345
|
start: (name) => machine(name, (m, now) => ({
|
|
64423
64346
|
...m,
|
|
64424
64347
|
status: "running",
|
|
64425
64348
|
started: now
|
|
64426
64349
|
})),
|
|
64427
|
-
stepStarted: (name, id) => items(name, id, (item, now) => item.status === "failed" ? item : {
|
|
64350
|
+
stepStarted: (name, id) => items(name, id, (item, now) => item.status === "failed" || item.status === "skipped" ? item : {
|
|
64428
64351
|
...item,
|
|
64429
|
-
changes: item.changes || !(id === "config" || isFetchStep(id)),
|
|
64430
64352
|
status: "running",
|
|
64431
|
-
doing:
|
|
64353
|
+
doing: item.steps.find((step) => step.id === id)?.doing ?? "running",
|
|
64432
64354
|
started: item.started ?? now
|
|
64433
64355
|
}),
|
|
64434
64356
|
stepDone: (name, id, result) => items(name, id, (item, now) => {
|
|
64435
|
-
if (item.status === "failed") return item;
|
|
64357
|
+
if (item.status === "failed" || item.status === "skipped") return item;
|
|
64436
64358
|
if (!result.ok) return {
|
|
64437
64359
|
...item,
|
|
64438
64360
|
changes: true,
|
|
@@ -64441,25 +64363,31 @@ const makeLedger = fn("makeLedger")(function* (input) {
|
|
|
64441
64363
|
...result.output.length === 0 ? {} : { output: result.output },
|
|
64442
64364
|
ended: now
|
|
64443
64365
|
};
|
|
64366
|
+
if (item.steps.at(-1)?.id !== id) return item;
|
|
64444
64367
|
const { doing: _, ...rest } = item;
|
|
64445
|
-
|
|
64368
|
+
return {
|
|
64446
64369
|
...rest,
|
|
64447
|
-
status: "
|
|
64370
|
+
status: "ok",
|
|
64448
64371
|
ended: now
|
|
64449
64372
|
};
|
|
64373
|
+
}),
|
|
64374
|
+
stepSkipped: (name, id, reason) => items(name, id, (item, now) => {
|
|
64375
|
+
if (item.status === "failed") return item;
|
|
64376
|
+
const { doing: _, ...rest } = item;
|
|
64450
64377
|
return {
|
|
64451
64378
|
...rest,
|
|
64452
|
-
status: "
|
|
64379
|
+
status: "skipped",
|
|
64380
|
+
detail: reason,
|
|
64453
64381
|
ended: now
|
|
64454
64382
|
};
|
|
64455
64383
|
}),
|
|
64456
|
-
|
|
64384
|
+
reached: (name, item, at) => machine(name, (m) => ({
|
|
64457
64385
|
...m,
|
|
64458
|
-
items: m.items.map((i) => i.
|
|
64386
|
+
items: m.items.map((i) => i.name !== item ? i : {
|
|
64459
64387
|
...i,
|
|
64460
|
-
to:
|
|
64461
|
-
changes: i.
|
|
64462
|
-
}
|
|
64388
|
+
to: at,
|
|
64389
|
+
changes: i.status === "failed" || i.from !== at
|
|
64390
|
+
})
|
|
64463
64391
|
})),
|
|
64464
64392
|
finish: (name, outcome) => machine(name, (m, now) => ({
|
|
64465
64393
|
...m,
|
|
@@ -64801,6 +64729,10 @@ const Cell = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
|
64801
64729
|
})
|
|
64802
64730
|
});
|
|
64803
64731
|
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}`;
|
|
64732
|
+
const unchanged = (i) => ({
|
|
64733
|
+
text: "",
|
|
64734
|
+
detail: i.kind === "record" ? "up to date" : void 0
|
|
64735
|
+
});
|
|
64804
64736
|
const statusOf = (i, dryRun) => {
|
|
64805
64737
|
switch (i.status) {
|
|
64806
64738
|
case "running": return {
|
|
@@ -64822,7 +64754,8 @@ const statusOf = (i, dryRun) => {
|
|
|
64822
64754
|
text: "✓ copied",
|
|
64823
64755
|
tone: "dim"
|
|
64824
64756
|
};
|
|
64825
|
-
if (i.
|
|
64757
|
+
if (!i.changes) return unchanged(i);
|
|
64758
|
+
if (i.kind === "record") return i.from !== void 0 && i.from === i.to ? {
|
|
64826
64759
|
text: "✓ rebuilt",
|
|
64827
64760
|
tone: "ok",
|
|
64828
64761
|
detail: i.reason
|
|
@@ -64841,10 +64774,7 @@ const statusOf = (i, dryRun) => {
|
|
|
64841
64774
|
tone: "dim",
|
|
64842
64775
|
detail: i.reason
|
|
64843
64776
|
};
|
|
64844
|
-
return
|
|
64845
|
-
text: "",
|
|
64846
|
-
detail: i.kind === "repo" ? "up to date" : void 0
|
|
64847
|
-
};
|
|
64777
|
+
return unchanged(i);
|
|
64848
64778
|
}
|
|
64849
64779
|
};
|
|
64850
64780
|
const countsOf = (m) => {
|
|
@@ -65187,6 +65117,757 @@ const report = fn("report")(function* () {
|
|
|
65187
65117
|
]
|
|
65188
65118
|
})));
|
|
65189
65119
|
});
|
|
65120
|
+
const decodeFact$2 = decodeUnknownOption(Union([Struct({
|
|
65121
|
+
fact: Literal("brew"),
|
|
65122
|
+
version: NullOr(String$5)
|
|
65123
|
+
}), AtFact]));
|
|
65124
|
+
const NO_BREW = "Homebrew is not installed; install it first";
|
|
65125
|
+
const homebrew = (name) => {
|
|
65126
|
+
const flag = name === "brew" ? "--formula" : "--cask";
|
|
65127
|
+
const lookup = name === "brew" ? "\"$ff_i\"" : "\"${ff_i##*/}\"";
|
|
65128
|
+
const report = (names) => `for ff_i in ${names.map(quote).join(" ")}; do ff_v=$(brew list --versions ${flag} ${lookup} 2>/dev/null | awk '{print $NF}'); [ -z "$ff_v" ] || ff_at ${name} "$ff_i" "$ff_v"; done`;
|
|
65129
|
+
const step = (what, batch) => {
|
|
65130
|
+
const names = batch.map((c) => c.name);
|
|
65131
|
+
const update = batch.some((c) => c.range === "latest") ? "brew update && " : "";
|
|
65132
|
+
return {
|
|
65133
|
+
id: `${name}:${name === "brew" ? "formulae" : "casks"}:${what}`,
|
|
65134
|
+
items: names,
|
|
65135
|
+
doing: what === "install" ? "installing" : "upgrading",
|
|
65136
|
+
run: [[
|
|
65137
|
+
`( ${update}NONINTERACTIVE=1 brew ${what} ${flag} ${names.map(quote).join(" ")}`,
|
|
65138
|
+
"ff_rc=$?",
|
|
65139
|
+
report(names),
|
|
65140
|
+
"exit $ff_rc )"
|
|
65141
|
+
].join("; ")]
|
|
65142
|
+
};
|
|
65143
|
+
};
|
|
65144
|
+
return {
|
|
65145
|
+
name,
|
|
65146
|
+
rule: "range",
|
|
65147
|
+
dependsOn: name === "cask" ? ["brew"] : [],
|
|
65148
|
+
items(section) {
|
|
65149
|
+
if (section === void 0) return [];
|
|
65150
|
+
const packages = Object.entries(section.items ?? {}).map(([item, entry]) => typeof entry === "string" ? {
|
|
65151
|
+
name: item,
|
|
65152
|
+
kind: "package",
|
|
65153
|
+
range: entry
|
|
65154
|
+
} : {
|
|
65155
|
+
...entry,
|
|
65156
|
+
name: item,
|
|
65157
|
+
kind: "package"
|
|
65158
|
+
});
|
|
65159
|
+
return name === "brew" ? [{
|
|
65160
|
+
name: "brew",
|
|
65161
|
+
kind: "homebrew"
|
|
65162
|
+
}, ...packages] : packages;
|
|
65163
|
+
},
|
|
65164
|
+
observe: (items) => [
|
|
65165
|
+
"if command -v brew >/dev/null 2>&1; then",
|
|
65166
|
+
` ff_state "{\\"provider\\":\\"${name}\\",\\"fact\\":\\"brew\\",\\"version\\":\\"$(ff_json "$(brew --version 2>/dev/null | head -n 1 | sed 's/^Homebrew //')")\\"}"`,
|
|
65167
|
+
` ${report(items.flatMap((i) => i.kind === "package" ? [i.name] : []))}`,
|
|
65168
|
+
"else",
|
|
65169
|
+
` ff_state '{"provider":"${name}","fact":"brew","version":null}'`,
|
|
65170
|
+
"fi"
|
|
65171
|
+
].join("\n"),
|
|
65172
|
+
parse(facts) {
|
|
65173
|
+
let brew;
|
|
65174
|
+
const at = /* @__PURE__ */ new Map();
|
|
65175
|
+
for (const raw of facts) {
|
|
65176
|
+
const fact = decodeFact$2(raw);
|
|
65177
|
+
if (fact._tag === "None") continue;
|
|
65178
|
+
const f = fact.value;
|
|
65179
|
+
if (f.fact === "brew") brew = f.version ?? void 0;
|
|
65180
|
+
else at.set(f.item, f.at);
|
|
65181
|
+
}
|
|
65182
|
+
return {
|
|
65183
|
+
...brew === void 0 ? {} : { brew },
|
|
65184
|
+
at
|
|
65185
|
+
};
|
|
65186
|
+
},
|
|
65187
|
+
resolve: () => void_,
|
|
65188
|
+
decide({ items, observed, ctx }) {
|
|
65189
|
+
return items.map((item) => {
|
|
65190
|
+
const installed = observed.at.get(item.name);
|
|
65191
|
+
if (item.kind === "homebrew") return observed.brew === void 0 ? {
|
|
65192
|
+
kind: "fail",
|
|
65193
|
+
item: item.name,
|
|
65194
|
+
reason: NO_BREW
|
|
65195
|
+
} : {
|
|
65196
|
+
kind: "leave",
|
|
65197
|
+
item: item.name,
|
|
65198
|
+
at: observed.brew
|
|
65199
|
+
};
|
|
65200
|
+
if (name === "cask" && ctx.machine.platform !== "darwin") return {
|
|
65201
|
+
kind: "fail",
|
|
65202
|
+
item: item.name,
|
|
65203
|
+
reason: "Casks need macOS"
|
|
65204
|
+
};
|
|
65205
|
+
if (observed.brew === void 0) return name === "brew" ? {
|
|
65206
|
+
kind: "skip",
|
|
65207
|
+
item: item.name,
|
|
65208
|
+
reason: "needs Homebrew"
|
|
65209
|
+
} : {
|
|
65210
|
+
kind: "fail",
|
|
65211
|
+
item: item.name,
|
|
65212
|
+
reason: NO_BREW
|
|
65213
|
+
};
|
|
65214
|
+
if (installed === void 0) return {
|
|
65215
|
+
kind: "change",
|
|
65216
|
+
item: item.name,
|
|
65217
|
+
to: "latest",
|
|
65218
|
+
change: {
|
|
65219
|
+
kind: "install",
|
|
65220
|
+
name: item.name,
|
|
65221
|
+
range: item.range
|
|
65222
|
+
}
|
|
65223
|
+
};
|
|
65224
|
+
if (item.range === "latest") return {
|
|
65225
|
+
kind: "change",
|
|
65226
|
+
item: item.name,
|
|
65227
|
+
from: installed,
|
|
65228
|
+
to: "latest",
|
|
65229
|
+
change: {
|
|
65230
|
+
kind: "upgrade",
|
|
65231
|
+
name: item.name,
|
|
65232
|
+
range: item.range
|
|
65233
|
+
}
|
|
65234
|
+
};
|
|
65235
|
+
return {
|
|
65236
|
+
kind: "leave",
|
|
65237
|
+
item: item.name,
|
|
65238
|
+
at: installed
|
|
65239
|
+
};
|
|
65240
|
+
});
|
|
65241
|
+
},
|
|
65242
|
+
act(decisions) {
|
|
65243
|
+
const changes = decisions.flatMap((d) => d.kind === "change" ? [d.change] : []);
|
|
65244
|
+
return ["install", "upgrade"].flatMap((what) => {
|
|
65245
|
+
const batch = changes.filter((c) => c.kind === what);
|
|
65246
|
+
return batch.length === 0 ? [] : [step(what, batch)];
|
|
65247
|
+
});
|
|
65248
|
+
}
|
|
65249
|
+
};
|
|
65250
|
+
};
|
|
65251
|
+
const brew = homebrew("brew");
|
|
65252
|
+
const cask = homebrew("cask");
|
|
65253
|
+
const decodeFact$1 = decodeUnknownOption(Union([
|
|
65254
|
+
Struct({
|
|
65255
|
+
fact: Literal("cloneRoot"),
|
|
65256
|
+
path: String$5,
|
|
65257
|
+
present: Boolean$4
|
|
65258
|
+
}),
|
|
65259
|
+
Struct({
|
|
65260
|
+
fact: Literal("repo"),
|
|
65261
|
+
name: String$5,
|
|
65262
|
+
present: Boolean$4,
|
|
65263
|
+
head: optionalKey(String$5),
|
|
65264
|
+
origin: optionalKey(String$5)
|
|
65265
|
+
}),
|
|
65266
|
+
Struct({
|
|
65267
|
+
fact: Literal("output"),
|
|
65268
|
+
repo: String$5,
|
|
65269
|
+
path: String$5,
|
|
65270
|
+
fingerprint: NullOr(String$5)
|
|
65271
|
+
})
|
|
65272
|
+
]));
|
|
65273
|
+
const decodeRecord = decodeUnknownOption(Struct({
|
|
65274
|
+
commit: String$5,
|
|
65275
|
+
path: String$5,
|
|
65276
|
+
install: String$5,
|
|
65277
|
+
files: Record(String$5, String$5),
|
|
65278
|
+
outputs: Record(String$5, String$5)
|
|
65279
|
+
}));
|
|
65280
|
+
const cloneRoot = (config, machine) => config.machines[machine]?.clones ?? "~/.local/share/fleetfizz/clones";
|
|
65281
|
+
const originUrl = (origin) => `https://github.com/${origin}.git`;
|
|
65282
|
+
const short = (commit) => commit.slice(0, 7);
|
|
65283
|
+
const expandHome = (path, home) => path === "~" ? home : path.startsWith("~/") ? `${home}${path.slice(1)}` : path;
|
|
65284
|
+
const jsonString = encodeSync(fromJsonString(String$5));
|
|
65285
|
+
const jsonRecord = encodeSync(fromJsonString(Record(String$5, String$5)));
|
|
65286
|
+
const lsRemote = fn("lsRemote")(function* (repo, token) {
|
|
65287
|
+
const spawner = yield* ChildProcessSpawner;
|
|
65288
|
+
const auth = token === void 0 ? [] : [
|
|
65289
|
+
"-c",
|
|
65290
|
+
"credential.helper=",
|
|
65291
|
+
"-c",
|
|
65292
|
+
"credential.helper=!f() { echo username=x-access-token; echo \"password=$FF_TOKEN\"; }; f"
|
|
65293
|
+
];
|
|
65294
|
+
return yield* scoped(gen(function* () {
|
|
65295
|
+
const handle = yield* spawner.spawn(make$12("git", [
|
|
65296
|
+
...auth,
|
|
65297
|
+
"ls-remote",
|
|
65298
|
+
originUrl(repo.origin),
|
|
65299
|
+
`refs/heads/${repo.ref}`
|
|
65300
|
+
], {
|
|
65301
|
+
stdout: "pipe",
|
|
65302
|
+
stderr: "pipe",
|
|
65303
|
+
env: {
|
|
65304
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
65305
|
+
...token === void 0 ? {} : { FF_TOKEN: token }
|
|
65306
|
+
},
|
|
65307
|
+
extendEnv: true
|
|
65308
|
+
}));
|
|
65309
|
+
const [stdout, stderr, code] = yield* all([
|
|
65310
|
+
runCollect(decodeText(handle.stdout)),
|
|
65311
|
+
runCollect(decodeText(handle.stderr)),
|
|
65312
|
+
handle.exitCode
|
|
65313
|
+
], { concurrency: "unbounded" });
|
|
65314
|
+
if (code !== 0) {
|
|
65315
|
+
const lines = stderr.join("").trim().split("\n");
|
|
65316
|
+
return { error: lines[lines.length - 1] || `git ls-remote exited ${code}` };
|
|
65317
|
+
}
|
|
65318
|
+
const commit = stdout.join("").split(/\s/)[0];
|
|
65319
|
+
return commit === void 0 || commit === "" ? { error: `no branch ${repo.ref} on origin` } : { commit };
|
|
65320
|
+
})).pipe(orElseSucceed(() => ({ error: "could not run git ls-remote" })));
|
|
65321
|
+
});
|
|
65322
|
+
const observe = (items, root) => {
|
|
65323
|
+
if (items.length === 0) return "";
|
|
65324
|
+
const lines = [
|
|
65325
|
+
`ff_root=${quotePath(root)}`,
|
|
65326
|
+
`if [ -d "$ff_root" ]; then ff_present=true; else ff_present=false; fi`,
|
|
65327
|
+
`ff_state "{\\"provider\\":\\"repos\\",\\"fact\\":\\"cloneRoot\\",\\"path\\":\\"$(ff_json "$ff_root")\\",\\"present\\":$ff_present}"`
|
|
65328
|
+
];
|
|
65329
|
+
for (const repo of items) {
|
|
65330
|
+
const name = quote(repo.name);
|
|
65331
|
+
lines.push(`ff_d="$ff_root"/${name}`, `if [ -d "$ff_d/.git" ]; then`, ` ff_state "{\\"provider\\":\\"repos\\",\\"fact\\":\\"repo\\",\\"name\\":\\"$(ff_json ${name})\\",\\"present\\":true,\\"head\\":\\"$(ff_json "$(git -C "$ff_d" rev-parse HEAD 2>/dev/null)")\\",\\"origin\\":\\"$(ff_json "$(git -C "$ff_d" remote get-url origin 2>/dev/null)")\\"}"`, "else", ` ff_state "{\\"provider\\":\\"repos\\",\\"fact\\":\\"repo\\",\\"name\\":\\"$(ff_json ${name})\\",\\"present\\":false}"`, "fi");
|
|
65332
|
+
for (const output of repo.outputs ?? []) lines.push(`ff_p=${quotePath(output)}; ff_f=$(ff_fp "$ff_p")`, `if [ -n "$ff_f" ]; then ff_fv="\\"$(ff_json "$ff_f")\\""; else ff_fv=null; fi`, `ff_state "{\\"provider\\":\\"repos\\",\\"fact\\":\\"output\\",\\"repo\\":\\"$(ff_json ${name})\\",\\"path\\":\\"$(ff_json ${quote(output)})\\",\\"fingerprint\\":$ff_fv}"`);
|
|
65333
|
+
}
|
|
65334
|
+
return lines.join("\n");
|
|
65335
|
+
};
|
|
65336
|
+
const decideRepo = (repo, root, observed, resolved, ctx) => {
|
|
65337
|
+
const clone = observed.clones.get(repo.name);
|
|
65338
|
+
const record = observed.records.get(repo.name);
|
|
65339
|
+
const at = clone?.head === void 0 ? {} : { at: short(clone.head) };
|
|
65340
|
+
const fail = (reason) => ({
|
|
65341
|
+
kind: "fail",
|
|
65342
|
+
item: repo.name,
|
|
65343
|
+
...at,
|
|
65344
|
+
reason
|
|
65345
|
+
});
|
|
65346
|
+
if (observed.cloneRoot?.present === false && !root.startsWith("~")) return fail(`clone root not mounted: ${observed.cloneRoot.path}`);
|
|
65347
|
+
const url = originUrl(repo.origin);
|
|
65348
|
+
if (clone?.present && clone.origin !== void 0 && clone.origin !== url) return fail(`managed clone has origin ${clone.origin}, expected ${url}`);
|
|
65349
|
+
const target = isPin(repo.ref) ? { commit: repo.ref } : resolved[repo.name];
|
|
65350
|
+
if (target === void 0) return fail(`could not resolve ${repo.ref}`);
|
|
65351
|
+
if ("error" in target) return fail(target.error);
|
|
65352
|
+
const { commit } = target;
|
|
65353
|
+
const outputs = observed.outputs.get(repo.name);
|
|
65354
|
+
const install = decideInstall(record, {
|
|
65355
|
+
commit,
|
|
65356
|
+
path: expandHome(`${root}/${repo.name}`, ctx.home),
|
|
65357
|
+
install: repo.install,
|
|
65358
|
+
files: repo.files ?? {},
|
|
65359
|
+
outputs: Object.fromEntries((repo.outputs ?? []).map((o) => [o, outputs?.get(o)]))
|
|
65360
|
+
}, ctx.rebuild(address("repos", repo.name)));
|
|
65361
|
+
const reason = install.run ? install.reason : !clone?.present ? "not cloned" : clone.head !== commit ? `checked out at ${short(clone.head ?? "")}` : void 0;
|
|
65362
|
+
const change = {
|
|
65363
|
+
item: repo,
|
|
65364
|
+
root,
|
|
65365
|
+
commit
|
|
65366
|
+
};
|
|
65367
|
+
if (reason === void 0) return {
|
|
65368
|
+
kind: "leave",
|
|
65369
|
+
item: repo.name,
|
|
65370
|
+
at: short(commit),
|
|
65371
|
+
upkeep: change
|
|
65372
|
+
};
|
|
65373
|
+
const from = record?.commit ?? clone?.head;
|
|
65374
|
+
return {
|
|
65375
|
+
kind: "change",
|
|
65376
|
+
item: repo.name,
|
|
65377
|
+
...from === void 0 ? {} : { from: short(from) },
|
|
65378
|
+
to: short(commit),
|
|
65379
|
+
reason,
|
|
65380
|
+
change
|
|
65381
|
+
};
|
|
65382
|
+
};
|
|
65383
|
+
const fetchStep = ({ item, root, commit }) => {
|
|
65384
|
+
const url = originUrl(item.origin);
|
|
65385
|
+
const sync = isPin(item.ref) ? [
|
|
65386
|
+
`if [ "$(git -C "$ff_d" rev-parse HEAD)" != ${quote(commit)} ]; then ff_git -C "$ff_d" fetch --quiet origin ${quote(commit)}; fi`,
|
|
65387
|
+
`git -C "$ff_d" checkout --quiet --detach ${quote(commit)}`,
|
|
65388
|
+
`git -C "$ff_d" reset --quiet --hard ${quote(commit)}`
|
|
65389
|
+
] : [`ff_git -C "$ff_d" fetch --quiet origin ${quote(item.ref)}`, `git -C "$ff_d" reset --quiet --hard ${quote(commit)}`];
|
|
65390
|
+
const files = Object.entries(item.files ?? {}).flatMap(([path, content]) => [`mkdir -p "$(dirname "$ff_d"/${quote(path)})"`, `printf '%s' ${quote(content)} > "$ff_d"/${quote(path)}`]);
|
|
65391
|
+
return {
|
|
65392
|
+
id: `repos:${item.name}:fetch`,
|
|
65393
|
+
items: [item.name],
|
|
65394
|
+
doing: "fetching",
|
|
65395
|
+
run: [
|
|
65396
|
+
`ff_root=${quotePath(root)}`,
|
|
65397
|
+
...root.startsWith("~") ? [`mkdir -p "$ff_root"`] : [],
|
|
65398
|
+
`ff_d="$ff_root"/${quote(item.name)}`,
|
|
65399
|
+
"(",
|
|
65400
|
+
" set -e",
|
|
65401
|
+
` if [ ! -d "$ff_d/.git" ]; then ff_git clone --quiet ${quote(url)} "$ff_d"; fi`,
|
|
65402
|
+
` 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`,
|
|
65403
|
+
...[
|
|
65404
|
+
...sync,
|
|
65405
|
+
`git -C "$ff_d" clean --quiet -fd`,
|
|
65406
|
+
...files
|
|
65407
|
+
].map((l) => ` ${l}`),
|
|
65408
|
+
")"
|
|
65409
|
+
]
|
|
65410
|
+
};
|
|
65411
|
+
};
|
|
65412
|
+
const installStep = ({ item, root }, ctx) => {
|
|
65413
|
+
const env = [
|
|
65414
|
+
`FLEETFIZZ_MACHINE=${quote(ctx.machine.name)}`,
|
|
65415
|
+
`FLEETFIZZ_PLATFORM=${quote(ctx.machine.platform)}`,
|
|
65416
|
+
`FLEETFIZZ_MACHINES=${quote(Object.keys(ctx.config.machines).join(","))}`
|
|
65417
|
+
].join(" ");
|
|
65418
|
+
const tmp = `${recordDir("repos")}/${quote(`${item.name}.json.tmp`)}`;
|
|
65419
|
+
const record = [
|
|
65420
|
+
`ff_after=$(git -C "$ff_d" rev-parse HEAD)`,
|
|
65421
|
+
`ff_outs=""`,
|
|
65422
|
+
...(item.outputs ?? []).map((o) => `ff_fpv=$(ff_fp ${quotePath(o)}); if [ -n "$ff_fpv" ]; then ff_outs="$ff_outs,\\"$(ff_json ${quote(o)})\\":\\"$(ff_json "$ff_fpv")\\""; fi`),
|
|
65423
|
+
`mkdir -p ${recordDir("repos")}`,
|
|
65424
|
+
`printf '%s\\n' "{\\"commit\\":\\"$ff_after\\",\\"path\\":\\"$(ff_json "$ff_d")\\",\\"install\\":$(ff_lit ${quote(jsonString(item.install))}),\\"files\\":$(ff_lit ${quote(jsonRecord(item.files ?? {}))}),\\"outputs\\":{\${ff_outs#,}}}" > ${tmp}`,
|
|
65425
|
+
`mv -f ${tmp} ${recordFile("repos", item.name)}`,
|
|
65426
|
+
`rm -f ${quotePath(RECORDS_DIR)}/${quote(`${item.name}.json`)}`
|
|
65427
|
+
];
|
|
65428
|
+
return {
|
|
65429
|
+
id: `repos:${item.name}:install`,
|
|
65430
|
+
items: [item.name],
|
|
65431
|
+
doing: "installing",
|
|
65432
|
+
run: [
|
|
65433
|
+
`ff_root=${quotePath(root)}`,
|
|
65434
|
+
`ff_d="$ff_root"/${quote(item.name)}`,
|
|
65435
|
+
`(cd "$ff_d" && ${env} "\${SHELL:-/bin/sh}" -lc ${quote(item.install)})`,
|
|
65436
|
+
`ff_rc=$?`,
|
|
65437
|
+
`if [ "$ff_rc" -eq 0 ]; then`,
|
|
65438
|
+
" (",
|
|
65439
|
+
" set -e",
|
|
65440
|
+
...record.map((l) => ` ${l}`),
|
|
65441
|
+
" )",
|
|
65442
|
+
` ff_rc=$?`,
|
|
65443
|
+
` if [ "$ff_rc" -ne 0 ]; then echo "could not write the install record"; fi`,
|
|
65444
|
+
"fi",
|
|
65445
|
+
`(exit "$ff_rc")`
|
|
65446
|
+
]
|
|
65447
|
+
};
|
|
65448
|
+
};
|
|
65449
|
+
const repos = {
|
|
65450
|
+
name: "repos",
|
|
65451
|
+
rule: "record",
|
|
65452
|
+
dependsOn: ["vp"],
|
|
65453
|
+
items: (section) => (section ?? []).map((repo) => ({
|
|
65454
|
+
...repo,
|
|
65455
|
+
name: repoName(repo)
|
|
65456
|
+
})),
|
|
65457
|
+
observe: (items, scope) => observe(items, cloneRoot(scope.config, scope.machine.name)),
|
|
65458
|
+
parse(facts, records) {
|
|
65459
|
+
let root;
|
|
65460
|
+
const clones = /* @__PURE__ */ new Map();
|
|
65461
|
+
const outputs = /* @__PURE__ */ new Map();
|
|
65462
|
+
for (const raw of facts) {
|
|
65463
|
+
const fact = decodeFact$1(raw);
|
|
65464
|
+
if (fact._tag === "None") continue;
|
|
65465
|
+
const f = fact.value;
|
|
65466
|
+
switch (f.fact) {
|
|
65467
|
+
case "cloneRoot":
|
|
65468
|
+
root = {
|
|
65469
|
+
path: f.path,
|
|
65470
|
+
present: f.present
|
|
65471
|
+
};
|
|
65472
|
+
break;
|
|
65473
|
+
case "repo":
|
|
65474
|
+
clones.set(f.name, {
|
|
65475
|
+
present: f.present,
|
|
65476
|
+
...f.head === void 0 || f.head === "" ? {} : { head: f.head },
|
|
65477
|
+
...f.origin === void 0 || f.origin === "" ? {} : { origin: f.origin }
|
|
65478
|
+
});
|
|
65479
|
+
break;
|
|
65480
|
+
case "output": {
|
|
65481
|
+
const paths = outputs.get(f.repo) ?? /* @__PURE__ */ new Map();
|
|
65482
|
+
paths.set(f.path, f.fingerprint ?? void 0);
|
|
65483
|
+
outputs.set(f.repo, paths);
|
|
65484
|
+
break;
|
|
65485
|
+
}
|
|
65486
|
+
}
|
|
65487
|
+
}
|
|
65488
|
+
const decoded = /* @__PURE__ */ new Map();
|
|
65489
|
+
for (const [name, raw] of Object.entries(records)) {
|
|
65490
|
+
const record = decodeRecord(raw);
|
|
65491
|
+
if (record._tag === "Some") decoded.set(name, record.value);
|
|
65492
|
+
}
|
|
65493
|
+
return {
|
|
65494
|
+
...root === void 0 ? {} : { cloneRoot: root },
|
|
65495
|
+
clones,
|
|
65496
|
+
outputs,
|
|
65497
|
+
records: decoded
|
|
65498
|
+
};
|
|
65499
|
+
},
|
|
65500
|
+
resolve: (items, { token }) => forEach$1(items.filter((repo) => !isPin(repo.ref)), (repo) => lsRemote(repo, token).pipe(map$5((result) => [repo.name, result])), { concurrency: 8 }).pipe(map$5((entries) => Object.fromEntries(entries))),
|
|
65501
|
+
decide: ({ items, observed, resolved, ctx }) => {
|
|
65502
|
+
const root = cloneRoot(ctx.config, ctx.machine.name);
|
|
65503
|
+
return items.map((repo) => decideRepo(repo, root, observed, resolved, ctx));
|
|
65504
|
+
},
|
|
65505
|
+
act: (decisions, ctx) => decisions.flatMap((d) => d.kind === "change" ? [fetchStep(d.change), installStep(d.change, ctx)] : d.kind === "leave" && d.upkeep !== void 0 ? [fetchStep(d.upkeep)] : [])
|
|
65506
|
+
};
|
|
65507
|
+
//#endregion
|
|
65508
|
+
//#region ../../packages/providers/src/vp/index.ts
|
|
65509
|
+
const VP_PACKAGE = "vite-plus";
|
|
65510
|
+
const TOOL_PACKAGES = {
|
|
65511
|
+
node: ["node"],
|
|
65512
|
+
npm: ["npm"],
|
|
65513
|
+
pnpm: ["pnpm"],
|
|
65514
|
+
yarn: ["yarn", "@yarnpkg/cli-dist"],
|
|
65515
|
+
bun: ["bun"]
|
|
65516
|
+
};
|
|
65517
|
+
const GlobalEntry = Struct({
|
|
65518
|
+
name: String$5,
|
|
65519
|
+
version: String$5,
|
|
65520
|
+
platform: optionalKey(Struct({ node: optionalKey(String$5) }))
|
|
65521
|
+
});
|
|
65522
|
+
const decodeFact = decodeUnknownOption(Union([
|
|
65523
|
+
Struct({
|
|
65524
|
+
fact: Literal("vp"),
|
|
65525
|
+
version: NullOr(String$5),
|
|
65526
|
+
path: optionalKey(String$5)
|
|
65527
|
+
}),
|
|
65528
|
+
Struct({
|
|
65529
|
+
fact: Literal("default"),
|
|
65530
|
+
tool: String$5,
|
|
65531
|
+
version: String$5
|
|
65532
|
+
}),
|
|
65533
|
+
Struct({
|
|
65534
|
+
fact: Literal("mode"),
|
|
65535
|
+
mode: Literals$2(["managed", "system-first"])
|
|
65536
|
+
}),
|
|
65537
|
+
Struct({
|
|
65538
|
+
fact: Literal("globals"),
|
|
65539
|
+
list: ArraySchema(GlobalEntry)
|
|
65540
|
+
})
|
|
65541
|
+
]));
|
|
65542
|
+
const VP_INSTALL = [
|
|
65543
|
+
"curl -fsSL https://vite.plus | bash",
|
|
65544
|
+
...vpEnv.split("\n"),
|
|
65545
|
+
"{ hash -r 2>/dev/null || true; }"
|
|
65546
|
+
].join(" && ");
|
|
65547
|
+
const isHomebrew = (path) => path !== void 0 && (path.includes("/homebrew/") || path.includes("/Cellar/"));
|
|
65548
|
+
const OBSERVE = [
|
|
65549
|
+
"if command -v vp >/dev/null 2>&1; then",
|
|
65550
|
+
` ff_v=$(command vp --version 2>/dev/null | sed -n 's/^vp v\\{0,1\\}\\([^ ]*\\).*/\\1/p' | head -n 1)`,
|
|
65551
|
+
` ff_state "{\\"provider\\":\\"vp\\",\\"fact\\":\\"vp\\",\\"version\\":\\"$(ff_json "$ff_v")\\",\\"path\\":\\"$(ff_json "$(command -v vp)")\\"}"`,
|
|
65552
|
+
" if command vp env doctor 2>/dev/null | grep -q 'system-first mode'; then ff_m=system-first; else ff_m=managed; fi",
|
|
65553
|
+
` ff_state "{\\"provider\\":\\"vp\\",\\"fact\\":\\"mode\\",\\"mode\\":\\"$ff_m\\"}"`,
|
|
65554
|
+
` command vp env default 2>/dev/null | sed -n -e 's/^Default Node.js version: /node /p' -e 's/^Default \\([a-z]*\\) version: /\\1 /p' | while read -r ff_t ff_n; do`,
|
|
65555
|
+
` ff_state "{\\"provider\\":\\"vp\\",\\"fact\\":\\"default\\",\\"tool\\":\\"$(ff_json "$ff_t")\\",\\"version\\":\\"$(ff_json "$ff_n")\\"}"`,
|
|
65556
|
+
" done",
|
|
65557
|
+
` ff_g=$(command vp list --global --json 2>/dev/null | tr -d '\\n')`,
|
|
65558
|
+
` ff_state "{\\"provider\\":\\"vp\\",\\"fact\\":\\"globals\\",\\"list\\":\${ff_g:-[]}}"`,
|
|
65559
|
+
"else",
|
|
65560
|
+
` ff_state '{"provider":"vp","fact":"vp","version":null}'`,
|
|
65561
|
+
"fi"
|
|
65562
|
+
].join("\n");
|
|
65563
|
+
const decideSelf = (item, installed, self) => {
|
|
65564
|
+
const at = installed === void 0 ? {} : { at: installed };
|
|
65565
|
+
if (installed === void 0 || import_semver.lt(installed, self)) return {
|
|
65566
|
+
kind: "change",
|
|
65567
|
+
item,
|
|
65568
|
+
...installed === void 0 ? {} : { from: installed },
|
|
65569
|
+
to: self,
|
|
65570
|
+
change: {
|
|
65571
|
+
kind: "global",
|
|
65572
|
+
name: item,
|
|
65573
|
+
version: self
|
|
65574
|
+
}
|
|
65575
|
+
};
|
|
65576
|
+
if (import_semver.gt(installed, self)) return {
|
|
65577
|
+
kind: "fail",
|
|
65578
|
+
item,
|
|
65579
|
+
...at,
|
|
65580
|
+
reason: `runs ${SELF_PACKAGE} ${installed}, newer than this ${self}; upgrade here first`
|
|
65581
|
+
};
|
|
65582
|
+
return {
|
|
65583
|
+
kind: "leave",
|
|
65584
|
+
item,
|
|
65585
|
+
...at
|
|
65586
|
+
};
|
|
65587
|
+
};
|
|
65588
|
+
const MODE_SUBJECT = address("vp", "mode");
|
|
65589
|
+
const NOT_MANAGED = "needs managed mode";
|
|
65590
|
+
const decideMode = (observed, ctx) => {
|
|
65591
|
+
if (observed.vp !== void 0 && observed.mode !== "system-first") return {
|
|
65592
|
+
kind: "leave",
|
|
65593
|
+
item: "mode",
|
|
65594
|
+
at: "managed"
|
|
65595
|
+
};
|
|
65596
|
+
const from = observed.mode === void 0 ? {} : { from: observed.mode };
|
|
65597
|
+
const answer = observed.vp === void 0 ? "managed" : ctx.answer("mode", MODE_SUBJECT);
|
|
65598
|
+
if (answer === "managed") return {
|
|
65599
|
+
kind: "change",
|
|
65600
|
+
item: "mode",
|
|
65601
|
+
...from,
|
|
65602
|
+
to: "managed",
|
|
65603
|
+
change: { kind: "mode" }
|
|
65604
|
+
};
|
|
65605
|
+
if (answer === "skip") return {
|
|
65606
|
+
kind: "skip",
|
|
65607
|
+
item: "mode",
|
|
65608
|
+
at: "system-first",
|
|
65609
|
+
reason: NOT_MANAGED
|
|
65610
|
+
};
|
|
65611
|
+
return {
|
|
65612
|
+
kind: "ask",
|
|
65613
|
+
item: "mode",
|
|
65614
|
+
at: "system-first",
|
|
65615
|
+
question: {
|
|
65616
|
+
kind: "mode",
|
|
65617
|
+
subject: MODE_SUBJECT,
|
|
65618
|
+
found: "system-first",
|
|
65619
|
+
problem: NOT_MANAGED,
|
|
65620
|
+
choices: [{
|
|
65621
|
+
value: "managed",
|
|
65622
|
+
title: "switch to managed mode"
|
|
65623
|
+
}, {
|
|
65624
|
+
value: "skip",
|
|
65625
|
+
title: "skip Node and package managers on these Machines"
|
|
65626
|
+
}],
|
|
65627
|
+
default: "skip"
|
|
65628
|
+
}
|
|
65629
|
+
};
|
|
65630
|
+
};
|
|
65631
|
+
const decideRanged = (item, observed, resolved, ctx) => {
|
|
65632
|
+
const installed = item.kind === "vp" ? observed.vp?.version : item.kind === "default" ? observed.defaults.get(item.name) : observed.globals.get(item.name)?.version;
|
|
65633
|
+
const decision = decideRangeItem({
|
|
65634
|
+
provider: "vp",
|
|
65635
|
+
item: item.name,
|
|
65636
|
+
range: item.range,
|
|
65637
|
+
installed,
|
|
65638
|
+
available: item.packages.flatMap((p) => resolved[p] ?? []),
|
|
65639
|
+
bumpPath: item.bumpPath,
|
|
65640
|
+
ctx
|
|
65641
|
+
});
|
|
65642
|
+
if (item.kind === "vp" && observed.vp === void 0) {
|
|
65643
|
+
if (decision.kind === "change") return {
|
|
65644
|
+
...decision,
|
|
65645
|
+
change: {
|
|
65646
|
+
kind: "bootstrap",
|
|
65647
|
+
version: decision.change
|
|
65648
|
+
}
|
|
65649
|
+
};
|
|
65650
|
+
if (decision.kind === "fail" && (item.range === "*" || item.range === "latest")) return {
|
|
65651
|
+
kind: "change",
|
|
65652
|
+
item: item.name,
|
|
65653
|
+
to: "latest",
|
|
65654
|
+
change: { kind: "bootstrap" }
|
|
65655
|
+
};
|
|
65656
|
+
return decision;
|
|
65657
|
+
}
|
|
65658
|
+
if (decision.kind !== "change") return decision;
|
|
65659
|
+
const version = decision.change;
|
|
65660
|
+
if (item.kind === "vp" && isHomebrew(observed.vp?.path)) return {
|
|
65661
|
+
kind: "fail",
|
|
65662
|
+
item: item.name,
|
|
65663
|
+
...installed === void 0 ? {} : { at: installed },
|
|
65664
|
+
reason: `vp ${installed} was installed with Homebrew; run brew upgrade vite-plus to get ${version}`
|
|
65665
|
+
};
|
|
65666
|
+
const change = item.kind === "vp" ? {
|
|
65667
|
+
kind: "upgrade",
|
|
65668
|
+
version
|
|
65669
|
+
} : item.kind === "default" ? {
|
|
65670
|
+
kind: "default",
|
|
65671
|
+
tool: item.name,
|
|
65672
|
+
version
|
|
65673
|
+
} : {
|
|
65674
|
+
kind: "global",
|
|
65675
|
+
name: item.name,
|
|
65676
|
+
version
|
|
65677
|
+
};
|
|
65678
|
+
return {
|
|
65679
|
+
...decision,
|
|
65680
|
+
change
|
|
65681
|
+
};
|
|
65682
|
+
};
|
|
65683
|
+
//#endregion
|
|
65684
|
+
//#region ../../packages/providers/src/index.ts
|
|
65685
|
+
const PROVIDERS = [
|
|
65686
|
+
{
|
|
65687
|
+
name: "vp",
|
|
65688
|
+
rule: "range",
|
|
65689
|
+
dependsOn: [],
|
|
65690
|
+
items(section) {
|
|
65691
|
+
const globals = Object.entries(section?.globals ?? {}).map(([name, global]) => {
|
|
65692
|
+
const targeting = typeof global === "string" ? {} : global;
|
|
65693
|
+
return {
|
|
65694
|
+
...targeting.platforms === void 0 ? {} : { platforms: targeting.platforms },
|
|
65695
|
+
...targeting.machines === void 0 ? {} : { machines: targeting.machines },
|
|
65696
|
+
name,
|
|
65697
|
+
kind: "global",
|
|
65698
|
+
packages: [name],
|
|
65699
|
+
range: typeof global === "string" ? global : global.range,
|
|
65700
|
+
bumpPath: typeof global === "string" ? [
|
|
65701
|
+
"vp",
|
|
65702
|
+
"globals",
|
|
65703
|
+
name
|
|
65704
|
+
] : [
|
|
65705
|
+
"vp",
|
|
65706
|
+
"globals",
|
|
65707
|
+
name,
|
|
65708
|
+
"range"
|
|
65709
|
+
]
|
|
65710
|
+
};
|
|
65711
|
+
});
|
|
65712
|
+
const defaults = ["node", ...PACKAGE_MANAGERS].flatMap((tool) => {
|
|
65713
|
+
const range = section?.[tool];
|
|
65714
|
+
return range === void 0 ? [] : [{
|
|
65715
|
+
name: tool,
|
|
65716
|
+
kind: "default",
|
|
65717
|
+
packages: TOOL_PACKAGES[tool],
|
|
65718
|
+
range,
|
|
65719
|
+
bumpPath: ["vp", tool]
|
|
65720
|
+
}];
|
|
65721
|
+
});
|
|
65722
|
+
return [
|
|
65723
|
+
{
|
|
65724
|
+
name: "vp",
|
|
65725
|
+
kind: "vp",
|
|
65726
|
+
packages: [VP_PACKAGE],
|
|
65727
|
+
range: section?.version ?? "*",
|
|
65728
|
+
bumpPath: ["vp", "version"]
|
|
65729
|
+
},
|
|
65730
|
+
{
|
|
65731
|
+
name: "mode",
|
|
65732
|
+
kind: "mode"
|
|
65733
|
+
},
|
|
65734
|
+
...defaults,
|
|
65735
|
+
...globals,
|
|
65736
|
+
{
|
|
65737
|
+
name: SELF_PACKAGE,
|
|
65738
|
+
kind: "self"
|
|
65739
|
+
}
|
|
65740
|
+
];
|
|
65741
|
+
},
|
|
65742
|
+
observe: () => OBSERVE,
|
|
65743
|
+
parse(facts) {
|
|
65744
|
+
let vpFact;
|
|
65745
|
+
let mode;
|
|
65746
|
+
const defaults = /* @__PURE__ */ new Map();
|
|
65747
|
+
let globals = [];
|
|
65748
|
+
for (const raw of facts) {
|
|
65749
|
+
const fact = decodeFact(raw);
|
|
65750
|
+
if (fact._tag === "None") continue;
|
|
65751
|
+
const f = fact.value;
|
|
65752
|
+
if (f.fact === "vp" && f.version !== null) vpFact = {
|
|
65753
|
+
version: f.version,
|
|
65754
|
+
...f.path === void 0 ? {} : { path: f.path }
|
|
65755
|
+
};
|
|
65756
|
+
else if (f.fact === "mode") mode = f.mode;
|
|
65757
|
+
else if (f.fact === "default") defaults.set(f.tool, f.version);
|
|
65758
|
+
else if (f.fact === "globals") globals = f.list;
|
|
65759
|
+
}
|
|
65760
|
+
if (vpFact === void 0) return {
|
|
65761
|
+
defaults: /* @__PURE__ */ new Map(),
|
|
65762
|
+
globals: /* @__PURE__ */ new Map()
|
|
65763
|
+
};
|
|
65764
|
+
return {
|
|
65765
|
+
vp: vpFact,
|
|
65766
|
+
...mode === void 0 ? {} : { mode },
|
|
65767
|
+
defaults,
|
|
65768
|
+
globals: new Map(globals.map((g) => [g.name, g]))
|
|
65769
|
+
};
|
|
65770
|
+
},
|
|
65771
|
+
resolve: (items) => resolveVersions(items.flatMap((item) => "packages" in item ? item.packages : [])),
|
|
65772
|
+
decide({ items, observed, resolved, ctx }) {
|
|
65773
|
+
const mode = decideMode(observed, ctx);
|
|
65774
|
+
const managed = mode.kind === "leave" || mode.kind === "change";
|
|
65775
|
+
const decisions = items.map((item) => {
|
|
65776
|
+
switch (item.kind) {
|
|
65777
|
+
case "mode": return mode;
|
|
65778
|
+
case "self": return decideSelf(item.name, observed.globals.get(item.name)?.version, ctx.self);
|
|
65779
|
+
case "default":
|
|
65780
|
+
if (!managed) {
|
|
65781
|
+
const at = observed.defaults.get(item.name);
|
|
65782
|
+
return {
|
|
65783
|
+
kind: "skip",
|
|
65784
|
+
item: item.name,
|
|
65785
|
+
...at === void 0 ? {} : { at },
|
|
65786
|
+
reason: NOT_MANAGED
|
|
65787
|
+
};
|
|
65788
|
+
}
|
|
65789
|
+
return decideRanged(item, observed, resolved, ctx);
|
|
65790
|
+
default: return decideRanged(item, observed, resolved, ctx);
|
|
65791
|
+
}
|
|
65792
|
+
});
|
|
65793
|
+
const nodeChange = decisions.find((d) => d.kind === "change" && d.change.kind === "default" && d.change.tool === "node");
|
|
65794
|
+
const nodeDefault = nodeChange?.kind === "change" ? nodeChange.to : observed.defaults.get("node");
|
|
65795
|
+
return decisions.map((d, i) => {
|
|
65796
|
+
const item = items[i];
|
|
65797
|
+
const current = observed.globals.get(d.item);
|
|
65798
|
+
const node = current?.platform?.node;
|
|
65799
|
+
if (d.kind !== "leave" || item === void 0 || item.kind !== "global" && item.kind !== "self" || current === void 0 || node === void 0 || nodeDefault === void 0 || node === nodeDefault) return d;
|
|
65800
|
+
return {
|
|
65801
|
+
kind: "change",
|
|
65802
|
+
item: d.item,
|
|
65803
|
+
from: current.version,
|
|
65804
|
+
to: current.version,
|
|
65805
|
+
reason: `Node ${node} → ${nodeDefault}`,
|
|
65806
|
+
change: {
|
|
65807
|
+
kind: "reinstall",
|
|
65808
|
+
name: d.item,
|
|
65809
|
+
version: current.version
|
|
65810
|
+
}
|
|
65811
|
+
};
|
|
65812
|
+
});
|
|
65813
|
+
},
|
|
65814
|
+
act(decisions) {
|
|
65815
|
+
const changes = decisions.flatMap((d) => d.kind === "change" ? [d.change] : []);
|
|
65816
|
+
const steps = [];
|
|
65817
|
+
for (const change of changes) switch (change.kind) {
|
|
65818
|
+
case "bootstrap":
|
|
65819
|
+
steps.push({
|
|
65820
|
+
id: "vp:vp:install",
|
|
65821
|
+
items: ["vp"],
|
|
65822
|
+
doing: "installing",
|
|
65823
|
+
run: [change.version === void 0 ? VP_INSTALL : `${VP_INSTALL} && command vp upgrade ${quote(change.version)}`]
|
|
65824
|
+
});
|
|
65825
|
+
break;
|
|
65826
|
+
case "upgrade":
|
|
65827
|
+
steps.push({
|
|
65828
|
+
id: "vp:vp:upgrade",
|
|
65829
|
+
items: ["vp"],
|
|
65830
|
+
doing: "upgrading",
|
|
65831
|
+
run: [`( command vp upgrade ${quote(change.version)} )`]
|
|
65832
|
+
});
|
|
65833
|
+
break;
|
|
65834
|
+
case "mode":
|
|
65835
|
+
steps.push({
|
|
65836
|
+
id: "vp:mode:on",
|
|
65837
|
+
items: ["mode"],
|
|
65838
|
+
doing: "switching",
|
|
65839
|
+
run: ["( command vp env on )"]
|
|
65840
|
+
});
|
|
65841
|
+
break;
|
|
65842
|
+
case "default":
|
|
65843
|
+
steps.push({
|
|
65844
|
+
id: `vp:${change.tool}:default`,
|
|
65845
|
+
items: [change.tool],
|
|
65846
|
+
doing: "installing",
|
|
65847
|
+
run: [`( command vp env default ${quote(change.tool === "node" ? change.version : `${change.tool}@${change.version}`)} )`]
|
|
65848
|
+
});
|
|
65849
|
+
break;
|
|
65850
|
+
case "reinstall": steps.push({
|
|
65851
|
+
id: `vp:${change.name}:reinstall`,
|
|
65852
|
+
items: [change.name],
|
|
65853
|
+
doing: "reinstalling",
|
|
65854
|
+
run: [`( command vp install -g ${quote(`${change.name}@${change.version}`)} )`]
|
|
65855
|
+
});
|
|
65856
|
+
}
|
|
65857
|
+
const installs = changes.flatMap((c) => c.kind === "global" ? [c] : []);
|
|
65858
|
+
if (installs.length > 0) steps.push({
|
|
65859
|
+
id: "vp:globals:install",
|
|
65860
|
+
items: installs.map((c) => c.name),
|
|
65861
|
+
doing: "installing",
|
|
65862
|
+
run: [`( command vp install -g ${installs.map((c) => quote(`${c.name}@${c.version}`)).join(" ")} )`]
|
|
65863
|
+
});
|
|
65864
|
+
return steps;
|
|
65865
|
+
}
|
|
65866
|
+
},
|
|
65867
|
+
brew,
|
|
65868
|
+
cask,
|
|
65869
|
+
repos
|
|
65870
|
+
];
|
|
65190
65871
|
//#endregion
|
|
65191
65872
|
//#region src/live.tsx
|
|
65192
65873
|
const cell = (initial) => {
|
|
@@ -65272,6 +65953,7 @@ const noLive = { ask: void 0 };
|
|
|
65272
65953
|
const canRedraw = () => Boolean(process.stdout.isTTY) && process.env.TERM !== "dumb";
|
|
65273
65954
|
//#endregion
|
|
65274
65955
|
//#region src/machine.ts
|
|
65956
|
+
const decodeAt = decodeUnknownOption(AtFact);
|
|
65275
65957
|
const runOn = fn("runOn")(function* (target, script, secrets, onLine, ledger) {
|
|
65276
65958
|
let first;
|
|
65277
65959
|
const run = yield* runScript(target, script, secrets, (line) => {
|
|
@@ -65341,10 +66023,17 @@ const apply = fn("apply")(function* (target, p, script, secrets, ledger) {
|
|
|
65341
66023
|
});
|
|
65342
66024
|
break;
|
|
65343
66025
|
}
|
|
66026
|
+
case "skip":
|
|
66027
|
+
yield* ledger.stepSkipped(target.name, line.skip.id, line.skip.reason);
|
|
66028
|
+
break;
|
|
65344
66029
|
case "text":
|
|
65345
66030
|
if (line.text.trim() !== "") (current?.output ?? head).push(line.text.trimEnd());
|
|
65346
66031
|
break;
|
|
65347
|
-
case "state":
|
|
66032
|
+
case "state": {
|
|
66033
|
+
const at = decodeAt(line.fact);
|
|
66034
|
+
if (at._tag === "Some") yield* ledger.reached(target.name, address(at.value.provider, at.value.item), at.value.at);
|
|
66035
|
+
break;
|
|
66036
|
+
}
|
|
65348
66037
|
}
|
|
65349
66038
|
}), ledger);
|
|
65350
66039
|
const finish = fn("finish")(function* (outcome) {
|
|
@@ -65365,43 +66054,53 @@ const apply = fn("apply")(function* (target, p, script, secrets, ledger) {
|
|
|
65365
66054
|
yield* ledger.fail(target.name, ["apply did not finish", ...head]);
|
|
65366
66055
|
return yield* finish("failed");
|
|
65367
66056
|
}
|
|
65368
|
-
|
|
66057
|
+
const planFailed = p.providers.some((pp) => pp.decisions.some((d) => d.kind === "fail"));
|
|
66058
|
+
return yield* finish(failed.length > 0 || planFailed ? "failed" : "ok");
|
|
65369
66059
|
});
|
|
65370
66060
|
//#endregion
|
|
65371
66061
|
//#region src/questions.ts
|
|
65372
|
-
const
|
|
65373
|
-
const
|
|
65374
|
-
const
|
|
65375
|
-
|
|
65376
|
-
|
|
65377
|
-
|
|
65378
|
-
const
|
|
65379
|
-
|
|
65380
|
-
const
|
|
65381
|
-
|
|
65382
|
-
|
|
65383
|
-
|
|
65384
|
-
|
|
65385
|
-
|
|
65386
|
-
|
|
65387
|
-
|
|
65388
|
-
|
|
65389
|
-
|
|
65390
|
-
|
|
65391
|
-
|
|
65392
|
-
|
|
65393
|
-
|
|
65394
|
-
|
|
65395
|
-
|
|
65396
|
-
|
|
65397
|
-
|
|
65398
|
-
|
|
65399
|
-
|
|
65400
|
-
|
|
65401
|
-
|
|
65402
|
-
|
|
66062
|
+
const parseAnswers = (raw) => {
|
|
66063
|
+
const given = /* @__PURE__ */ new Map();
|
|
66064
|
+
for (const entry of raw) {
|
|
66065
|
+
const at = entry.lastIndexOf("=");
|
|
66066
|
+
const key = entry.slice(0, at);
|
|
66067
|
+
const choice = entry.slice(at + 1);
|
|
66068
|
+
const kind = key.split(":")[0] ?? "";
|
|
66069
|
+
if (at <= 0 || !isQuestionKind(kind)) return { invalid: `--answer ${entry}: expected kind=choice or kind:subject=choice, kind one of ${Object.keys(QUESTION_CHOICES).join(", ")}` };
|
|
66070
|
+
const choices = QUESTION_CHOICES[kind];
|
|
66071
|
+
if (!choices.includes(choice)) return { invalid: `--answer ${entry}: ${kind} takes ${choices.join(", ")}` };
|
|
66072
|
+
given.set(key, choice);
|
|
66073
|
+
}
|
|
66074
|
+
return { answer: (kind, subject) => given.get(`${kind}:${subject}`) ?? given.get(kind) };
|
|
66075
|
+
};
|
|
66076
|
+
const newestOf = (found) => found.toSorted((a, b) => b.localeCompare(a, void 0, { numeric: true }))[0] ?? "";
|
|
66077
|
+
const askQuestions = fn("askQuestions")(function* (input) {
|
|
66078
|
+
const groups = Map.groupBy(input.asked, (a) => `${a.question.kind}:${a.question.subject}`);
|
|
66079
|
+
const answered = [];
|
|
66080
|
+
for (const group of groups.values()) {
|
|
66081
|
+
const question = group[0]?.question;
|
|
66082
|
+
if (question === void 0) continue;
|
|
66083
|
+
const newest = newestOf(group.map((a) => a.question.found));
|
|
66084
|
+
const where = group.map((a) => `${a.question.found} on ${a.machine}`).join(", ");
|
|
66085
|
+
yield* input.ledger.note("ask", `${question.subject} ${where} ${question.problem}`);
|
|
66086
|
+
if (input.dryRun) {
|
|
66087
|
+
yield* input.ledger.note("answer", "not asked (dry run)");
|
|
66088
|
+
continue;
|
|
66089
|
+
}
|
|
66090
|
+
const choices = question.choices.map((c) => ({
|
|
66091
|
+
value: c.value,
|
|
66092
|
+
title: c.value === "bump" && question.bump !== void 0 ? `bump the config to ${bumpedRange(question.bump.range, newest)}` : c.title
|
|
66093
|
+
}));
|
|
66094
|
+
const answer = input.answer(question.kind, question.subject) ?? (input.ask === void 0 ? question.default : yield* input.ask(choices));
|
|
66095
|
+
yield* input.ledger.note("answer", choices.find((c) => c.value === answer)?.title ?? answer);
|
|
66096
|
+
answered.push({
|
|
66097
|
+
question,
|
|
66098
|
+
machines: group.map((a) => a.machine),
|
|
66099
|
+
newest,
|
|
66100
|
+
answer
|
|
66101
|
+
});
|
|
65403
66102
|
}
|
|
65404
|
-
return
|
|
66103
|
+
return answered;
|
|
65405
66104
|
});
|
|
65406
66105
|
//#endregion
|
|
65407
66106
|
//#region src/ship.ts
|
|
@@ -65412,22 +66111,19 @@ const unknownMachines = (config, tailnet, only) => {
|
|
|
65412
66111
|
const known = new Set(tailnet.fleet.map((m) => m.name));
|
|
65413
66112
|
const referenced = [
|
|
65414
66113
|
...Object.keys(config.machines),
|
|
65415
|
-
...
|
|
65416
|
-
...config.vp?.machines ?? [],
|
|
66114
|
+
...PROVIDERS.flatMap((p) => machinesNamed(p, config)),
|
|
65417
66115
|
...only
|
|
65418
66116
|
];
|
|
65419
66117
|
return [...new Set(referenced.filter((name) => !known.has(name)))];
|
|
65420
66118
|
};
|
|
65421
|
-
const
|
|
65422
|
-
const
|
|
65423
|
-
if (
|
|
65424
|
-
|
|
65425
|
-
|
|
65426
|
-
|
|
65427
|
-
|
|
65428
|
-
];
|
|
66119
|
+
const unshippable = (config, tailnet, only, source) => {
|
|
66120
|
+
const unknown = unknownMachines(config, tailnet, only);
|
|
66121
|
+
if (unknown.length > 0) return `unknown Machine: ${unknown.join(", ")} (the tailnet has: ${tailnet.fleet.map((m) => m.name).join(", ")})`;
|
|
66122
|
+
const fleet = new Set(Object.keys(config.machines));
|
|
66123
|
+
if (!fleet.has(tailnet.self.name)) return `this Machine (${tailnet.self.name}) is not in machines in ${source}`;
|
|
66124
|
+
const outsideFleet = only.filter((name) => !fleet.has(name));
|
|
66125
|
+
if (outsideFleet.length > 0) return `not in machines in ${source}: ${outsideFleet.join(", ")}`;
|
|
65429
66126
|
};
|
|
65430
|
-
const rebuildOf = (flags) => flags.rebuild.length === 0 ? "none" : flags.rebuild.includes("all") ? "all" : flags.rebuild;
|
|
65431
66127
|
const staleConfigs = fn("staleConfigs")(function* (config, own, checked, ledger) {
|
|
65432
66128
|
const copies = [];
|
|
65433
66129
|
for (const { machine, state } of checked) {
|
|
@@ -65436,24 +66132,43 @@ const staleConfigs = fn("staleConfigs")(function* (config, own, checked, ledger)
|
|
|
65436
66132
|
if (parsed._tag === "Failure") yield* ledger.note("info", `the config on ${machine.name} does not parse; it will be overwritten`);
|
|
65437
66133
|
else copies.push({
|
|
65438
66134
|
machine: machine.name,
|
|
65439
|
-
config: parsed.success
|
|
66135
|
+
config: parsed.success,
|
|
66136
|
+
text: state.config
|
|
65440
66137
|
});
|
|
65441
66138
|
}
|
|
65442
|
-
return staleCopies(config, copies, own)
|
|
66139
|
+
return [...Map.groupBy(staleCopies(config, copies, own), (c) => c.text)].flatMap(([text, [first, ...rest]]) => {
|
|
66140
|
+
if (first === void 0) return [];
|
|
66141
|
+
const { config, machine } = first;
|
|
66142
|
+
const machines = [machine, ...rest.map((c) => c.machine)];
|
|
66143
|
+
return [{
|
|
66144
|
+
text,
|
|
66145
|
+
config,
|
|
66146
|
+
machines,
|
|
66147
|
+
description: `${machines.join(", ")} (shipped ${config.shippedAt} from ${config.shippedFrom ?? "unknown"})`
|
|
66148
|
+
}];
|
|
66149
|
+
});
|
|
65443
66150
|
});
|
|
65444
66151
|
const schemaJson = encodeSync(fromJsonString(Unknown, { space: 2 }));
|
|
65445
66152
|
const ship = fn("ship")(function* (flags) {
|
|
66153
|
+
while ((yield* shipPass(flags)) === "adopted");
|
|
66154
|
+
});
|
|
66155
|
+
const shipPass = fn("shipPass")(function* (flags) {
|
|
65446
66156
|
const fs = yield* FileSystem;
|
|
66157
|
+
const parsed = parseAnswers(flags.answers);
|
|
66158
|
+
if ("invalid" in parsed) return yield* fail$1(new ShipError(parsed.invalid));
|
|
66159
|
+
const given = parsed.answer;
|
|
66160
|
+
const notAddress = flags.rebuild.find((r) => r !== "all" && !PROVIDERS.some((p) => p.rule === "record" && p.name === parseAddress(r)?.provider));
|
|
66161
|
+
if (notAddress !== void 0) return yield* fail$1(new ShipError(`--rebuild ${notAddress}: expected a record Item (repos:<name>) or all`));
|
|
65447
66162
|
const path = flags.config ?? configPath(process.env);
|
|
65448
66163
|
const config = yield* loadConfig(path);
|
|
66164
|
+
const ordered = orderProviders(PROVIDERS, (p) => dependenciesOf(p, config));
|
|
66165
|
+
if ("cycle" in ordered) return yield* fail$1(new ShipError(`dependsOn cycle: ${ordered.cycle.join(" → ")}`));
|
|
66166
|
+
const providers = ordered.order;
|
|
65449
66167
|
const tailnet = yield* readTailnet();
|
|
65450
|
-
const
|
|
65451
|
-
if (
|
|
66168
|
+
const problem = unshippable(config, tailnet, flags.only, path);
|
|
66169
|
+
if (problem !== void 0) return yield* fail$1(new ShipError(problem));
|
|
65452
66170
|
const fleet = new Set(Object.keys(config.machines));
|
|
65453
66171
|
const own = flags.config === void 0 ? tailnet.self.name : void 0;
|
|
65454
|
-
if (!fleet.has(tailnet.self.name)) return yield* fail$1(new ShipError(`this Machine (${tailnet.self.name}) is not in machines in ${path}`));
|
|
65455
|
-
const outsideFleet = flags.only.filter((name) => !fleet.has(name));
|
|
65456
|
-
if (outsideFleet.length > 0) return yield* fail$1(new ShipError(`not in machines in ${path}: ${outsideFleet.join(", ")}`));
|
|
65457
66172
|
const order = Object.keys(config.machines);
|
|
65458
66173
|
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));
|
|
65459
66174
|
const count = `${targetsList.length} machine${targetsList.length === 1 ? "" : "s"}`;
|
|
@@ -65467,8 +66182,10 @@ const ship = fn("ship")(function* (flags) {
|
|
|
65467
66182
|
});
|
|
65468
66183
|
const redraw = canRedraw();
|
|
65469
66184
|
let changing = false;
|
|
66185
|
+
let adopted = false;
|
|
65470
66186
|
const finish = fn("finish")(function* () {
|
|
65471
66187
|
const state = yield* ledger.end;
|
|
66188
|
+
if (adopted) return;
|
|
65472
66189
|
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
66190
|
yield* log(ledgerText(state, redraw ? {
|
|
65474
66191
|
full: false,
|
|
@@ -65482,11 +66199,6 @@ const ship = fn("ship")(function* (flags) {
|
|
|
65482
66199
|
});
|
|
65483
66200
|
yield* addFinalizer(() => finish());
|
|
65484
66201
|
const live = redraw ? yield* startLive(store, color) : noLive;
|
|
65485
|
-
const cloneRootOf = (machine) => config.machines[machine.name]?.clones ?? "~/.local/share/fleetfizz/clones";
|
|
65486
|
-
const checkRepos = (config.repos ?? []).map((r) => ({
|
|
65487
|
-
name: repoName(r),
|
|
65488
|
-
outputs: r.outputs ?? []
|
|
65489
|
-
}));
|
|
65490
66202
|
const checked = yield* forEach$1(targetsList, fn("checkOne")(function* (machine) {
|
|
65491
66203
|
const local = machine.name === tailnet.self.name;
|
|
65492
66204
|
if (!machine.online) {
|
|
@@ -65497,9 +66209,19 @@ const ship = fn("ship")(function* (flags) {
|
|
|
65497
66209
|
yield* ledger.skip(machine.name, "no Tailscale SSH server");
|
|
65498
66210
|
return;
|
|
65499
66211
|
}
|
|
66212
|
+
const identity = {
|
|
66213
|
+
name: machine.name,
|
|
66214
|
+
platform: machine.platform
|
|
66215
|
+
};
|
|
65500
66216
|
const script = checkScript({
|
|
65501
|
-
|
|
65502
|
-
|
|
66217
|
+
scope: {
|
|
66218
|
+
machine: identity,
|
|
66219
|
+
config
|
|
66220
|
+
},
|
|
66221
|
+
providers: providers.map((provider) => ({
|
|
66222
|
+
provider,
|
|
66223
|
+
items: targetedItems(provider, config, identity)
|
|
66224
|
+
}))
|
|
65503
66225
|
});
|
|
65504
66226
|
const result = yield* check({
|
|
65505
66227
|
name: machine.name,
|
|
@@ -65515,58 +66237,107 @@ const ship = fn("ship")(function* (flags) {
|
|
|
65515
66237
|
}), { concurrency: "unbounded" }).pipe(map$5((list) => list.filter((g) => g !== void 0)));
|
|
65516
66238
|
const stale = yield* staleConfigs(config, own, checked, ledger);
|
|
65517
66239
|
if (stale.length > 0) {
|
|
65518
|
-
const what = `newer config on ${stale.join(", ")}`;
|
|
66240
|
+
const what = `newer config on ${stale.map((s) => s.description).join(", ")}`;
|
|
66241
|
+
const answers = stale.flatMap((s) => s.machines).map((m) => given("config", m));
|
|
65519
66242
|
if (flags.dryRun) yield* ledger.note("info", `${what}; a Ship would ask before overwriting it`);
|
|
65520
|
-
else if (
|
|
66243
|
+
else if (answers.every((a) => a === "overwrite")) yield* ledger.note("info", `${what}; overwriting it, as answered`);
|
|
66244
|
+
else if (answers.includes("stop")) return yield* fail$1(new ShipError("aborted: the config here is older"));
|
|
65521
66245
|
else if (live.ask === void 0) return yield* fail$1(new ShipError(`${what}; copy it here first, or pass --overwrite-config`));
|
|
65522
66246
|
else {
|
|
66247
|
+
const usable = [];
|
|
66248
|
+
for (const s of stale) {
|
|
66249
|
+
const why = unshippable(s.config, tailnet, flags.only, `the config on ${s.machines[0]}`);
|
|
66250
|
+
if (why === void 0) usable.push(s);
|
|
66251
|
+
else yield* ledger.note("info", `cannot ship from here: ${why}`);
|
|
66252
|
+
}
|
|
65523
66253
|
yield* ledger.note("ask", what);
|
|
65524
|
-
const
|
|
65525
|
-
|
|
65526
|
-
|
|
65527
|
-
|
|
65528
|
-
|
|
65529
|
-
|
|
65530
|
-
|
|
65531
|
-
|
|
65532
|
-
|
|
66254
|
+
const choice = yield* live.ask([
|
|
66255
|
+
...usable.map((s) => ({
|
|
66256
|
+
title: `use the config on ${s.machines[0]} (replaces this one)`,
|
|
66257
|
+
value: s
|
|
66258
|
+
})),
|
|
66259
|
+
{
|
|
66260
|
+
title: "overwrite it with this Machine's config",
|
|
66261
|
+
value: "overwrite"
|
|
66262
|
+
},
|
|
66263
|
+
{
|
|
66264
|
+
title: "stop",
|
|
66265
|
+
value: "stop"
|
|
66266
|
+
}
|
|
66267
|
+
]);
|
|
66268
|
+
if (choice === "stop") {
|
|
66269
|
+
yield* ledger.note("answer", "stop");
|
|
66270
|
+
return yield* fail$1(new ShipError("aborted: the config here is older"));
|
|
66271
|
+
}
|
|
66272
|
+
if (choice === "overwrite") yield* ledger.note("answer", "overwrite it");
|
|
66273
|
+
else {
|
|
66274
|
+
yield* ledger.note("answer", `use the config on ${choice.machines[0]}`);
|
|
66275
|
+
yield* fs.writeFileString(path, choice.text);
|
|
66276
|
+
adopted = true;
|
|
66277
|
+
return "adopted";
|
|
66278
|
+
}
|
|
65533
66279
|
}
|
|
65534
66280
|
}
|
|
65535
|
-
const
|
|
65536
|
-
|
|
66281
|
+
const targeted = providers.map((provider) => {
|
|
66282
|
+
const items = /* @__PURE__ */ new Map();
|
|
66283
|
+
for (const { machine } of checked) for (const item of targetedItems(provider, config, machine)) items.set(item.name, item);
|
|
66284
|
+
return {
|
|
66285
|
+
provider,
|
|
66286
|
+
items: [...items.values()]
|
|
66287
|
+
};
|
|
66288
|
+
});
|
|
66289
|
+
const lent = targeted.some(({ provider, items }) => provider.name === "repos" && items.length > 0) ? yield* readLentToken() : { token: void 0 };
|
|
66290
|
+
if (lent.token === void 0 && lent.reason !== void 0) yield* ledger.note("info", `no gh token (${lent.reason}); private repos will fail`);
|
|
66291
|
+
const resolved = Object.fromEntries(yield* forEach$1(targeted, ({ provider, items }) => provider.resolve(items, lent.token === void 0 ? {} : { token: lent.token }).pipe(map$5((r) => [provider.name, r])), { concurrency: "unbounded" }));
|
|
66292
|
+
const planAll = (cfg, answer) => checked.map((g) => ({
|
|
65537
66293
|
...g,
|
|
65538
|
-
plan:
|
|
65539
|
-
|
|
65540
|
-
machine: {
|
|
65541
|
-
name: g.machine.name,
|
|
65542
|
-
platform: g.machine.platform
|
|
65543
|
-
},
|
|
66294
|
+
plan: planMachine({
|
|
66295
|
+
providers,
|
|
65544
66296
|
state: g.state,
|
|
65545
|
-
|
|
65546
|
-
|
|
65547
|
-
|
|
65548
|
-
|
|
66297
|
+
resolved,
|
|
66298
|
+
ctx: {
|
|
66299
|
+
machine: {
|
|
66300
|
+
name: g.machine.name,
|
|
66301
|
+
platform: g.machine.platform
|
|
66302
|
+
},
|
|
66303
|
+
config: cfg,
|
|
66304
|
+
self: flags.version,
|
|
66305
|
+
rebuild: (address) => flags.rebuild.includes("all") || flags.rebuild.includes(address),
|
|
66306
|
+
answer
|
|
66307
|
+
}
|
|
65549
66308
|
})
|
|
65550
66309
|
}));
|
|
65551
66310
|
let cfg = config;
|
|
65552
|
-
|
|
65553
|
-
const
|
|
65554
|
-
|
|
65555
|
-
|
|
65556
|
-
|
|
65557
|
-
|
|
65558
|
-
|
|
65559
|
-
|
|
65560
|
-
|
|
65561
|
-
|
|
66311
|
+
const decided = /* @__PURE__ */ new Map();
|
|
66312
|
+
const answer = (kind, subject) => decided.get(`${kind}:${subject}`) ?? given(kind, subject);
|
|
66313
|
+
let planned = planAll(cfg, answer);
|
|
66314
|
+
for (;;) {
|
|
66315
|
+
const asked = planned.flatMap((g) => g.plan.providers.flatMap((pp) => pp.decisions.flatMap((d) => d.kind === "ask" ? [{
|
|
66316
|
+
machine: g.machine.name,
|
|
66317
|
+
question: d.question
|
|
66318
|
+
}] : [])));
|
|
66319
|
+
const answered = yield* askQuestions({
|
|
66320
|
+
asked,
|
|
66321
|
+
answer,
|
|
66322
|
+
ledger,
|
|
66323
|
+
ask: live.ask,
|
|
66324
|
+
dryRun: flags.dryRun
|
|
66325
|
+
});
|
|
66326
|
+
let progressed = false;
|
|
66327
|
+
for (const { question, newest, answer: choice } of answered) {
|
|
66328
|
+
const key = `${question.kind}:${question.subject}`;
|
|
66329
|
+
progressed ||= !decided.has(key);
|
|
66330
|
+
decided.set(key, choice);
|
|
66331
|
+
if (choice !== "bump" || question.bump === void 0) continue;
|
|
65562
66332
|
changing = true;
|
|
65563
|
-
yield* bumpConfig(path,
|
|
66333
|
+
const range = yield* bumpConfig(path, question.bump.path, newest);
|
|
66334
|
+
progressed ||= range !== question.bump.range;
|
|
66335
|
+
cfg = yield* loadConfig(path);
|
|
65564
66336
|
}
|
|
65565
|
-
if (
|
|
65566
|
-
|
|
65567
|
-
planned = planAll(cfg, answers);
|
|
66337
|
+
if (!progressed) break;
|
|
66338
|
+
planned = planAll(cfg, answer);
|
|
65568
66339
|
}
|
|
65569
|
-
for (const { machine, plan: p
|
|
66340
|
+
for (const { machine, plan: p } of planned) yield* ledger.plan(machine.name, p);
|
|
65570
66341
|
if (flags.dryRun) return;
|
|
65571
66342
|
const shippedAt = formatIso(yield* now);
|
|
65572
66343
|
changing = true;
|
|
@@ -65580,44 +66351,36 @@ const ship = fn("ship")(function* (flags) {
|
|
|
65580
66351
|
const stamped = stampWith(planned.map((g) => g.machine.name));
|
|
65581
66352
|
yield* fs.writeFileString(path, stamped);
|
|
65582
66353
|
const schema = `${schemaJson(fleetConfigJsonSchema())}\n`;
|
|
65583
|
-
const lent = planned.some((g) => g.plan.repos.length > 0) ? yield* readLentToken() : { token: void 0 };
|
|
65584
|
-
if (lent.token === void 0 && lent.reason !== void 0) yield* ledger.note("info", `no gh token (${lent.reason}); private repos will fail`);
|
|
65585
|
-
const machineNames = [...fleet];
|
|
65586
66354
|
const stdin = applyStdin(lent.token, stamped, schema);
|
|
65587
66355
|
const delivered = yield* forEach$1(planned, fn("applyOne")(function* ({ machine, local, plan: p }) {
|
|
65588
|
-
const script = applyScript({
|
|
65589
|
-
plan: p,
|
|
65590
|
-
cloneRoot: cloneRootOf(machine),
|
|
65591
|
-
env: {
|
|
65592
|
-
machine: machine.name,
|
|
65593
|
-
platform: machine.platform,
|
|
65594
|
-
machines: machineNames
|
|
65595
|
-
}
|
|
65596
|
-
});
|
|
65597
66356
|
return (yield* apply({
|
|
65598
66357
|
name: machine.name,
|
|
65599
66358
|
local
|
|
65600
|
-
}, p,
|
|
66359
|
+
}, p, applyScript(p), stdin, ledger)).done.includes("config") ? machine.name : void 0;
|
|
65601
66360
|
}), { concurrency: "unbounded" }).pipe(map$5((list) => list.filter((name) => name !== void 0)));
|
|
65602
66361
|
yield* fs.writeFileString(path, stampWith(delivered));
|
|
65603
66362
|
}, scoped);
|
|
65604
66363
|
//#endregion
|
|
65605
|
-
//#region src/
|
|
66364
|
+
//#region src/command.ts
|
|
65606
66365
|
const shipCommand = make("ship", {
|
|
65607
66366
|
dryRun: Boolean$1("dry-run").pipe(withDefault(false), withDescription$1("Check and plan only. Print every Machine's plan and change nothing.")),
|
|
65608
|
-
only: String$1("only").pipe(atLeast(0), withDescription$1("Ship only to
|
|
65609
|
-
rebuild: String$1("rebuild").pipe(atLeast(0), withDescription$1("
|
|
65610
|
-
|
|
65611
|
-
|
|
65612
|
-
|
|
65613
|
-
|
|
66367
|
+
only: String$1("only").pipe(atLeast(0), withMetavar("machine"), withDescription$1("Ship only to this Machine. Repeat the flag for more.")),
|
|
66368
|
+
rebuild: String$1("rebuild").pipe(atLeast(0), withMetavar("item"), withDescription$1("Rebuild a record Item (`repos:<name>`) even when it is up to date. `all` for every one.")),
|
|
66369
|
+
answer: String$1("answer").pipe(atLeast(0), withMetavar("answer"), withDescription$1("Answer a Question in advance: `kind=choice` or `kind:subject=choice`. Repeat the flag for more.")),
|
|
66370
|
+
bump: Boolean$1("bump").pipe(withDefault(false), withDescription$1("Answer every above-range Question by bumping the config (`--answer range=bump`).")),
|
|
66371
|
+
enforce: Boolean$1("enforce").pipe(withDefault(false), withDescription$1("Answer every above-range Question by downgrading into the Range (`--answer range=downgrade`).")),
|
|
66372
|
+
overwriteConfig: Boolean$1("overwrite-config").pipe(withDefault(false), withDescription$1("Ship even when another Machine holds a newer config, overwriting it (`--answer config=overwrite`).")),
|
|
66373
|
+
config: String$1("config").pipe(optional, withMetavar("path"), withDescription$1("The config file. Default: `$XDG_CONFIG_HOME/fleetfizz/config.jsonc`."))
|
|
65614
66374
|
}).pipe(withDescription("Ship the config to every reachable Machine."), withHandler((flags) => ship({
|
|
65615
66375
|
dryRun: flags.dryRun,
|
|
65616
66376
|
only: flags.only,
|
|
65617
66377
|
rebuild: flags.rebuild,
|
|
65618
|
-
|
|
65619
|
-
|
|
65620
|
-
|
|
66378
|
+
answers: [
|
|
66379
|
+
...flags.answer,
|
|
66380
|
+
...flags.bump ? ["range=bump"] : [],
|
|
66381
|
+
...flags.enforce ? ["range=downgrade"] : [],
|
|
66382
|
+
...flags.overwriteConfig ? ["config=overwrite"] : []
|
|
66383
|
+
],
|
|
65621
66384
|
config: flags.config._tag === "Some" ? flags.config.value : void 0,
|
|
65622
66385
|
version
|
|
65623
66386
|
}).pipe(catch_((e) => gen(function* () {
|
|
@@ -65625,6 +66388,8 @@ const shipCommand = make("ship", {
|
|
|
65625
66388
|
process.exitCode = 1;
|
|
65626
66389
|
})))));
|
|
65627
66390
|
const reportCommand = make("report", {}).pipe(withDescription("Print the full Ledger of the last Ship from this Machine."), withHandler(() => report()));
|
|
66391
|
+
//#endregion
|
|
66392
|
+
//#region src/bin.ts
|
|
65628
66393
|
run(make("fizz").pipe(withDescription("Keep every Machine on your tailnet in line with one config."), withSubcommands([shipCommand, reportCommand])), { version }).pipe(provide(layer), runMain);
|
|
65629
66394
|
//#endregion
|
|
65630
66395
|
export {};
|