fix(ingest): hide scan internals from public database ingest

This commit is contained in:
Andrey Avtomonov 2026-05-13 18:50:30 +02:00
parent 6c4bf2a52a
commit 0764acaadd
2 changed files with 77 additions and 4 deletions

View file

@ -365,6 +365,40 @@ describe('runKtxPublicIngest', () => {
);
});
it('suppresses internal scan output for public database ingest summaries', async () => {
const io = makeIo();
const project = projectWithConnections({ warehouse: { driver: 'postgres' } });
const runScan = vi.fn(async (_args, scanIo) => {
scanIo.stdout.write('KTX scan completed\n');
scanIo.stdout.write('Mode: structural\n');
scanIo.stdout.write('Report: raw-sources/warehouse/live-database/sync-1/scan-report.json\n');
scanIo.stdout.write('Raw sources: raw-sources/warehouse/live-database/sync-1\n');
return 0;
});
await expect(
runKtxPublicIngest(
{
command: 'run',
projectDir: '/tmp/project',
targetConnectionId: 'warehouse',
all: false,
json: false,
inputMode: 'disabled',
},
io.io,
{ loadProject: vi.fn(async () => project), runScan },
),
).resolves.toBe(0);
expect(io.stdout()).toContain('Ingest finished\n');
expect(io.stdout()).toContain('warehouse');
expect(io.stdout()).not.toContain('KTX scan completed');
expect(io.stdout()).not.toContain('Mode: structural');
expect(io.stdout()).not.toContain('Report: raw-sources');
expect(io.stdout()).not.toContain('live-database');
});
it('runs all independent targets and reports partial failures', async () => {
const io = makeIo();
const project = projectWithConnections({

View file

@ -378,6 +378,7 @@ function markTargetResult(
target: KtxPublicIngestPlanTarget,
status: 'done' | 'failed',
failedOperation?: KtxPublicIngestStepName,
failureDetail?: string,
): KtxPublicIngestTargetResult {
const selectedFailedOperation =
failedOperation ?? (target.operation === 'database-ingest' ? 'database-schema' : 'source-ingest');
@ -395,7 +396,7 @@ function markTargetResult(
return {
...step,
status: 'failed',
detail: `${target.connectionId} failed at ${selectedFailedOperation}.`,
detail: failureDetail ?? `${target.connectionId} failed at ${selectedFailedOperation}.`,
};
}
return { ...step, status: 'not-run' };
@ -454,6 +455,37 @@ function sourceIngestOutputMode(args: Extract<KtxPublicIngestArgs, { command: 'r
return args.inputMode === 'auto' && io.stdout.isTTY === true && hasInteractiveInput(io) ? 'viz' : 'plain';
}
interface CapturedPublicIngestIo extends KtxCliIo {
capturedOutput(): string;
}
function createCapturedPublicIngestIo(): CapturedPublicIngestIo {
let output = '';
return {
stdout: {
isTTY: false,
write(chunk: string) {
output += chunk;
},
},
stderr: {
write(chunk: string) {
output += chunk;
},
},
capturedOutput() {
return output;
},
};
}
function firstCapturedFailureLine(output: string): string | undefined {
return output
.split(/\r?\n/)
.map((line) => line.trim())
.find((line) => line.length > 0 && !line.startsWith('KTX scan completed'));
}
export async function executePublicIngestTarget(
target: KtxPublicIngestPlanTarget,
args: Extract<KtxPublicIngestArgs, { command: 'run' }>,
@ -487,11 +519,18 @@ export async function executePublicIngestTarget(
dryRun: false,
};
const runScan = deps.runScan ?? runKtxScan;
const capturedScanIo = deps.scanProgress ? null : createCapturedPublicIngestIo();
const scanIo = capturedScanIo ?? io;
const scanExitCode = deps.scanProgress
? await runScan(scanArgs, io, { progress: deps.scanProgress })
: await runScan(scanArgs, io);
? await runScan(scanArgs, scanIo, { progress: deps.scanProgress })
: await runScan(scanArgs, scanIo);
if (scanExitCode !== 0) {
return markTargetResult(target, 'failed', 'database-schema');
return markTargetResult(
target,
'failed',
'database-schema',
capturedScanIo ? firstCapturedFailureLine(capturedScanIo.capturedOutput()) : undefined,
);
}
if (target.queryHistory?.enabled === true) {