diff --git a/src/cli.ts b/src/cli.ts index 92bc95044..971203d58 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -74,6 +74,7 @@ function parseCliArgs(argv: string[]): CliFlags { process.stdout.write(' --help, -h Print this help and exit\n') process.stdout.write('\nSubcommands:\n') process.stdout.write(' config Re-run the setup wizard\n') + process.stdout.write(' update Update GSD to the latest version\n') process.exit(0) } else if (!arg.startsWith('--') && !arg.startsWith('-')) { flags.messages.push(arg) @@ -92,6 +93,13 @@ if (cliFlags.messages[0] === 'config') { process.exit(0) } +// `gsd update` — update to the latest version via npm +if (cliFlags.messages[0] === 'update') { + const { runUpdate } = await import('./update-cmd.js') + await runUpdate() + process.exit(0) +} + // Pi's tool bootstrap can mis-detect already-installed fd/rg on some systems // because spawnSync(..., ["--version"]) returns EPERM despite a zero exit code. // Provision local managed binaries first so Pi sees them without probing PATH. diff --git a/src/update-cmd.ts b/src/update-cmd.ts new file mode 100644 index 000000000..ac16a8209 --- /dev/null +++ b/src/update-cmd.ts @@ -0,0 +1,45 @@ +import { execSync } from 'node:child_process' +import { compareSemver } from './update-check.js' + +const NPM_PACKAGE = 'gsd-pi' + +export async function runUpdate(): Promise { + const current = process.env.GSD_VERSION || '0.0.0' + const bold = '\x1b[1m' + const dim = '\x1b[2m' + const green = '\x1b[32m' + const yellow = '\x1b[33m' + const reset = '\x1b[0m' + + process.stdout.write(`${dim}Current version:${reset} v${current}\n`) + process.stdout.write(`${dim}Checking npm registry...${reset}\n`) + + // Fetch latest version + let latest: string + try { + latest = execSync(`npm view ${NPM_PACKAGE} version`, { + encoding: 'utf-8', + stdio: ['ignore', 'pipe', 'ignore'], + }).trim() + } catch { + process.stderr.write(`${yellow}Failed to reach npm registry.${reset}\n`) + process.exit(1) + } + + if (compareSemver(latest, current) <= 0) { + process.stdout.write(`${green}Already up to date.${reset}\n`) + return + } + + process.stdout.write(`${dim}Updating:${reset} v${current} → ${bold}v${latest}${reset}\n`) + + try { + execSync(`npm install -g ${NPM_PACKAGE}@latest`, { + 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.exit(1) + } +}