mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-07 07:55:13 +02:00
fix: update ktx CI boundary checks (#223)
This commit is contained in:
parent
0eeac6f980
commit
bc7373fa8e
5 changed files with 6 additions and 110 deletions
|
|
@ -40,6 +40,7 @@ import {
|
|||
} from './setup-prompts.js';
|
||||
|
||||
const HISTORIC_SQL_WORK_UNIT_MAX_CONCURRENCY = 6;
|
||||
const KTX_QUICKSTART_URL = 'https://docs.kaelio.com/ktx/docs/getting-started/quickstart';
|
||||
const execFileAsync = promisify(execFileCallback);
|
||||
|
||||
export type KtxSetupDatabaseDriver =
|
||||
|
|
@ -1782,7 +1783,7 @@ async function chooseDrivers(
|
|||
}
|
||||
const initialValues = unique(options?.initialDrivers ?? []);
|
||||
createKtxSetupUiAdapter().note(
|
||||
'Get demo credentials at https://kaelio.com/start',
|
||||
`Get demo credentials from the Quickstart: ${KTX_QUICKSTART_URL}`,
|
||||
'🎁 Need a warehouse to play with?',
|
||||
io,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -170,6 +170,7 @@ export interface KtxSetupDeps {
|
|||
}
|
||||
|
||||
const SOURCE_DRIVERS = new Set(['dbt', 'metricflow', 'metabase', 'looker', 'lookml', 'notion']);
|
||||
const KTX_DOCS_URL = 'https://docs.kaelio.com/ktx';
|
||||
|
||||
type KtxSetupEntryAction = 'setup' | 'new-project' | 'agents' | 'status' | 'demo' | 'exit';
|
||||
type KtxSetupFlowStep = 'models' | 'embeddings' | 'databases' | 'sources' | 'runtime' | 'context' | 'agents';
|
||||
|
|
@ -586,7 +587,7 @@ export async function runKtxSetup(args: KtxSetupArgs, io: KtxCliIo, deps: KtxSet
|
|||
async function runKtxSetupInner(args: KtxSetupArgs, io: KtxCliIo, deps: KtxSetupDeps = {}): Promise<number> {
|
||||
const setupUi = deps.setupUi ?? createKtxSetupUiAdapter();
|
||||
setupUi.intro('KTX setup', io);
|
||||
setupUi.note('https://docs.kaelio.com/ktx', '📚 Docs', io);
|
||||
setupUi.note(KTX_DOCS_URL, '📚 Docs', io);
|
||||
let entryAction: KtxSetupEntryAction | undefined;
|
||||
let projectResult: Awaited<ReturnType<typeof runKtxSetupProjectStep>>;
|
||||
let agentNextActions: string | undefined;
|
||||
|
|
|
|||
|
|
@ -5,15 +5,6 @@ import path from 'node:path';
|
|||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
|
||||
const codeExtensions = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.py']);
|
||||
const runtimeAssetPatterns = [/^packages\/cli\/src\/prompts\/.+\.md$/, /^packages\/cli\/src\/skills\/.+\.md$/];
|
||||
const identifierSkipPrefixes = ['docs/', 'docs-site/', 'examples/', 'python/ktx-sl/plans/', 'python/ktx-sl/openspec/'];
|
||||
const identifierAllowPatterns = [
|
||||
/^packages\/cli\/src\/(?:index|managed-local-embeddings|managed-python-command|managed-python-daemon|managed-python-runtime|release-version|runtime)(?:\.test)?\.ts$/,
|
||||
/^python\/ktx-daemon\/src\/ktx_daemon\/__init__\.py$/,
|
||||
/^scripts\/(?:build-python-runtime-wheel|local-embeddings-runtime-smoke|package-artifacts|public-npm-release-metadata|published-package-smoke|release-readiness)(?:\.test)?\.mjs$/,
|
||||
/^scripts\/semantic-release-config\.cjs$/,
|
||||
];
|
||||
const forbiddenIdentifierTerms = ['kae' + 'lio', 'Kae' + 'lio', 'KAE' + 'LIO_'];
|
||||
|
||||
const appImportPatterns = [
|
||||
{
|
||||
|
|
@ -83,10 +74,6 @@ function isCodeSource(relativePath) {
|
|||
return codeExtensions.has(path.extname(relativePath));
|
||||
}
|
||||
|
||||
function isRuntimeAsset(relativePath) {
|
||||
return runtimeAssetPatterns.some((pattern) => pattern.test(relativePath));
|
||||
}
|
||||
|
||||
function scansForAppImports(relativePath) {
|
||||
return isCodeSource(relativePath);
|
||||
}
|
||||
|
|
@ -113,18 +100,6 @@ function scansForConcreteDialectImportBoundaries(relativePath) {
|
|||
);
|
||||
}
|
||||
|
||||
function scansForForbiddenIdentifiers(relativePath) {
|
||||
return (isCodeSource(relativePath) && !isTestSource(relativePath)) || isRuntimeAsset(relativePath);
|
||||
}
|
||||
|
||||
function skipsIdentifierScan(relativePath) {
|
||||
return identifierSkipPrefixes.some((prefix) => relativePath.startsWith(prefix));
|
||||
}
|
||||
|
||||
function allowsForbiddenIdentifier(relativePath) {
|
||||
return identifierAllowPatterns.some((pattern) => pattern.test(relativePath));
|
||||
}
|
||||
|
||||
export function scanFileContent(relativePath, content) {
|
||||
const normalizedPath = normalizePath(relativePath);
|
||||
const violations = [];
|
||||
|
|
@ -178,22 +153,6 @@ export function scanFileContent(relativePath, content) {
|
|||
}
|
||||
}
|
||||
|
||||
if (
|
||||
scansForForbiddenIdentifiers(normalizedPath) &&
|
||||
!skipsIdentifierScan(normalizedPath) &&
|
||||
!allowsForbiddenIdentifier(normalizedPath)
|
||||
) {
|
||||
for (const term of forbiddenIdentifierTerms) {
|
||||
if (content.includes(term)) {
|
||||
violations.push({
|
||||
file: normalizedPath,
|
||||
kind: 'identifier',
|
||||
message: `Forbidden product identifier "${term}"`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return violations;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,14 +3,6 @@ import { describe, it } from 'node:test';
|
|||
|
||||
import { scanFileContent } from './check-boundaries.mjs';
|
||||
|
||||
function productName() {
|
||||
return ['Kae', 'lio'].join('');
|
||||
}
|
||||
|
||||
function lowerProductName() {
|
||||
return ['kae', 'lio'].join('');
|
||||
}
|
||||
|
||||
describe('scanFileContent', () => {
|
||||
it('rejects source imports from application directories', () => {
|
||||
const serverAlias = '@' + 'server/contracts';
|
||||
|
|
@ -27,64 +19,6 @@ describe('scanFileContent', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('rejects forbidden product identifiers in code source files', () => {
|
||||
const violations = scanFileContent('packages/cli/src/context/index.ts', `export const owner = '${lowerProductName()}';`);
|
||||
|
||||
assert.equal(violations.length, 1);
|
||||
assert.equal(violations[0]?.kind, 'identifier');
|
||||
});
|
||||
|
||||
it('rejects forbidden product identifiers in shipped runtime prompt assets', () => {
|
||||
const violations = scanFileContent(
|
||||
'packages/cli/src/prompts/memory_agent_bundle_ingest_work_unit.md',
|
||||
`Write output for ${productName()}.`,
|
||||
);
|
||||
|
||||
assert.equal(violations.length, 1);
|
||||
assert.equal(violations[0]?.kind, 'identifier');
|
||||
assert.equal(violations[0]?.file, 'packages/cli/src/prompts/memory_agent_bundle_ingest_work_unit.md');
|
||||
});
|
||||
|
||||
it('rejects forbidden product identifiers in shipped runtime skill assets', () => {
|
||||
const violations = scanFileContent(
|
||||
'packages/cli/src/skills/metabase_ingest/SKILL.md',
|
||||
`Use ${productName()} project conventions.`,
|
||||
);
|
||||
|
||||
assert.equal(violations.length, 1);
|
||||
assert.equal(violations[0]?.kind, 'identifier');
|
||||
assert.equal(violations[0]?.file, 'packages/cli/src/skills/metabase_ingest/SKILL.md');
|
||||
});
|
||||
|
||||
it('allows product identifiers in docs, examples, and transition metadata', () => {
|
||||
const name = productName();
|
||||
|
||||
assert.equal(scanFileContent('docs/transition.md', name).length, 0);
|
||||
assert.equal(scanFileContent('examples/transition.md', name).length, 0);
|
||||
assert.equal(scanFileContent('python/ktx-sl/plans/brainstorm.md', name).length, 0);
|
||||
assert.equal(scanFileContent('python/ktx-sl/openspec/specs/semantic-layer/spec.md', name).length, 0);
|
||||
});
|
||||
|
||||
it('allows product identifiers in test fixtures', () => {
|
||||
const name = lowerProductName();
|
||||
|
||||
assert.equal(scanFileContent('packages/cli/test/setup.test.ts', `project: ${name}-dev`).length, 0);
|
||||
assert.equal(scanFileContent('packages/cli/test/context/ingest/importer.test.ts', `email: system@${name}.dev`).length, 0);
|
||||
assert.equal(scanFileContent('python/ktx-daemon/tests/test_package.py', `${name}-ktx`).length, 0);
|
||||
});
|
||||
|
||||
it('allows public package identifiers in release packaging and managed runtime source', () => {
|
||||
const name = lowerProductName();
|
||||
|
||||
assert.equal(scanFileContent('scripts/local-embeddings-runtime-smoke.mjs', `@${name}/ktx`).length, 0);
|
||||
assert.equal(scanFileContent('scripts/package-artifacts.test.mjs', `${name}-ktx`).length, 0);
|
||||
assert.equal(scanFileContent('scripts/public-npm-release-metadata.mjs', `@${name}/ktx`).length, 0);
|
||||
assert.equal(scanFileContent('scripts/semantic-release-config.cjs', `${name}-ktx-`).length, 0);
|
||||
assert.equal(scanFileContent('packages/cli/src/release-version.ts', `@${name}/ktx`).length, 0);
|
||||
assert.equal(scanFileContent('packages/cli/src/managed-python-runtime.ts', `${name}_ktx`).length, 0);
|
||||
assert.equal(scanFileContent('python/ktx-daemon/src/ktx_daemon/__init__.py', `${name}-ktx`).length, 0);
|
||||
});
|
||||
|
||||
it('allows clean source files and clean runtime prompt assets', () => {
|
||||
assert.deepEqual(
|
||||
scanFileContent('packages/cli/src/context/index.ts', "export const packageName = 'ktx';"),
|
||||
|
|
|
|||
|
|
@ -255,7 +255,8 @@ describe('standalone example docs', () => {
|
|||
assert.match(reviewingContext, /ktx ingest --all --no-input/);
|
||||
assert.match(quickstart, /schema context/);
|
||||
assert.match(primarySources, /context:\n queryHistory:/);
|
||||
assert.match(rootReadme, /`ktx ingest <id>` \| Build context for one connection/);
|
||||
assert.match(rootReadme, /`ktx ingest` \| Build context for every configured connection/);
|
||||
assert.doesNotMatch(rootReadme, /`ktx ingest <id>`/);
|
||||
assert.match(quickstart, /Databases:\n warehouse: deep context complete/);
|
||||
assert.match(quickstart, /Databases configured: yes \(warehouse\)/);
|
||||
assert.match(setupReference, /Databases configured: yes \(postgres-warehouse\)/);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue