Files
codegraph/__tests__/viewer-gate.test.ts
Colby Mchenry 2c4d4eb9e4 release: withhold codegraph ui until the viewer launches; hold its changelog entries (#2089)
* feat(cli): withhold `codegraph ui` until the viewer launches (CODEGRAPH_UI=1 opts in)

The viewer has been exercised on Expo apps only, not on API, Laravel,
Spring, Angular or Swift projects, so it stays out of this release. `ui` and
its alias `web` — also as `help ui` or `ui --help` — are refused before any
startup work, and the command is hidden from `--help`, unless CODEGRAPH_UI=1
is set, which keeps it usable for the people testing it. The viewer assets
still ship, so the opt-in works on a released build.

Reverting this commit is the launch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(site): say the viewer is not in a release yet

The viewer pages have been live on the docs site since they landed, and
1.6.0 answers `codegraph ui` with "unknown command" (#1816, #1900). Until the
viewer launches, the guide, the CLI reference and the next-steps link say so.

Reverting this commit is part of the launch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(changelog): hold the viewer's entries for its launch; new Highlights for the fixes release

53 entries that describe `codegraph ui` and its screens move, verbatim, to
docs/viewer-launch-changelog.md along with the launch Highlights. 12 entries
whose graph and `codegraph_explore` half ships now (the router navigation
edges, the cross-tier hops, explore's `when` conditions, SvelteKit layouts,
wrapped Express handlers, …) are reworded around the graph; their original
wording is kept in the holding file for the launch. A few kept entries lose a
viewer clause. The Highlights are rewritten for this release.

prepare-release.mjs promotes the section and extract-release-notes.mjs reads
it back with no mention of the viewer; the released sections are untouched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 05:01:34 +00:00

73 lines
3.1 KiB
TypeScript

/**
* `codegraph ui` (alias `web`) is withheld until the viewer is released: every
* way of reaching it is refused before startup, and it is left out of `--help`,
* unless CODEGRAPH_UI=1 opts in (src/bin/viewer-gate.ts).
*/
import { describe, it, expect } from 'vitest';
import { execFileSync } from 'child_process';
import * as path from 'path';
import { requestedViewerCommand, viewerEnabled } from '../src/bin/viewer-gate';
const BIN = path.resolve(__dirname, '../dist/bin/codegraph.js');
function runCli(args: string[], optIn: boolean): { code: number; output: string } {
const env: NodeJS.ProcessEnv = { ...process.env, CODEGRAPH_NO_DAEMON: '1', CODEGRAPH_WASM_RELAUNCHED: '1', NO_COLOR: '1' };
if (optIn) env.CODEGRAPH_UI = '1';
else delete env.CODEGRAPH_UI;
try {
const output = execFileSync(process.execPath, [BIN, ...args], { encoding: 'utf-8', env, stdio: ['ignore', 'pipe', 'pipe'] });
return { code: 0, output };
} catch (err) {
const e = err as { status?: number; stdout?: string; stderr?: string };
return { code: e.status ?? 1, output: `${e.stdout ?? ''}${e.stderr ?? ''}` };
}
}
describe('viewer gate', () => {
it('finds the viewer command however it is asked for, and nothing else', () => {
expect(requestedViewerCommand(['ui'])).toBe('ui');
expect(requestedViewerCommand(['web', '--port', '0'])).toBe('web');
expect(requestedViewerCommand(['help', 'ui'])).toBe('ui');
expect(requestedViewerCommand(['ui', '--help'])).toBe('ui');
expect(requestedViewerCommand(['--no-color', 'ui', '.'])).toBe('ui');
expect(requestedViewerCommand([])).toBeNull();
expect(requestedViewerCommand(['help'])).toBeNull();
expect(requestedViewerCommand(['explore', 'ui'])).toBeNull();
expect(requestedViewerCommand(['query', 'web'])).toBeNull();
});
it('is enabled only by CODEGRAPH_UI=1', () => {
expect(viewerEnabled({})).toBe(false);
expect(viewerEnabled({ CODEGRAPH_UI: '0' })).toBe(false);
expect(viewerEnabled({ CODEGRAPH_UI: 'true' })).toBe(false);
expect(viewerEnabled({ CODEGRAPH_UI: '1' })).toBe(true);
});
it.each([['ui'], ['web'], ['help', 'ui'], ['ui', '--help'], ['--no-color', 'web', '--no-open']])(
'refuses `codegraph %s` without the opt-in',
(...args) => {
const r = runCli(args, false);
expect(r.code).toBe(1);
expect(r.output).toMatch(/is not in this release yet/);
expect(r.output).not.toMatch(/Listening|http:\/\/127\.0\.0\.1/);
}
);
it('leaves the viewer out of --help unless opted in', () => {
const hidden = runCli(['--help'], false);
expect(hidden.code).toBe(0);
expect(hidden.output).not.toMatch(/^\s+ui \[path\]/m);
expect(hidden.output).toMatch(/^\s+explore /m);
const shown = runCli(['--help'], true);
expect(shown.code).toBe(0);
expect(shown.output).toMatch(/^\s+ui\|web \[options\] \[path\]|^\s+ui \[options\] \[path\]/m);
});
it('with the opt-in, `ui --help` still describes the command', () => {
const r = runCli(['ui', '--help'], true);
expect(r.code).toBe(0);
expect(r.output).toContain('--no-open');
});
});