@flowscripter/dynamic-cli-framework 5.3.6 → 5.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/service/plugin/command/PluginAddSubCommand.d.ts.map +1 -1
- package/dist/src/service/plugin/command/PluginAddSubCommand.js +4 -2
- package/dist/src/service/plugin/command/PluginRemoveSubCommand.d.ts.map +1 -1
- package/dist/src/service/plugin/command/PluginRemoveSubCommand.js +2 -1
- package/dist/src/service/upgrade/DefaultUpgradeService.d.ts +2 -2
- package/dist/src/service/upgrade/DefaultUpgradeService.d.ts.map +1 -1
- package/dist/src/service/upgrade/DefaultUpgradeService.js +64 -17
- package/dist/src/service/upgrade/UpgradeServiceProvider.d.ts.map +1 -1
- package/dist/src/service/upgrade/UpgradeServiceProvider.js +2 -1
- package/dist/src/service/upgrade/command/UpgradeSubCommand.d.ts.map +1 -1
- package/dist/src/service/upgrade/command/UpgradeSubCommand.js +4 -1
- package/package.json +2 -2
- package/src/service/plugin/command/PluginAddSubCommand.ts +4 -2
- package/src/service/plugin/command/PluginRemoveSubCommand.ts +2 -1
- package/src/service/upgrade/DefaultUpgradeService.ts +80 -41
- package/src/service/upgrade/UpgradeServiceProvider.ts +2 -1
- package/src/service/upgrade/command/UpgradeSubCommand.ts +7 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PluginAddSubCommand.d.ts","sourceRoot":"","sources":["../../../../../src/service/plugin/command/PluginAddSubCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yCAAyC,CAAC;AAC1E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AACvE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yCAAyC,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AASxE,qBAAa,mBAAoB,YAAW,UAAU;IACpD,QAAQ,CAAC,IAAI,SAAS;IACtB,QAAQ,CAAC,WAAW,6BAA6B;IACjD,QAAQ,CAAC,mBAAmB,SAAS;IACrC,QAAQ,CAAC,OAAO,UAAM;IACtB,QAAQ,CAAC,WAAW;;;;QAMlB;IAEI,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"PluginAddSubCommand.d.ts","sourceRoot":"","sources":["../../../../../src/service/plugin/command/PluginAddSubCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yCAAyC,CAAC;AAC1E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AACvE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yCAAyC,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AASxE,qBAAa,mBAAoB,YAAW,UAAU;IACpD,QAAQ,CAAC,IAAI,SAAS;IACtB,QAAQ,CAAC,WAAW,6BAA6B;IACjD,QAAQ,CAAC,mBAAmB,SAAS;IACrC,QAAQ,CAAC,OAAO,UAAM;IACtB,QAAQ,CAAC,WAAW;;;;QAMlB;IAEI,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgFrE;CACF"}
|
|
@@ -20,7 +20,7 @@ export class PluginAddSubCommand {
|
|
|
20
20
|
const pluginService = context.getServiceById(PLUGIN_SERVICE_ID);
|
|
21
21
|
const { pluginId, version } = parsePluginSpecifier(argumentValues["pluginId"]);
|
|
22
22
|
const searchLabel = version ? `${pluginId}@${version}` : pluginId;
|
|
23
|
-
await printerService.
|
|
23
|
+
await printerService.showSpinner(`Searching for plugin: ${searchLabel}`);
|
|
24
24
|
let descriptor;
|
|
25
25
|
for await (const d of pluginService.search({ text: pluginId })) {
|
|
26
26
|
if (getPluginId(d) === pluginId || d.pluginId === pluginId) {
|
|
@@ -28,6 +28,7 @@ export class PluginAddSubCommand {
|
|
|
28
28
|
break;
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
+
await printerService.hideSpinner();
|
|
31
32
|
if (descriptor && version) {
|
|
32
33
|
// search only ever returns the latest version - substitute the explicitly requested
|
|
33
34
|
// version so install() is asked for the correct one.
|
|
@@ -62,7 +63,7 @@ export class PluginAddSubCommand {
|
|
|
62
63
|
? `Version ${versionToCheck} of plugin ${descriptor.pluginId} was not found in the configured plugin registry`
|
|
63
64
|
: `Plugin ${descriptor.pluginId} was not found in the configured plugin registry`);
|
|
64
65
|
}
|
|
65
|
-
await printerService.
|
|
66
|
+
await printerService.showSpinner(`Installing ${installLabel}...`);
|
|
66
67
|
await pluginService.install(descriptor);
|
|
67
68
|
// Look up the actually-installed version rather than trusting `descriptor.version`: when no
|
|
68
69
|
// version was requested and the plugin wasn't found via search (direct-install fallback),
|
|
@@ -74,6 +75,7 @@ export class PluginAddSubCommand {
|
|
|
74
75
|
break;
|
|
75
76
|
}
|
|
76
77
|
}
|
|
78
|
+
await printerService.hideSpinner();
|
|
77
79
|
await printerService.print(`Plugin ${descriptor.pluginId}@${installedVersion} installed.\n`, Icon.SUCCESS);
|
|
78
80
|
}
|
|
79
81
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PluginRemoveSubCommand.d.ts","sourceRoot":"","sources":["../../../../../src/service/plugin/command/PluginRemoveSubCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yCAAyC,CAAC;AAC1E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AACvE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yCAAyC,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAMxE,qBAAa,sBAAuB,YAAW,UAAU;IACvD,QAAQ,CAAC,IAAI,YAAY;IACzB,QAAQ,CAAC,WAAW,uCAAuC;IAC3D,QAAQ,CAAC,mBAAmB,SAAS;IACrC,QAAQ,CAAC,OAAO,UAAM;IACtB,QAAQ,CAAC,WAAW;;;;QAMlB;IAEI,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"PluginRemoveSubCommand.d.ts","sourceRoot":"","sources":["../../../../../src/service/plugin/command/PluginRemoveSubCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yCAAyC,CAAC;AAC1E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AACvE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yCAAyC,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAMxE,qBAAa,sBAAuB,YAAW,UAAU;IACvD,QAAQ,CAAC,IAAI,YAAY;IACzB,QAAQ,CAAC,WAAW,uCAAuC;IAC3D,QAAQ,CAAC,mBAAmB,SAAS;IACrC,QAAQ,CAAC,OAAO,UAAM;IACtB,QAAQ,CAAC,WAAW;;;;QAMlB;IAEI,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CASrE;CACF"}
|
|
@@ -17,8 +17,9 @@ export class PluginRemoveSubCommand {
|
|
|
17
17
|
const printerService = context.getServiceById(PRINTER_SERVICE_ID);
|
|
18
18
|
const pluginService = context.getServiceById(PLUGIN_SERVICE_ID);
|
|
19
19
|
const pluginId = argumentValues["pluginId"];
|
|
20
|
-
await printerService.
|
|
20
|
+
await printerService.showSpinner(`Removing plugin: ${pluginId}...`);
|
|
21
21
|
await pluginService.uninstall(pluginId);
|
|
22
|
+
await printerService.hideSpinner();
|
|
22
23
|
await printerService.print(`Plugin ${pluginId} removed.\n`, Icon.SUCCESS);
|
|
23
24
|
}
|
|
24
25
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CLIConfig, FetchService, SpawnService, UpgradeCheckResult, UpgradeResult } from "@flowscripter/dynamic-cli-framework-api";
|
|
1
|
+
import type { CLIConfig, FetchService, PrinterService, SpawnService, UpgradeCheckResult, UpgradeResult } from "@flowscripter/dynamic-cli-framework-api";
|
|
2
2
|
import { InstallMethod, SupportedArch, SupportedOs, type UpgradeService } from "@flowscripter/dynamic-cli-framework-api";
|
|
3
3
|
import type { UpgradeLocationsConfig } from "./UpgradeLocationsConfig.ts";
|
|
4
4
|
export declare const VERSION_CHECK_TIMEOUT_MS = 250;
|
|
@@ -6,7 +6,7 @@ export declare function describeUpgradeCheckResult(result: UpgradeCheckResult):
|
|
|
6
6
|
export default class DefaultUpgradeService implements UpgradeService {
|
|
7
7
|
#private;
|
|
8
8
|
constructor(config: UpgradeLocationsConfig, cliConfig: CLIConfig);
|
|
9
|
-
setDependencies(spawnService: SpawnService | undefined, fetchService: FetchService | undefined): void;
|
|
9
|
+
setDependencies(spawnService: SpawnService | undefined, fetchService: FetchService | undefined, printerService: PrinterService | undefined): void;
|
|
10
10
|
getUpgradeCheckResult(waitForResult?: boolean): Promise<UpgradeCheckResult>;
|
|
11
11
|
detectOs(): SupportedOs | undefined;
|
|
12
12
|
detectArch(): SupportedArch | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DefaultUpgradeService.d.ts","sourceRoot":"","sources":["../../../../src/service/upgrade/DefaultUpgradeService.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,SAAS,EACT,YAAY,
|
|
1
|
+
{"version":3,"file":"DefaultUpgradeService.d.ts","sourceRoot":"","sources":["../../../../src/service/upgrade/DefaultUpgradeService.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,SAAS,EACT,YAAY,EACZ,cAAc,EAEd,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACd,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACL,aAAa,EACb,aAAa,EACb,WAAW,EACX,KAAK,cAAc,EACpB,MAAM,yCAAyC,CAAC;AAEjD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAS1E,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAY5C,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAI7E;AAQD,MAAM,CAAC,OAAO,OAAO,qBAAsB,YAAW,cAAc;;IAQlE,YAAmB,MAAM,EAAE,sBAAsB,EAAE,SAAS,EAAE,SAAS,EAGtE;IAEM,eAAe,CACpB,YAAY,EAAE,YAAY,GAAG,SAAS,EACtC,YAAY,EAAE,YAAY,GAAG,SAAS,EACtC,cAAc,EAAE,cAAc,GAAG,SAAS,GACzC,IAAI,CAIN;IAoCM,qBAAqB,CAAC,aAAa,UAAQ,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA0B/E;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,CAcpF;IAEY,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,CAsDxB;CAiRF"}
|
|
@@ -29,6 +29,7 @@ const OS_LABELS = {
|
|
|
29
29
|
export default class DefaultUpgradeService {
|
|
30
30
|
#spawnService;
|
|
31
31
|
#fetchService;
|
|
32
|
+
#printerService;
|
|
32
33
|
#upgradeCheckPromise;
|
|
33
34
|
#config;
|
|
34
35
|
#cliConfig;
|
|
@@ -36,9 +37,40 @@ export default class DefaultUpgradeService {
|
|
|
36
37
|
this.#config = config;
|
|
37
38
|
this.#cliConfig = cliConfig;
|
|
38
39
|
}
|
|
39
|
-
setDependencies(spawnService, fetchService) {
|
|
40
|
+
setDependencies(spawnService, fetchService, printerService) {
|
|
40
41
|
this.#spawnService = spawnService;
|
|
41
42
|
this.#fetchService = fetchService;
|
|
43
|
+
this.#printerService = printerService;
|
|
44
|
+
}
|
|
45
|
+
// Mirrors SpawnInterfaceAdapter's plugin:add/plugin:remove pattern: wrap a spawned command's
|
|
46
|
+
// output in a quoted, marked block that's cleared on success (so a clean install stays quiet)
|
|
47
|
+
// but left on screen on failure (so the diagnostic output remains visible).
|
|
48
|
+
async #spawnQuoted(command) {
|
|
49
|
+
const spawnService = this.#spawnService;
|
|
50
|
+
const printerService = this.#printerService;
|
|
51
|
+
if (!printerService) {
|
|
52
|
+
return spawnService.spawn(command, { mode: "ignore" });
|
|
53
|
+
}
|
|
54
|
+
printerService.startQuote();
|
|
55
|
+
printerService.startMark();
|
|
56
|
+
// onOutput is synchronous and may be called concurrently for stdout/stderr lines, but
|
|
57
|
+
// printerService.info() is async and must not be invoked concurrently with itself - queue
|
|
58
|
+
// writes so they're applied one at a time, in call order.
|
|
59
|
+
let writeQueue = Promise.resolve();
|
|
60
|
+
const onOutput = (line) => {
|
|
61
|
+
writeQueue = writeQueue.then(() => printerService.info(`${line}\n`));
|
|
62
|
+
};
|
|
63
|
+
const result = await spawnService.spawn(command, { mode: "wrapped", onOutput });
|
|
64
|
+
await writeQueue;
|
|
65
|
+
printerService.endQuote();
|
|
66
|
+
printerService.endMark();
|
|
67
|
+
if (result.ok) {
|
|
68
|
+
await printerService.clearMarked();
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
printerService.discardMark();
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
42
74
|
}
|
|
43
75
|
getUpgradeCheckResult(waitForResult = false) {
|
|
44
76
|
if (!this.#upgradeCheckPromise) {
|
|
@@ -161,7 +193,13 @@ export default class DefaultUpgradeService {
|
|
|
161
193
|
if (checkResult.installMethod === InstallMethod.GITHUB_RELEASE && !this.#fetchService) {
|
|
162
194
|
return { ok: false, oldVersion, error: new Error("FetchService is not available") };
|
|
163
195
|
}
|
|
196
|
+
if (this.#printerService) {
|
|
197
|
+
await this.#printerService.showSpinner(`Installing version ${checkResult.latestVersion}...`);
|
|
198
|
+
}
|
|
164
199
|
try {
|
|
200
|
+
if (this.#printerService) {
|
|
201
|
+
await this.#printerService.hideSpinner();
|
|
202
|
+
}
|
|
165
203
|
switch (checkResult.installMethod) {
|
|
166
204
|
case InstallMethod.LINUX_SCRIPT:
|
|
167
205
|
await this.#upgradeViaLinuxScript();
|
|
@@ -306,25 +344,21 @@ export default class DefaultUpgradeService {
|
|
|
306
344
|
}
|
|
307
345
|
async #upgradeViaLinuxScript() {
|
|
308
346
|
const { scriptUrl } = this.#config.linuxScript;
|
|
309
|
-
const result = await this.#
|
|
310
|
-
mode: "ignore",
|
|
311
|
-
});
|
|
347
|
+
const result = await this.#spawnQuoted(["sh", "-c", `curl -fsSL ${scriptUrl} | sh`]);
|
|
312
348
|
if (!result.ok) {
|
|
313
349
|
throw new Error(`Install script failed: ${describeSpawnFailure(result)}`);
|
|
314
350
|
}
|
|
315
351
|
}
|
|
316
352
|
async #upgradeViaHomebrew() {
|
|
317
353
|
const { tap, formula } = this.#config.homebrew;
|
|
318
|
-
const result = await this.#
|
|
319
|
-
mode: "ignore",
|
|
320
|
-
});
|
|
354
|
+
const result = await this.#spawnQuoted(["brew", "upgrade", `${tap}/${formula}`]);
|
|
321
355
|
if (!result.ok) {
|
|
322
356
|
throw new Error(`brew upgrade failed: ${describeSpawnFailure(result)}`);
|
|
323
357
|
}
|
|
324
358
|
}
|
|
325
359
|
async #upgradeViaWinget() {
|
|
326
360
|
const { packageId } = this.#config.winget;
|
|
327
|
-
const result = await this.#
|
|
361
|
+
const result = await this.#spawnQuoted([
|
|
328
362
|
"winget",
|
|
329
363
|
"upgrade",
|
|
330
364
|
"--id",
|
|
@@ -332,7 +366,7 @@ export default class DefaultUpgradeService {
|
|
|
332
366
|
"--silent",
|
|
333
367
|
"--accept-package-agreements",
|
|
334
368
|
"--accept-source-agreements",
|
|
335
|
-
]
|
|
369
|
+
]);
|
|
336
370
|
if (!result.ok) {
|
|
337
371
|
throw new Error(`winget upgrade failed: ${describeSpawnFailure(result)}`);
|
|
338
372
|
}
|
|
@@ -356,11 +390,11 @@ export default class DefaultUpgradeService {
|
|
|
356
390
|
await Bun.write(archivePath, archiveData);
|
|
357
391
|
const currentExecutable = process.execPath;
|
|
358
392
|
if (os === SupportedOs.WINDOWS) {
|
|
359
|
-
const extractResult = await this.#
|
|
393
|
+
const extractResult = await this.#spawnQuoted([
|
|
360
394
|
"powershell",
|
|
361
395
|
"-Command",
|
|
362
396
|
`Expand-Archive -Path '${archivePath}' -DestinationPath '${tmpDir}' -Force`,
|
|
363
|
-
]
|
|
397
|
+
]);
|
|
364
398
|
if (!extractResult.ok) {
|
|
365
399
|
throw new Error("Failed to extract release archive");
|
|
366
400
|
}
|
|
@@ -370,23 +404,36 @@ export default class DefaultUpgradeService {
|
|
|
370
404
|
// Windows won't let us delete the just-renamed-aside exe while this process still has it
|
|
371
405
|
// open/mapped - that can only happen once we're no longer holding it, i.e. at the start of
|
|
372
406
|
// the NEXT invocation, before we move today's running exe aside. Ignore failures: the file
|
|
373
|
-
// may not exist, or may still be locked (e.g. another instance still running).
|
|
407
|
+
// may not exist, or may still be locked (e.g. another instance still running). Not quoted -
|
|
408
|
+
// this is expected to fail silently, there's nothing worth showing the user.
|
|
374
409
|
await this.#spawnService.spawn(["cmd", "/c", "del", "/f", "/q", oldPath], {
|
|
375
410
|
mode: "ignore",
|
|
376
411
|
});
|
|
377
|
-
const moveResult = await this.#
|
|
412
|
+
const moveResult = await this.#spawnQuoted([
|
|
413
|
+
"cmd",
|
|
414
|
+
"/c",
|
|
415
|
+
"move",
|
|
416
|
+
"/y",
|
|
417
|
+
currentExecutable,
|
|
418
|
+
oldPath,
|
|
419
|
+
]);
|
|
378
420
|
if (!moveResult.ok) {
|
|
379
421
|
throw new Error("Failed to move current executable aside");
|
|
380
422
|
}
|
|
381
|
-
const copyResult = await this.#
|
|
423
|
+
const copyResult = await this.#spawnQuoted([
|
|
424
|
+
"cmd",
|
|
425
|
+
"/c",
|
|
426
|
+
"copy",
|
|
427
|
+
"/y",
|
|
428
|
+
extractedBinary,
|
|
429
|
+
currentExecutable,
|
|
430
|
+
]);
|
|
382
431
|
if (!copyResult.ok) {
|
|
383
432
|
throw new Error("Failed to copy new executable into place");
|
|
384
433
|
}
|
|
385
434
|
}
|
|
386
435
|
else {
|
|
387
|
-
const extractResult = await this.#
|
|
388
|
-
mode: "ignore",
|
|
389
|
-
});
|
|
436
|
+
const extractResult = await this.#spawnQuoted(["unzip", "-o", archivePath, "-d", tmpDir]);
|
|
390
437
|
if (!extractResult.ok) {
|
|
391
438
|
throw new Error("Failed to extract release archive");
|
|
392
439
|
}
|
|
@@ -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;AAczE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAK1E,MAAM,CAAC,OAAO,OAAO,sBAAuB,YAAW,eAAe;;IACpE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAsB;IAChD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAKjC,YAAmB,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,sBAAsB,EAGzE;IAEM,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CAOhE;IAEY,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,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;AAczE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAK1E,MAAM,CAAC,OAAO,OAAO,sBAAuB,YAAW,eAAe;;IACpE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAsB;IAChD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAKjC,YAAmB,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,sBAAsB,EAGzE;IAEM,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CAOhE;IAEY,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAuFxD;CAoCF"}
|
|
@@ -41,7 +41,8 @@ export default class UpgradeServiceProvider {
|
|
|
41
41
|
if (fetchService === undefined) {
|
|
42
42
|
logger.debug(() => "FetchService not available, upgrade version checks will be unavailable");
|
|
43
43
|
}
|
|
44
|
-
|
|
44
|
+
const printerService = context.getServiceById(PRINTER_SERVICE_ID);
|
|
45
|
+
upgradeService.setDependencies(spawnService, fetchService, printerService);
|
|
45
46
|
void upgradeService.getUpgradeCheckResult();
|
|
46
47
|
if (!context.doesServiceExist(PROMPTER_SERVICE_ID)) {
|
|
47
48
|
logger.debug(() => "PrompterService not available, skipping auto-upgrade");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UpgradeSubCommand.d.ts","sourceRoot":"","sources":["../../../../../src/service/upgrade/command/UpgradeSubCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yCAAyC,CAAC;AAC1E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yCAAyC,CAAC;AACtE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AACvE,OAAO,EAAE,KAAK,MAAM,EAAiB,MAAM,yCAAyC,CAAC;AASrF,OAAO,KAAK,qBAAqB,MAAM,6BAA6B,CAAC;AAErE,qBAAa,iBAAkB,YAAW,UAAU;;IAClD,QAAQ,CAAC,IAAI,aAAa;IAC1B,QAAQ,CAAC,WAAW,6CAA6C;IACjE,QAAQ,CAAC,mBAAmB,SAAS;IACrC,QAAQ,CAAC,WAAW,UAAM;IAE1B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAerC;IAIF,YAAmB,cAAc,EAAE,qBAAqB,EAEvD;IAEY,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"UpgradeSubCommand.d.ts","sourceRoot":"","sources":["../../../../../src/service/upgrade/command/UpgradeSubCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yCAAyC,CAAC;AAC1E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yCAAyC,CAAC;AACtE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AACvE,OAAO,EAAE,KAAK,MAAM,EAAiB,MAAM,yCAAyC,CAAC;AASrF,OAAO,KAAK,qBAAqB,MAAM,6BAA6B,CAAC;AAErE,qBAAa,iBAAkB,YAAW,UAAU;;IAClD,QAAQ,CAAC,IAAI,aAAa;IAC1B,QAAQ,CAAC,WAAW,6CAA6C;IACjE,QAAQ,CAAC,mBAAmB,SAAS;IACrC,QAAQ,CAAC,WAAW,UAAM;IAE1B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAerC;IAIF,YAAmB,cAAc,EAAE,qBAAqB,EAEvD;IAEY,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA4D5E;CACF"}
|
|
@@ -28,12 +28,15 @@ export class UpgradeSubCommand {
|
|
|
28
28
|
async execute(context, argumentValues) {
|
|
29
29
|
const printerService = context.getServiceById(PRINTER_SERVICE_ID);
|
|
30
30
|
const cliName = context.cliConfig.name;
|
|
31
|
+
const currentVersion = context.cliConfig.version;
|
|
31
32
|
const os = argumentValues.os;
|
|
32
33
|
const arch = undefined;
|
|
33
34
|
const installMethod = argumentValues["install-method"];
|
|
35
|
+
await printerService.showSpinner(`Looking for version newer than ${currentVersion}`);
|
|
34
36
|
const checkResult = os === undefined && installMethod === undefined
|
|
35
37
|
? await this.#upgradeService.getUpgradeCheckResult(true)
|
|
36
38
|
: await this.#upgradeService.checkForUpgrade(os, arch, installMethod);
|
|
39
|
+
await printerService.hideSpinner();
|
|
37
40
|
if (checkResult.status === "unsupported") {
|
|
38
41
|
await printerService.error(`No upgrade location is configured for the detected or requested platform.\n`, Icon.FAILURE);
|
|
39
42
|
return;
|
|
@@ -48,7 +51,7 @@ export class UpgradeSubCommand {
|
|
|
48
51
|
return;
|
|
49
52
|
}
|
|
50
53
|
if (!checkResult.updateAvailable) {
|
|
51
|
-
await printerService.print(`${cliName} is already up to date
|
|
54
|
+
await printerService.print(`${cliName} is already up to date: ${checkResult.currentVersion}\n`, Icon.INFORMATION);
|
|
52
55
|
return;
|
|
53
56
|
}
|
|
54
57
|
const upgradeResult = await this.#upgradeService.upgrade(os, arch, installMethod);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flowscripter/dynamic-cli-framework",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.4.1",
|
|
4
4
|
"description": "A framework for developing CLI applications which supports dynamic discovery and installation of new commands",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"build": "tsc -p tsconfig.build.json"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@flowscripter/dynamic-cli-framework-api": "^2.3.
|
|
51
|
+
"@flowscripter/dynamic-cli-framework-api": "^2.3.3",
|
|
52
52
|
"@flowscripter/dynamic-plugin-framework": "^2.2.4",
|
|
53
53
|
"emphasize": "^7.0.0",
|
|
54
54
|
"fastest-levenshtein": "^1.0.16",
|
|
@@ -29,7 +29,7 @@ export class PluginAddSubCommand implements SubCommand {
|
|
|
29
29
|
|
|
30
30
|
const { pluginId, version } = parsePluginSpecifier(argumentValues["pluginId"] as string);
|
|
31
31
|
const searchLabel = version ? `${pluginId}@${version}` : pluginId;
|
|
32
|
-
await printerService.
|
|
32
|
+
await printerService.showSpinner(`Searching for plugin: ${searchLabel}`);
|
|
33
33
|
|
|
34
34
|
let descriptor: VersionedPluginDescriptor | undefined;
|
|
35
35
|
for await (const d of pluginService.search({ text: pluginId })) {
|
|
@@ -38,6 +38,7 @@ export class PluginAddSubCommand implements SubCommand {
|
|
|
38
38
|
break;
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
+
await printerService.hideSpinner();
|
|
41
42
|
|
|
42
43
|
if (descriptor && version) {
|
|
43
44
|
// search only ever returns the latest version - substitute the explicitly requested
|
|
@@ -83,7 +84,7 @@ export class PluginAddSubCommand implements SubCommand {
|
|
|
83
84
|
);
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
await printerService.
|
|
87
|
+
await printerService.showSpinner(`Installing ${installLabel}...`);
|
|
87
88
|
await pluginService.install(descriptor);
|
|
88
89
|
|
|
89
90
|
// Look up the actually-installed version rather than trusting `descriptor.version`: when no
|
|
@@ -96,6 +97,7 @@ export class PluginAddSubCommand implements SubCommand {
|
|
|
96
97
|
break;
|
|
97
98
|
}
|
|
98
99
|
}
|
|
100
|
+
await printerService.hideSpinner();
|
|
99
101
|
|
|
100
102
|
await printerService.print(
|
|
101
103
|
`Plugin ${descriptor.pluginId}@${installedVersion} installed.\n`,
|
|
@@ -25,8 +25,9 @@ export class PluginRemoveSubCommand implements SubCommand {
|
|
|
25
25
|
const pluginService = context.getServiceById(PLUGIN_SERVICE_ID) as PluginService;
|
|
26
26
|
|
|
27
27
|
const pluginId = argumentValues["pluginId"] as string;
|
|
28
|
-
await printerService.
|
|
28
|
+
await printerService.showSpinner(`Removing plugin: ${pluginId}...`);
|
|
29
29
|
await pluginService.uninstall(pluginId);
|
|
30
|
+
await printerService.hideSpinner();
|
|
30
31
|
await printerService.print(`Plugin ${pluginId} removed.\n`, Icon.SUCCESS);
|
|
31
32
|
}
|
|
32
33
|
}
|
|
@@ -5,6 +5,7 @@ import { dirname, join } from "node:path";
|
|
|
5
5
|
import type {
|
|
6
6
|
CLIConfig,
|
|
7
7
|
FetchService,
|
|
8
|
+
PrinterService,
|
|
8
9
|
SpawnResult,
|
|
9
10
|
SpawnService,
|
|
10
11
|
UpgradeCheckResult,
|
|
@@ -53,6 +54,7 @@ const OS_LABELS: Record<SupportedOs, string> = {
|
|
|
53
54
|
export default class DefaultUpgradeService implements UpgradeService {
|
|
54
55
|
#spawnService: SpawnService | undefined;
|
|
55
56
|
#fetchService: FetchService | undefined;
|
|
57
|
+
#printerService: PrinterService | undefined;
|
|
56
58
|
#upgradeCheckPromise: Promise<UpgradeCheckResult> | undefined;
|
|
57
59
|
readonly #config: UpgradeLocationsConfig;
|
|
58
60
|
readonly #cliConfig: CLIConfig;
|
|
@@ -65,9 +67,45 @@ export default class DefaultUpgradeService implements UpgradeService {
|
|
|
65
67
|
public setDependencies(
|
|
66
68
|
spawnService: SpawnService | undefined,
|
|
67
69
|
fetchService: FetchService | undefined,
|
|
70
|
+
printerService: PrinterService | undefined,
|
|
68
71
|
): void {
|
|
69
72
|
this.#spawnService = spawnService;
|
|
70
73
|
this.#fetchService = fetchService;
|
|
74
|
+
this.#printerService = printerService;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Mirrors SpawnInterfaceAdapter's plugin:add/plugin:remove pattern: wrap a spawned command's
|
|
78
|
+
// output in a quoted, marked block that's cleared on success (so a clean install stays quiet)
|
|
79
|
+
// but left on screen on failure (so the diagnostic output remains visible).
|
|
80
|
+
async #spawnQuoted(command: ReadonlyArray<string>): Promise<SpawnResult> {
|
|
81
|
+
const spawnService = this.#spawnService!;
|
|
82
|
+
const printerService = this.#printerService;
|
|
83
|
+
if (!printerService) {
|
|
84
|
+
return spawnService.spawn(command, { mode: "ignore" });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
printerService.startQuote();
|
|
88
|
+
printerService.startMark();
|
|
89
|
+
|
|
90
|
+
// onOutput is synchronous and may be called concurrently for stdout/stderr lines, but
|
|
91
|
+
// printerService.info() is async and must not be invoked concurrently with itself - queue
|
|
92
|
+
// writes so they're applied one at a time, in call order.
|
|
93
|
+
let writeQueue: Promise<void> = Promise.resolve();
|
|
94
|
+
const onOutput = (line: string): void => {
|
|
95
|
+
writeQueue = writeQueue.then(() => printerService.info(`${line}\n`));
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const result = await spawnService.spawn(command, { mode: "wrapped", onOutput });
|
|
99
|
+
await writeQueue;
|
|
100
|
+
|
|
101
|
+
printerService.endQuote();
|
|
102
|
+
printerService.endMark();
|
|
103
|
+
if (result.ok) {
|
|
104
|
+
await printerService.clearMarked();
|
|
105
|
+
} else {
|
|
106
|
+
printerService.discardMark();
|
|
107
|
+
}
|
|
108
|
+
return result;
|
|
71
109
|
}
|
|
72
110
|
|
|
73
111
|
public getUpgradeCheckResult(waitForResult = false): Promise<UpgradeCheckResult> {
|
|
@@ -214,7 +252,14 @@ export default class DefaultUpgradeService implements UpgradeService {
|
|
|
214
252
|
return { ok: false, oldVersion, error: new Error("FetchService is not available") };
|
|
215
253
|
}
|
|
216
254
|
|
|
255
|
+
if (this.#printerService) {
|
|
256
|
+
await this.#printerService.showSpinner(`Installing version ${checkResult.latestVersion}...`);
|
|
257
|
+
}
|
|
258
|
+
|
|
217
259
|
try {
|
|
260
|
+
if (this.#printerService) {
|
|
261
|
+
await this.#printerService.hideSpinner();
|
|
262
|
+
}
|
|
218
263
|
switch (checkResult.installMethod) {
|
|
219
264
|
case InstallMethod.LINUX_SCRIPT:
|
|
220
265
|
await this.#upgradeViaLinuxScript();
|
|
@@ -385,9 +430,7 @@ export default class DefaultUpgradeService implements UpgradeService {
|
|
|
385
430
|
|
|
386
431
|
async #upgradeViaLinuxScript(): Promise<void> {
|
|
387
432
|
const { scriptUrl } = this.#config.linuxScript!;
|
|
388
|
-
const result = await this.#
|
|
389
|
-
mode: "ignore",
|
|
390
|
-
});
|
|
433
|
+
const result = await this.#spawnQuoted(["sh", "-c", `curl -fsSL ${scriptUrl} | sh`]);
|
|
391
434
|
if (!result.ok) {
|
|
392
435
|
throw new Error(`Install script failed: ${describeSpawnFailure(result)}`);
|
|
393
436
|
}
|
|
@@ -395,9 +438,7 @@ export default class DefaultUpgradeService implements UpgradeService {
|
|
|
395
438
|
|
|
396
439
|
async #upgradeViaHomebrew(): Promise<void> {
|
|
397
440
|
const { tap, formula } = this.#config.homebrew!;
|
|
398
|
-
const result = await this.#
|
|
399
|
-
mode: "ignore",
|
|
400
|
-
});
|
|
441
|
+
const result = await this.#spawnQuoted(["brew", "upgrade", `${tap}/${formula}`]);
|
|
401
442
|
if (!result.ok) {
|
|
402
443
|
throw new Error(`brew upgrade failed: ${describeSpawnFailure(result)}`);
|
|
403
444
|
}
|
|
@@ -405,18 +446,15 @@ export default class DefaultUpgradeService implements UpgradeService {
|
|
|
405
446
|
|
|
406
447
|
async #upgradeViaWinget(): Promise<void> {
|
|
407
448
|
const { packageId } = this.#config.winget!;
|
|
408
|
-
const result = await this.#
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
],
|
|
418
|
-
{ mode: "ignore" },
|
|
419
|
-
);
|
|
449
|
+
const result = await this.#spawnQuoted([
|
|
450
|
+
"winget",
|
|
451
|
+
"upgrade",
|
|
452
|
+
"--id",
|
|
453
|
+
packageId,
|
|
454
|
+
"--silent",
|
|
455
|
+
"--accept-package-agreements",
|
|
456
|
+
"--accept-source-agreements",
|
|
457
|
+
]);
|
|
420
458
|
if (!result.ok) {
|
|
421
459
|
throw new Error(`winget upgrade failed: ${describeSpawnFailure(result)}`);
|
|
422
460
|
}
|
|
@@ -446,14 +484,11 @@ export default class DefaultUpgradeService implements UpgradeService {
|
|
|
446
484
|
const currentExecutable = process.execPath;
|
|
447
485
|
|
|
448
486
|
if (os === SupportedOs.WINDOWS) {
|
|
449
|
-
const extractResult = await this.#
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
],
|
|
455
|
-
{ mode: "ignore" },
|
|
456
|
-
);
|
|
487
|
+
const extractResult = await this.#spawnQuoted([
|
|
488
|
+
"powershell",
|
|
489
|
+
"-Command",
|
|
490
|
+
`Expand-Archive -Path '${archivePath}' -DestinationPath '${tmpDir}' -Force`,
|
|
491
|
+
]);
|
|
457
492
|
if (!extractResult.ok) {
|
|
458
493
|
throw new Error("Failed to extract release archive");
|
|
459
494
|
}
|
|
@@ -464,32 +499,36 @@ export default class DefaultUpgradeService implements UpgradeService {
|
|
|
464
499
|
// Windows won't let us delete the just-renamed-aside exe while this process still has it
|
|
465
500
|
// open/mapped - that can only happen once we're no longer holding it, i.e. at the start of
|
|
466
501
|
// the NEXT invocation, before we move today's running exe aside. Ignore failures: the file
|
|
467
|
-
// may not exist, or may still be locked (e.g. another instance still running).
|
|
502
|
+
// may not exist, or may still be locked (e.g. another instance still running). Not quoted -
|
|
503
|
+
// this is expected to fail silently, there's nothing worth showing the user.
|
|
468
504
|
await this.#spawnService!.spawn(["cmd", "/c", "del", "/f", "/q", oldPath], {
|
|
469
505
|
mode: "ignore",
|
|
470
506
|
});
|
|
471
507
|
|
|
472
|
-
const moveResult = await this.#
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
508
|
+
const moveResult = await this.#spawnQuoted([
|
|
509
|
+
"cmd",
|
|
510
|
+
"/c",
|
|
511
|
+
"move",
|
|
512
|
+
"/y",
|
|
513
|
+
currentExecutable,
|
|
514
|
+
oldPath,
|
|
515
|
+
]);
|
|
476
516
|
if (!moveResult.ok) {
|
|
477
517
|
throw new Error("Failed to move current executable aside");
|
|
478
518
|
}
|
|
479
|
-
const copyResult = await this.#
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
519
|
+
const copyResult = await this.#spawnQuoted([
|
|
520
|
+
"cmd",
|
|
521
|
+
"/c",
|
|
522
|
+
"copy",
|
|
523
|
+
"/y",
|
|
524
|
+
extractedBinary,
|
|
525
|
+
currentExecutable,
|
|
526
|
+
]);
|
|
483
527
|
if (!copyResult.ok) {
|
|
484
528
|
throw new Error("Failed to copy new executable into place");
|
|
485
529
|
}
|
|
486
530
|
} else {
|
|
487
|
-
const extractResult = await this.#
|
|
488
|
-
["unzip", "-o", archivePath, "-d", tmpDir],
|
|
489
|
-
{
|
|
490
|
-
mode: "ignore",
|
|
491
|
-
},
|
|
492
|
-
);
|
|
531
|
+
const extractResult = await this.#spawnQuoted(["unzip", "-o", archivePath, "-d", tmpDir]);
|
|
493
532
|
if (!extractResult.ok) {
|
|
494
533
|
throw new Error("Failed to extract release archive");
|
|
495
534
|
}
|
|
@@ -56,7 +56,8 @@ export default class UpgradeServiceProvider implements ServiceProvider {
|
|
|
56
56
|
if (fetchService === undefined) {
|
|
57
57
|
logger.debug(() => "FetchService not available, upgrade version checks will be unavailable");
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
const printerService = context.getServiceById(PRINTER_SERVICE_ID) as PrinterService;
|
|
60
|
+
upgradeService.setDependencies(spawnService, fetchService, printerService);
|
|
60
61
|
|
|
61
62
|
void upgradeService.getUpgradeCheckResult();
|
|
62
63
|
|
|
@@ -44,15 +44,20 @@ export class UpgradeSubCommand implements SubCommand {
|
|
|
44
44
|
public async execute(context: Context, argumentValues: Values): Promise<void> {
|
|
45
45
|
const printerService = context.getServiceById(PRINTER_SERVICE_ID) as PrinterService;
|
|
46
46
|
const cliName = context.cliConfig.name;
|
|
47
|
+
const currentVersion = context.cliConfig.version;
|
|
47
48
|
|
|
48
49
|
const os = argumentValues.os as SupportedOs | undefined;
|
|
49
50
|
const arch = undefined as SupportedArch | undefined;
|
|
50
51
|
const installMethod = argumentValues["install-method"] as InstallMethod | undefined;
|
|
51
52
|
|
|
53
|
+
await printerService.showSpinner(`Looking for version newer than ${currentVersion}`);
|
|
54
|
+
|
|
52
55
|
const checkResult =
|
|
53
56
|
os === undefined && installMethod === undefined
|
|
54
57
|
? await this.#upgradeService.getUpgradeCheckResult(true)
|
|
55
58
|
: await this.#upgradeService.checkForUpgrade(os, arch, installMethod);
|
|
59
|
+
|
|
60
|
+
await printerService.hideSpinner();
|
|
56
61
|
if (checkResult.status === "unsupported") {
|
|
57
62
|
await printerService.error(
|
|
58
63
|
`No upgrade location is configured for the detected or requested platform.\n`,
|
|
@@ -77,7 +82,8 @@ export class UpgradeSubCommand implements SubCommand {
|
|
|
77
82
|
|
|
78
83
|
if (!checkResult.updateAvailable) {
|
|
79
84
|
await printerService.print(
|
|
80
|
-
`${cliName} is already up to date
|
|
85
|
+
`${cliName} is already up to date: ${checkResult.currentVersion}\n`,
|
|
86
|
+
Icon.INFORMATION,
|
|
81
87
|
);
|
|
82
88
|
return;
|
|
83
89
|
}
|