From df21d413d73782788140d4c4e7ff12cecb5be8a3 Mon Sep 17 00:00:00 2001 From: Andrey Avtomonov Date: Mon, 11 May 2026 13:09:56 +0200 Subject: [PATCH] release: document public npm release handoff --- .github/workflows/release.yml | 69 +++++++++++++++++++++++++++++++ README.md | 14 ++++--- scripts/release-workflow.test.mjs | 21 ++++++++++ 3 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 scripts/release-workflow.test.mjs diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..16c9f1e2 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,69 @@ +name: KTX Release + +on: + workflow_dispatch: + inputs: + publish_live: + description: "Publish @kaelio/ktx to npm instead of running a dry-run" + required: true + type: boolean + default: false + +permissions: + contents: read + +concurrency: + group: ktx-release-${{ github.ref }} + cancel-in-progress: false + +jobs: + npm-public-release: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Setup pnpm + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 + with: + run_install: false + + - name: Setup Node.js + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 + with: + node-version: "24" + cache: "pnpm" + cache-dependency-path: "pnpm-lock.yaml" + + - name: Install TypeScript dependencies + run: pnpm install --frozen-lockfile + + - name: Setup Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: "3.13" + + - name: Setup uv + uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7.3.0 + with: + enable-cache: true + cache-dependency-glob: "uv.lock" + + - name: Install Python dependencies + run: uv sync --all-packages + + - name: Build and verify artifacts + run: pnpm run artifacts:check + + - name: Check release readiness + run: pnpm run release:readiness + + - name: Dry-run npm publish + if: ${{ !inputs.publish_live }} + run: pnpm run release:npm-publish + + - name: Publish npm package + if: ${{ inputs.publish_live }} + run: pnpm run release:npm-publish -- --publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/README.md b/README.md index f29d393f..82dc880c 100644 --- a/README.md +++ b/README.md @@ -278,19 +278,23 @@ packages. ## Release status -This repository builds a single public npm artifact named `@kaelio/ktx`. -Package publishing is still disabled by `release-policy.json`; registry -credentials, public versions, release tags, and provenance policy must be -chosen before publishing artifacts to npm or Python package indexes. +This repository builds one public npm artifact named `@kaelio/ktx`. The first +public npm handoff is policy-gated through `release-policy.json`, which keeps +Python package publishing disabled because KTX-owned Python code ships inside +the npm package as a bundled wheel. -Build local package artifacts with: +Build local package artifacts and verify the guarded dry-run publish path with: ```bash source .venv/bin/activate pnpm run artifacts:check pnpm run release:readiness +pnpm run release:npm-publish ``` +Run the live npm publish only from the manual `KTX Release` workflow with the +`publish_live` input enabled after the `NPM_TOKEN` secret is configured. + ## License KTX is licensed under the Apache License, Version 2.0. See `LICENSE`. diff --git a/scripts/release-workflow.test.mjs b/scripts/release-workflow.test.mjs new file mode 100644 index 00000000..7e313c9c --- /dev/null +++ b/scripts/release-workflow.test.mjs @@ -0,0 +1,21 @@ +import assert from 'node:assert/strict'; +import { readFile } from 'node:fs/promises'; +import { describe, it } from 'node:test'; + +describe('release workflow', () => { + it('publishes only from manual dispatch with an explicit live input', async () => { + const workflow = await readFile(new URL('../.github/workflows/release.yml', import.meta.url), 'utf8'); + + assert.match(workflow, /^name: KTX Release$/m); + assert.match(workflow, /^ workflow_dispatch:$/m); + assert.match(workflow, /publish_live:/); + assert.match(workflow, /default: false/); + assert.match(workflow, /pnpm run artifacts:check/); + assert.match(workflow, /pnpm run release:readiness/); + assert.match(workflow, /pnpm run release:npm-publish$/m); + assert.match(workflow, /pnpm run release:npm-publish -- --publish/); + assert.match(workflow, /NODE_AUTH_TOKEN: \$\{\{ secrets.NPM_TOKEN \}\}/); + assert.doesNotMatch(workflow, /^ push:/m); + assert.doesNotMatch(workflow, /^ pull_request:/m); + }); +});