Publish #24
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Publish | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| channel: | |
| description: "Release channel, or publish an existing tag/ref" | |
| required: true | |
| type: choice | |
| options: | |
| - next | |
| - finalize | |
| - stable | |
| - existing | |
| default: next | |
| bump: | |
| description: "Version bump (next and stable only)" | |
| required: false | |
| type: choice | |
| options: | |
| - patch | |
| - minor | |
| - major | |
| default: patch | |
| prerelease_tag: | |
| description: "Prerelease tag to finalize (defaults to latest v*-next.* tag)" | |
| required: false | |
| type: string | |
| ref: | |
| description: "Existing release tag/ref to publish (required when channel=existing)" | |
| required: false | |
| type: string | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ inputs.channel }}-${{ inputs.ref || inputs.prerelease_tag || inputs.bump || github.ref }} | |
| cancel-in-progress: false | |
| permissions: {} | |
| jobs: | |
| prepare: | |
| name: Prepare release | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| outputs: | |
| publish_action: ${{ steps.context.outputs.publish_action }} | |
| release_tag: ${{ steps.context.outputs.release_tag }} | |
| version: ${{ steps.context.outputs.version }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| ref: ${{ inputs.channel == 'existing' && inputs.ref || github.ref }} | |
| - name: Configure git | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| if [ "${{ inputs.channel }}" != "existing" ]; then | |
| git checkout -B main origin/main | |
| fi | |
| - name: Validate existing ref input | |
| if: inputs.channel == 'existing' && inputs.ref == '' | |
| run: | | |
| echo "::error::ref is required when channel=existing" | |
| exit 1 | |
| - name: Resolve finalize ref | |
| if: inputs.channel == 'finalize' | |
| id: finalize_ref | |
| env: | |
| PRERELEASE_TAG: ${{ inputs.prerelease_tag }} | |
| run: | | |
| set -euo pipefail | |
| if [ -n "$PRERELEASE_TAG" ]; then | |
| TAG="$PRERELEASE_TAG" | |
| else | |
| TAG=$(git tag --list 'v*-next.*' --sort=-version:refname | head -n 1) | |
| fi | |
| if [ -z "$TAG" ]; then | |
| echo "::error::No prerelease tag found to finalize" | |
| exit 1 | |
| fi | |
| case "$TAG" in | |
| v*) ;; | |
| *) TAG="v$TAG" ;; | |
| esac | |
| echo "tag=$TAG" >> "$GITHUB_OUTPUT" | |
| git checkout --detach "$TAG" | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version-file: package.json | |
| - name: Install dependencies | |
| run: bun install --frozen-lockfile | |
| - name: Bump version, commit, and tag | |
| if: inputs.channel != 'existing' | |
| env: | |
| CHANNEL: ${{ inputs.channel }} | |
| BUMP: ${{ inputs.bump }} | |
| FINALIZE_TAG: ${{ steps.finalize_ref.outputs.tag }} | |
| run: | | |
| set -euo pipefail | |
| if [ "$CHANNEL" = "next" ]; then | |
| bun run release next "$BUMP" | |
| elif [ "$CHANNEL" = "finalize" ]; then | |
| bun run release finalize "$FINALIZE_TAG" | |
| else | |
| bun run release "$BUMP" | |
| fi | |
| - name: Resolve publish context | |
| id: context | |
| run: | | |
| set -euo pipefail | |
| VERSION=$(node -p "require('./package.json').version") | |
| RELEASE_TAG="v${VERSION}" | |
| CHECKOUT_TAG=$(git tag --points-at HEAD | grep -Fx "$RELEASE_TAG" | head -n 1 || true) | |
| if [ -z "$CHECKOUT_TAG" ]; then | |
| echo "::error::Publish checkout must point at $RELEASE_TAG; tags at HEAD are:" | |
| git tag --points-at HEAD || true | |
| exit 1 | |
| fi | |
| if [[ "$VERSION" == *"-next."* ]]; then | |
| PUBLISH_ACTION="publish-next" | |
| else | |
| PUBLISH_ACTION="publish-latest" | |
| fi | |
| echo "Publishing $RELEASE_TAG with $PUBLISH_ACTION" | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "release_tag=$RELEASE_TAG" >> "$GITHUB_OUTPUT" | |
| echo "publish_action=$PUBLISH_ACTION" >> "$GITHUB_OUTPUT" | |
| - name: Ensure GitHub Release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -euo pipefail | |
| VERSION="${{ steps.context.outputs.version }}" | |
| RELEASE_TAG="${{ steps.context.outputs.release_tag }}" | |
| if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then | |
| echo "GitHub Release $RELEASE_TAG already exists" | |
| exit 0 | |
| fi | |
| if [[ "$VERSION" == *"-next."* ]]; then | |
| gh release create "$RELEASE_TAG" --generate-notes --prerelease | |
| else | |
| gh release create "$RELEASE_TAG" --generate-notes | |
| fi | |
| publish: | |
| name: Publish npm package | |
| needs: prepare | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| id-token: write | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| ref: ${{ needs.prepare.outputs.release_tag }} | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: "24" | |
| registry-url: "https://registry.npmjs.org" | |
| - name: Upgrade npm | |
| run: npm install -g npm@latest | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version-file: package.json | |
| - name: Install dependencies | |
| run: bun install --frozen-lockfile | |
| - name: Validate publish checkout | |
| run: | | |
| set -euo pipefail | |
| VERSION=$(node -p "require('./package.json').version") | |
| RELEASE_TAG="v${VERSION}" | |
| CHECKOUT_TAG=$(git tag --points-at HEAD | grep -Fx "$RELEASE_TAG" | head -n 1 || true) | |
| if [ -z "$CHECKOUT_TAG" ]; then | |
| echo "::error::Publish checkout must point at $RELEASE_TAG; tags at HEAD are:" | |
| git tag --points-at HEAD || true | |
| exit 1 | |
| fi | |
| if [[ "$VERSION" == *"-next."* ]]; then | |
| if [ "${{ needs.prepare.outputs.publish_action }}" != "publish-next" ]; then | |
| echo "::error::Cannot publish prerelease $VERSION with ${{ needs.prepare.outputs.publish_action }}" | |
| exit 1 | |
| fi | |
| else | |
| if [ "${{ needs.prepare.outputs.publish_action }}" != "publish-latest" ]; then | |
| echo "::error::Cannot publish stable version $VERSION with ${{ needs.prepare.outputs.publish_action }}" | |
| exit 1 | |
| fi | |
| fi | |
| echo "Publishing $RELEASE_TAG with ${{ needs.prepare.outputs.publish_action }}" | |
| - name: Publish to npm | |
| env: | |
| NPM_CONFIG_PROVENANCE: true | |
| run: | | |
| if [ "${{ needs.prepare.outputs.publish_action }}" = "publish-next" ]; then | |
| bun run publish:next | |
| else | |
| bun run publish:latest | |
| fi |