mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-16 04:28:11 +00:00
* chore: release v1.6.11 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(test): read /api/info version from package.json server-info unit tests hardcoded 1.6.10 while buildServerInfo reads package.json, so the 1.6.11 bump failed ubuntu coverage 3/3. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor: read the published version from one helper Release bumps kept breaking tests that each re-required package.json. packageVersion() is now the single read for CLI, MCP, serve, and those tests. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
123 lines
3.8 KiB
TypeScript
123 lines
3.8 KiB
TypeScript
/**
|
|
* Explicit `gitnexus update`: refresh the latest dist-tag, then install
|
|
* `gitnexus@<version>` globally with npm — the same shape as `claude update`
|
|
* / `codex update`. Other commands only notify; they never spawn npm.
|
|
*/
|
|
|
|
import { spawn } from 'node:child_process';
|
|
import { homedir } from 'node:os';
|
|
import { composeWin32NpmCommand } from '../core/embeddings/runtime-install.js';
|
|
import { packageVersion } from '../core/package-version.js';
|
|
import { STRICT_UPDATE_VERSION } from '../core/update-cache.js';
|
|
import { refresh, type UpdateState } from '../core/update-check.js';
|
|
import { t } from './i18n/index.js';
|
|
|
|
export const UPDATE_PACKAGE = 'gitnexus';
|
|
|
|
export interface UpdateCommandDependencies {
|
|
installedVersion: string;
|
|
refresh: (options: {
|
|
eligible: true;
|
|
ignoreOptOut: true;
|
|
installedVersion: string;
|
|
}) => Promise<UpdateState | null>;
|
|
writeStdout: (line: string) => void;
|
|
runInstall: (version: string) => Promise<number>;
|
|
setExitCode: (code: number) => void;
|
|
}
|
|
|
|
function defaultInstalledVersion(): string {
|
|
return packageVersion();
|
|
}
|
|
|
|
export function updateInstallArgs(version: string): string[] {
|
|
return ['i', '-g', `${UPDATE_PACKAGE}@${version}`];
|
|
}
|
|
|
|
export function updateInstallCommand(version: string): string {
|
|
return `npm ${updateInstallArgs(version).join(' ')}`;
|
|
}
|
|
|
|
function isTestDeps(value: unknown): value is Partial<UpdateCommandDependencies> {
|
|
return (
|
|
typeof value === 'object' &&
|
|
value !== null &&
|
|
('refresh' in value || 'runInstall' in value || 'writeStdout' in value)
|
|
);
|
|
}
|
|
|
|
function defaultRunInstall(version: string): Promise<number> {
|
|
const args = updateInstallArgs(version);
|
|
return new Promise((resolve, reject) => {
|
|
const child =
|
|
process.platform === 'win32'
|
|
? spawn(composeWin32NpmCommand(args), {
|
|
cwd: homedir(),
|
|
windowsHide: true,
|
|
shell: true,
|
|
stdio: 'inherit',
|
|
})
|
|
: spawn('npm', args, {
|
|
cwd: homedir(),
|
|
windowsHide: true,
|
|
stdio: 'inherit',
|
|
});
|
|
child.on('error', reject);
|
|
child.on('exit', (code, signal) => {
|
|
resolve(signal ? 1 : (code ?? 1));
|
|
});
|
|
});
|
|
}
|
|
|
|
export async function updateCommand(maybeDeps?: unknown): Promise<void> {
|
|
const deps = isTestDeps(maybeDeps) ? maybeDeps : {};
|
|
const installedVersion = deps.installedVersion ?? defaultInstalledVersion();
|
|
const runRefresh = deps.refresh ?? refresh;
|
|
const writeStdout = deps.writeStdout ?? ((line: string) => console.log(line));
|
|
const runInstall = deps.runInstall ?? defaultRunInstall;
|
|
const setExitCode =
|
|
deps.setExitCode ??
|
|
((code: number) => {
|
|
process.exitCode = code;
|
|
});
|
|
|
|
const state = await runRefresh({
|
|
eligible: true,
|
|
ignoreOptOut: true,
|
|
installedVersion,
|
|
});
|
|
|
|
const latestVersion =
|
|
state?.latestVersion && STRICT_UPDATE_VERSION.test(state.latestVersion)
|
|
? state.latestVersion
|
|
: undefined;
|
|
if (state?.updateAvailable && latestVersion) {
|
|
writeStdout(t('update.available', { installedVersion, latestVersion }));
|
|
writeStdout(t('update.installing', { command: updateInstallCommand(latestVersion) }));
|
|
try {
|
|
const code = await runInstall(latestVersion);
|
|
if (code !== 0) {
|
|
writeStdout(t('update.installFailed', { command: updateInstallCommand(latestVersion) }));
|
|
setExitCode(code);
|
|
return;
|
|
}
|
|
} catch (error) {
|
|
writeStdout(
|
|
t('update.installError', {
|
|
message: error instanceof Error ? error.message : String(error),
|
|
}),
|
|
);
|
|
setExitCode(1);
|
|
return;
|
|
}
|
|
writeStdout(t('update.installed', { version: latestVersion }));
|
|
return;
|
|
}
|
|
|
|
if (latestVersion) {
|
|
writeStdout(t('update.current', { installedVersion }));
|
|
return;
|
|
}
|
|
|
|
writeStdout(t('update.checkFailed'));
|
|
}
|