Merge pull request #4147 from NilsR0711/fix/bun-update-command
fix(gsd): use bun for update when installed via Bun
This commit is contained in:
commit
ef6abf48bc
5 changed files with 79 additions and 17 deletions
|
|
@ -28,6 +28,11 @@ import { loadPrompt } from "./prompt-loader.js";
|
|||
const UPDATE_REGISTRY_URL = "https://registry.npmjs.org/gsd-pi/latest";
|
||||
const UPDATE_FETCH_TIMEOUT_MS = 5000;
|
||||
|
||||
function resolveInstallCommand(pkg: string): string {
|
||||
if ('bun' in process.versions) return `bun add -g ${pkg}`;
|
||||
return `npm install -g ${pkg}`;
|
||||
}
|
||||
|
||||
async function fetchLatestVersionForCommand(): Promise<string | null> {
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), UPDATE_FETCH_TIMEOUT_MS);
|
||||
|
|
@ -431,8 +436,9 @@ export async function handleUpdate(ctx: ExtensionCommandContext): Promise<void>
|
|||
|
||||
ctx.ui.notify(`Updating: v${current} → v${latest}...`, "info");
|
||||
|
||||
const installCmd = resolveInstallCommand(`${NPM_PACKAGE}@latest`);
|
||||
try {
|
||||
execSync(`npm install -g ${NPM_PACKAGE}@latest`, {
|
||||
execSync(installCmd, {
|
||||
stdio: ["ignore", "pipe", "ignore"],
|
||||
});
|
||||
ctx.ui.notify(
|
||||
|
|
@ -441,7 +447,7 @@ export async function handleUpdate(ctx: ExtensionCommandContext): Promise<void>
|
|||
);
|
||||
} catch {
|
||||
ctx.ui.notify(
|
||||
`Update failed. Try manually: npm install -g ${NPM_PACKAGE}@latest`,
|
||||
`Update failed. Try manually: ${installCmd}`,
|
||||
"error",
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,13 +77,9 @@ const ALLOW_HARDCODED_TMP: Array<[string, string]> = [
|
|||
|
||||
/** Pattern 4 — shell commands with interpolated variables */
|
||||
const ALLOW_SHELL_INTERPOLATION: Array<[string, string]> = [
|
||||
// NPM_PACKAGE is a compile-time constant ('gsd-pi'), not user input.
|
||||
["update-cmd.ts", "npm view ${NPM_PACKAGE}"],
|
||||
["update-cmd.ts", "npm install -g ${NPM_PACKAGE}"],
|
||||
["update-check.ts", "npm install -g ${NPM_PACKAGE_NAME}"],
|
||||
// Same constant forwarded through commands-handlers.
|
||||
["resources/extensions/gsd/commands-handlers.ts", "npm view ${NPM_PACKAGE}"],
|
||||
["resources/extensions/gsd/commands-handlers.ts", "npm install -g ${NPM_PACKAGE}"],
|
||||
// update-cmd.ts, update-check.ts, and commands-handlers.ts all pass a
|
||||
// pre-built variable (installCmd) to execSync — no template literal inside
|
||||
// the execSync call, so no entries are needed here.
|
||||
];
|
||||
|
||||
function isAllowlisted(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
/**
|
||||
* Regression test for #3445: gsd update must print both current and latest
|
||||
* versions for diagnostics, and bypass npm cache.
|
||||
* Regression test for #4145: gsd update must use bun when installed via Bun.
|
||||
*/
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
|
@ -32,3 +33,52 @@ test("update commands use the registry fetch helper instead of npm view (#3806)"
|
|||
);
|
||||
assert.ok(!handlerSrc.includes("npm view "), "/gsd update should no longer shell out to npm view");
|
||||
});
|
||||
|
||||
test("update-check exports resolveInstallCommand (#4145)", async () => {
|
||||
const { resolveInstallCommand } = await import("../update-check.js");
|
||||
assert.equal(typeof resolveInstallCommand, "function", "resolveInstallCommand must be exported from update-check");
|
||||
});
|
||||
|
||||
test("resolveInstallCommand returns bun command when running under Bun (#4145)", async () => {
|
||||
const { resolveInstallCommand } = await import("../update-check.js");
|
||||
const orig = (process.versions as Record<string, string | undefined>).bun;
|
||||
try {
|
||||
(process.versions as Record<string, string | undefined>).bun = "1.0.0";
|
||||
assert.equal(resolveInstallCommand("gsd-pi@latest"), "bun add -g gsd-pi@latest");
|
||||
} finally {
|
||||
if (orig === undefined) {
|
||||
delete (process.versions as Record<string, string | undefined>).bun;
|
||||
} else {
|
||||
(process.versions as Record<string, string | undefined>).bun = orig;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("resolveInstallCommand returns npm command when not running under Bun (#4145)", async () => {
|
||||
const { resolveInstallCommand } = await import("../update-check.js");
|
||||
const orig = (process.versions as Record<string, string | undefined>).bun;
|
||||
try {
|
||||
delete (process.versions as Record<string, string | undefined>).bun;
|
||||
assert.equal(resolveInstallCommand("gsd-pi@latest"), "npm install -g gsd-pi@latest");
|
||||
} finally {
|
||||
if (orig !== undefined) {
|
||||
(process.versions as Record<string, string | undefined>).bun = orig;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("update-cmd uses resolveInstallCommand instead of hardcoded npm (#4145)", () => {
|
||||
const src = readFileSync(join(__dirname, "..", "update-cmd.ts"), "utf-8");
|
||||
assert.ok(
|
||||
src.includes("resolveInstallCommand"),
|
||||
"update-cmd should use resolveInstallCommand for package manager detection",
|
||||
);
|
||||
});
|
||||
|
||||
test("commands-handlers uses resolveInstallCommand instead of hardcoded npm (#4145)", () => {
|
||||
const handlerSrc = readFileSync(join(__dirname, "..", "resources", "extensions", "gsd", "commands-handlers.ts"), "utf-8");
|
||||
assert.ok(
|
||||
handlerSrc.includes("resolveInstallCommand"),
|
||||
"/gsd update handler should use resolveInstallCommand for package manager detection",
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -74,10 +74,18 @@ export async function fetchLatestVersionFromRegistry(
|
|||
}
|
||||
}
|
||||
|
||||
export function resolveInstallCommand(pkg: string): string {
|
||||
if ('bun' in process.versions) {
|
||||
return `bun add -g ${pkg}`
|
||||
}
|
||||
return `npm install -g ${pkg}`
|
||||
}
|
||||
|
||||
function printUpdateBanner(current: string, latest: string): void {
|
||||
const installCmd = resolveInstallCommand('gsd-pi')
|
||||
process.stderr.write(
|
||||
` ${chalk.yellow('Update available:')} ${chalk.dim(`v${current}`)} → ${chalk.bold(`v${latest}`)}\n` +
|
||||
` ${chalk.dim('Run')} npm update -g gsd-pi ${chalk.dim('or')} /gsd update ${chalk.dim('to upgrade')}\n\n`,
|
||||
` ${chalk.dim('Run')} ${installCmd} ${chalk.dim('or')} /gsd update ${chalk.dim('to upgrade')}\n\n`,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -184,7 +192,7 @@ export async function checkAndPromptForUpdates(options: UpdateCheckOptions = {})
|
|||
|
||||
const choice = await new Promise<string>((resolve) => {
|
||||
process.stderr.write(
|
||||
` ${chalk.bold('[1]')} Update now ${chalk.dim(`npm install -g ${NPM_PACKAGE_NAME}@latest`)}\n` +
|
||||
` ${chalk.bold('[1]')} Update now ${chalk.dim(resolveInstallCommand(`${NPM_PACKAGE_NAME}@latest`))}\n` +
|
||||
` ${chalk.bold('[2]')} Skip\n\n`,
|
||||
)
|
||||
|
||||
|
|
@ -210,13 +218,14 @@ export async function checkAndPromptForUpdates(options: UpdateCheckOptions = {})
|
|||
process.stdin.pause()
|
||||
|
||||
if (choice === '1') {
|
||||
process.stderr.write(`\n ${chalk.dim('Running:')} npm install -g ${NPM_PACKAGE_NAME}@latest\n\n`)
|
||||
const installCmd = resolveInstallCommand(`${NPM_PACKAGE_NAME}@latest`)
|
||||
process.stderr.write(`\n ${chalk.dim('Running:')} ${installCmd}\n\n`)
|
||||
try {
|
||||
execSync(`npm install -g ${NPM_PACKAGE_NAME}@latest`, { stdio: 'inherit' })
|
||||
execSync(installCmd, { stdio: 'inherit' })
|
||||
process.stderr.write(`\n ${chalk.green.bold(`✓ Updated to v${latestVersion}`)}\n\n`)
|
||||
return true
|
||||
} catch {
|
||||
process.stderr.write(`\n ${chalk.yellow(`Update failed. You can run: npm install -g ${NPM_PACKAGE_NAME}@latest`)}\n\n`)
|
||||
process.stderr.write(`\n ${chalk.yellow(`Update failed. You can run: ${installCmd}`)}\n\n`)
|
||||
}
|
||||
} else {
|
||||
process.stderr.write(` ${chalk.dim('Skipped. Run')} gsd update ${chalk.dim('anytime to upgrade.')}\n\n`)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { execSync } from 'node:child_process'
|
||||
import { compareSemver, fetchLatestVersionFromRegistry } from './update-check.js'
|
||||
import { compareSemver, fetchLatestVersionFromRegistry, resolveInstallCommand } from './update-check.js'
|
||||
|
||||
const NPM_PACKAGE = 'gsd-pi'
|
||||
|
||||
|
|
@ -29,13 +29,14 @@ export async function runUpdate(): Promise<void> {
|
|||
|
||||
process.stdout.write(`${dim}Updating:${reset} v${current} → ${bold}v${latest}${reset}\n`)
|
||||
|
||||
const installCmd = resolveInstallCommand(`${NPM_PACKAGE}@latest`)
|
||||
try {
|
||||
execSync(`npm install -g ${NPM_PACKAGE}@latest`, {
|
||||
execSync(installCmd, {
|
||||
stdio: 'inherit',
|
||||
})
|
||||
process.stdout.write(`\n${green}${bold}Updated to v${latest}${reset}\n`)
|
||||
} catch {
|
||||
process.stderr.write(`\n${yellow}Update failed. Try manually: npm install -g ${NPM_PACKAGE}@latest${reset}\n`)
|
||||
process.stderr.write(`\n${yellow}Update failed. Try manually: ${installCmd}${reset}\n`)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue