@flowscripter/dynamic-cli-framework 7.0.11 → 7.1.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/src/cli/BaseCLI.d.ts.map +1 -1
- package/dist/src/cli/BaseCLI.js +7 -1
- package/dist/src/cli/BaseCLIFeatureOptions.d.ts +5 -0
- package/dist/src/cli/BaseCLIFeatureOptions.d.ts.map +1 -1
- package/dist/src/cli/DefaultRuntimeCLI.js +1 -1
- package/dist/src/runtime/runner.d.ts +10 -1
- package/dist/src/runtime/runner.d.ts.map +1 -1
- package/dist/src/runtime/runner.js +7 -1
- package/dist/src/service/configuration/DefaultKeyValueService.d.ts +9 -1
- package/dist/src/service/configuration/DefaultKeyValueService.d.ts.map +1 -1
- package/dist/src/service/configuration/DefaultKeyValueService.js +16 -1
- package/dist/src/service/configuration/KeyValueServiceProvider.d.ts.map +1 -1
- package/dist/src/service/configuration/KeyValueServiceProvider.js +13 -6
- package/dist/src/service/shutdown/ShutdownServiceProvider.js +3 -3
- package/dist/src/service/spawn/DefaultSpawnService.d.ts.map +1 -1
- package/dist/src/service/spawn/DefaultSpawnService.js +1 -0
- package/dist/src/service/upgrade/DefaultUpgradeService.d.ts +30 -2
- package/dist/src/service/upgrade/DefaultUpgradeService.d.ts.map +1 -1
- package/dist/src/service/upgrade/DefaultUpgradeService.js +99 -6
- package/dist/src/service/upgrade/UpgradeServiceProvider.d.ts +19 -4
- package/dist/src/service/upgrade/UpgradeServiceProvider.d.ts.map +1 -1
- package/dist/src/service/upgrade/UpgradeServiceProvider.js +51 -12
- package/dist/src/startup/banner/bannerStartupTask.d.ts.map +1 -1
- package/dist/src/startup/banner/bannerStartupTask.js +9 -5
- package/package.json +2 -2
- package/src/cli/BaseCLI.ts +7 -0
- package/src/cli/BaseCLIFeatureOptions.ts +5 -0
- package/src/cli/DefaultRuntimeCLI.ts +1 -1
- package/src/runtime/runner.ts +17 -2
- package/src/service/configuration/DefaultKeyValueService.ts +22 -1
- package/src/service/configuration/KeyValueServiceProvider.ts +18 -6
- package/src/service/shutdown/ShutdownServiceProvider.ts +3 -3
- package/src/service/spawn/DefaultSpawnService.ts +1 -0
- package/src/service/upgrade/DefaultUpgradeService.ts +112 -5
- package/src/service/upgrade/UpgradeServiceProvider.ts +77 -19
- package/src/service/upgrade/command/UpgradeSubCommand.ts +2 -2
- package/src/startup/banner/bannerStartupTask.ts +11 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseCLI.d.ts","sourceRoot":"","sources":["../../../src/cli/BaseCLI.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,yCAAyC,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIzE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AAGvE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAC/E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAsBzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;AAS3E,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,QAAQ,MAAM,yBAAyB,CAAC;AAGpD,OAAO,KAAK,MAAM,MAAM,uBAAuB,CAAC;AAChD,OAAO,KAAK,SAAS,MAAM,0BAA0B,CAAC;AACtD,OAAO,KAAK,qBAAqB,MAAM,4BAA4B,CAAC;AAqCpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,MAAM,CAAC,OAAO,OAAO,OAAQ,YAAW,GAAG;;IAazC,YACE,SAAS,EAAE,SAAS,EACpB,oBAAoB,EAAE,cAAc,EACpC,oBAAoB,EAAE,cAAc,EACpC,aAAa,EAAE,OAAO,EACtB,aAAa,EAAE,OAAO,EACtB,cAAc,EAAE,QAAQ,EACxB,cAAc,EAAE,QAAQ,EACxB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,SAAS,GAAG,SAAS,EAChC,OAAO,CAAC,EAAE,qBAAqB,
|
|
1
|
+
{"version":3,"file":"BaseCLI.d.ts","sourceRoot":"","sources":["../../../src/cli/BaseCLI.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,yCAAyC,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIzE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AAGvE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAC/E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAsBzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;AAS3E,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,QAAQ,MAAM,yBAAyB,CAAC;AAGpD,OAAO,KAAK,MAAM,MAAM,uBAAuB,CAAC;AAChD,OAAO,KAAK,SAAS,MAAM,0BAA0B,CAAC;AACtD,OAAO,KAAK,qBAAqB,MAAM,4BAA4B,CAAC;AAqCpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,MAAM,CAAC,OAAO,OAAO,OAAQ,YAAW,GAAG;;IAazC,YACE,SAAS,EAAE,SAAS,EACpB,oBAAoB,EAAE,cAAc,EACpC,oBAAoB,EAAE,cAAc,EACpC,aAAa,EAAE,OAAO,EACtB,aAAa,EAAE,OAAO,EACtB,cAAc,EAAE,QAAQ,EACxB,cAAc,EAAE,QAAQ,EACxB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,SAAS,GAAG,SAAS,EAChC,OAAO,CAAC,EAAE,qBAAqB,EAmEhC;IAED;;;;;;;OAOG;IACI,kBAAkB,CAAC,eAAe,EAAE,eAAe,QAEzD;IAED;;;;;;;;OAQG;IACH;;;;;OAKG;IACI,cAAc,CAAC,IAAI,EAAE,WAAW,QAEtC;IAEM,UAAU,CAAC,OAAO,EAAE,OAAO,QAQjC;IAEK,GAAG,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CAoQzD;CACF"}
|
package/dist/src/cli/BaseCLI.js
CHANGED
|
@@ -109,6 +109,7 @@ export default class BaseCLI {
|
|
|
109
109
|
fetchServiceEnabled: false,
|
|
110
110
|
upgradeServiceEnabled: false,
|
|
111
111
|
upgradeLocationsConfig: { supportedPlatforms: [] },
|
|
112
|
+
restartAfterAutoUpgrade: false,
|
|
112
113
|
pluginServiceEnabled: false,
|
|
113
114
|
pluginServiceRemoteConfig: { name: "", registryUrl: "", packageJsonNamespace: "" },
|
|
114
115
|
pluginServiceLocalConfig: { nodeModulesPath: "", packageJsonNamespace: "" },
|
|
@@ -229,7 +230,8 @@ export default class BaseCLI {
|
|
|
229
230
|
}
|
|
230
231
|
let upgradeServiceProvider;
|
|
231
232
|
if (this.#options.upgradeServiceEnabled) {
|
|
232
|
-
upgradeServiceProvider = new UpgradeServiceProvider(UPGRADE_SERVICE_PRIORITY, this.#options.upgradeLocationsConfig);
|
|
233
|
+
upgradeServiceProvider = new UpgradeServiceProvider(UPGRADE_SERVICE_PRIORITY, this.#options.upgradeLocationsConfig, this.#options.restartAfterAutoUpgrade);
|
|
234
|
+
upgradeServiceProvider.setRestartArgs(args);
|
|
233
235
|
this.addServiceProvider(upgradeServiceProvider);
|
|
234
236
|
}
|
|
235
237
|
if (this.#options.pluginServiceEnabled) {
|
|
@@ -285,6 +287,10 @@ export default class BaseCLI {
|
|
|
285
287
|
this.#commandRegistry.addCommand(new VersionCommand());
|
|
286
288
|
// run...
|
|
287
289
|
const runResult = await run(args, this.#commandRegistry, this.#serviceProviderRegistry, configurationServiceProvider, keyValueServiceProvider, this.#context, defaultCommand, this.#startupServiceProvider.startupService);
|
|
290
|
+
// a startup task ended the run, having already handled its outcome itself
|
|
291
|
+
if (runResult.exitRequested) {
|
|
292
|
+
return { runState: runResult.runState };
|
|
293
|
+
}
|
|
288
294
|
// then handle the result...
|
|
289
295
|
if (runResult.runState === RunState.NO_COMMAND) {
|
|
290
296
|
// print usage
|
|
@@ -12,6 +12,11 @@ export default interface BaseCLIFeatureOptions {
|
|
|
12
12
|
readonly fetchServiceEnabled?: boolean;
|
|
13
13
|
readonly upgradeServiceEnabled?: boolean;
|
|
14
14
|
readonly upgradeLocationsConfig?: UpgradeLocationsConfig;
|
|
15
|
+
/**
|
|
16
|
+
* When an automatic upgrade on startup succeeds, run the upgraded executable with the same
|
|
17
|
+
* arguments and end this run with its result, rather than continuing on the old version.
|
|
18
|
+
*/
|
|
19
|
+
readonly restartAfterAutoUpgrade?: boolean;
|
|
15
20
|
readonly pluginServiceEnabled?: boolean;
|
|
16
21
|
readonly pluginServiceRemoteConfig?: NpmjsPluginRepositoryConfig;
|
|
17
22
|
readonly pluginServiceLocalConfig?: NpmPluginRepositoryConfig;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseCLIFeatureOptions.d.ts","sourceRoot":"","sources":["../../../src/cli/BaseCLIFeatureOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8CAA8C,CAAC;AAC3F,OAAO,KAAK,EACV,2BAA2B,EAC3B,yBAAyB,EAC1B,MAAM,wCAAwC,CAAC;AAEhD,MAAM,CAAC,OAAO,WAAW,qBAAqB;IAC5C,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAC5C,QAAQ,CAAC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IACzC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAC1C,QAAQ,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IACxC,QAAQ,CAAC,8BAA8B,CAAC,EAAE,OAAO,CAAC;IAClD,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAC5C,QAAQ,CAAC,0BAA0B,CAAC,EAAE,OAAO,CAAC;IAC9C,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IACvC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IACvC,QAAQ,CAAC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IACzC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IACzD,QAAQ,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IACxC,QAAQ,CAAC,yBAAyB,CAAC,EAAE,2BAA2B,CAAC;IACjE,QAAQ,CAAC,wBAAwB,CAAC,EAAE,yBAAyB,CAAC;IAC9D,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IACvC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CACrC"}
|
|
1
|
+
{"version":3,"file":"BaseCLIFeatureOptions.d.ts","sourceRoot":"","sources":["../../../src/cli/BaseCLIFeatureOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8CAA8C,CAAC;AAC3F,OAAO,KAAK,EACV,2BAA2B,EAC3B,yBAAyB,EAC1B,MAAM,wCAAwC,CAAC;AAEhD,MAAM,CAAC,OAAO,WAAW,qBAAqB;IAC5C,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAC5C,QAAQ,CAAC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IACzC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAC1C,QAAQ,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IACxC,QAAQ,CAAC,8BAA8B,CAAC,EAAE,OAAO,CAAC;IAClD,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAC5C,QAAQ,CAAC,0BAA0B,CAAC,EAAE,OAAO,CAAC;IAC9C,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IACvC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IACvC,QAAQ,CAAC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IACzC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IACzD;;;OAGG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAC3C,QAAQ,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IACxC,QAAQ,CAAC,yBAAyB,CAAC,EAAE,2BAA2B,CAAC;IACjE,QAAQ,CAAC,wBAAwB,CAAC,EAAE,yBAAyB,CAAC;IAC9D,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IACvC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CACrC"}
|
|
@@ -39,7 +39,7 @@ export default class DefaultRuntimeCLI extends BaseCLI {
|
|
|
39
39
|
logger.debug(() => `keyReader constructed: ${keyReader !== undefined}`);
|
|
40
40
|
const promptingEnabled = stdinIsTty && stderrIsTty;
|
|
41
41
|
logger.debug(() => `promptingEnabled default: ${promptingEnabled}`);
|
|
42
|
-
super(cliConfig, Writable.toWeb(process.stdout), Writable.toWeb(process.stderr), supportsColor.stdout !== false, supportsColor.stderr !== false, stdoutTerminal, stderrTerminal, new TtyStyler(colorLevel, supportsHyperlinks(process.stderr)), keyReader, { promptingEnabled, ...options });
|
|
42
|
+
super(cliConfig, Writable.toWeb(process.stdout), Writable.toWeb(process.stderr), supportsColor.stdout !== false, supportsColor.stderr !== false, stdoutTerminal, stderrTerminal, new TtyStyler(colorLevel, supportsHyperlinks(process.stderr)), keyReader, { promptingEnabled, restartAfterAutoUpgrade: true, ...options });
|
|
43
43
|
}
|
|
44
44
|
async run() {
|
|
45
45
|
const runResult = await super.run(process.argv.slice(2));
|
|
@@ -6,6 +6,13 @@ import type ConfigurationServiceProvider from "../service/configuration/Configur
|
|
|
6
6
|
import type KeyValueServiceProvider from "../service/configuration/KeyValueServiceProvider.ts";
|
|
7
7
|
import type { Command } from "@flowscripter/dynamic-cli-framework-api";
|
|
8
8
|
import DefaultStartupService from "../service/startup/DefaultStartupService.ts";
|
|
9
|
+
/**
|
|
10
|
+
* The {@link RunResult} returned by {@link run}, with `exitRequested` set when a startup task ended
|
|
11
|
+
* the run early by returning an `exitRequest`.
|
|
12
|
+
*/
|
|
13
|
+
export type RunnerResult = RunResult & {
|
|
14
|
+
readonly exitRequested?: true;
|
|
15
|
+
};
|
|
9
16
|
/**
|
|
10
17
|
* Scan arguments and execute the parsed {@link Command} clauses with provided argument values
|
|
11
18
|
* using the provided {@link Context}.
|
|
@@ -34,6 +41,8 @@ import DefaultStartupService from "../service/startup/DefaultStartupService.ts";
|
|
|
34
41
|
* - Run the task: awaited before continuing if its mode is "blocking" (the default, and how
|
|
35
42
|
* every ServiceProvider's initService() behaves); fired without awaiting if "background"
|
|
36
43
|
* (errors logged, not propagated).
|
|
44
|
+
* - Return immediately if a "blocking" task returned an exitRequest, using its runState and
|
|
45
|
+
* skipping the remaining tasks and the command.
|
|
37
46
|
* 2. Scan arguments for any GlobalModifierCommand clauses not provided by a StartupTask
|
|
38
47
|
* - For each discovered clause:
|
|
39
48
|
* - Set any argument defaults from ConfigurationServiceProvider
|
|
@@ -80,5 +89,5 @@ import DefaultStartupService from "../service/startup/DefaultStartupService.ts";
|
|
|
80
89
|
* @param defaultCommand optional {@link SubCommand} or {@link GlobalCommand} implementation to attempt to parse arguments for and execute if
|
|
81
90
|
* no non-modifier {@link Command} name is identified in the provided arguments.
|
|
82
91
|
*/
|
|
83
|
-
export declare function run(args: ReadonlyArray<string>, commandRegistry: CommandRegistry, serviceProviderRegistry: ServiceProviderRegistry, configurationServiceProvider: ConfigurationServiceProvider | undefined, keyValueServiceProvider: KeyValueServiceProvider | undefined, context: Context, defaultCommand?: Command, startupService?: DefaultStartupService): Promise<
|
|
92
|
+
export declare function run(args: ReadonlyArray<string>, commandRegistry: CommandRegistry, serviceProviderRegistry: ServiceProviderRegistry, configurationServiceProvider: ConfigurationServiceProvider | undefined, keyValueServiceProvider: KeyValueServiceProvider | undefined, context: Context, defaultCommand?: Command, startupService?: DefaultStartupService): Promise<RunnerResult>;
|
|
84
93
|
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/runtime/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AACvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAiB/E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAEvF,OAAO,KAAK,4BAA4B,MAAM,0DAA0D,CAAC;AACzG,OAAO,KAAK,uBAAuB,MAAM,qDAAqD,CAAC;AAK/F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AAKvE,OAAO,qBAAqB,MAAM,6CAA6C,CAAC;
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/runtime/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AACvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAiB/E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAEvF,OAAO,KAAK,4BAA4B,MAAM,0DAA0D,CAAC;AACzG,OAAO,KAAK,uBAAuB,MAAM,qDAAqD,CAAC;AAK/F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AAKvE,OAAO,qBAAqB,MAAM,6CAA6C,CAAC;AAKhF;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG;IAAE,QAAQ,CAAC,aAAa,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC;AAkfzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2EG;AACH,wBAAsB,GAAG,CACvB,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,eAAe,EAAE,eAAe,EAChC,uBAAuB,EAAE,uBAAuB,EAChD,4BAA4B,EAAE,4BAA4B,GAAG,SAAS,EACtE,uBAAuB,EAAE,uBAAuB,GAAG,SAAS,EAC5D,OAAO,EAAE,OAAO,EAChB,cAAc,CAAC,EAAE,OAAO,EACxB,cAAc,CAAC,EAAE,qBAAqB,GACrC,OAAO,CAAC,YAAY,CAAC,CAoKvB"}
|
|
@@ -389,6 +389,8 @@ async function findAndExecuteDefaultNonModifierCommand(availableArgs, unusedArgs
|
|
|
389
389
|
* - Run the task: awaited before continuing if its mode is "blocking" (the default, and how
|
|
390
390
|
* every ServiceProvider's initService() behaves); fired without awaiting if "background"
|
|
391
391
|
* (errors logged, not propagated).
|
|
392
|
+
* - Return immediately if a "blocking" task returned an exitRequest, using its runState and
|
|
393
|
+
* skipping the remaining tasks and the command.
|
|
392
394
|
* 2. Scan arguments for any GlobalModifierCommand clauses not provided by a StartupTask
|
|
393
395
|
* - For each discovered clause:
|
|
394
396
|
* - Set any argument defaults from ConfigurationServiceProvider
|
|
@@ -477,7 +479,11 @@ export async function run(args, commandRegistry, serviceProviderRegistry, config
|
|
|
477
479
|
? keyValueServiceProvider.getContextForScope(context, "service", task.id)
|
|
478
480
|
: context;
|
|
479
481
|
if ((task.mode ?? "blocking") === "blocking") {
|
|
480
|
-
await task.run(taskContext);
|
|
482
|
+
const outcome = await task.run(taskContext);
|
|
483
|
+
if (outcome?.exitRequest) {
|
|
484
|
+
logger.debug(() => `Startup task '${task.id}' requested exit with run state ${outcome.exitRequest.runState}`);
|
|
485
|
+
return { runState: outcome.exitRequest.runState, exitRequested: true };
|
|
486
|
+
}
|
|
481
487
|
}
|
|
482
488
|
else {
|
|
483
489
|
void task.run(taskContext).catch((error) => {
|
|
@@ -7,8 +7,16 @@ import type DefaultSecretService from "./DefaultSecretService.ts";
|
|
|
7
7
|
*/
|
|
8
8
|
export default class DefaultKeyValueService implements KeyValueService {
|
|
9
9
|
#private;
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
* @param keyValueData the scope's data.
|
|
12
|
+
* @param secretService optional secret service for {@link Secret} values.
|
|
13
|
+
* @param flushToStorage optional callback which writes every scope's pending changes to storage
|
|
14
|
+
* and marks them clean, invoked by {@link flush}.
|
|
15
|
+
*/
|
|
16
|
+
constructor(keyValueData: Map<string, ValueNode>, secretService?: DefaultSecretService, flushToStorage?: () => Promise<void>);
|
|
11
17
|
isDirty(): boolean;
|
|
18
|
+
markClean(): void;
|
|
19
|
+
flush(): Promise<void>;
|
|
12
20
|
get<T extends ValueNode = ValueNode>(key: string): Promise<T>;
|
|
13
21
|
has(key: string): Promise<boolean>;
|
|
14
22
|
set(key: string, value: SettableValueNode): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DefaultKeyValueService.d.ts","sourceRoot":"","sources":["../../../../src/service/configuration/DefaultKeyValueService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,eAAe,EACf,iBAAiB,EAClB,MAAM,yCAAyC,CAAC;AAEjD,OAAO,KAAK,oBAAoB,MAAM,2BAA2B,CAAC;AAGlE;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,sBAAuB,YAAW,eAAe;;
|
|
1
|
+
{"version":3,"file":"DefaultKeyValueService.d.ts","sourceRoot":"","sources":["../../../../src/service/configuration/DefaultKeyValueService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,eAAe,EACf,iBAAiB,EAClB,MAAM,yCAAyC,CAAC;AAEjD,OAAO,KAAK,oBAAoB,MAAM,2BAA2B,CAAC;AAGlE;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,sBAAuB,YAAW,eAAe;;IAMpE;;;;;OAKG;IACH,YACE,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,EACpC,aAAa,CAAC,EAAE,oBAAoB,EACpC,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAKrC;IAEM,OAAO,IAAI,OAAO,CAExB;IAEM,SAAS,IAAI,IAAI,CAEvB;IAEY,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAGlC;IAEY,GAAG,CAAC,CAAC,SAAS,SAAS,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAsBzE;IAEM,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAExC;IAEY,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAGrE;IAsCY,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO9C;CAsBF"}
|
|
@@ -9,13 +9,28 @@ export default class DefaultKeyValueService {
|
|
|
9
9
|
#keyValueData;
|
|
10
10
|
#dirty = false;
|
|
11
11
|
#secretService;
|
|
12
|
-
|
|
12
|
+
#flushToStorage;
|
|
13
|
+
/**
|
|
14
|
+
* @param keyValueData the scope's data.
|
|
15
|
+
* @param secretService optional secret service for {@link Secret} values.
|
|
16
|
+
* @param flushToStorage optional callback which writes every scope's pending changes to storage
|
|
17
|
+
* and marks them clean, invoked by {@link flush}.
|
|
18
|
+
*/
|
|
19
|
+
constructor(keyValueData, secretService, flushToStorage) {
|
|
13
20
|
this.#keyValueData = keyValueData;
|
|
14
21
|
this.#secretService = secretService;
|
|
22
|
+
this.#flushToStorage = flushToStorage;
|
|
15
23
|
}
|
|
16
24
|
isDirty() {
|
|
17
25
|
return this.#dirty;
|
|
18
26
|
}
|
|
27
|
+
markClean() {
|
|
28
|
+
this.#dirty = false;
|
|
29
|
+
}
|
|
30
|
+
async flush() {
|
|
31
|
+
await this.#flushToStorage?.();
|
|
32
|
+
this.#dirty = false;
|
|
33
|
+
}
|
|
19
34
|
async get(key) {
|
|
20
35
|
const value = this.#keyValueData.get(key);
|
|
21
36
|
if (value === undefined) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KeyValueServiceProvider.d.ts","sourceRoot":"","sources":["../../../../src/service/configuration/KeyValueServiceProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAC5F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AACvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EACV,eAAe,EAGhB,MAAM,yCAAyC,CAAC;AAIjD,OAAO,KAAK,4BAA4B,MAAM,mCAAmC,CAAC;AAClF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"KeyValueServiceProvider.d.ts","sourceRoot":"","sources":["../../../../src/service/configuration/KeyValueServiceProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAC5F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AACvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EACV,eAAe,EAGhB,MAAM,yCAAyC,CAAC;AAIjD,OAAO,KAAK,4BAA4B,MAAM,mCAAmC,CAAC;AAClF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AAoClF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,MAAM,CAAC,OAAO,OAAO,uBAAwB,YAAW,eAAe;;IACrE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAwB;IAClD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAIjC,SAAgB,sBAAsB,EAAE,OAAO,CAAC;IAChD,SAAgB,oBAAoB,EAAE,OAAO,CAAC;IAW9C;;;;;;;;OAQG;IACH,YACE,eAAe,EAAE,MAAM,EACvB,4BAA4B,EAAE,4BAA4B,EAC1D,sBAAsB,UAAQ,EAC9B,oBAAoB,UAAQ,EAY7B;IAEM,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CAUhE;IAED;;;;;;;;;;OAUG;IACI,wBAAwB,CAC7B,SAAS,EAAE,wBAAwB,EACnC,QAAQ,EAAE,MAAM,GACf,eAAe,CAwBjB;IAED;;;;;;;;;;;;;;OAcG;IACI,kBAAkB,CACvB,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,wBAAwB,EACnC,QAAQ,EAAE,MAAM,GACf,OAAO,CAaT;IAqBY,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CASxD;CACF"}
|
|
@@ -22,6 +22,9 @@ class UnreachableKeyValueService {
|
|
|
22
22
|
delete() {
|
|
23
23
|
return Promise.reject(new Error(UnreachableKeyValueService.#message));
|
|
24
24
|
}
|
|
25
|
+
flush() {
|
|
26
|
+
return Promise.reject(new Error(UnreachableKeyValueService.#message));
|
|
27
|
+
}
|
|
25
28
|
static #message = "KeyValueService accessed without a resolved scope - this should be unreachable";
|
|
26
29
|
}
|
|
27
30
|
/**
|
|
@@ -151,7 +154,7 @@ export default class KeyValueServiceProvider {
|
|
|
151
154
|
const secretService = this.secretServiceEnabled
|
|
152
155
|
? new DefaultSecretService(this.#cliConfigName, `${scopeType}_${scopeKey}`)
|
|
153
156
|
: undefined;
|
|
154
|
-
const scopedService = new DefaultKeyValueService(keyValueData, secretService);
|
|
157
|
+
const scopedService = new DefaultKeyValueService(keyValueData, secretService, () => this.#flushDirtyScopes());
|
|
155
158
|
scopedServices.set(scopeKey, scopedService);
|
|
156
159
|
return scopedService;
|
|
157
160
|
}
|
|
@@ -184,15 +187,19 @@ export default class KeyValueServiceProvider {
|
|
|
184
187
|
}
|
|
185
188
|
/**
|
|
186
189
|
* Flush every scoped KeyValueService's data to the configuration file, if any of them report
|
|
187
|
-
* themselves dirty. Registered as a low-priority ShutdownTask (see
|
|
188
|
-
* runs once, after other shutdown cleanup, regardless of how long any
|
|
190
|
+
* themselves dirty, then mark them all clean. Registered as a low-priority ShutdownTask (see
|
|
191
|
+
* {@link initService}) so it runs once, after other shutdown cleanup, regardless of how long any
|
|
192
|
+
* scope was written to. Also backs {@link KeyValueService.flush} for every scope.
|
|
189
193
|
*/
|
|
190
194
|
async #flushDirtyScopes() {
|
|
191
|
-
const
|
|
195
|
+
const scopedServices = [
|
|
192
196
|
...this.#commandScopedKeyValueServices.values(),
|
|
193
197
|
...this.#serviceScopedKeyValueServices.values(),
|
|
194
|
-
]
|
|
195
|
-
await this.#configurationServiceProvider.flushIfDirty(
|
|
198
|
+
];
|
|
199
|
+
await this.#configurationServiceProvider.flushIfDirty(scopedServices.some((service) => service.isDirty()));
|
|
200
|
+
for (const service of scopedServices) {
|
|
201
|
+
service.markClean();
|
|
202
|
+
}
|
|
196
203
|
}
|
|
197
204
|
async initService(context) {
|
|
198
205
|
if (this.keyValueServiceEnabled || this.secretServiceEnabled) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import process from "node:process";
|
|
2
2
|
import DefaultShutdownService from "./DefaultShutdownService.js";
|
|
3
|
-
import { SHUTDOWN_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
|
|
3
|
+
import { RunState, SHUTDOWN_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
|
|
4
4
|
import getLogger from "../../util/logger.js";
|
|
5
5
|
import { shutdownState } from "./ShutdownState.js";
|
|
6
6
|
const logger = getLogger("ShutdownServiceProvider");
|
|
@@ -29,7 +29,7 @@ export default class ShutdownServiceProvider {
|
|
|
29
29
|
shutdownState.interruptCount++;
|
|
30
30
|
if (!shutdownState.longRunningMode || shutdownState.interruptCount >= 3) {
|
|
31
31
|
ShutdownServiceProvider.shutdown().then(() => {
|
|
32
|
-
process.exit(
|
|
32
|
+
process.exit(RunState.INTERRUPTED);
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
35
|
else {
|
|
@@ -38,7 +38,7 @@ export default class ShutdownServiceProvider {
|
|
|
38
38
|
}
|
|
39
39
|
static onTerminate() {
|
|
40
40
|
ShutdownServiceProvider.shutdown().then(() => {
|
|
41
|
-
process.exit(
|
|
41
|
+
process.exit(RunState.TERMINATED);
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
44
|
static async shutdown() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DefaultSpawnService.d.ts","sourceRoot":"","sources":["../../../../src/service/spawn/DefaultSpawnService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,OAAO,EAGP,YAAY,EACZ,WAAW,EACX,YAAY,EACb,MAAM,yCAAyC,CAAC;AASjD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,CAa3E;AAED,MAAM,CAAC,OAAO,OAAO,mBAAoB,YAAW,YAAY;;IAGvD,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAExC;IA+CY,KAAK,CAChB,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,EAC9B,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,WAAW,CAAC,
|
|
1
|
+
{"version":3,"file":"DefaultSpawnService.d.ts","sourceRoot":"","sources":["../../../../src/service/spawn/DefaultSpawnService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,OAAO,EAGP,YAAY,EACZ,WAAW,EACX,YAAY,EACb,MAAM,yCAAyC,CAAC;AASjD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,CAa3E;AAED,MAAM,CAAC,OAAO,OAAO,mBAAoB,YAAW,YAAY;;IAGvD,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAExC;IA+CY,KAAK,CAChB,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,EAC9B,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,WAAW,CAAC,CA2FtB;CACF"}
|
|
@@ -80,6 +80,7 @@ export default class DefaultSpawnService {
|
|
|
80
80
|
try {
|
|
81
81
|
proc = Bun.spawn(resolveForPlatform(command), {
|
|
82
82
|
cwd: options.cwd,
|
|
83
|
+
env: options.env === undefined ? undefined : { ...process.env, ...options.env },
|
|
83
84
|
stdin: mode === "inherit" ? "inherit" : "ignore",
|
|
84
85
|
stdout: mode === "wrapped" ? "pipe" : mode === "ignore" ? "ignore" : "inherit",
|
|
85
86
|
stderr: mode === "wrapped" ? "pipe" : mode === "ignore" ? "ignore" : "inherit",
|
|
@@ -1,10 +1,22 @@
|
|
|
1
|
-
import type { CLIConfig, Context, UpgradeCheckResult, UpgradeResult } from "@flowscripter/dynamic-cli-framework-api";
|
|
2
|
-
import { InstallMethod, SupportedArch, SupportedOs, type UpgradeService } from "@flowscripter/dynamic-cli-framework-api";
|
|
1
|
+
import type { CLIConfig, Context, SpawnResult, UpgradeCheckResult, UpgradeResult } from "@flowscripter/dynamic-cli-framework-api";
|
|
2
|
+
import { InstallMethod, RunState, SupportedArch, SupportedOs, type UpgradeService } from "@flowscripter/dynamic-cli-framework-api";
|
|
3
3
|
import type { UpgradeLocationsConfig } from "./UpgradeLocationsConfig.ts";
|
|
4
4
|
export declare const UPGRADE_CHECK_CACHE_KEY = "upgrade-check-result";
|
|
5
5
|
export declare function describeUpgradeCheckResult(result: UpgradeCheckResult): string;
|
|
6
|
+
/**
|
|
7
|
+
* Map the result of spawning a restarted CLI to the {@link RunState} it ended with.
|
|
8
|
+
*
|
|
9
|
+
* The child is the same framework CLI, so it normally exits with a {@link RunState} value, which is
|
|
10
|
+
* returned as is (this includes 130 for SIGINT and 143 for SIGTERM). Termination by any other
|
|
11
|
+
* signal, or a timeout, maps to {@link RunState.TERMINATED}, and any other exit code to
|
|
12
|
+
* {@link RunState.RUNTIME_ERROR}.
|
|
13
|
+
*
|
|
14
|
+
* @return the {@link RunState}, or `undefined` if the child could not be started.
|
|
15
|
+
*/
|
|
16
|
+
export declare function toRunState(result: SpawnResult): RunState | undefined;
|
|
6
17
|
export default class DefaultUpgradeService implements UpgradeService {
|
|
7
18
|
#private;
|
|
19
|
+
readonly restartedFromVersion: string | undefined;
|
|
8
20
|
constructor(config: UpgradeLocationsConfig, cliConfig: CLIConfig);
|
|
9
21
|
setContext(context: Context): void;
|
|
10
22
|
getUpgradeCheckResult(): Promise<UpgradeCheckResult>;
|
|
@@ -19,6 +31,22 @@ export default class DefaultUpgradeService implements UpgradeService {
|
|
|
19
31
|
* Returns `undefined` if nothing has been persisted yet or no KeyValueService is available.
|
|
20
32
|
*/
|
|
21
33
|
getCachedUpgradeCheckResult(): Promise<UpgradeCheckResult | undefined>;
|
|
34
|
+
/**
|
|
35
|
+
* Resolve the executable to restart into after a successful upgrade via `installMethod`.
|
|
36
|
+
*
|
|
37
|
+
* @return the executable path, or `undefined` if it cannot be resolved (e.g. for winget, or a
|
|
38
|
+
* homebrew install whose `opt` link is missing).
|
|
39
|
+
*/
|
|
40
|
+
resolveUpgradedExecutable(installMethod: InstallMethod): string | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Run `executable` with `args` in place of this process, with terminal input and output
|
|
43
|
+
* inherited, and wait for it to exit. The child process sees the current version as its
|
|
44
|
+
* {@link restartedFromVersion}.
|
|
45
|
+
*
|
|
46
|
+
* @return the {@link RunState} the child ended with (see {@link toRunState}), or `undefined` if
|
|
47
|
+
* it could not be started.
|
|
48
|
+
*/
|
|
49
|
+
restart(executable: string, args: ReadonlyArray<string>): Promise<RunState | undefined>;
|
|
22
50
|
detectOs(): SupportedOs | undefined;
|
|
23
51
|
detectArch(): SupportedArch | undefined;
|
|
24
52
|
detectInstallMethod(os: SupportedOs): Promise<InstallMethod | undefined>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DefaultUpgradeService.d.ts","sourceRoot":"","sources":["../../../../src/service/upgrade/DefaultUpgradeService.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,SAAS,EACT,OAAO,
|
|
1
|
+
{"version":3,"file":"DefaultUpgradeService.d.ts","sourceRoot":"","sources":["../../../../src/service/upgrade/DefaultUpgradeService.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,SAAS,EACT,OAAO,EAKP,WAAW,EAEX,kBAAkB,EAClB,aAAa,EAEd,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAEL,aAAa,EAGb,QAAQ,EAER,aAAa,EACb,WAAW,EACX,KAAK,cAAc,EACpB,MAAM,yCAAyC,CAAC;AAEjD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAwC1E,eAAO,MAAM,uBAAuB,yBAAyB,CAAC;AAY9D,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAI7E;AAaD;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,SAAS,CAiBpE;AAQD,MAAM,CAAC,OAAO,OAAO,qBAAsB,YAAW,cAAc;;IAKlE,SAAgB,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC;IAEzD,YAAmB,MAAM,EAAE,sBAAsB,EAAE,SAAS,EAAE,SAAS,EAItE;IAEM,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAExC;IA4DM,qBAAqB,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAe1D;IAED;;;;OAIG;IACU,wBAAwB,IAAI,OAAO,CAAC,kBAAkB,CAAC,CASnE;IAED;;;OAGG;IACU,2BAA2B,IAAI,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAWlF;IAED;;;;;OAKG;IACI,yBAAyB,CAAC,aAAa,EAAE,aAAa,GAAG,MAAM,GAAG,SAAS,CAUjF;IAoBD;;;;;;;OAOG;IACU,OAAO,CAClB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,GAC1B,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAY/B;IAEM,QAAQ,IAAI,WAAW,GAAG,SAAS,CAWzC;IAEM,UAAU,IAAI,aAAa,GAAG,SAAS,CAS7C;IAEY,mBAAmB,CAAC,EAAE,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAsCpF;IA2CY,eAAe,CAC1B,UAAU,CAAC,EAAE,WAAW,EACxB,YAAY,CAAC,EAAE,aAAa,EAC5B,qBAAqB,CAAC,EAAE,aAAa,GACpC,OAAO,CAAC,kBAAkB,CAAC,CAsC7B;IAEY,OAAO,CAClB,UAAU,CAAC,EAAE,WAAW,EACxB,YAAY,CAAC,EAAE,aAAa,EAC5B,qBAAqB,CAAC,EAAE,aAAa,GACpC,OAAO,CAAC,aAAa,CAAC,CAkDxB;CA2TF"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import process from "node:process";
|
|
2
|
-
import { realpathSync } from "node:fs";
|
|
2
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
3
3
|
import { mkdtemp, rename, rm } from "node:fs/promises";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
|
-
import { dirname, join } from "node:path";
|
|
6
|
-
import { FETCH_SERVICE_ID, InstallMethod, KEY_VALUE_SERVICE_ID, PRINTER_SERVICE_ID, SPAWN_SERVICE_ID, SupportedArch, SupportedOs, } from "@flowscripter/dynamic-cli-framework-api";
|
|
5
|
+
import { basename, dirname, join } from "node:path";
|
|
6
|
+
import { FETCH_SERVICE_ID, InstallMethod, KEY_VALUE_SERVICE_ID, PRINTER_SERVICE_ID, RunState, SPAWN_SERVICE_ID, SupportedArch, SupportedOs, } from "@flowscripter/dynamic-cli-framework-api";
|
|
7
7
|
import semver from "semver";
|
|
8
8
|
import getLogger from "../../util/logger.js";
|
|
9
9
|
const logger = getLogger("DefaultUpgradeService");
|
|
@@ -38,6 +38,40 @@ export function describeUpgradeCheckResult(result) {
|
|
|
38
38
|
? `Upgrade check result: failed - ${result.error.message}`
|
|
39
39
|
: `Upgrade check result: ${JSON.stringify(result)}`;
|
|
40
40
|
}
|
|
41
|
+
// Environment variable set on a process restarted after an automatic upgrade, holding the version
|
|
42
|
+
// which restarted it (see DefaultUpgradeService.restart() and restartedFromVersion).
|
|
43
|
+
const RESTARTED_FROM_VERSION_ENV_VAR = "DYNAMIC_CLI_FRAMEWORK_REEXEC_FROM";
|
|
44
|
+
// Exit codes from 129 upwards are reported for a child terminated by a signal (128 + signal number).
|
|
45
|
+
const SIGNAL_EXIT_CODE_BASE = 128;
|
|
46
|
+
const RUN_STATE_VALUES = new Set(Object.values(RunState).filter((value) => typeof value === "number"));
|
|
47
|
+
/**
|
|
48
|
+
* Map the result of spawning a restarted CLI to the {@link RunState} it ended with.
|
|
49
|
+
*
|
|
50
|
+
* The child is the same framework CLI, so it normally exits with a {@link RunState} value, which is
|
|
51
|
+
* returned as is (this includes 130 for SIGINT and 143 for SIGTERM). Termination by any other
|
|
52
|
+
* signal, or a timeout, maps to {@link RunState.TERMINATED}, and any other exit code to
|
|
53
|
+
* {@link RunState.RUNTIME_ERROR}.
|
|
54
|
+
*
|
|
55
|
+
* @return the {@link RunState}, or `undefined` if the child could not be started.
|
|
56
|
+
*/
|
|
57
|
+
export function toRunState(result) {
|
|
58
|
+
if (result.ok) {
|
|
59
|
+
return RunState.SUCCESS;
|
|
60
|
+
}
|
|
61
|
+
if ("timedOut" in result) {
|
|
62
|
+
return RunState.TERMINATED;
|
|
63
|
+
}
|
|
64
|
+
if (result.exitCode === undefined) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
if (RUN_STATE_VALUES.has(result.exitCode)) {
|
|
68
|
+
return result.exitCode;
|
|
69
|
+
}
|
|
70
|
+
if (result.exitCode > SIGNAL_EXIT_CODE_BASE) {
|
|
71
|
+
return RunState.TERMINATED;
|
|
72
|
+
}
|
|
73
|
+
return RunState.RUNTIME_ERROR;
|
|
74
|
+
}
|
|
41
75
|
const OS_LABELS = {
|
|
42
76
|
[SupportedOs.LINUX]: "Linux",
|
|
43
77
|
[SupportedOs.MACOS]: "MacOS",
|
|
@@ -48,9 +82,11 @@ export default class DefaultUpgradeService {
|
|
|
48
82
|
#upgradeCheckPromise;
|
|
49
83
|
#config;
|
|
50
84
|
#cliConfig;
|
|
85
|
+
restartedFromVersion;
|
|
51
86
|
constructor(config, cliConfig) {
|
|
52
87
|
this.#config = config;
|
|
53
88
|
this.#cliConfig = cliConfig;
|
|
89
|
+
this.restartedFromVersion = process.env[RESTARTED_FROM_VERSION_ENV_VAR] || undefined;
|
|
54
90
|
}
|
|
55
91
|
setContext(context) {
|
|
56
92
|
this.#context = context;
|
|
@@ -150,6 +186,61 @@ export default class DefaultUpgradeService {
|
|
|
150
186
|
const cached = await this.#safeKeyValueCall(() => keyValueService.get(UPGRADE_CHECK_CACHE_KEY));
|
|
151
187
|
return cached;
|
|
152
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* Resolve the executable to restart into after a successful upgrade via `installMethod`.
|
|
191
|
+
*
|
|
192
|
+
* @return the executable path, or `undefined` if it cannot be resolved (e.g. for winget, or a
|
|
193
|
+
* homebrew install whose `opt` link is missing).
|
|
194
|
+
*/
|
|
195
|
+
resolveUpgradedExecutable(installMethod) {
|
|
196
|
+
switch (installMethod) {
|
|
197
|
+
case InstallMethod.GITHUB_RELEASE:
|
|
198
|
+
case InstallMethod.LINUX_SCRIPT:
|
|
199
|
+
return process.execPath;
|
|
200
|
+
case InstallMethod.HOMEBREW:
|
|
201
|
+
return this.#resolveHomebrewOptExecutable();
|
|
202
|
+
case InstallMethod.WINGET:
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// The running executable lives in a version-specific Cellar directory, e.g.
|
|
207
|
+
// "<prefix>/Cellar/<formula>/<version>/bin/<exe>", while "<prefix>/opt/<formula>" links to the
|
|
208
|
+
// installed version, so the upgraded executable is "<prefix>/opt/<formula>/bin/<exe>".
|
|
209
|
+
#resolveHomebrewOptExecutable() {
|
|
210
|
+
if (!this.#config.homebrew) {
|
|
211
|
+
return undefined;
|
|
212
|
+
}
|
|
213
|
+
const { formula } = this.#config.homebrew;
|
|
214
|
+
const realExecutable = this.#resolveRealExecutable();
|
|
215
|
+
const cellarIndex = realExecutable.indexOf(`/Cellar/${formula}/`);
|
|
216
|
+
if (cellarIndex === -1) {
|
|
217
|
+
return undefined;
|
|
218
|
+
}
|
|
219
|
+
const prefix = realExecutable.slice(0, cellarIndex);
|
|
220
|
+
const executable = join(prefix, "opt", formula, "bin", basename(realExecutable));
|
|
221
|
+
return existsSync(executable) ? executable : undefined;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Run `executable` with `args` in place of this process, with terminal input and output
|
|
225
|
+
* inherited, and wait for it to exit. The child process sees the current version as its
|
|
226
|
+
* {@link restartedFromVersion}.
|
|
227
|
+
*
|
|
228
|
+
* @return the {@link RunState} the child ended with (see {@link toRunState}), or `undefined` if
|
|
229
|
+
* it could not be started.
|
|
230
|
+
*/
|
|
231
|
+
async restart(executable, args) {
|
|
232
|
+
const spawnService = this.#spawnService;
|
|
233
|
+
if (!spawnService) {
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
const result = await spawnService.spawn([executable, ...args], {
|
|
237
|
+
mode: "inherit",
|
|
238
|
+
// Ctrl-C reaches the child directly from the terminal, so this process only notes it
|
|
239
|
+
longRunning: true,
|
|
240
|
+
env: { [RESTARTED_FROM_VERSION_ENV_VAR]: this.#cliConfig.version },
|
|
241
|
+
});
|
|
242
|
+
return toRunState(result);
|
|
243
|
+
}
|
|
153
244
|
detectOs() {
|
|
154
245
|
switch (process.platform) {
|
|
155
246
|
case "linux":
|
|
@@ -331,15 +422,17 @@ export default class DefaultUpgradeService {
|
|
|
331
422
|
// `brew`, which has a slow cold start - avoiding it keeps the background upgrade-check
|
|
332
423
|
// StartupTask (see UpgradeServiceProvider) fast even though it runs to completion.
|
|
333
424
|
#isRunningFromHomebrewCellar(formula) {
|
|
334
|
-
|
|
425
|
+
return this.#resolveRealExecutable().includes(`/Cellar/${formula}/`);
|
|
426
|
+
}
|
|
427
|
+
#resolveRealExecutable() {
|
|
335
428
|
try {
|
|
336
|
-
|
|
429
|
+
return realpathSync(process.execPath);
|
|
337
430
|
}
|
|
338
431
|
catch {
|
|
339
432
|
// process.execPath may not resolve on disk (e.g. a fabricated path in tests) - fall back to
|
|
340
433
|
// the unresolved path rather than treating that as "not installed".
|
|
434
|
+
return process.execPath;
|
|
341
435
|
}
|
|
342
|
-
return realExecutable.includes(`/Cellar/${formula}/`);
|
|
343
436
|
}
|
|
344
437
|
async #isHomebrewInstalled() {
|
|
345
438
|
if (!this.#spawnService || !this.#config.homebrew) {
|
|
@@ -3,7 +3,7 @@ import type { Context } from "@flowscripter/dynamic-cli-framework-api";
|
|
|
3
3
|
import type { CLIConfig } from "@flowscripter/dynamic-cli-framework-api";
|
|
4
4
|
import DefaultUpgradeService from "./DefaultUpgradeService.ts";
|
|
5
5
|
import type { UpgradeLocationsConfig } from "./UpgradeLocationsConfig.ts";
|
|
6
|
-
import type { StartupTask } from "@flowscripter/dynamic-cli-framework-api";
|
|
6
|
+
import type { StartupTask, StartupTaskOutcome } from "@flowscripter/dynamic-cli-framework-api";
|
|
7
7
|
/**
|
|
8
8
|
* Build the background {@link StartupTask} which opportunistically refreshes the upgrade-check
|
|
9
9
|
* cache (see {@link DefaultUpgradeService.refreshUpgradeCheckCache}) that the banner task reads
|
|
@@ -13,7 +13,7 @@ import type { StartupTask } from "@flowscripter/dynamic-cli-framework-api";
|
|
|
13
13
|
export declare function createUpgradeCheckStartupTask(upgradeService: DefaultUpgradeService, priority: number): StartupTask;
|
|
14
14
|
/**
|
|
15
15
|
* Build the blocking {@link StartupTask} which offers to enable automatic upgrades and, when they
|
|
16
|
-
* are enabled, checks for and installs a newer version (see
|
|
16
|
+
* are enabled, checks for and installs a newer version, optionally restarting into it (see
|
|
17
17
|
* {@link UpgradeServiceProvider.runAutoUpgrade}). It is registered separately from the provider's
|
|
18
18
|
* own init task so it can run at a lower priority, after consumer startup tasks such as the banner.
|
|
19
19
|
*/
|
|
@@ -22,7 +22,18 @@ export default class UpgradeServiceProvider implements ServiceProvider {
|
|
|
22
22
|
#private;
|
|
23
23
|
readonly serviceId: string;
|
|
24
24
|
readonly servicePriority: number;
|
|
25
|
-
|
|
25
|
+
/**
|
|
26
|
+
* @param servicePriority the priority of the service.
|
|
27
|
+
* @param config the upgrade locations.
|
|
28
|
+
* @param restartAfterAutoUpgrade whether to run the upgraded executable, with the arguments from
|
|
29
|
+
* {@link setRestartArgs}, after a successful automatic upgrade and end the run with its result.
|
|
30
|
+
*/
|
|
31
|
+
constructor(servicePriority: number, config: UpgradeLocationsConfig, restartAfterAutoUpgrade?: boolean);
|
|
32
|
+
/**
|
|
33
|
+
* Set the CLI arguments to run the upgraded executable with when restarting after an automatic
|
|
34
|
+
* upgrade. These are kept internal to the framework and never exposed to startup tasks.
|
|
35
|
+
*/
|
|
36
|
+
setRestartArgs(args: ReadonlyArray<string>): void;
|
|
26
37
|
get upgradeService(): DefaultUpgradeService | undefined;
|
|
27
38
|
getServiceInfo(cliConfig: CLIConfig): Promise<ServiceInfo>;
|
|
28
39
|
initService(context: Context): Promise<void>;
|
|
@@ -31,8 +42,12 @@ export default class UpgradeServiceProvider implements ServiceProvider {
|
|
|
31
42
|
* service's scope) and, when they are enabled, check for and install a newer version. Must be
|
|
32
43
|
* called after {@link initService}.
|
|
33
44
|
*
|
|
45
|
+
* After a successful upgrade, when restarting is enabled, the upgraded executable is run with the
|
|
46
|
+
* arguments from {@link setRestartArgs} and its result is returned as an `exitRequest`. Does
|
|
47
|
+
* nothing in a process which was itself started by such a restart.
|
|
48
|
+
*
|
|
34
49
|
* @param context the startup task's context, used to look up the Prompter and Printer services.
|
|
35
50
|
*/
|
|
36
|
-
runAutoUpgrade(context: Context): Promise<void>;
|
|
51
|
+
runAutoUpgrade(context: Context): Promise<void | StartupTaskOutcome>;
|
|
37
52
|
}
|
|
38
53
|
//# sourceMappingURL=UpgradeServiceProvider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UpgradeServiceProvider.d.ts","sourceRoot":"","sources":["../../../../src/service/upgrade/UpgradeServiceProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAC5F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AACvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAYzE,OAAO,qBAAqB,MAAM,4BAA4B,CAAC;AAE/D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAE1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"UpgradeServiceProvider.d.ts","sourceRoot":"","sources":["../../../../src/service/upgrade/UpgradeServiceProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAC5F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AACvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAYzE,OAAO,qBAAqB,MAAM,4BAA4B,CAAC;AAE/D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAE1E,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAI/F;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,cAAc,EAAE,qBAAqB,EACrC,QAAQ,EAAE,MAAM,GACf,WAAW,CASb;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,sBAAsB,EAChC,QAAQ,EAAE,MAAM,GACf,WAAW,CAOb;AAED,MAAM,CAAC,OAAO,OAAO,sBAAuB,YAAW,eAAe;;IACpE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAsB;IAChD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAQjC;;;;;OAKG;IACH,YACE,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,sBAAsB,EAC9B,uBAAuB,UAAQ,EAKhC;IAED;;;OAGG;IACI,cAAc,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,IAAI,CAEvD;IAED,IAAW,cAAc,IAAI,qBAAqB,GAAG,SAAS,CAE7D;IAEM,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CAOhE;IAEM,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBlD;IAED;;;;;;;;;;OAUG;IACU,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,GAAG,kBAAkB,CAAC,CA4EhF;CA8DF"}
|