diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b9c465612..571bf972c 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,6 +1,9 @@ name: Publish on: + push: + tags: + - 'v*' workflow_dispatch: inputs: action: @@ -9,15 +12,11 @@ on: type: choice options: - publish-next - - promote-latest + - publish-latest default: publish-next - version: - description: "Version to promote (promote-latest only; leave empty to use package.json version)" - required: false - type: string concurrency: - group: ${{ github.workflow }} + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: false permissions: @@ -26,7 +25,6 @@ permissions: jobs: publish: - name: ${{ inputs.action == 'promote-latest' && 'Promote to latest' || 'Publish to npm (next)' }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -40,17 +38,26 @@ jobs: - uses: ./.github/actions/setup-bun - - name: Publish to npm (next) - if: inputs.action == 'publish-next' - run: bun run publish:next - env: - NPM_CONFIG_PROVENANCE: true + - name: Detect channel + id: channel + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "action=${{ inputs.action }}" >> $GITHUB_OUTPUT + else + VERSION=$(node -p "require('./apps/cli/package.json').version") + if [[ "$VERSION" == *"-next."* ]]; then + echo "action=publish-next" >> $GITHUB_OUTPUT + else + echo "action=publish-latest" >> $GITHUB_OUTPUT + fi + fi - - name: Promote to latest - if: inputs.action == 'promote-latest' + - name: Publish to npm run: | - if [ -n "${{ inputs.version }}" ]; then - bun run promote:latest "${{ inputs.version }}" + if [ "${{ steps.channel.outputs.action }}" = "publish-next" ]; then + bun run publish:next else - bun run promote:latest + bun run publish fi + env: + NPM_CONFIG_PROVENANCE: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d97b81785..83796b85c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,10 +3,19 @@ name: Release on: workflow_dispatch: inputs: - bump: - description: "Version bump type" + channel: + description: "Release channel" required: true type: choice + options: + - next + - finalize + - stable + default: next + bump: + description: "Version bump (next and stable only)" + required: false + type: choice options: - patch - minor @@ -22,7 +31,7 @@ permissions: jobs: release: - name: Release + name: Release (${{ inputs.channel }}${{ inputs.channel != 'finalize' && format(' {0}', inputs.bump) || '' }}) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -38,11 +47,22 @@ jobs: - uses: ./.github/actions/setup-bun - name: Bump version, commit, and tag - run: bun run release ${{ inputs.bump }} + run: | + if [ "${{ inputs.channel }}" = "next" ]; then + bun run release next ${{ inputs.bump }} + elif [ "${{ inputs.channel }}" = "finalize" ]; then + bun run release finalize + else + bun run release ${{ inputs.bump }} + fi - name: Create GitHub Release run: | VERSION=$(node -p "require('./apps/cli/package.json').version") - gh release create "v${VERSION}" --generate-notes + if [[ "$VERSION" == *"-next."* ]]; then + gh release create "v${VERSION}" --generate-notes --prerelease + else + gh release create "v${VERSION}" --generate-notes + fi env: GH_TOKEN: ${{ github.token }} diff --git a/scripts/release.ts b/scripts/release.ts index 3414cfe22..8ec3417e4 100644 --- a/scripts/release.ts +++ b/scripts/release.ts @@ -3,8 +3,10 @@ * Simple version bump and release script * * Usage: - * bun scripts/release.ts [patch|minor|major] - * bun scripts/release.ts next [patch|minor|major] + * bun scripts/release.ts [patch|minor|major] # stable release + * bun scripts/release.ts next [patch|minor|major] # new pre-release series + * bun scripts/release.ts next # increment pre-release (e.g. next.1 -> next.2) + * bun scripts/release.ts finalize # promote pre-release to stable (e.g. 4.12.0-next.3 -> 4.12.0) * * This script: * 1. Validates we're on the main branch @@ -21,7 +23,7 @@ import { resolve } from 'node:path'; import { $ } from 'bun'; type BumpType = 'patch' | 'minor' | 'major'; -type ReleaseChannel = 'stable' | 'next'; +type ReleaseChannel = 'stable' | 'next' | 'finalize'; const VALID_BUMP_TYPES: BumpType[] = ['patch', 'minor', 'major']; const NEXT_PRERELEASE_TAG = 'next'; @@ -83,6 +85,16 @@ function parseNextPrerelease(version: string): { baseVersion: string; number: nu }; } +function finalizeVersion(currentVersion: string): string { + const parsed = parseNextPrerelease(currentVersion); + if (!parsed) { + throw new Error( + `Version ${currentVersion} is not a pre-release version (expected format: X.Y.Z-next.N)`, + ); + } + return parsed.baseVersion; +} + function bumpNextVersion(currentVersion: string, bumpType?: BumpType): string { const parsedNext = parseNextPrerelease(currentVersion); @@ -104,6 +116,10 @@ function parseArgs(argv: readonly string[]): { channel: ReleaseChannel; bumpType return { channel: 'stable', bumpType: 'patch' }; } + if (first === 'finalize') { + return { channel: 'finalize' }; + } + if (first === NEXT_PRERELEASE_TAG) { if (second === undefined) { return { channel: 'next' }; @@ -135,6 +151,7 @@ async function main() { console.error(`āŒ ${message}`); console.error(' Usage: bun scripts/release.ts [patch|minor|major]'); console.error(` bun scripts/release.ts ${NEXT_PRERELEASE_TAG} [patch|minor|major]`); + console.error(' bun scripts/release.ts finalize'); process.exit(1); } @@ -164,12 +181,16 @@ async function main() { const newVersion = channel === 'next' ? bumpNextVersion(currentVersion, bumpType) - : bumpVersion(currentVersion, bumpType ?? 'patch'); + : channel === 'finalize' + ? finalizeVersion(currentVersion) + : bumpVersion(currentVersion, bumpType ?? 'patch'); const releaseMode = channel === 'next' ? `${NEXT_PRERELEASE_TAG}${bumpType ? ` (${bumpType})` : ' (increment)'}` - : (bumpType ?? 'patch'); + : channel === 'finalize' + ? 'finalize' + : (bumpType ?? 'patch'); console.log(`\nšŸ“¦ Bumping version: ${currentVersion} → ${newVersion} [${releaseMode}]\n`); // Check if tag already exists @@ -214,8 +235,7 @@ async function main() { console.log(`\nāœ… Released v${newVersion}\n`); console.log('Next steps:'); - console.log(` 1. Run: bun run ${channel === 'next' ? 'publish:next' : 'publish'}`); - console.log(' 2. Create GitHub release (optional)'); + console.log(' 1. Trigger the Publish workflow in GitHub Actions'); } main().catch((error) => {