feat(cli): add codegraph upgrade self-update + stale-index re-index hint (#710)
`codegraph upgrade [version]` detects how the CLI was installed — the standalone install.sh/install.ps1 bundle, npm-global, npx, or a source checkout — and updates in place: re-running the canonical install.sh on macOS/Linux, an in-place rename-and-extract swap on Windows (a running node.exe can't be deleted, only renamed, so the detached-helper approach is avoided), and npm/npx/source-specific guidance otherwise. Flags: `--check` (report only), `--force`, and a positional version to pin. Each full index is now stamped with the engine's EXTRACTION_VERSION in project_metadata; `codegraph status` (and `--json`) flags an index built by an older engine and recommends re-indexing, and `upgrade` prints the same reminder. Gated on EXTRACTION_VERSION so it never nags on extraction-neutral releases. Validated end-to-end on macOS (real bundle upgrade), Linux (Docker, real curl|sh) and Windows (Parallels VM, real in-place swap). 32 new unit tests. Closes #679 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
07af3db6c7
commit
4e5cf2de56
@@ -20,6 +20,7 @@
|
||||
* codegraph callees <symbol> Find what a function/method calls
|
||||
* codegraph impact <symbol> Analyze what code is affected by changing a symbol
|
||||
* codegraph affected [files] Find test files affected by changes
|
||||
* codegraph upgrade [version] Update CodeGraph to the latest release
|
||||
*/
|
||||
|
||||
import { Command } from 'commander';
|
||||
@@ -32,6 +33,7 @@ import { getGlyphs } from '../ui/glyphs';
|
||||
|
||||
import { buildNode25BlockBanner, buildNodeTooOldBanner, MIN_NODE_MAJOR } from './node-version-check';
|
||||
import { relaunchWithWasmRuntimeFlagsIfNeeded } from '../extraction/wasm-runtime-flags';
|
||||
import { EXTRACTION_VERSION } from '../extraction/extraction-version';
|
||||
|
||||
// Lazy-load heavy modules (CodeGraph, runInstaller) to keep CLI startup fast.
|
||||
async function loadCodeGraph(): Promise<typeof import('../index')> {
|
||||
@@ -699,6 +701,9 @@ program
|
||||
const backend = cg.getBackend();
|
||||
const journalMode = cg.getJournalMode();
|
||||
|
||||
const buildInfo = cg.getIndexBuildInfo();
|
||||
const reindexRecommended = cg.isIndexStale();
|
||||
|
||||
// JSON output mode
|
||||
if (options.json) {
|
||||
const lastIndexedMs = cg.getLastIndexedAt();
|
||||
@@ -724,6 +729,12 @@ program
|
||||
worktreeMismatch: worktreeMismatch
|
||||
? { worktreeRoot: worktreeMismatch.worktreeRoot, indexRoot: worktreeMismatch.indexRoot }
|
||||
: null,
|
||||
index: {
|
||||
builtWithVersion: buildInfo.version,
|
||||
builtWithExtractionVersion: buildInfo.extractionVersion,
|
||||
currentExtractionVersion: EXTRACTION_VERSION,
|
||||
reindexRecommended,
|
||||
},
|
||||
}));
|
||||
cg.destroy();
|
||||
return;
|
||||
@@ -797,6 +808,15 @@ program
|
||||
}
|
||||
console.log();
|
||||
|
||||
// Re-index hint: the index was built by an older engine than the one now
|
||||
// running, so a rebuild would add data a migration can't backfill.
|
||||
if (reindexRecommended) {
|
||||
const builtWith = buildInfo.version ? `v${buildInfo.version.replace(/^v/, '')}` : 'an earlier version';
|
||||
warn(`Index was built by ${builtWith}; re-index to pick up this engine's improvements.`);
|
||||
info('Run "codegraph index -f" (full rebuild) or "codegraph sync"');
|
||||
console.log();
|
||||
}
|
||||
|
||||
cg.destroy();
|
||||
} catch (err) {
|
||||
error(`Failed to get status: ${err instanceof Error ? err.message : String(err)}`);
|
||||
@@ -1664,6 +1684,43 @@ program
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* codegraph upgrade [version]
|
||||
*
|
||||
* Self-update, however CodeGraph was installed (bundle via install.sh/.ps1,
|
||||
* npm-global, npx, or a source checkout). See ../upgrade for the detection and
|
||||
* per-method upgrade logic.
|
||||
*/
|
||||
program
|
||||
.command('upgrade [version]')
|
||||
.description('Update CodeGraph to the latest release (or a specific version)')
|
||||
.option('--check', 'Check whether an update is available without installing')
|
||||
.option('-f, --force', 'Reinstall even if already on the target version')
|
||||
.action(async (versionArg: string | undefined, options: { check?: boolean; force?: boolean }) => {
|
||||
const up = await import('../upgrade');
|
||||
const method = up.detectInstallMethod({
|
||||
filename: __filename,
|
||||
platform: process.platform,
|
||||
cwd: process.cwd(),
|
||||
});
|
||||
const pin = versionArg || process.env.CODEGRAPH_VERSION || undefined;
|
||||
const code = await up.runUpgrade(
|
||||
{ version: pin, check: options.check, force: options.force },
|
||||
{
|
||||
currentVersion: packageJson.version,
|
||||
method,
|
||||
resolveLatest: () => up.resolveLatestVersion(),
|
||||
run: up.defaultRun,
|
||||
hasCommand: up.hasCommand,
|
||||
log: (m: string) => console.log(m),
|
||||
warn: (m: string) => warn(m),
|
||||
error: (m: string) => error(m),
|
||||
platform: process.platform,
|
||||
}
|
||||
);
|
||||
process.exit(code);
|
||||
});
|
||||
|
||||
// Parse and run
|
||||
program.parse();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user