Release #13
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: Release | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| ref: | |
| description: 'Commit ref or existing version tag (e.g., abc1234 or v1.2.3)' | |
| required: true | |
| type: string | |
| new_version: | |
| description: 'New version for npm version (e.g., patch, minor, major, 1.2.3). Must be empty when providing an existing tag.' | |
| required: false | |
| type: string | |
| skip_ci_check: | |
| description: 'Skip CI status check' | |
| required: false | |
| type: boolean | |
| default: false | |
| jobs: | |
| preflight: | |
| name: Validate | |
| runs-on: ubuntu-slim | |
| permissions: | |
| actions: read | |
| contents: read | |
| outputs: | |
| ref_is_tag: ${{steps.validation.outputs.ref_is_tag}} | |
| steps: | |
| - name: Validate inputs | |
| id: validation | |
| env: | |
| REF: ${{inputs.ref}} | |
| NEW_VERSION: ${{inputs.new_version}} | |
| run: | | |
| if [[ "$REF" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$ ]]; then | |
| echo "ref_is_tag=true" >> "$GITHUB_OUTPUT" | |
| if [[ -n "$NEW_VERSION" ]]; then | |
| echo "::error::new_version must be empty when an existing tag is provided" | |
| exit 1 | |
| fi | |
| else | |
| echo "ref_is_tag=false" >> "$GITHUB_OUTPUT" | |
| if [[ -z "$NEW_VERSION" ]]; then | |
| echo "::error::new_version is required when a commit ref is provided" | |
| exit 1 | |
| fi | |
| fi | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{inputs.ref}} | |
| fetch-depth: 0 | |
| - name: Verify ref is HEAD of main | |
| if: steps.validation.outputs.ref_is_tag == 'false' | |
| env: | |
| REF: ${{inputs.ref}} | |
| run: | | |
| MAIN_SHA=$(git rev-parse origin/main) | |
| CURRENT_SHA=$(git rev-parse HEAD) | |
| if [[ "$CURRENT_SHA" != "$MAIN_SHA" ]]; then | |
| echo "::error::ref ${REF} (${CURRENT_SHA}) is not the HEAD of main (${MAIN_SHA})" | |
| exit 1 | |
| fi | |
| - name: Verify tag matches package.json version | |
| if: steps.validation.outputs.ref_is_tag == 'true' | |
| env: | |
| REF: ${{inputs.ref}} | |
| run: | | |
| jq --raw-output --exit-status --arg tag "$REF" ' | |
| if (.version == ($tag | ltrimstr("v"))) then | |
| "Package version (\(.version)) matches tag version (\($tag | ltrimstr(\"v\")))" | |
| else | |
| "Package version (\(.version)) does not match tag version (\($tag | ltrimstr(\"v\")))" | halt_error(1) | |
| end' package.json | |
| - name: Check CI status | |
| if: '!inputs.skip_ci_check' | |
| run: | | |
| gh run list --commit "$(git rev-parse HEAD)" --workflow ci.yml --status success --json databaseId \ | |
| | jq --raw-output --exit-status ' | |
| if (length > 0) then | |
| "CI checks have passed" | |
| else | |
| "CI has not completed successfully for this commit" | halt_error(1) | |
| end' | |
| env: | |
| GH_TOKEN: ${{secrets.GITHUB_TOKEN}} | |
| release: | |
| name: Release | |
| runs-on: ubuntu-latest | |
| needs: preflight | |
| environment: npm | |
| permissions: | |
| contents: write | |
| id-token: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{inputs.ref}} | |
| fetch-depth: 0 | |
| - name: Verify ref is still HEAD of main | |
| if: needs.preflight.outputs.ref_is_tag == 'false' | |
| env: | |
| REF: ${{inputs.ref}} | |
| run: | | |
| MAIN_SHA=$(git rev-parse origin/main) | |
| CURRENT_SHA=$(git rev-parse HEAD) | |
| if [[ "$CURRENT_SHA" != "$MAIN_SHA" ]]; then | |
| echo "::error::ref ${REF} (${CURRENT_SHA}) is no longer the HEAD of main (${MAIN_SHA})" | |
| exit 1 | |
| fi | |
| - name: Generate app token | |
| if: needs.preflight.outputs.ref_is_tag == 'false' | |
| id: app-token | |
| uses: actions/create-github-app-token@v1 | |
| with: | |
| app-id: ${{vars.LAUNCHBOT_ID}} | |
| private-key: ${{secrets.LAUNCHBOT_PRIVATE_KEY}} | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: package.json | |
| cache: npm | |
| registry-url: https://registry.npmjs.org | |
| - name: Bump version | |
| if: needs.preflight.outputs.ref_is_tag == 'false' | |
| env: | |
| NEW_VERSION: ${{inputs.new_version}} | |
| run: npm version "$NEW_VERSION" --no-git-tag-version | |
| - name: Push version commit | |
| if: needs.preflight.outputs.ref_is_tag == 'false' | |
| id: push-commit | |
| env: | |
| GH_TOKEN: ${{steps.app-token.outputs.token}} | |
| run: | | |
| RELEASE_TAG="v$(jq --raw-output .version package.json)" | |
| PARENT_SHA=$(git rev-parse HEAD) | |
| jq --null-input \ | |
| --arg repo "$GITHUB_REPOSITORY" \ | |
| --arg parentSha "$PARENT_SHA" \ | |
| --arg headline "$RELEASE_TAG" \ | |
| --rawfile pkgContent package.json \ | |
| --rawfile lockContent package-lock.json \ | |
| '{ | |
| query: "mutation($input: CreateCommitOnBranchInput!) { createCommitOnBranch(input: $input) { commit { oid } } }", | |
| variables: { | |
| input: { | |
| branch: { repositoryNameWithOwner: $repo, branchName: "main" }, | |
| message: { headline: $headline }, | |
| expectedHeadOid: $parentSha, | |
| fileChanges: { | |
| additions: [ | |
| { path: "package.json", contents: ($pkgContent | @base64) }, | |
| { path: "package-lock.json", contents: ($lockContent | @base64) } | |
| ] | |
| } | |
| } | |
| } | |
| }' > /tmp/request.json | |
| COMMIT_SHA=$(gh api graphql --input /tmp/request.json \ | |
| --jq '.data.createCommitOnBranch.commit.oid') | |
| echo "release_tag=$RELEASE_TAG" >> "$GITHUB_OUTPUT" | |
| echo "version_commit_sha=$COMMIT_SHA" >> "$GITHUB_OUTPUT" | |
| - name: Create version tag | |
| if: needs.preflight.outputs.ref_is_tag == 'false' | |
| run: | | |
| gh api "repos/$GITHUB_REPOSITORY/git/refs" \ | |
| -f ref="refs/tags/${{steps.push-commit.outputs.release_tag}}" \ | |
| -f sha="${{steps.push-commit.outputs.version_commit_sha}}" | |
| env: | |
| GH_TOKEN: ${{secrets.GITHUB_TOKEN}} | |
| - name: Publish to npm with provenance | |
| run: npm publish --provenance | |
| - name: Create GitHub Release | |
| run: | | |
| RELEASE_TAG="v$(jq --raw-output .version package.json)" | |
| gh release create "$RELEASE_TAG" \ | |
| --title "$RELEASE_TAG" \ | |
| --draft \ | |
| --generate-notes | |
| env: | |
| GH_TOKEN: ${{secrets.GITHUB_TOKEN}} |