mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-16 08:25:14 +02:00
fix(cli): remove top-level scan command
This commit is contained in:
parent
601591bfbf
commit
011d694ed3
10 changed files with 182 additions and 359 deletions
|
|
@ -41,8 +41,8 @@ export function defaultOrbitVerificationProjectDir() {
|
|||
return defaultProjectDir;
|
||||
}
|
||||
|
||||
function shellCommand(argv) {
|
||||
return ['pnpm', 'run', 'ktx', '--', ...argv].join(' ');
|
||||
function internalScanCommand(input) {
|
||||
return `internal runKtxScan connection=${input.connectionId} mode=relationships projectDir=${input.projectDir}`;
|
||||
}
|
||||
|
||||
function firstNonEmptyLine(...values) {
|
||||
|
|
@ -55,7 +55,7 @@ function firstNonEmptyLine(...values) {
|
|||
return line;
|
||||
}
|
||||
}
|
||||
return 'Orbit scan command failed before producing diagnostic output';
|
||||
return 'Orbit relationship scan failed before producing diagnostic output';
|
||||
}
|
||||
|
||||
function parseArgs(argv) {
|
||||
|
|
@ -88,8 +88,15 @@ function parseArgs(argv) {
|
|||
return options;
|
||||
}
|
||||
|
||||
export function buildOrbitScanArgv(input) {
|
||||
return ['scan', input.connectionId, '--mode', 'relationships', '--project-dir', input.projectDir];
|
||||
export function buildOrbitScanArgs(input) {
|
||||
return {
|
||||
command: 'run',
|
||||
projectDir: input.projectDir,
|
||||
connectionId: input.connectionId,
|
||||
mode: 'relationships',
|
||||
detectRelationships: true,
|
||||
dryRun: false,
|
||||
};
|
||||
}
|
||||
|
||||
export function extractRunId(stdout) {
|
||||
|
|
@ -171,7 +178,7 @@ function formatBlocked(result) {
|
|||
'',
|
||||
'## Evidence',
|
||||
'',
|
||||
'- Orbit verification was not executed because the current local Orbit scan command failed.',
|
||||
'- Orbit verification was not executed because the current local Orbit relationship scan failed.',
|
||||
'- Re-run with `--report-path` to write verification evidence to a custom location.',
|
||||
'',
|
||||
'Scan stdout:',
|
||||
|
|
@ -228,6 +235,36 @@ async function runBufferedWorkspaceKtx(runner, argv, rootDir, execFile) {
|
|||
};
|
||||
}
|
||||
|
||||
function cliScanModulePath(rootDir) {
|
||||
return resolve(rootDir, 'packages/cli/dist/scan.js');
|
||||
}
|
||||
|
||||
async function loadRunKtxScan(rootDir) {
|
||||
const module = await import(pathToFileURL(cliScanModulePath(rootDir)).href);
|
||||
return module.runKtxScan;
|
||||
}
|
||||
|
||||
async function runBufferedInternalScan(input) {
|
||||
const stdout = new BufferWriter();
|
||||
const stderr = new BufferWriter();
|
||||
let runKtxScan = input.runKtxScan;
|
||||
|
||||
if (!runKtxScan) {
|
||||
const build = await runBufferedWorkspaceKtx(input.runner, ['--version'], input.rootDir, input.execFile);
|
||||
if (build.exitCode !== 0) {
|
||||
return build;
|
||||
}
|
||||
runKtxScan = await loadRunKtxScan(input.rootDir);
|
||||
}
|
||||
|
||||
const exitCode = await runKtxScan(input.scanArgs, { stdout, stderr });
|
||||
return {
|
||||
exitCode,
|
||||
stdout: stdout.text(),
|
||||
stderr: stderr.text(),
|
||||
};
|
||||
}
|
||||
|
||||
function orbitVerificationEnv(projectDir) {
|
||||
if (projectDir !== defaultProjectDir) {
|
||||
return process.env;
|
||||
|
|
@ -253,8 +290,15 @@ export async function runOrbitVerification(options = {}) {
|
|||
const env = options.env ?? orbitVerificationEnv(projectDir);
|
||||
const runWithEnv = (argv, runnerOptions) => runner(argv, { ...runnerOptions, env });
|
||||
|
||||
const scanArgv = buildOrbitScanArgv({ connectionId, projectDir });
|
||||
const scan = await runBufferedWorkspaceKtx(runWithEnv, scanArgv, rootDir, execFile);
|
||||
const scanArgs = buildOrbitScanArgs({ connectionId, projectDir });
|
||||
const scanCommand = internalScanCommand({ connectionId, projectDir });
|
||||
const scan = await runBufferedInternalScan({
|
||||
scanArgs,
|
||||
rootDir,
|
||||
execFile,
|
||||
runner: runWithEnv,
|
||||
runKtxScan: options.runKtxScan,
|
||||
});
|
||||
let result;
|
||||
|
||||
if (scan.exitCode !== 0) {
|
||||
|
|
@ -263,7 +307,7 @@ export async function runOrbitVerification(options = {}) {
|
|||
date,
|
||||
connectionId,
|
||||
projectDir,
|
||||
scanCommand: shellCommand(scanArgv),
|
||||
scanCommand,
|
||||
scanExitCode: scan.exitCode,
|
||||
blocker: firstNonEmptyLine(scan.stderr, scan.stdout),
|
||||
scanStdout: scan.stdout,
|
||||
|
|
@ -277,7 +321,7 @@ export async function runOrbitVerification(options = {}) {
|
|||
date,
|
||||
connectionId,
|
||||
projectDir,
|
||||
scanCommand: shellCommand(scanArgv),
|
||||
scanCommand,
|
||||
scanExitCode: scan.exitCode,
|
||||
blocker: 'KTX scan completed without printing a Run id',
|
||||
scanStdout: scan.stdout,
|
||||
|
|
@ -291,7 +335,7 @@ export async function runOrbitVerification(options = {}) {
|
|||
date,
|
||||
connectionId,
|
||||
projectDir,
|
||||
scanCommand: shellCommand(scanArgv),
|
||||
scanCommand,
|
||||
scanExitCode: scan.exitCode,
|
||||
blocker: 'KTX scan completed without printing a report artifact path',
|
||||
scanStdout: scan.stdout,
|
||||
|
|
@ -304,7 +348,7 @@ export async function runOrbitVerification(options = {}) {
|
|||
date,
|
||||
connectionId,
|
||||
projectDir,
|
||||
scanCommand: shellCommand(scanArgv),
|
||||
scanCommand,
|
||||
reportPath: fullScanReportPath,
|
||||
scanExitCode: scan.exitCode,
|
||||
scanStdout: scan.stdout,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import assert from 'node:assert/strict';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { dirname } from 'node:path';
|
||||
import { describe, it } from 'node:test';
|
||||
import {
|
||||
buildOrbitScanArgv,
|
||||
buildOrbitScanArgs,
|
||||
defaultOrbitVerificationProjectDir,
|
||||
extractReportPath,
|
||||
extractRunId,
|
||||
|
|
@ -49,6 +48,14 @@ function successReportJson() {
|
|||
});
|
||||
}
|
||||
|
||||
function successfulRunKtxScan(calls = []) {
|
||||
return async (args, io) => {
|
||||
calls.push(args);
|
||||
io.stdout.write('KTX scan completed\nRun: scan-orbit-1\nConnection: orbit\n Report: reports/scan-report.json\n');
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
describe('relationship Orbit verification helper', () => {
|
||||
it('exposes the Orbit verification command from the KTX workspace package', async () => {
|
||||
const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8'));
|
||||
|
|
@ -59,20 +66,19 @@ describe('relationship Orbit verification helper', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('builds the current KTX launcher arguments for scan commands', () => {
|
||||
assert.deepEqual(buildOrbitScanArgv({ connectionId: 'orbit', projectDir: '/tmp/orbit-project' }), [
|
||||
'scan',
|
||||
'orbit',
|
||||
'--mode',
|
||||
'relationships',
|
||||
'--project-dir',
|
||||
'/tmp/orbit-project',
|
||||
]);
|
||||
it('builds the internal relationship scan arguments', () => {
|
||||
assert.deepEqual(buildOrbitScanArgs({ connectionId: 'orbit', projectDir: '/tmp/orbit-project' }), {
|
||||
command: 'run',
|
||||
projectDir: '/tmp/orbit-project',
|
||||
connectionId: 'orbit',
|
||||
mode: 'relationships',
|
||||
detectRelationships: true,
|
||||
dryRun: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('uses the checked-in Orbit verification project by default', async () => {
|
||||
const calls = [];
|
||||
const envs = [];
|
||||
const scanCalls = [];
|
||||
const writes = [];
|
||||
const defaultProjectDir = defaultOrbitVerificationProjectDir();
|
||||
|
||||
|
|
@ -83,27 +89,28 @@ describe('relationship Orbit verification helper', () => {
|
|||
writeFile: async (path, content) => {
|
||||
writes.push({ path, content });
|
||||
},
|
||||
runWorkspaceKtx: async (argv, options) => {
|
||||
calls.push(argv);
|
||||
envs.push(options.env);
|
||||
options.stdout.write('KTX scan completed\nRun: scan-orbit-1\nConnection: orbit\n Report: reports/scan-report.json\n');
|
||||
return 0;
|
||||
},
|
||||
runKtxScan: successfulRunKtxScan(scanCalls),
|
||||
readFile: async () => successReportJson(),
|
||||
});
|
||||
|
||||
assert.equal(result.status, 'success');
|
||||
assert.deepEqual(calls, [
|
||||
['scan', 'orbit', '--mode', 'relationships', '--project-dir', defaultProjectDir],
|
||||
assert.deepEqual(scanCalls, [
|
||||
{
|
||||
command: 'run',
|
||||
projectDir: defaultProjectDir,
|
||||
connectionId: 'orbit',
|
||||
mode: 'relationships',
|
||||
detectRelationships: true,
|
||||
dryRun: false,
|
||||
},
|
||||
]);
|
||||
assert.equal(envs[0].GIT_CEILING_DIRECTORIES, dirname(defaultProjectDir));
|
||||
assert.equal(writes.length, 1);
|
||||
assert.match(writes[0].content, new RegExp(defaultProjectDir.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
|
||||
});
|
||||
|
||||
it('uses KTX_PROJECT_DIR for the Orbit verification project override', async () => {
|
||||
const previousProjectDir = process.env.KTX_PROJECT_DIR;
|
||||
const calls = [];
|
||||
const scanCalls = [];
|
||||
|
||||
try {
|
||||
process.env.KTX_PROJECT_DIR = '/tmp/orbit-project-from-env';
|
||||
|
|
@ -113,17 +120,20 @@ describe('relationship Orbit verification helper', () => {
|
|||
now: () => new Date('2026-05-07T10:00:00.000Z'),
|
||||
mkdir: async () => {},
|
||||
writeFile: async () => {},
|
||||
runWorkspaceKtx: async (argv, options) => {
|
||||
calls.push(argv);
|
||||
options.stdout.write('KTX scan completed\nRun: scan-orbit-1\nConnection: orbit\n Report: reports/scan-report.json\n');
|
||||
return 0;
|
||||
},
|
||||
runKtxScan: successfulRunKtxScan(scanCalls),
|
||||
readFile: async () => successReportJson(),
|
||||
});
|
||||
|
||||
assert.equal(result.projectDir, '/tmp/orbit-project-from-env');
|
||||
assert.deepEqual(calls, [
|
||||
['scan', 'orbit', '--mode', 'relationships', '--project-dir', '/tmp/orbit-project-from-env'],
|
||||
assert.deepEqual(scanCalls, [
|
||||
{
|
||||
command: 'run',
|
||||
projectDir: '/tmp/orbit-project-from-env',
|
||||
connectionId: 'orbit',
|
||||
mode: 'relationships',
|
||||
detectRelationships: true,
|
||||
dryRun: false,
|
||||
},
|
||||
]);
|
||||
} finally {
|
||||
if (previousProjectDir === undefined) {
|
||||
|
|
@ -146,7 +156,7 @@ describe('relationship Orbit verification helper', () => {
|
|||
date: '2026-05-07',
|
||||
connectionId: 'orbit',
|
||||
projectDir: '/tmp/orbit-project',
|
||||
scanCommand: 'pnpm run ktx -- scan orbit --mode relationships --project-dir /tmp/orbit-project',
|
||||
scanCommand: 'internal runKtxScan connection=orbit mode=relationships projectDir=/tmp/orbit-project',
|
||||
reportPath: '/tmp/orbit-project/reports/scan-report.json',
|
||||
scanExitCode: 0,
|
||||
scanStdout: 'KTX scan completed\nRun: scan-orbit-1\n',
|
||||
|
|
@ -171,7 +181,7 @@ describe('relationship Orbit verification helper', () => {
|
|||
date: '2026-05-07',
|
||||
connectionId: 'orbit',
|
||||
projectDir: '/tmp/orbit-project',
|
||||
scanCommand: 'pnpm run ktx -- scan orbit --mode relationships --project-dir /tmp/orbit-project',
|
||||
scanCommand: 'internal runKtxScan connection=orbit mode=relationships projectDir=/tmp/orbit-project',
|
||||
scanExitCode: 1,
|
||||
blocker: 'Connection "orbit" was not found',
|
||||
scanStdout: '',
|
||||
|
|
@ -180,12 +190,12 @@ describe('relationship Orbit verification helper', () => {
|
|||
|
||||
assert.match(markdown, /Exit code: 1/);
|
||||
assert.match(markdown, /Connection "orbit" was not found/);
|
||||
assert.match(markdown, /Orbit verification was not executed because the current local Orbit scan command failed/);
|
||||
assert.match(markdown, /Orbit verification was not executed because the current local Orbit relationship scan failed/);
|
||||
assert.doesNotMatch(markdown, /scan\.enrichment\.mode is required/);
|
||||
});
|
||||
|
||||
it('runs scan then reads the report artifact and writes success Markdown', async () => {
|
||||
const calls = [];
|
||||
const scanCalls = [];
|
||||
const writes = [];
|
||||
const result = await runOrbitVerification({
|
||||
connectionId: 'orbit',
|
||||
|
|
@ -196,24 +206,27 @@ describe('relationship Orbit verification helper', () => {
|
|||
writeFile: async (path, content) => {
|
||||
writes.push({ path, content });
|
||||
},
|
||||
runWorkspaceKtx: async (argv, options) => {
|
||||
calls.push(argv);
|
||||
options.stdout.write('KTX scan completed\nRun: scan-orbit-1\nConnection: orbit\n Report: reports/scan-report.json\n');
|
||||
return 0;
|
||||
},
|
||||
runKtxScan: successfulRunKtxScan(scanCalls),
|
||||
readFile: async () => successReportJson(),
|
||||
});
|
||||
|
||||
assert.equal(result.status, 'success');
|
||||
assert.deepEqual(calls, [
|
||||
['scan', 'orbit', '--mode', 'relationships', '--project-dir', '/tmp/orbit-project'],
|
||||
assert.deepEqual(scanCalls, [
|
||||
{
|
||||
command: 'run',
|
||||
projectDir: '/tmp/orbit-project',
|
||||
connectionId: 'orbit',
|
||||
mode: 'relationships',
|
||||
detectRelationships: true,
|
||||
dryRun: false,
|
||||
},
|
||||
]);
|
||||
assert.equal(writes.length, 1);
|
||||
assert.equal(writes[0].path, '/tmp/orbit-report.md');
|
||||
assert.match(writes[0].content, /Accepted: 14/);
|
||||
});
|
||||
|
||||
it('writes blocked Markdown when the scan command fails before a run id exists', async () => {
|
||||
it('writes blocked Markdown when the internal scan fails before a run id exists', async () => {
|
||||
const writes = [];
|
||||
const result = await runOrbitVerification({
|
||||
connectionId: 'orbit',
|
||||
|
|
@ -224,8 +237,8 @@ describe('relationship Orbit verification helper', () => {
|
|||
writeFile: async (path, content) => {
|
||||
writes.push({ path, content });
|
||||
},
|
||||
runWorkspaceKtx: async (_argv, options) => {
|
||||
options.stderr.write('Connection "orbit" was not found\n');
|
||||
runKtxScan: async (_args, io) => {
|
||||
io.stderr.write('Connection "orbit" was not found\n');
|
||||
return 1;
|
||||
},
|
||||
});
|
||||
|
|
@ -236,7 +249,7 @@ describe('relationship Orbit verification helper', () => {
|
|||
assert.match(writes[0].content, /Connection "orbit" was not found/);
|
||||
});
|
||||
|
||||
it('runs the workspace launcher in buffered mode so real scan errors are captured', async () => {
|
||||
it('runs the workspace launcher in buffered mode when preparing the internal scan module', async () => {
|
||||
let sawExecFile = false;
|
||||
const result = await runOrbitVerification({
|
||||
connectionId: 'orbit',
|
||||
|
|
@ -246,7 +259,8 @@ describe('relationship Orbit verification helper', () => {
|
|||
mkdir: async () => {},
|
||||
writeFile: async () => {},
|
||||
execFile: async () => ({ stdout: '', stderr: '' }),
|
||||
runWorkspaceKtx: async (_argv, options) => {
|
||||
runWorkspaceKtx: async (argv, options) => {
|
||||
assert.deepEqual(argv, ['--version']);
|
||||
sawExecFile = typeof options.execFile === 'function';
|
||||
options.stderr.write('ENOENT: no such file or directory, open \'/tmp/orbit-project/ktx.yaml\'\n');
|
||||
return 1;
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ test('runWorkspaceKtx rebuilds before running when workspace sources are newer t
|
|||
const logs = [];
|
||||
let sourceMtimeMs = 3000;
|
||||
|
||||
const exitCode = await runWorkspaceKtx(['scan', 'orbit', '--mode', 'relationships'], {
|
||||
const exitCode = await runWorkspaceKtx(['status', '--json', '--no-input'], {
|
||||
rootDir: '/workspace/ktx',
|
||||
access: async () => undefined,
|
||||
stat: async (path) => ({
|
||||
|
|
@ -174,7 +174,7 @@ test('runWorkspaceKtx rebuilds before running when workspace sources are newer t
|
|||
sourceMtimeMs = 1000;
|
||||
return { stdout: 'build ok\n', stderr: '' };
|
||||
}
|
||||
return { stdout: 'scan ok\n', stderr: '' };
|
||||
return { stdout: '{"status":"ready"}\n', stderr: '' };
|
||||
},
|
||||
stdout: { write: (chunk) => logs.push(['stdout', chunk]) },
|
||||
stderr: { write: (chunk) => logs.push(['stderr', chunk]) },
|
||||
|
|
@ -185,12 +185,12 @@ test('runWorkspaceKtx rebuilds before running when workspace sources are newer t
|
|||
calls.map((call) => [call.command, call.args]),
|
||||
[
|
||||
['pnpm', ['run', 'build']],
|
||||
[process.execPath, ['/workspace/ktx/packages/cli/dist/bin.js', 'scan', 'orbit', '--mode', 'relationships']],
|
||||
[process.execPath, ['/workspace/ktx/packages/cli/dist/bin.js', 'status', '--json', '--no-input']],
|
||||
],
|
||||
);
|
||||
assert.deepEqual(logs, [
|
||||
['stderr', 'KTX CLI build output is stale. Rebuilding it now with `pnpm run build`...\n'],
|
||||
['stdout', 'build ok\n'],
|
||||
['stdout', 'scan ok\n'],
|
||||
['stdout', '{"status":"ready"}\n'],
|
||||
]);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue