diff --git a/.craft.yml b/.craft.yml index c0d5d6a..85a4021 100644 --- a/.craft.yml +++ b/.craft.yml @@ -7,7 +7,9 @@ targets: includeNames: /^vitest-evals-\d.*\.tgz$/ - name: npm id: "@vitest-evals/harness-ai-sdk" + access: public includeNames: /^vitest-evals-harness-ai-sdk-\d.*\.tgz$/ - name: npm id: "@vitest-evals/harness-pi-ai" + access: public includeNames: /^vitest-evals-harness-pi-ai-\d.*\.tgz$/ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 55ba615..99d749e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,6 +11,20 @@ on: - minor - patch - major + prerelease: + description: Prepare a prerelease instead of a stable release + required: false + type: boolean + default: false + prerelease_id: + description: Prerelease identifier + required: false + type: choice + default: beta + options: + - beta + - rc + - alpha force: description: Force release (bypass blockers) required: false @@ -53,9 +67,13 @@ jobs: id: version env: BUMP: ${{ inputs.bump }} + PRERELEASE: ${{ inputs.prerelease }} + PRERELEASE_ID: ${{ inputs.prerelease_id }} run: | + set -euo pipefail CURRENT=$(node -p "require('./packages/vitest-evals/package.json').version") - NEW=$(npx semver -i "$BUMP" "$CURRENT") + NEW=$(node ./scripts/calculate-release-version.mjs \ + "$CURRENT" "$BUMP" "$PRERELEASE" "$PRERELEASE_ID") echo "current=$CURRENT" >> "$GITHUB_OUTPUT" echo "new=$NEW" >> "$GITHUB_OUTPUT" diff --git a/README.md b/README.md index b2b041b..808a77b 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,18 @@ tables. Pull request CI runs the same core safety checks: release config validation, lint, typecheck, the CI test suite, and the workspace build. +## Releases + +Use the manual Release workflow for stable releases. Select `patch`, `minor`, +or `major` for the normal version bump. + +For a preview release that should not become the main stable version, set +`prerelease` to `true` and leave `prerelease_id` as `beta` unless you want an +`rc` or `alpha` line. From `0.8.0`, `bump=minor` produces `0.9.0-beta.0` and +`bump=major` produces `1.0.0-beta.0`; running prerelease again from +`1.0.0-beta.0` produces `1.0.0-beta.1`. Craft publishes npm prereleases under a +prerelease dist-tag so the stable `latest` line is not moved. + ## Example The `apps/demo-pi` app shows the intended explicit-run flow: diff --git a/packages/harness-ai-sdk/package.json b/packages/harness-ai-sdk/package.json index 9645459..81a3277 100644 --- a/packages/harness-ai-sdk/package.json +++ b/packages/harness-ai-sdk/package.json @@ -6,6 +6,9 @@ "main": "./dist/index.js", "module": "./dist/index.mjs", "files": ["dist"], + "publishConfig": { + "access": "public" + }, "exports": { ".": { "source": "./src/index.ts", diff --git a/packages/harness-pi-ai/package.json b/packages/harness-pi-ai/package.json index 740ca9d..274878c 100644 --- a/packages/harness-pi-ai/package.json +++ b/packages/harness-pi-ai/package.json @@ -6,6 +6,9 @@ "main": "./dist/index.js", "module": "./dist/index.mjs", "files": ["dist"], + "publishConfig": { + "access": "public" + }, "exports": { ".": { "source": "./src/index.ts", diff --git a/scripts/calculate-release-version.mjs b/scripts/calculate-release-version.mjs new file mode 100644 index 0000000..5746075 --- /dev/null +++ b/scripts/calculate-release-version.mjs @@ -0,0 +1,103 @@ +#!/usr/bin/env node + +const current = process.argv[2]; +const bump = process.argv[3]; +const prerelease = process.argv[4] === "true"; +const prereleaseId = process.argv[5] || "beta"; + +const allowedBumps = new Set(["patch", "minor", "major"]); +const allowedPrereleaseIds = new Set(["beta", "rc", "alpha"]); + +if (!current || !bump) { + console.error( + "Usage: node scripts/calculate-release-version.mjs [prerelease-id]", + ); + process.exit(1); +} + +if (!allowedBumps.has(bump)) { + console.error(`Invalid bump: ${bump}`); + process.exit(1); +} + +if (!allowedPrereleaseIds.has(prereleaseId)) { + console.error(`Invalid prerelease id: ${prereleaseId}`); + process.exit(1); +} + +const version = parseVersion(current); +if (!version) { + console.error(`Invalid current version: ${current}`); + process.exit(1); +} + +const next = prerelease + ? nextPrereleaseVersion(version, bump, prereleaseId) + : nextStableVersion(version, bump); + +console.log(next); + +function parseVersion(value) { + const match = value.match( + /^([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/, + ); + + if (!match) { + return null; + } + + return { + major: Number(match[1]), + minor: Number(match[2]), + patch: Number(match[3]), + prerelease: match[4]?.split(".") ?? [], + }; +} + +function formatVersion({ major, minor, patch }, prereleaseParts = []) { + const base = `${major}.${minor}.${patch}`; + return prereleaseParts.length > 0 + ? `${base}-${prereleaseParts.join(".")}` + : base; +} + +function bumpStableBase(version, bumpType) { + switch (bumpType) { + case "major": + return { major: version.major + 1, minor: 0, patch: 0 }; + case "minor": + return { major: version.major, minor: version.minor + 1, patch: 0 }; + case "patch": + return { + major: version.major, + minor: version.minor, + patch: version.patch + 1, + }; + } +} + +function nextStableVersion(version, bumpType) { + if (version.prerelease.length > 0) { + return formatVersion(version); + } + + return formatVersion(bumpStableBase(version, bumpType)); +} + +function nextPrereleaseVersion(version, bumpType, id) { + if (version.prerelease.length === 0) { + return formatVersion(bumpStableBase(version, bumpType), [id, "0"]); + } + + const [currentId] = version.prerelease; + const lastPart = version.prerelease.at(-1); + + if (currentId !== id || !/^[0-9]+$/.test(lastPart)) { + return formatVersion(version, [id, "0"]); + } + + return formatVersion(version, [ + ...version.prerelease.slice(0, -1), + String(Number(lastPart) + 1), + ]); +}