fix(cli): clean up dev runtime commands (#59)

This commit is contained in:
Andrey Avtomonov 2026-05-13 12:28:24 +02:00 committed by GitHub
parent b9e0a746af
commit eaaabb361e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 127 additions and 218 deletions

View file

@ -192,8 +192,7 @@ describe('standalone example docs', () => {
assert.match(packageArtifacts, /requires `uv` on `PATH`/);
assert.match(packageArtifacts, /ktx dev runtime status/);
assert.match(packageArtifacts, /ktx dev runtime status/);
assert.match(packageArtifacts, /ktx dev runtime prune --dry-run/);
assert.match(packageArtifacts, /ktx dev runtime prune --yes/);
assert.doesNotMatch(packageArtifacts, /ktx dev runtime prune/);
assert.match(
packageArtifacts,
new RegExp(
@ -226,8 +225,7 @@ describe('standalone example docs', () => {
assert.match(readme, /requires `uv` on `PATH`/);
assert.match(readme, /ktx dev runtime status/);
assert.match(readme, /ktx dev runtime status/);
assert.match(readme, /ktx dev runtime prune --dry-run/);
assert.match(readme, /ktx dev runtime prune --yes/);
assert.doesNotMatch(readme, /ktx dev runtime prune/);
assert.doesNotMatch(readme, /@ktx\/context/);
assert.doesNotMatch(readme, /@ktx\/cli/);
assert.doesNotMatch(readme, /python -m ktx_daemon semantic-validate/);

View file

@ -205,6 +205,17 @@ function parseJsonStdout(label, result) {
}
}
function parseJsonStdoutWithExitCode(label, result, expectedCode) {
if (result.code !== expectedCode) {
throw new Error(`${label} failed with code ${result.code}\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}`);
}
try {
return JSON.parse(result.stdout);
} catch (error) {
throw new Error(`${label} did not write JSON stdout: ${error.message}\nstdout:\n${result.stdout}`);
}
}
function requireOutput(label, result, pattern) {
if (!pattern.test(result.stdout)) {
throw new Error(`${label} stdout did not match ${pattern}\nstdout:\n${result.stdout}`);
@ -283,13 +294,14 @@ export async function runLocalEmbeddingsRuntimeSmoke(options = {}) {
requireSuccess(commands[0].label, version);
requireOutput(commands[0].label, version, expectedPublicKtxVersionPattern());
const missingStatus = parseJsonStdout(
const missingStatus = parseJsonStdoutWithExitCode(
commands[1].label,
await run(commands[1].command, commands[1].args, {
cwd: installDir,
env: smokeEnv,
timeoutMs: commands[1].timeoutMs,
}),
1,
);
if (missingStatus.kind !== 'missing') {
throw new Error(`Expected missing runtime before install, got ${JSON.stringify(missingStatus)}`);

View file

@ -548,6 +548,15 @@ function parseJsonResult(label, result) {
return JSON.parse(result.stdout);
}
function parseJsonResultWithExitCode(label, result, expectedCode) {
assert.equal(
result.code,
expectedCode,
label + ' failed with code ' + result.code + '\\nstdout:\\n' + result.stdout + '\\nstderr:\\n' + result.stderr,
);
return JSON.parse(result.stdout);
}
function parseJsonFailure(label, result) {
assert.equal(result.code, 1, label + ' should fail with exit code 1');
assert.equal(result.stdout, '', label + ' should not write stdout when failing');
@ -594,9 +603,10 @@ try {
requireSuccess('ktx public package version', version);
requireOutput('ktx public package version', version, /@kaelio\\/ktx 0\\.1\\.0/);
const runtimeStatusBefore = parseJsonResult(
const runtimeStatusBefore = parseJsonResultWithExitCode(
'ktx dev runtime status missing',
await run('pnpm', ['exec', 'ktx', 'dev', 'runtime', 'status', '--json']),
1,
);
assert.equal(runtimeStatusBefore.kind, 'missing');
assert.equal(runtimeStatusBefore.layout.runtimeRoot, process.env.KTX_RUNTIME_ROOT);
@ -889,27 +899,6 @@ try {
requireOutput('ktx dev runtime stop', runtimeStop, /Stopped KTX Python daemon/);
process.stdout.write('ktx dev runtime daemon lifecycle verified\\n');
const staleRuntimeDir = join(process.env.KTX_RUNTIME_ROOT, '0.0.0');
await mkdir(staleRuntimeDir, { recursive: true });
const runtimePruneDryRun = await run('pnpm', ['exec', 'ktx', 'dev', 'runtime', 'prune', '--dry-run']);
requireSuccess('ktx dev runtime prune dry run', runtimePruneDryRun);
requireOutput('ktx dev runtime prune dry run', runtimePruneDryRun, /Stale KTX Python runtimes/);
requireOutput('ktx dev runtime prune dry run', runtimePruneDryRun, /0\\.0\\.0/);
await access(staleRuntimeDir);
const runtimePruneNeedsConfirmation = await run('pnpm', ['exec', 'ktx', 'dev', 'runtime', 'prune']);
assert.equal(runtimePruneNeedsConfirmation.code, 1, 'ktx dev runtime prune needs confirmation');
assert.equal(runtimePruneNeedsConfirmation.stdout, '', 'ktx dev runtime prune needs confirmation wrote stdout');
assert.match(runtimePruneNeedsConfirmation.stderr, /Refusing to prune without --yes/);
const runtimePruneConfirmed = await run('pnpm', ['exec', 'ktx', 'dev', 'runtime', 'prune', '--yes']);
requireSuccess('ktx dev runtime prune confirmed', runtimePruneConfirmed);
requireOutput('ktx dev runtime prune confirmed', runtimePruneConfirmed, /Removed stale KTX Python runtimes/);
requireOutput('ktx dev runtime prune confirmed', runtimePruneConfirmed, /0\\.0\\.0/);
await assert.rejects(() => access(staleRuntimeDir));
process.stdout.write('ktx dev runtime prune verified\\n');
const structuralScan = await run('pnpm', ['exec', 'ktx', 'scan', 'warehouse',
'--project-dir',
projectDir,

View file

@ -490,13 +490,8 @@ describe('verification snippets', () => {
assert.match(source, /ktx dev runtime start reuse/);
assert.match(source, /Using existing KTX Python daemon/);
assert.match(source, /ktx dev runtime stop/);
assert.match(source, /ktx dev runtime prune dry run/);
assert.match(source, /0\.0\.0/);
assert.match(source, /ktx dev runtime prune needs confirmation/);
assert.match(source, /Refusing to prune without --yes/);
assert.match(source, /ktx dev runtime prune confirmed/);
assert.match(source, /Removed stale KTX Python runtimes/);
assert.match(source, /assert\.rejects\(\(\) => access\(staleRuntimeDir\)\)/);
assert.doesNotMatch(source, /ktx dev runtime prune/);
assert.doesNotMatch(source, /staleRuntimeDir/);
assert.match(source, /run\('pnpm', \[\s*'exec',\s*'ktx',\s*'scan',\s*'warehouse'/);
assert.match(source, /'--mode',\s*'enriched'/);
assert.doesNotMatch(source, /'--enrich'/);