Merge pull request #17 from slate-rehm/dev #9
Workflow file for this run
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 | |
| # Production releases only, and only from master. Work reaches master by being | |
| # merged dev -> master, so anything published here has already been through CI | |
| # on a PR into dev and again on the promotion PR. | |
| # | |
| # This project is not published to any registry. The release artifact is the npm | |
| # tarball attached to the GitHub Release, which users install by URL: | |
| # | |
| # npm i -g https://github.com/slate-rehm/knapper/releases/download/v0.1.0/knapper-0.1.0.tgz | |
| # | |
| # The version bump itself is an ordinary change: bump it in a PR into dev, then | |
| # promote dev -> master. This workflow only tags and releases what is already on | |
| # master, so it never has to write to a protected branch. | |
| # | |
| # Two ways in, both equivalent: | |
| # | |
| # 1. Push a tag — `npm version minor` on a dev PR, promote, then | |
| # `git tag v0.2.0 && git push origin v0.2.0`. | |
| # 2. Run this workflow from the Actions tab — it tags master's current HEAD with | |
| # the version already in package.json and publishes that. | |
| on: | |
| push: | |
| tags: ["v*.*.*"] | |
| workflow_dispatch: | |
| inputs: | |
| dry_run: | |
| description: "Build and validate, but do not publish or tag" | |
| required: false | |
| default: false | |
| type: boolean | |
| concurrency: | |
| group: release | |
| cancel-in-progress: false | |
| permissions: | |
| contents: read | |
| jobs: | |
| # Reuse the exact CI gates rather than a weaker copy of them. | |
| verify: | |
| uses: ./.github/workflows/ci.yml | |
| release: | |
| name: release | |
| runs-on: ubuntu-latest | |
| needs: verify | |
| environment: release | |
| permissions: | |
| contents: write # create the tag and the GitHub Release | |
| outputs: | |
| version: ${{ steps.resolve.outputs.version }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| # Full history so the master-ancestry check below can resolve. | |
| fetch-depth: 0 | |
| ref: ${{ github.event_name == 'workflow_dispatch' && 'master' || github.ref }} | |
| - name: Refuse to release anything that is not on master | |
| run: | | |
| git fetch origin master --depth=1 | |
| if ! git merge-base --is-ancestor HEAD origin/master; then | |
| echo "::error::this commit is not on master; promote dev -> master first" | |
| exit 1 | |
| fi | |
| - uses: actions/setup-node@v7 | |
| with: | |
| node-version: "22" | |
| cache: npm | |
| - run: npm ci | |
| - name: Tag master at the version already in package.json | |
| if: github.event_name == 'workflow_dispatch' | |
| run: | | |
| VERSION=$(node -p "require('./package.json').version") | |
| if git ls-remote --exit-code --tags origin "refs/tags/v$VERSION" >/dev/null 2>&1; then | |
| echo "::error::v$VERSION is already tagged. Bump the version in a PR into dev first." | |
| exit 1 | |
| fi | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git tag "v$VERSION" | |
| if [ "${{ inputs.dry_run }}" != "true" ]; then | |
| # A tag is not a branch push, so this works against protected master. | |
| git push origin "v$VERSION" | |
| else | |
| echo "dry run: not pushing v$VERSION" | |
| fi | |
| - name: Resolve and validate the release version | |
| id: resolve | |
| run: | | |
| VERSION=$(node -p "require('./package.json').version") | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| if [ "${{ github.event_name }}" = "push" ]; then | |
| TAG="${GITHUB_REF#refs/tags/}" | |
| if [ "$TAG" != "v$VERSION" ]; then | |
| echo "::error::tag $TAG does not match package.json version $VERSION" | |
| exit 1 | |
| fi | |
| fi | |
| echo "Releasing v$VERSION" | |
| - name: Manifests agree with package.json | |
| run: node scripts/sync-version.mjs --check | |
| - name: Build | |
| run: npm run build | |
| - name: Degraded-mode smoke test on the build | |
| run: node scripts/ci-smoke.mjs | |
| # The tarball attached below is the only distribution artifact, so packing it | |
| # is a release gate rather than a convenience: if `npm pack` fails, no user | |
| # can install this version. | |
| - name: Pack the release artifact | |
| id: pack | |
| run: | | |
| npm pack --json > pack.json | |
| echo "tarball=$(node -p "require('./pack.json')[0].filename")" >> "$GITHUB_OUTPUT" | |
| - name: Create the GitHub Release | |
| if: ${{ !inputs.dry_run }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| VERSION="${{ steps.resolve.outputs.version }}" | |
| gh release create "v$VERSION" \ | |
| --title "v$VERSION" \ | |
| --generate-notes \ | |
| --verify-tag \ | |
| "${{ steps.pack.outputs.tarball }}" |