mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-07 07:55:13 +02:00
fix: improve setup wizard behavior (#127)
* fix: improve setup wizard behavior * fix: derive runtime versions from release metadata * test: validate metabase source mapping requirements * Fix boundary check release identifiers
This commit is contained in:
parent
33a142f769
commit
d1c84e5564
35 changed files with 671 additions and 90 deletions
|
|
@ -6,11 +6,13 @@ import { dirname, join, resolve } from 'node:path';
|
|||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import { promisify } from 'node:util';
|
||||
|
||||
import { publicPythonRuntimePackageVersion } from './public-npm-release-metadata.mjs';
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
export const RUNTIME_WHEEL_DISTRIBUTION_NAME = 'kaelio-ktx';
|
||||
export const RUNTIME_WHEEL_NORMALIZED_NAME = 'kaelio_ktx';
|
||||
export const RUNTIME_WHEEL_PACKAGE_VERSION = '0.1.0';
|
||||
export const RUNTIME_WHEEL_PACKAGE_VERSION = publicPythonRuntimePackageVersion();
|
||||
|
||||
function scriptRootDir() {
|
||||
return resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
||||
|
|
|
|||
|
|
@ -48,11 +48,11 @@ describe('runtimeWheelLayout', () => {
|
|||
});
|
||||
|
||||
describe('runtimeWheelPyproject', () => {
|
||||
it('describes one kaelio-ktx wheel with lazy local embeddings', () => {
|
||||
it('describes one kaelio-ktx wheel with the release-derived Python version and lazy local embeddings', () => {
|
||||
const pyproject = runtimeWheelPyproject();
|
||||
|
||||
assert.match(pyproject, /name = "kaelio-ktx"/);
|
||||
assert.match(pyproject, /version = "0\.1\.0"/);
|
||||
assert.match(pyproject, /version = "0\.1\.0rc1"/);
|
||||
assert.match(pyproject, /ktx-daemon = "ktx_daemon\.__main__:main"/);
|
||||
assert.match(pyproject, /packages = \["semantic_layer", "ktx_daemon"\]/);
|
||||
assert.match(pyproject, /\[project\.optional-dependencies\]/);
|
||||
|
|
@ -110,6 +110,6 @@ describe('runtimeWheelBuildCommand', () => {
|
|||
cwd: '/repo/ktx',
|
||||
});
|
||||
assert.equal(RUNTIME_WHEEL_DISTRIBUTION_NAME, 'kaelio-ktx');
|
||||
assert.equal(RUNTIME_WHEEL_PACKAGE_VERSION, '0.1.0');
|
||||
assert.equal(RUNTIME_WHEEL_PACKAGE_VERSION, '0.1.0rc1');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ const codeExtensions = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.
|
|||
const runtimeAssetPatterns = [/^packages\/[^/]+\/prompts\/.+\.md$/, /^packages\/[^/]+\/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|runtime)(?:\.test)?\.ts$/,
|
||||
/^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-public-npm-package|build-python-runtime-wheel|local-embeddings-runtime-smoke|package-artifacts|public-npm-release-metadata|publish-public-npm-package|published-package-smoke|release-readiness)(?:\.test)?\.mjs$/,
|
||||
];
|
||||
const forbiddenIdentifierTerms = ['kae' + 'lio', 'Kae' + 'lio', 'KAE' + 'LIO_'];
|
||||
|
|
@ -87,7 +88,10 @@ function scansForLlmBoundaries(relativePath) {
|
|||
}
|
||||
|
||||
function isTestSource(relativePath) {
|
||||
return /(?:^|\/)[^/]+\.(?:test|spec)\.[cm]?[jt]sx?$/.test(relativePath);
|
||||
return (
|
||||
/(?:^|\/)[^/]+\.(?:test|spec)\.[cm]?[jt]sx?$/.test(relativePath) ||
|
||||
/(?:^|\/)tests\/(?:.+\/)?(?:test_[^/]+|[^/]+_test)\.py$/.test(relativePath)
|
||||
);
|
||||
}
|
||||
|
||||
function scansForContextProductionLlmBoundaries(relativePath) {
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ describe('scanFileContent', () => {
|
|||
|
||||
assert.equal(scanFileContent('packages/cli/src/setup.test.ts', `project: ${name}-dev`).length, 0);
|
||||
assert.equal(scanFileContent('packages/context/src/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', () => {
|
||||
|
|
@ -79,7 +80,9 @@ describe('scanFileContent', () => {
|
|||
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/publish-public-npm-package.test.mjs', `@${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', () => {
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ async function writeUploadableArtifactFixtures(layout) {
|
|||
`${packageInfo.name}-tarball`,
|
||||
]),
|
||||
[
|
||||
join(layout.pythonDir, 'kaelio_ktx-0.1.0-py3-none-any.whl'),
|
||||
join(layout.pythonDir, 'kaelio_ktx-0.1.0rc1-py3-none-any.whl'),
|
||||
'kaelio-ktx-runtime-wheel',
|
||||
],
|
||||
]);
|
||||
|
|
@ -139,7 +139,7 @@ describe('packageReleaseMetadata', () => {
|
|||
ecosystem: 'python',
|
||||
packageName: 'kaelio-ktx',
|
||||
packageRoot: 'python/runtime-wheel',
|
||||
packageVersion: '0.1.0',
|
||||
packageVersion: '0.1.0rc1',
|
||||
private: false,
|
||||
releaseMode: 'ci-artifact-only',
|
||||
},
|
||||
|
|
@ -154,10 +154,10 @@ describe('findPythonArtifacts', () => {
|
|||
it('finds the bundled runtime wheel only', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-artifacts-test-'));
|
||||
try {
|
||||
await writeFile(join(root, 'kaelio_ktx-0.1.0-py3-none-any.whl'), '');
|
||||
await writeFile(join(root, 'kaelio_ktx-0.1.0rc1-py3-none-any.whl'), '');
|
||||
|
||||
assert.deepEqual(await findPythonArtifacts(root), {
|
||||
runtimeWheel: join(root, 'kaelio_ktx-0.1.0-py3-none-any.whl'),
|
||||
runtimeWheel: join(root, 'kaelio_ktx-0.1.0rc1-py3-none-any.whl'),
|
||||
});
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
|
|
@ -210,7 +210,7 @@ describe('artifact manifest', () => {
|
|||
ecosystem: 'python',
|
||||
packageName: 'kaelio-ktx',
|
||||
packageRoot: 'python/runtime-wheel',
|
||||
packageVersion: '0.1.0',
|
||||
packageVersion: '0.1.0rc1',
|
||||
private: false,
|
||||
releaseMode: 'ci-artifact-only',
|
||||
},
|
||||
|
|
@ -252,8 +252,8 @@ describe('artifact manifest', () => {
|
|||
artifactKind: 'wheel',
|
||||
ecosystem: 'python',
|
||||
packageName: 'kaelio-ktx',
|
||||
packageVersion: '0.1.0',
|
||||
path: 'python/kaelio_ktx-0.1.0-py3-none-any.whl',
|
||||
packageVersion: '0.1.0rc1',
|
||||
path: 'python/kaelio_ktx-0.1.0rc1-py3-none-any.whl',
|
||||
},
|
||||
],
|
||||
);
|
||||
|
|
@ -362,17 +362,17 @@ describe('copyRuntimeWheelAssets', () => {
|
|||
try {
|
||||
await mkdir(layout.pythonDir, { recursive: true });
|
||||
await writeFile(
|
||||
join(layout.pythonDir, 'kaelio_ktx-0.1.0-py3-none-any.whl'),
|
||||
join(layout.pythonDir, 'kaelio_ktx-0.1.0rc1-py3-none-any.whl'),
|
||||
'kaelio-ktx-runtime-wheel',
|
||||
);
|
||||
|
||||
const assets = await copyRuntimeWheelAssets(layout, {
|
||||
runtimeWheel: join(layout.pythonDir, 'kaelio_ktx-0.1.0-py3-none-any.whl'),
|
||||
runtimeWheel: join(layout.pythonDir, 'kaelio_ktx-0.1.0rc1-py3-none-any.whl'),
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
assets.wheelPath,
|
||||
join(root, 'packages', 'cli', 'assets', 'python', 'kaelio_ktx-0.1.0-py3-none-any.whl'),
|
||||
join(root, 'packages', 'cli', 'assets', 'python', 'kaelio_ktx-0.1.0rc1-py3-none-any.whl'),
|
||||
);
|
||||
assert.equal(
|
||||
assets.manifestPath,
|
||||
|
|
@ -385,7 +385,7 @@ describe('copyRuntimeWheelAssets', () => {
|
|||
normalizedName: RUNTIME_WHEEL_NORMALIZED_NAME,
|
||||
version: RUNTIME_WHEEL_PACKAGE_VERSION,
|
||||
wheel: {
|
||||
file: 'kaelio_ktx-0.1.0-py3-none-any.whl',
|
||||
file: 'kaelio_ktx-0.1.0rc1-py3-none-any.whl',
|
||||
sha256: createHash('sha256')
|
||||
.update('kaelio-ktx-runtime-wheel')
|
||||
.digest('hex'),
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ export const PUBLIC_NPM_RELEASE_TAGS = new Set(['latest', 'next']);
|
|||
|
||||
const SEMVER_PATTERN =
|
||||
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
||||
const SEMVER_PARTS_PATTERN =
|
||||
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
||||
|
||||
function scriptRootDir() {
|
||||
return resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
||||
|
|
@ -29,6 +31,30 @@ export function assertPublicNpmPackageVersion(version) {
|
|||
return version;
|
||||
}
|
||||
|
||||
export function publicNpmPackageVersionToPythonVersion(version) {
|
||||
const safeVersion = assertPublicNpmPackageVersion(version);
|
||||
const match = SEMVER_PARTS_PATTERN.exec(safeVersion);
|
||||
if (!match) {
|
||||
throw new Error(`Invalid public npm package version: ${version}`);
|
||||
}
|
||||
|
||||
const [, major, minor, patch, prerelease, buildMetadata] = match;
|
||||
if (buildMetadata) {
|
||||
throw new Error(`Unsupported public npm build metadata for Python runtime version: ${safeVersion}`);
|
||||
}
|
||||
|
||||
const baseVersion = `${major}.${minor}.${patch}`;
|
||||
if (!prerelease) {
|
||||
return baseVersion;
|
||||
}
|
||||
|
||||
const rcMatch = /^rc\.([1-9]\d*|0)$/.exec(prerelease);
|
||||
if (!rcMatch) {
|
||||
throw new Error(`Unsupported public npm prerelease for Python runtime version: ${safeVersion}`);
|
||||
}
|
||||
return `${baseVersion}rc${rcMatch[1]}`;
|
||||
}
|
||||
|
||||
export function assertPublicNpmReleaseTag(tag) {
|
||||
if (!PUBLIC_NPM_RELEASE_TAGS.has(tag)) {
|
||||
throw new Error(`Invalid public npm release tag: ${tag}`);
|
||||
|
|
@ -51,3 +77,7 @@ export function readPublicNpmReleaseMetadata(rootDir = scriptRootDir()) {
|
|||
export function publicNpmPackageVersion(rootDir = scriptRootDir()) {
|
||||
return readPublicNpmReleaseMetadata(rootDir).version;
|
||||
}
|
||||
|
||||
export function publicPythonRuntimePackageVersion(rootDir = scriptRootDir()) {
|
||||
return publicNpmPackageVersionToPythonVersion(publicNpmPackageVersion(rootDir));
|
||||
}
|
||||
|
|
|
|||
26
scripts/public-npm-release-metadata.test.mjs
Normal file
26
scripts/public-npm-release-metadata.test.mjs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import assert from 'node:assert/strict';
|
||||
import { describe, it } from 'node:test';
|
||||
|
||||
import { publicNpmPackageVersionToPythonVersion } from './public-npm-release-metadata.mjs';
|
||||
|
||||
describe('publicNpmPackageVersionToPythonVersion', () => {
|
||||
it('keeps stable public npm versions unchanged for Python wheels', () => {
|
||||
assert.equal(publicNpmPackageVersionToPythonVersion('1.2.3'), '1.2.3');
|
||||
});
|
||||
|
||||
it('converts semantic-release rc versions to PEP 440 rc versions', () => {
|
||||
assert.equal(publicNpmPackageVersionToPythonVersion('0.1.0-rc.1'), '0.1.0rc1');
|
||||
assert.equal(publicNpmPackageVersionToPythonVersion('2.0.0-rc.12'), '2.0.0rc12');
|
||||
});
|
||||
|
||||
it('rejects unsupported prerelease and build metadata forms', () => {
|
||||
assert.throws(
|
||||
() => publicNpmPackageVersionToPythonVersion('1.2.3-beta.1'),
|
||||
/Unsupported public npm prerelease for Python runtime version/,
|
||||
);
|
||||
assert.throws(
|
||||
() => publicNpmPackageVersionToPythonVersion('1.2.3+build.1'),
|
||||
/Unsupported public npm build metadata for Python runtime version/,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -37,7 +37,7 @@ async function writeUploadableArtifactFixtures(layout) {
|
|||
layout.npmTarballs[packageInfo.name],
|
||||
`${packageInfo.name}-tarball`,
|
||||
]),
|
||||
[join(layout.pythonDir, 'kaelio_ktx-0.1.0-py3-none-any.whl'), 'kaelio-ktx-runtime-wheel'],
|
||||
[join(layout.pythonDir, 'kaelio_ktx-0.1.0rc1-py3-none-any.whl'), 'kaelio-ktx-runtime-wheel'],
|
||||
]);
|
||||
|
||||
for (const [path, contents] of fileContents) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue