mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-16 08:25:14 +02:00
refactor(release): single source of truth for package version
Make packages/cli/package.json the single source of truth for the @kaelio/ktx version. publicNpmPackageVersion() now reads it directly, so artifact filenames, release-readiness checks, and the Python wheel version all derive from one field. The duplicate release-policy.json.publicNpmPackageVersion is removed. Previously the two fields could drift: tarballs were named kaelio-ktx-0.4.1.tgz while internally containing @kaelio/ktx@0.0.0-private. - update-public-release-version.mjs rewrites both Python pyproject.toml files (ktx-daemon, ktx-sl) alongside the npm package.jsons, normalizing the version for PEP 440 (e.g. 0.1.0-rc.2 -> 0.1.0rc2). - semantic-release-config.cjs adds the two pyproject.toml files to @semantic-release/git assets so the release commit back to main carries every version source in lockstep. - The six "?? '0.0.0-private'" fallback literals across the CLI are replaced with "?? getKtxCliPackageInfo().version", and createDefaultKtxMcpServer makes its version arg required. - docs/release.md describes the actual commit-back model: the dev tree always reflects the most recent release; no sentinel pin to maintain. Verified: pnpm run artifacts:build now produces kaelio-ktx-0.4.1.tgz and kaelio_ktx-0.4.1-py3-none-any.whl with @kaelio/ktx@0.4.1 inside. Full type-check, dead-code, and 2287 vitests + 173 script tests pass.
This commit is contained in:
parent
36805c4533
commit
99f7a31919
22 changed files with 168 additions and 54 deletions
|
|
@ -105,27 +105,36 @@ prior rc lineage is consumed cleanly.
|
|||
## Release metadata
|
||||
|
||||
semantic-release calls `scripts/update-public-release-version.mjs` during the
|
||||
prepare step before the exec publish command runs. That script updates the
|
||||
following files **inside the CI runner only**:
|
||||
prepare step before the exec publish command runs. That script rewrites the
|
||||
following files in lockstep:
|
||||
|
||||
- `package.json` with the semantic-release version.
|
||||
- `release-policy.json` with `publicNpmPackageVersion`, npm publish settings,
|
||||
and the published package smoke-test version.
|
||||
- `package.json` and `packages/cli/package.json` with the semantic-release
|
||||
version.
|
||||
- `python/ktx-daemon/pyproject.toml` and `python/ktx-sl/pyproject.toml` with
|
||||
the same version, normalized for PEP 440 (e.g. `0.1.0-rc.2` →
|
||||
`0.1.0rc2`). Branch-prefixed npm releases skip this step because they are
|
||||
not published to PyPI.
|
||||
- `release-policy.json` with the npm publish settings, release mode, and
|
||||
published package smoke-test version. The package version itself is not
|
||||
stored here; it is derived from `packages/cli/package.json.version` by
|
||||
`scripts/public-npm-release-metadata.mjs`, so there is only one place that
|
||||
owns the version.
|
||||
|
||||
The artifact packaging, readiness, and smoke-test scripts read
|
||||
`publicNpmPackageVersion` from `release-policy.json` within the same CI run.
|
||||
Nothing reads these files at runtime — the daemon and CLI rely on the
|
||||
published `package.json` (for the installed `@kaelio/ktx` package) or
|
||||
`packages/cli/package.json` (for dev-tree runs from this repo, which always
|
||||
report `0.0.0-private`). Because the metadata mutation never has to survive
|
||||
the run, no commit is pushed back to `main`. The git tag plus the published
|
||||
npm artifact carry the version forward.
|
||||
The `@semantic-release/git` plugin then commits these files back to `main`
|
||||
with the release tag (see `scripts/semantic-release-config.cjs`). As a
|
||||
result, the dev tree always reflects the most recently published version —
|
||||
there is no sentinel pin. `ktx --version` and the bundled Python wheel both
|
||||
report whatever was last released.
|
||||
|
||||
The bundled Python runtime wheel also derives its version from
|
||||
`publicNpmPackageVersion`. Stable npm versions are reused as-is, and rc
|
||||
versions are normalized to Python's version format. For example,
|
||||
`0.1.0-rc.2` becomes `0.1.0rc2` in the `kaelio-ktx` wheel filename and wheel
|
||||
metadata.
|
||||
At runtime the CLI reads its version from its own `package.json` via
|
||||
`getKtxCliPackageInfo()` (`packages/cli/src/cli-runtime.ts`); the Python
|
||||
daemon reads its version from installed-package metadata via
|
||||
`importlib.metadata.version()` (`python/ktx-daemon/src/ktx_daemon/__init__.py`).
|
||||
|
||||
The bundled Python runtime wheel also derives its version from the same
|
||||
single source. Stable npm versions are reused as-is, and rc versions are
|
||||
normalized to Python's version format. For example, `0.1.0-rc.2` becomes
|
||||
`0.1.0rc2` in the `kaelio-ktx` wheel filename and wheel metadata.
|
||||
|
||||
## npm authentication
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@kaelio/ktx",
|
||||
"version": "0.4.1",
|
||||
"description": "Standalone ktx context layer for database agents",
|
||||
"description": "Standalone ktx context layer for data agents",
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=22.0.0"
|
||||
|
|
@ -30,7 +30,7 @@
|
|||
"scripts": {
|
||||
"assets:demo": "node scripts/build-demo-assets.mjs",
|
||||
"build": "tsc -p tsconfig.json && node scripts/copy-runtime-assets.mjs && node ../../scripts/prepare-cli-bin.mjs",
|
||||
"clean": "node -e \"fs.rmSync('dist', { recursive: true, force: true }); fs.rmSync('node_modules/.cache/tsc.tsbuildinfo', { force: true })\"",
|
||||
"clean": "node -e \"fs.rmSync('dist', { recursive: true, force: true })\"",
|
||||
"docs:commands": "pnpm run build && node dist/print-command-tree.js",
|
||||
"smoke": "vitest run src/standalone-smoke.test.ts src/example-smoke.test.ts --testTimeout 30000",
|
||||
"test": "vitest run --exclude src/standalone-smoke.test.ts --exclude src/example-smoke.test.ts --exclude src/setup-databases.test.ts --exclude src/scan.test.ts --exclude src/commands/connection-metabase-setup.test.ts --exclude src/setup-models.test.ts --exclude src/setup-sources.test.ts --exclude src/setup.test.ts --exclude src/connection.test.ts --exclude src/setup-embeddings.test.ts --exclude src/ingest.test.ts --exclude src/commands/connection-mapping.test.ts --exclude src/ingest-viz.test.ts --exclude src/demo.test.ts --exclude src/setup-project.test.ts --exclude src/sl.test.ts --exclude src/local-scan-connectors.test.ts --exclude src/commands/connection-notion.test.ts --exclude src/context/scan/local-scan.test.ts --exclude src/context/mcp/local-project-ports.test.ts --exclude src/context/ingest/local-stage-ingest.test.ts --exclude src/context/sl/pglite-sl-search-prototype.test.ts --exclude src/context/core/git.service.test.ts --exclude src/context/ingest/local-adapters.test.ts --exclude src/context/ingest/local-bundle-ingest.test.ts --exclude src/context/ingest/local-metabase-ingest.test.ts --exclude src/context/sl/local-sl.test.ts --exclude src/context/search/pglite-owner-process.test.ts --exclude src/context/scan/local-enrichment-artifacts.test.ts --exclude src/context/search/pglite-spike.test.ts --exclude src/context/wiki/local-knowledge.test.ts --exclude src/context/sl/local-query.test.ts --exclude src/context/scan/relationship-review-decisions.test.ts --exclude src/context/scan/relationship-profiling.test.ts",
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@ export function createKtxMcpServer(deps: KtxMcpServerDeps): KtxMcpServerDeps['se
|
|||
}
|
||||
|
||||
export function createDefaultKtxMcpServer(
|
||||
deps: Omit<KtxMcpServerDeps, 'server'> & { name?: string; version?: string },
|
||||
deps: Omit<KtxMcpServerDeps, 'server'> & { name?: string; version: string },
|
||||
): McpServer {
|
||||
const server = new McpServer({
|
||||
name: deps.name ?? 'ktx',
|
||||
version: deps.version ?? '0.0.0-private',
|
||||
version: deps.version,
|
||||
});
|
||||
createKtxMcpServer({
|
||||
server: server as KtxMcpServerLike,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import type { MemoryFlowEvent, MemoryFlowReplayInput } from './context/ingest/me
|
|||
import { renderMemoryFlowReplay } from './context/ingest/memory-flow/render.js';
|
||||
import type { KtxSqlQueryExecutorPort } from './context/connections/query-executor.js';
|
||||
import { loadKtxProject, type KtxLocalProject } from './context/project/project.js';
|
||||
import { getKtxCliPackageInfo } from './cli-runtime.js';
|
||||
import { resolveProjectEmbeddingProvider } from './embedding-resolution.js';
|
||||
import { createKtxCliIngestQueryExecutor } from './ingest-query-executor.js';
|
||||
import { readIngestReportSnapshotFile } from './ingest-report-file.js';
|
||||
|
|
@ -677,7 +678,7 @@ export async function runKtxIngest(
|
|||
const resolution = await resolveProjectEmbeddingProvider(project, {
|
||||
mode: 'ensure',
|
||||
installPolicy: args.runtimeInstallPolicy ?? 'never',
|
||||
cliVersion: args.cliVersion ?? '0.0.0-private',
|
||||
cliVersion: args.cliVersion ?? getKtxCliPackageInfo().version,
|
||||
io,
|
||||
});
|
||||
const embeddingProvider =
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { loadKtxProject } from './context/project/project.js';
|
|||
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
||||
import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
|
||||
import type { KtxCliIo } from './cli-runtime.js';
|
||||
import { getKtxCliPackageInfo, type KtxCliIo } from './cli-runtime.js';
|
||||
import { createKtxMcpServerFactory } from './mcp-server-factory.js';
|
||||
|
||||
const DEFAULT_ALLOWED_HOSTS = ['localhost', '127.0.0.1', '::1'] as const;
|
||||
|
|
@ -178,7 +178,7 @@ export async function runKtxMcpHttpServer(options: RunKtxMcpHttpServerOptions):
|
|||
(await createKtxMcpServerFactory({
|
||||
project: project!,
|
||||
projectDir: options.projectDir,
|
||||
cliVersion: options.cliVersion ?? '0.0.0-private',
|
||||
cliVersion: options.cliVersion ?? getKtxCliPackageInfo().version,
|
||||
io: options.io,
|
||||
}));
|
||||
const sessions = new Map<string, StreamableHTTPServerTransport>();
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import type { Readable, Writable } from 'node:stream';
|
|||
import { loadKtxProject } from './context/project/project.js';
|
||||
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
||||
import type { KtxCliIo } from './cli-runtime.js';
|
||||
import { getKtxCliPackageInfo, type KtxCliIo } from './cli-runtime.js';
|
||||
import { createKtxMcpServerFactory } from './mcp-server-factory.js';
|
||||
|
||||
export interface RunKtxMcpStdioServerOptions {
|
||||
|
|
@ -30,7 +30,7 @@ export async function runKtxMcpStdioServer(options: RunKtxMcpStdioServerOptions)
|
|||
(await createKtxMcpServerFactory({
|
||||
project: project!,
|
||||
projectDir: options.projectDir,
|
||||
cliVersion: options.cliVersion ?? '0.0.0-private',
|
||||
cliVersion: options.cliVersion ?? getKtxCliPackageInfo().version,
|
||||
io: protocolIo,
|
||||
}));
|
||||
const stdin = options.stdin ?? process.stdin;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { getKtxCliPackageInfo } from './cli-runtime.js';
|
||||
import { loadKtxProject, type KtxLocalProject } from './context/project/project.js';
|
||||
import type { KtxProjectConnectionConfig } from './context/project/config.js';
|
||||
import type { KtxProgressPort } from './context/scan/types.js';
|
||||
|
|
@ -878,7 +879,7 @@ export async function runKtxPublicIngest(
|
|||
for (const feature of requirements.features) {
|
||||
try {
|
||||
await ensureRuntime({
|
||||
cliVersion: args.cliVersion ?? '0.0.0-private',
|
||||
cliVersion: args.cliVersion ?? getKtxCliPackageInfo().version,
|
||||
installPolicy: args.runtimeInstallPolicy ?? 'prompt',
|
||||
io,
|
||||
feature,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import type { KtxProgressPort, KtxScanMode, KtxScanReport, KtxScanWarning } from './context/scan/types.js';
|
||||
import { runLocalScan } from './context/scan/local-scan.js';
|
||||
import { loadKtxProject } from './context/project/project.js';
|
||||
import { getKtxCliPackageInfo } from './cli-runtime.js';
|
||||
import { resolveProjectEmbeddingProvider } from './embedding-resolution.js';
|
||||
import type { KtxCliIo } from './index.js';
|
||||
import { createKtxCliLocalIngestAdapters } from './local-adapters.js';
|
||||
|
|
@ -314,7 +315,7 @@ export async function runKtxScan(args: KtxScanArgs, io: KtxCliIo = process, deps
|
|||
const resolution = await resolveProjectEmbeddingProvider(project, {
|
||||
mode: 'ensure',
|
||||
installPolicy: args.runtimeInstallPolicy ?? 'never',
|
||||
cliVersion: args.cliVersion ?? '0.0.0-private',
|
||||
cliVersion: args.cliVersion ?? getKtxCliPackageInfo().version,
|
||||
io,
|
||||
});
|
||||
const embeddingProvider =
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[project]
|
||||
name = "ktx-daemon"
|
||||
version = "0.0.0+private"
|
||||
version = "0.4.1"
|
||||
description = "Portable compute package for KTX semantic-layer operations"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.13"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[project]
|
||||
name = "ktx-sl"
|
||||
version = "0.0.0+private"
|
||||
version = "0.4.1"
|
||||
description = "Agent-first semantic layer engine with aggregate locality"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.13"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"publicNpmPackageVersion": "0.4.1",
|
||||
"releaseMode": "npm-public-release-ready",
|
||||
"npm": {
|
||||
"publish": true,
|
||||
|
|
|
|||
|
|
@ -52,7 +52,10 @@ describe('runtimeWheelPyproject', () => {
|
|||
const pyproject = runtimeWheelPyproject();
|
||||
|
||||
assert.match(pyproject, /name = "kaelio-ktx"/);
|
||||
assert.match(pyproject, new RegExp(`version = "${RUNTIME_WHEEL_PACKAGE_VERSION.replace(/\./g, '\\.')}"`));
|
||||
assert.match(
|
||||
pyproject,
|
||||
new RegExp(`version = "${RUNTIME_WHEEL_PACKAGE_VERSION.replace(/[.+]/g, (char) => `\\${char}`)}"`),
|
||||
);
|
||||
assert.match(pyproject, /ktx-daemon = "ktx_daemon\.__main__:main"/);
|
||||
assert.match(pyproject, /packages = \["semantic_layer", "ktx_daemon"\]/);
|
||||
assert.match(pyproject, /\[project\.optional-dependencies\]/);
|
||||
|
|
|
|||
|
|
@ -57,11 +57,11 @@ describe('publicKtxTarballName', () => {
|
|||
});
|
||||
|
||||
describe('expectedPublicKtxVersionPattern', () => {
|
||||
it('matches the public package version and rejects the private workspace version', () => {
|
||||
it('matches the public package version and rejects other versions', () => {
|
||||
const pattern = expectedPublicKtxVersionPattern();
|
||||
|
||||
assert.match(`@kaelio/ktx ${PUBLIC_NPM_PACKAGE_VERSION}\n`, pattern);
|
||||
assert.doesNotMatch('@kaelio/ktx 0.0.0-private\n', pattern);
|
||||
assert.doesNotMatch('@kaelio/ktx 9.9.9-other\n', pattern);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ async function writeJson(path, value) {
|
|||
async function writeReleaseMetadataInputs(root) {
|
||||
await writeJson(join(root, 'release-policy.json'), {
|
||||
schemaVersion: 1,
|
||||
publicNpmPackageVersion: PUBLIC_NPM_PACKAGE_VERSION,
|
||||
releaseMode: 'ci-artifact-only',
|
||||
npm: {
|
||||
publish: false,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,10 @@ export function releasePolicyPath(rootDir = scriptRootDir()) {
|
|||
return join(rootDir, 'release-policy.json');
|
||||
}
|
||||
|
||||
export function cliPackageJsonPath(rootDir = scriptRootDir()) {
|
||||
return join(rootDir, 'packages', 'cli', 'package.json');
|
||||
}
|
||||
|
||||
function readJsonSync(path) {
|
||||
return JSON.parse(readFileSync(path, 'utf8'));
|
||||
}
|
||||
|
|
@ -70,20 +74,24 @@ export function assertPublicNpmReleaseTag(tag) {
|
|||
throw new Error(`Invalid public npm release tag: ${tag}`);
|
||||
}
|
||||
|
||||
function readCliPackageVersion(rootDir = scriptRootDir()) {
|
||||
const packageJson = readJsonSync(cliPackageJsonPath(rootDir));
|
||||
return assertPublicNpmPackageVersion(packageJson.version);
|
||||
}
|
||||
|
||||
export function readPublicNpmReleaseMetadata(rootDir = scriptRootDir()) {
|
||||
const policy = readJsonSync(releasePolicyPath(rootDir));
|
||||
const version = assertPublicNpmPackageVersion(policy.publicNpmPackageVersion);
|
||||
const tag = assertPublicNpmReleaseTag(policy.npm?.tag);
|
||||
|
||||
return {
|
||||
packageName: PUBLIC_NPM_PACKAGE_NAME,
|
||||
version,
|
||||
version: readCliPackageVersion(rootDir),
|
||||
tag,
|
||||
};
|
||||
}
|
||||
|
||||
export function publicNpmPackageVersion(rootDir = scriptRootDir()) {
|
||||
return readPublicNpmReleaseMetadata(rootDir).version;
|
||||
return readCliPackageVersion(rootDir);
|
||||
}
|
||||
|
||||
export const PUBLIC_NPM_PACKAGE_VERSION = publicNpmPackageVersion();
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { dirname, join, resolve } from 'node:path';
|
|||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
|
||||
import { packageArtifactLayout, packageReleaseMetadata, verifyArtifactManifest } from './package-artifacts.mjs';
|
||||
import { assertPublicNpmPackageVersion, publicNpmPackageVersion } from './public-npm-release-metadata.mjs';
|
||||
import { publicNpmPackageVersion } from './public-npm-release-metadata.mjs';
|
||||
import { readPublishedPackageSmokeConfig } from './published-package-smoke-config.mjs';
|
||||
|
||||
function scriptRootDir() {
|
||||
|
|
@ -138,8 +138,6 @@ export function validateReleasePolicy(policy) {
|
|||
throw new Error(`Unsupported release policy schemaVersion: ${policy.schemaVersion}`);
|
||||
}
|
||||
assertSupportedReleaseMode(policy.releaseMode);
|
||||
assertString(policy.publicNpmPackageVersion, 'Release policy publicNpmPackageVersion');
|
||||
assertPublicNpmPackageVersion(policy.publicNpmPackageVersion);
|
||||
assertPlainObject(policy.npm, 'Release policy npm');
|
||||
assertPlainObject(policy.python, 'Release policy python');
|
||||
assertPlainObject(policy.publishedPackageSmoke, 'Release policy publishedPackageSmoke');
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ function releasePolicy(overrides = {}) {
|
|||
|
||||
return {
|
||||
schemaVersion: 1,
|
||||
publicNpmPackageVersion: PUBLIC_NPM_PACKAGE_VERSION,
|
||||
releaseMode: 'ci-artifact-only',
|
||||
npm: {
|
||||
publish: false,
|
||||
|
|
|
|||
|
|
@ -166,7 +166,13 @@ function createReleaseConfig(env = process.env) {
|
|||
[
|
||||
'@semantic-release/git',
|
||||
{
|
||||
assets: ['package.json', 'release-policy.json', 'packages/cli/package.json'],
|
||||
assets: [
|
||||
'package.json',
|
||||
'release-policy.json',
|
||||
'packages/cli/package.json',
|
||||
'python/ktx-daemon/pyproject.toml',
|
||||
'python/ktx-sl/pyproject.toml',
|
||||
],
|
||||
message: 'chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}',
|
||||
},
|
||||
],
|
||||
|
|
|
|||
|
|
@ -88,7 +88,13 @@ describe('semantic-release config', () => {
|
|||
const config = createReleaseConfig({ KTX_RELEASE_KIND: kind, GITHUB_REF_NAME: 'main' });
|
||||
const git = gitPluginOptions(config);
|
||||
assert.ok(git, `${kind}: @semantic-release/git plugin must be configured`);
|
||||
assert.deepEqual(git.assets, ['package.json', 'release-policy.json', 'packages/cli/package.json']);
|
||||
assert.deepEqual(git.assets, [
|
||||
'package.json',
|
||||
'release-policy.json',
|
||||
'packages/cli/package.json',
|
||||
'python/ktx-daemon/pyproject.toml',
|
||||
'python/ktx-sl/pyproject.toml',
|
||||
]);
|
||||
assert.match(git.message, /^chore\(release\): \$\{nextRelease\.version\} \[skip ci\]/);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
PUBLIC_NPM_PACKAGE_NAME,
|
||||
assertPublicNpmPackageVersion,
|
||||
assertPublicNpmReleaseTag,
|
||||
publicNpmPackageVersionToPythonVersion,
|
||||
releasePolicyPath,
|
||||
} from './public-npm-release-metadata.mjs';
|
||||
|
||||
|
|
@ -23,6 +24,42 @@ async function writeJson(path, value) {
|
|||
await writeFile(path, `${JSON.stringify(value, null, 2)}\n`);
|
||||
}
|
||||
|
||||
function pyprojectWithProjectVersion(source, version) {
|
||||
const lines = source.split('\n');
|
||||
let inProject = false;
|
||||
let replaced = false;
|
||||
for (let index = 0; index < lines.length; index += 1) {
|
||||
const line = lines[index];
|
||||
const sectionMatch = /^\s*\[([^\]]+)\]\s*$/.exec(line);
|
||||
if (sectionMatch) {
|
||||
inProject = sectionMatch[1] === 'project';
|
||||
continue;
|
||||
}
|
||||
if (inProject && /^\s*version\s*=\s*"[^"]*"\s*$/.test(line)) {
|
||||
lines[index] = `version = "${version}"`;
|
||||
replaced = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!replaced) {
|
||||
throw new Error('No [project].version assignment found in pyproject.toml');
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
async function rewritePyprojectVersion(path, version) {
|
||||
const source = await readFile(path, 'utf8');
|
||||
await writeFile(path, pyprojectWithProjectVersion(source, version));
|
||||
}
|
||||
|
||||
function safePythonVersionFor(version) {
|
||||
try {
|
||||
return publicNpmPackageVersionToPythonVersion(version);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function updatePublicReleaseVersion(rootDir, version, tag) {
|
||||
const safeVersion = assertPublicNpmPackageVersion(version);
|
||||
const safeTag = assertPublicNpmReleaseTag(tag);
|
||||
|
|
@ -37,9 +74,15 @@ export async function updatePublicReleaseVersion(rootDir, version, tag) {
|
|||
cliPackageJson.version = safeVersion;
|
||||
await writeJson(cliPackageJsonPath, cliPackageJson);
|
||||
|
||||
const pythonVersion = safePythonVersionFor(safeVersion);
|
||||
if (pythonVersion !== null) {
|
||||
await rewritePyprojectVersion(join(rootDir, 'python', 'ktx-daemon', 'pyproject.toml'), pythonVersion);
|
||||
await rewritePyprojectVersion(join(rootDir, 'python', 'ktx-sl', 'pyproject.toml'), pythonVersion);
|
||||
}
|
||||
|
||||
const policyPath = releasePolicyPath(rootDir);
|
||||
const policy = await readJson(policyPath);
|
||||
policy.publicNpmPackageVersion = safeVersion;
|
||||
delete policy.publicNpmPackageVersion;
|
||||
policy.releaseMode = 'npm-public-release-ready';
|
||||
policy.requiredBeforePublishing = [];
|
||||
policy.npm = {
|
||||
|
|
@ -60,6 +103,7 @@ export async function updatePublicReleaseVersion(rootDir, version, tag) {
|
|||
return {
|
||||
version: safeVersion,
|
||||
tag: safeTag,
|
||||
pythonVersion,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,23 +11,51 @@ async function writeJson(path, value) {
|
|||
await writeFile(path, `${JSON.stringify(value, null, 2)}\n`);
|
||||
}
|
||||
|
||||
async function writeText(path, value) {
|
||||
await mkdir(join(path, '..'), { recursive: true });
|
||||
await writeFile(path, value);
|
||||
}
|
||||
|
||||
async function readJson(path) {
|
||||
return JSON.parse(await readFile(path, 'utf8'));
|
||||
}
|
||||
|
||||
async function readText(path) {
|
||||
return readFile(path, 'utf8');
|
||||
}
|
||||
|
||||
const DAEMON_PYPROJECT = `[project]
|
||||
name = "ktx-daemon"
|
||||
version = "0.4.0"
|
||||
description = "Portable compute package for KTX semantic-layer operations"
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
`;
|
||||
|
||||
const SL_PYPROJECT = `[project]
|
||||
name = "ktx-sl"
|
||||
version = "0.4.0"
|
||||
description = "Agent-first semantic layer engine with aggregate locality"
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
`;
|
||||
|
||||
async function writeReleaseFixture(root) {
|
||||
await writeJson(join(root, 'package.json'), {
|
||||
name: 'ktx-workspace',
|
||||
version: '0.0.0-private',
|
||||
version: '0.4.0',
|
||||
private: true,
|
||||
});
|
||||
await writeJson(join(root, 'packages', 'cli', 'package.json'), {
|
||||
name: '@kaelio/ktx',
|
||||
version: '0.0.0-private',
|
||||
version: '0.4.0',
|
||||
});
|
||||
await writeJson(join(root, 'release-policy.json'), {
|
||||
schemaVersion: 1,
|
||||
publicNpmPackageVersion: '0.1.0-rc.1',
|
||||
releaseMode: 'ci-artifact-only',
|
||||
npm: {
|
||||
publish: false,
|
||||
|
|
@ -43,7 +71,7 @@ async function writeReleaseFixture(root) {
|
|||
},
|
||||
publishedPackageSmoke: {
|
||||
packageName: '@kaelio/ktx',
|
||||
version: '0.1.0-rc.1',
|
||||
version: 'latest',
|
||||
registry: null,
|
||||
},
|
||||
runtimeInstaller: {
|
||||
|
|
@ -53,6 +81,8 @@ async function writeReleaseFixture(root) {
|
|||
},
|
||||
requiredBeforePublishing: ['Choose public release version.'],
|
||||
});
|
||||
await writeText(join(root, 'python', 'ktx-daemon', 'pyproject.toml'), DAEMON_PYPROJECT);
|
||||
await writeText(join(root, 'python', 'ktx-sl', 'pyproject.toml'), SL_PYPROJECT);
|
||||
}
|
||||
|
||||
describe('updatePublicReleaseVersion', () => {
|
||||
|
|
@ -65,9 +95,10 @@ describe('updatePublicReleaseVersion', () => {
|
|||
|
||||
assert.equal((await readJson(join(root, 'package.json'))).version, '0.1.0-rc.2');
|
||||
assert.equal((await readJson(join(root, 'packages', 'cli', 'package.json'))).version, '0.1.0-rc.2');
|
||||
assert.match(await readText(join(root, 'python', 'ktx-daemon', 'pyproject.toml')), /^version = "0\.1\.0rc2"$/m);
|
||||
assert.match(await readText(join(root, 'python', 'ktx-sl', 'pyproject.toml')), /^version = "0\.1\.0rc2"$/m);
|
||||
assert.deepEqual(await readJson(join(root, 'release-policy.json')), {
|
||||
schemaVersion: 1,
|
||||
publicNpmPackageVersion: '0.1.0-rc.2',
|
||||
releaseMode: 'npm-public-release-ready',
|
||||
npm: {
|
||||
publish: true,
|
||||
|
|
@ -110,8 +141,17 @@ describe('updatePublicReleaseVersion', () => {
|
|||
(await readJson(join(root, 'packages', 'cli', 'package.json'))).version,
|
||||
'0.1.0-feature-foo.0',
|
||||
);
|
||||
assert.match(
|
||||
await readText(join(root, 'python', 'ktx-daemon', 'pyproject.toml')),
|
||||
/^version = "0\.4\.0"$/m,
|
||||
);
|
||||
assert.match(
|
||||
await readText(join(root, 'python', 'ktx-sl', 'pyproject.toml')),
|
||||
/^version = "0\.4\.0"$/m,
|
||||
);
|
||||
const policy = await readJson(join(root, 'release-policy.json'));
|
||||
assert.equal(policy.publicNpmPackageVersion, '0.1.0-feature-foo.0');
|
||||
assert.equal(policy.publicNpmPackageVersion, undefined);
|
||||
assert.equal(policy.publishedPackageSmoke.version, '0.1.0-feature-foo.0');
|
||||
assert.equal(policy.npm.tag, 'branch-feature-foo');
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
|
|
|
|||
4
uv.lock
generated
4
uv.lock
generated
|
|
@ -440,7 +440,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "ktx-daemon"
|
||||
version = "0.0.0+private"
|
||||
version = "0.4.1"
|
||||
source = { editable = "python/ktx-daemon" }
|
||||
dependencies = [
|
||||
{ name = "fastapi" },
|
||||
|
|
@ -495,7 +495,7 @@ dev = [
|
|||
|
||||
[[package]]
|
||||
name = "ktx-sl"
|
||||
version = "0.0.0+private"
|
||||
version = "0.4.1"
|
||||
source = { editable = "python/ktx-sl" }
|
||||
dependencies = [
|
||||
{ name = "pydantic" },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue