diff --git a/.github/prerelease-config.json b/.github/prerelease-config.json new file mode 100644 index 0000000..14cf864 --- /dev/null +++ b/.github/prerelease-config.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "release-type": "node", + "include-component-in-tag": false, + "versioning": "prerelease", + "prerelease": true, + "prerelease-type": "rc", + "packages": { + ".": {} + } +} diff --git a/.github/prerelease-manifest.json b/.github/prerelease-manifest.json new file mode 100644 index 0000000..256be89 --- /dev/null +++ b/.github/prerelease-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "9.0.1" +} diff --git a/.github/release-config.json b/.github/release-config.json new file mode 100644 index 0000000..0df416e --- /dev/null +++ b/.github/release-config.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "release-type": "node", + "include-component-in-tag": false, + "versioning": "default", + "prerelease": false, + "packages": { + ".": { + "extra-files": [ + { + "type": "json", + "path": ".github/prerelease-manifest.json", + "jsonpath": "$[\".\"]" + } + ] + } + } +} diff --git a/.github/release-manifest.json b/.github/release-manifest.json new file mode 100644 index 0000000..256be89 --- /dev/null +++ b/.github/release-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "9.0.1" +} diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 74d0fbf..d1ec75a 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -75,5 +75,12 @@ jobs: node-version: 26.x registry-url: https://registry.npmjs.org - - name: publish to npm - run: npm publish --provenance + - name: npm stage publish + env: + RELEASE_TAG: ${{ inputs.tag }} + run: | + if [[ "$RELEASE_TAG" == *-rc* ]]; then + npm stage publish --provenance --tag rc + else + npm stage publish --provenance + fi diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 7db34f2..adb4330 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -11,21 +11,43 @@ permissions: # npm-publish is dispatched explicitly below. workflow_dispatch is one of the # few events that always creates a run, even from GITHUB_TOKEN. actions: write + +concurrency: + group: release-please + cancel-in-progress: false + jobs: release-please: runs-on: ubuntu-latest steps: - uses: googleapis/release-please-action@v5 - id: release + id: prerelease + with: + token: ${{ secrets.GITHUB_TOKEN }} + config-file: .github/prerelease-config.json + manifest-file: .github/prerelease-manifest.json + target-branch: ${{ github.ref_name }} + + # Once an RC PR is merged and tagged, replace it with the stable Release + # PR. The stable config also synchronizes the prerelease manifest when + # that stable PR is eventually merged. + - name: create stable Release PR + if: >- + steps.prerelease.outputs.releases_created == 'true' && + contains(steps.prerelease.outputs.tag_name, '-rc') + uses: googleapis/release-please-action@v5 with: - release-type: node + token: ${{ secrets.GITHUB_TOKEN }} + config-file: .github/release-config.json + manifest-file: .github/release-manifest.json + target-branch: ${{ github.ref_name }} - # releases_created is only true on the run that tagged a release, which is - # the run triggered by merging the release PR. + # The prerelease action recognizes merges of both RC and stable Release + # PRs. It creates the corresponding tag and GitHub Release in either case. - name: trigger npm-publish - if: steps.release.outputs.releases_created == 'true' + if: steps.prerelease.outputs.releases_created == 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - TAG: ${{ steps.release.outputs.tag_name }} + TAG: ${{ steps.prerelease.outputs.tag_name }} run: gh workflow run npm-publish.yml --ref "$GITHUB_REF_NAME" -f tag="$TAG" diff --git a/README.md b/README.md index 4692cdd..4344520 100644 --- a/README.md +++ b/README.md @@ -545,12 +545,13 @@ make codestyle-fix ### Releasing -Releases are automated with [release-please](https://github.com/googleapis/release-please#readme): +Releases are automated with [release-please](https://github.com/googleapis/release-please#readme). Versioning comes from conventional commits on `master` (`fix:` patches, `feat:` minor, `!` / `BREAKING CHANGE` major). `chore:` does not bump. There is a **single** shared Release PR; RC and stable take turns on it. -1. Merge conventional-commit-style PRs (`fix:`, `feat:`, etc.) into `master`. -2. `release-please` opens or updates a Release PR with the version bump and changelog. -3. Review and merge the Release PR when ready to ship. -4. `release-please` tags the release and dispatches the `npm-publish` workflow, which validates and publishes the package to npm. +1. Merge `fix:` / `feat:` PRs into `master`. `release-please` opens or updates that PR as a **release candidate** (`X.Y.Z-rc`, then `X.Y.Z-rc.1`, …). `package.json` on the PR is already the version that will be tagged. +2. Merge the RC PR when you want to cut an RC. `release-please` tags a GitHub **prerelease** (`vX.Y.Z-rc`) and dispatches `npm-publish`. Approve the **Publish** environment to run `npm stage publish --provenance --tag rc`; withhold approval to skip staging (the git tag still exists). A maintainer then approves the staged package on npm (2FA) before it is installable. +3. After the RC merge, the shared PR is rewritten as the matching **stable** Release PR (`X.Y.Z`). +4. Merge that stable PR to ship `X.Y.Z` (GitHub Release + `npm stage publish --provenance`, npm dist-tag `latest` after npm approval). Same **Publish** environment gate, then npm 2FA approve. +5. Further `fix:` / `feat:` commits on `master` while the stable PR is still open rewrite it into the **next RC** (`X.Y.Z-rc.1`, …) instead of shipping that stable. Use that when you want another candidate; merge stable with no extra bumping commits when you want to publish `latest`. ## License