From 2493ee721b6cf8e83298a68726a9f9b281268ba4 Mon Sep 17 00:00:00 2001 From: Yuriy Kirillov Date: Wed, 12 Aug 2026 11:52:28 +0200 Subject: [PATCH 1/5] feat: add Release Please workflow --- .github/workflows/release-please.yml | 61 ++++++++++++++++++++++++++++ .pre-commit-config.yaml | 2 +- .pre-commit-config.yaml.example | 2 +- .release-please-manifest.json | 3 ++ AGENTS.md | 39 +++++------------- CHANGELOG.md | 2 - README.md | 38 ++++++----------- release-please-config.json | 25 ++++++++++++ test/test_release_please_config.py | 39 ++++++++++++++++++ 9 files changed, 152 insertions(+), 59 deletions(-) create mode 100644 .github/workflows/release-please.yml create mode 100644 .release-please-manifest.json create mode 100644 release-please-config.json create mode 100644 test/test_release_please_config.py diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 0000000..0dc390b --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,61 @@ +name: Release Please +on: + push: + branches: [main] +permissions: + contents: write + issues: write + pull-requests: write +jobs: + release: + runs-on: ubuntu-latest + outputs: + release_created: ${{ steps.release.outputs.release_created }} + tag_name: ${{ steps.release.outputs.tag_name }} + major: ${{ steps.release.outputs.major }} + minor: ${{ steps.release.outputs.minor }} + steps: + # GITHUB_TOKEN-created pull requests do not trigger required CI workflows. + - uses: actions/create-github-app-token@v3 + id: app-token + with: + app-id: ${{ vars.RELEASE_PLEASE_APP_ID }} + private-key: ${{ secrets.RELEASE_PLEASE_APP_PRIVATE_KEY }} + - uses: googleapis/release-please-action@v5 + id: release + with: + token: ${{ steps.app-token.outputs.token }} + + publish: + needs: release + if: needs.release.outputs.release_created == 'true' + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v7 + with: + ref: ${{ needs.release.outputs.tag_name }} + fetch-depth: 0 + - uses: ruby/setup-ruby@v1 + with: + bundler-cache: false + - name: Publish gem + run: | + gem build baseline.gemspec + gem push rubykatzen-baseline-*.gem + env: + GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }} + - name: Update floating tags + env: + TAG_NAME: ${{ needs.release.outputs.tag_name }} + MAJOR: ${{ needs.release.outputs.major }} + MINOR: ${{ needs.release.outputs.minor }} + run: | + git config user.name github-actions[bot] + git config user.email 41898282+github-actions[bot]@users.noreply.github.com + target=$(git rev-list -n 1 "$TAG_NAME") + for tag in "v$MAJOR" "v$MAJOR.$MINOR"; do + git tag --force --annotate "$tag" "$target" --message "$tag" + git push origin "refs/tags/$tag" --force + done diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 913e96a..72ba381 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/rubykatzen/baseline - rev: v0.9.0 + rev: v0.9.0 # x-release-please-version hooks: - id: yamllint - id: pymarkdown diff --git a/.pre-commit-config.yaml.example b/.pre-commit-config.yaml.example index 74458e1..79cd3b9 100644 --- a/.pre-commit-config.yaml.example +++ b/.pre-commit-config.yaml.example @@ -1,6 +1,6 @@ repos: - repo: https://github.com/rubykatzen/baseline - rev: v0.9.0 + rev: v0.9.0 # x-release-please-version hooks: - id: yamllint - id: pymarkdown diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..76d5538 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.9.0" +} diff --git a/AGENTS.md b/AGENTS.md index bf80aeb..8992947 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -74,8 +74,8 @@ tools to already be installed in the developer environment. - `.github/workflows/embedder-shared.yml` — reusable workflow exported for required content validation - `.github/workflows/commit.yml` — baseline caller for its Conventional Commit pull request title check - `.github/workflows/lint.yml` — baseline self-lint (uses local `./` references, not `@vX`) -- `.github/workflows/prepare-release.yml` — dispatch workflow: calls `rubykatzen/releaser` to prepare `release/vX.Y.Z` -- `.github/workflows/publish-release.yml` — publishes merged `release/*` PRs via `rubykatzen/releaser` +- `.github/workflows/release-please.yml` — maintains the release PR and publishes merged releases +- `.github/workflows/prepare-release.yml` and `.github/workflows/publish-release.yml` — legacy release path kept until the first Release Please release is validated - `.github/workflows/notify-telegram-unreleased.yml` — baseline's own caller (delegates to `rubykatzen/releaser`) - `.pre-commit-hooks.yaml` — hook definitions for pre-commit - `.pre-commit-config.yaml.example` — example for consuming repos (all hooks, prune as needed) @@ -120,34 +120,15 @@ validated, not a pinned release. ## Cutting Releases -Use the [rubykatzen/releaser](https://github.com/rubykatzen/releaser) CLI. -Run from inside this repository: +Release Please maintains a release pull request from Conventional Commits on +`main`. Review the proposed version, changelog, and file updates, then merge the +pull request to release. The workflow creates the version tag and GitHub +Release, publishes the gem, and updates the floating major and minor tags. -```bash -releaser patch # or: releaser minor / releaser major -``` - -The CLI does the following automatically: - -1. Fetches `origin/main` and finds the latest SemVer tag -2. Verifies CI is green on `origin/main` -3. Calculates the next version -4. Dispatches `.github/workflows/prepare-release.yml` with the computed version - and `base_sha` -5. Watches the workflow run -6. Opens a PR from `release/vX.Y.Z` → `main` and enables auto-merge - -`.github/workflows/publish-release.yml` fires automatically once the PR merges -and creates the annotated tag and GitHub release. - -To check readiness without triggering a release: - -```bash -releaser status -releaser patch --dry-run -``` - -If the CLI is not installed: `brew tap rubykatzen/tap && brew install releaser`. +The workflow requires a GitHub App ID in the `RELEASE_PLEASE_APP_ID` repository +variable and its private key in the `RELEASE_PLEASE_APP_PRIVATE_KEY` repository +secret. The App needs read and write access to contents, issues, and pull +requests. Its token ensures release pull requests trigger required CI. ## Linter Selection diff --git a/CHANGELOG.md b/CHANGELOG.md index dc67737..4184ad6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,5 @@ # Changelog -## [Unreleased] - ## [v0.9.0] - 2026-08-12 - chore(deps): bump rubocop-rails from 2.35.4 to 2.36.0 (#122) diff --git a/README.md b/README.md index f5ede50..5893bc2 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ Include only the hooks relevant to your stack: ```yaml repos: - repo: https://github.com/rubykatzen/baseline - rev: v0.9.0 + rev: v0.9.0 # x-release-please-version hooks: - id: yamllint - id: pymarkdown @@ -346,31 +346,17 @@ Bundler-resolved toolchain. ## Releases -Baseline releases are cut with the -[rubykatzen/releaser](https://github.com/rubykatzen/releaser) CLI. Run from -inside this repository: - -```bash -releaser patch # or: releaser minor / releaser major -``` - -The CLI verifies that CI is green on `origin/main`, calculates the next version, -dispatches `prepare-release.yml`, watches it run, then opens a `release/vX.Y.Z` -PR and enables auto-merge. `publish-release.yml` fires automatically once the PR -merges and creates the tag and GitHub release. - -Check release readiness without triggering anything: - -```bash -releaser status -releaser patch --dry-run -``` - -Install the CLI: - -```bash -brew tap rubykatzen/tap && brew install releaser -``` +Release Please maintains a release pull request from Conventional Commits on +`main`. Review its proposed version, changelog, and file updates, then merge the +pull request to release. The merge creates the version tag and GitHub Release, +publishes `rubykatzen-baseline` to RubyGems, and updates the floating major and +minor tags used by consumers. + +The release workflow authenticates as a GitHub App so its pull requests trigger +required CI. Configure the app ID as the `RELEASE_PLEASE_APP_ID` repository +variable and its private key as the `RELEASE_PLEASE_APP_PRIVATE_KEY` repository +secret. The App needs read and write access to contents, issues, and pull +requests. ## Linters: defaults & overrides diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..3207788 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "release-type": "ruby", + "include-component-in-tag": false, + "packages": { + ".": { + "package-name": "rubykatzen-baseline", + "version-file": "lib/baseline/version.rb", + "extra-files": [ + { + "type": "generic", + "path": ".pre-commit-config.yaml" + }, + { + "type": "generic", + "path": ".pre-commit-config.yaml.example" + }, + { + "type": "generic", + "path": "README.md" + } + ] + } + } +} diff --git a/test/test_release_please_config.py b/test/test_release_please_config.py new file mode 100644 index 0000000..0d6df6a --- /dev/null +++ b/test/test_release_please_config.py @@ -0,0 +1,39 @@ +import json +import re +import unittest +from pathlib import Path + +ROOT = Path(__file__).parent.parent + + +class ReleasePleaseConfigTest(unittest.TestCase): + def test_manifest_matches_packaged_version(self): + manifest = self.load_json(".release-please-manifest.json") + version_file = (ROOT / "lib/baseline/version.rb").read_text() + gemfile_lock = (ROOT / "Gemfile.lock").read_text() + version = re.search(r'VERSION = "([^"]+)"', version_file).group(1) + + self.assertEqual(manifest, {".": version}) + self.assertIn(f"rubykatzen-baseline ({version})", gemfile_lock) + + def test_extra_files_have_version_annotations(self): + config = self.load_json("release-please-config.json") + version = self.load_json(".release-please-manifest.json")["."] + package = config["packages"]["."] + + self.assertEqual(config["release-type"], "ruby") + self.assertFalse(config["include-component-in-tag"]) + self.assertEqual(package["package-name"], "rubykatzen-baseline") + self.assertEqual(package["version-file"], "lib/baseline/version.rb") + + for extra_file in package["extra-files"]: + self.assertEqual(extra_file["type"], "generic") + content = (ROOT / extra_file["path"]).read_text() + self.assertIn(f"v{version} # x-release-please-version", content) + + def load_json(self, path): + return json.loads((ROOT / path).read_text()) + + +if __name__ == "__main__": + unittest.main() From 3557bb00badeab134ed38d83cedf48c03190e7b2 Mon Sep 17 00:00:00 2001 From: Yuriy Kirillov Date: Wed, 12 Aug 2026 12:04:58 +0200 Subject: [PATCH 2/5] chore: remove legacy release workflows --- .../actions/bump-pre-commit-rev/action.yml | 21 -------- .github/workflows/prepare-release.yml | 51 ------------------- .github/workflows/publish-release.yml | 27 ---------- AGENTS.md | 1 - 4 files changed, 100 deletions(-) delete mode 100644 .github/actions/bump-pre-commit-rev/action.yml delete mode 100644 .github/workflows/prepare-release.yml delete mode 100644 .github/workflows/publish-release.yml diff --git a/.github/actions/bump-pre-commit-rev/action.yml b/.github/actions/bump-pre-commit-rev/action.yml deleted file mode 100644 index 8658706..0000000 --- a/.github/actions/bump-pre-commit-rev/action.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Bump pre-commit rev -description: Update rev in .pre-commit-config.yaml to the release version and commit. -inputs: - version: - description: Release version without v prefix. - required: true -runs: - using: composite - steps: - - name: Bump and commit - shell: bash - run: | - set -euo pipefail - sed -i "s/rev: v[0-9].*/rev: v${{ inputs.version }}/" \ - .pre-commit-config.yaml \ - .pre-commit-config.yaml.example \ - README.md - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add .pre-commit-config.yaml .pre-commit-config.yaml.example README.md - git diff --cached --quiet || git commit -m "chore: bump pre-commit rev to v${{ inputs.version }}" diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml deleted file mode 100644 index d4ac6a3..0000000 --- a/.github/workflows/prepare-release.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: Prepare release branch -on: - workflow_dispatch: - inputs: - version: - description: New version without v prefix (e.g. 0.1.0) - required: true - type: string - base_sha: - description: Expected HEAD SHA of main - required: true - type: string -concurrency: - group: prepare-release-baseline -jobs: - prepare: - runs-on: ubuntu-latest - permissions: - contents: write - actions: read - steps: - - uses: actions/checkout@v7 - with: - fetch-depth: 0 - - uses: ruby/setup-ruby@v1 - with: - bundler-cache: false - - uses: rubykatzen/releaser/.github/actions/verify-release@v0.5 - with: - base_sha: ${{ inputs.base_sha }} - ci_workflow: lint / lint - - uses: rubykatzen/releaser/.github/actions/generate-notes@v0.5 - id: notes - with: - version: ${{ inputs.version }} - - uses: rubykatzen/releaser/.github/actions/prepare-release-branch@v0.5 - with: - version: ${{ inputs.version }} - - uses: rubykatzen/releaser/.github/actions/update-changelog@v0.5 - with: - version: ${{ inputs.version }} - notes: ${{ steps.notes.outputs.notes }} - - uses: rubykatzen/releaser/.github/actions/bump-ruby-gem-version@v0.5 - with: - version: ${{ inputs.version }} - - uses: ./.github/actions/bump-pre-commit-rev - with: - version: ${{ inputs.version }} - - uses: rubykatzen/releaser/.github/actions/push-release-branch@v0.5 - with: - version: ${{ inputs.version }} diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml deleted file mode 100644 index 7978873..0000000 --- a/.github/workflows/publish-release.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Publish release -on: - pull_request: - types: [closed] - branches: [main] -jobs: - publish: - if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'release/') - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - uses: actions/checkout@v7 - with: - ref: main - fetch-depth: 0 - - uses: rubykatzen/releaser/.github/actions/read-release-data@v0.5 - id: meta - with: - branch: ${{ github.event.pull_request.head.ref }} - - uses: rubykatzen/releaser/.github/actions/create-release@v0.5 - with: - version: ${{ steps.meta.outputs.version }} - notes: ${{ steps.meta.outputs.notes }} - - uses: rubykatzen/releaser/.github/actions/publish-rubygems@v0.5 - with: - api-key: ${{ secrets.RUBYGEMS_API_KEY }} diff --git a/AGENTS.md b/AGENTS.md index 8992947..e706c08 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -75,7 +75,6 @@ tools to already be installed in the developer environment. - `.github/workflows/commit.yml` — baseline caller for its Conventional Commit pull request title check - `.github/workflows/lint.yml` — baseline self-lint (uses local `./` references, not `@vX`) - `.github/workflows/release-please.yml` — maintains the release PR and publishes merged releases -- `.github/workflows/prepare-release.yml` and `.github/workflows/publish-release.yml` — legacy release path kept until the first Release Please release is validated - `.github/workflows/notify-telegram-unreleased.yml` — baseline's own caller (delegates to `rubykatzen/releaser`) - `.pre-commit-hooks.yaml` — hook definitions for pre-commit - `.pre-commit-config.yaml.example` — example for consuming repos (all hooks, prune as needed) From f9a01b33e627983b2f383a3f15d46b73c89b8787 Mon Sep 17 00:00:00 2001 From: Yuriy Kirillov Date: Wed, 12 Aug 2026 12:33:49 +0200 Subject: [PATCH 3/5] test: remove Release Please config test --- test/test_release_please_config.py | 39 ------------------------------ 1 file changed, 39 deletions(-) delete mode 100644 test/test_release_please_config.py diff --git a/test/test_release_please_config.py b/test/test_release_please_config.py deleted file mode 100644 index 0d6df6a..0000000 --- a/test/test_release_please_config.py +++ /dev/null @@ -1,39 +0,0 @@ -import json -import re -import unittest -from pathlib import Path - -ROOT = Path(__file__).parent.parent - - -class ReleasePleaseConfigTest(unittest.TestCase): - def test_manifest_matches_packaged_version(self): - manifest = self.load_json(".release-please-manifest.json") - version_file = (ROOT / "lib/baseline/version.rb").read_text() - gemfile_lock = (ROOT / "Gemfile.lock").read_text() - version = re.search(r'VERSION = "([^"]+)"', version_file).group(1) - - self.assertEqual(manifest, {".": version}) - self.assertIn(f"rubykatzen-baseline ({version})", gemfile_lock) - - def test_extra_files_have_version_annotations(self): - config = self.load_json("release-please-config.json") - version = self.load_json(".release-please-manifest.json")["."] - package = config["packages"]["."] - - self.assertEqual(config["release-type"], "ruby") - self.assertFalse(config["include-component-in-tag"]) - self.assertEqual(package["package-name"], "rubykatzen-baseline") - self.assertEqual(package["version-file"], "lib/baseline/version.rb") - - for extra_file in package["extra-files"]: - self.assertEqual(extra_file["type"], "generic") - content = (ROOT / extra_file["path"]).read_text() - self.assertIn(f"v{version} # x-release-please-version", content) - - def load_json(self, path): - return json.loads((ROOT / path).read_text()) - - -if __name__ == "__main__": - unittest.main() From 5542de06f4e6aaff4494f960d09093723ca6b007 Mon Sep 17 00:00:00 2001 From: Yuriy Kirillov Date: Wed, 12 Aug 2026 13:12:28 +0200 Subject: [PATCH 4/5] ci: use PAT for Release Please --- .github/workflows/release-please.yml | 7 +------ AGENTS.md | 8 ++++---- README.md | 9 ++++----- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 0dc390b..52653bd 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -16,15 +16,10 @@ jobs: minor: ${{ steps.release.outputs.minor }} steps: # GITHUB_TOKEN-created pull requests do not trigger required CI workflows. - - uses: actions/create-github-app-token@v3 - id: app-token - with: - app-id: ${{ vars.RELEASE_PLEASE_APP_ID }} - private-key: ${{ secrets.RELEASE_PLEASE_APP_PRIVATE_KEY }} - uses: googleapis/release-please-action@v5 id: release with: - token: ${{ steps.app-token.outputs.token }} + token: ${{ secrets.RELEASE_PLEASE_TOKEN }} publish: needs: release diff --git a/AGENTS.md b/AGENTS.md index e706c08..d4680bc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -124,10 +124,10 @@ Release Please maintains a release pull request from Conventional Commits on pull request to release. The workflow creates the version tag and GitHub Release, publishes the gem, and updates the floating major and minor tags. -The workflow requires a GitHub App ID in the `RELEASE_PLEASE_APP_ID` repository -variable and its private key in the `RELEASE_PLEASE_APP_PRIVATE_KEY` repository -secret. The App needs read and write access to contents, issues, and pull -requests. Its token ensures release pull requests trigger required CI. +The workflow requires a fine-grained personal access token in the +`RELEASE_PLEASE_TOKEN` repository secret, limited to this repository with read +and write access to contents, issues, and pull requests. The token ensures +release pull requests trigger required CI. ## Linter Selection diff --git a/README.md b/README.md index 5893bc2..c87a9b9 100644 --- a/README.md +++ b/README.md @@ -352,11 +352,10 @@ pull request to release. The merge creates the version tag and GitHub Release, publishes `rubykatzen-baseline` to RubyGems, and updates the floating major and minor tags used by consumers. -The release workflow authenticates as a GitHub App so its pull requests trigger -required CI. Configure the app ID as the `RELEASE_PLEASE_APP_ID` repository -variable and its private key as the `RELEASE_PLEASE_APP_PRIVATE_KEY` repository -secret. The App needs read and write access to contents, issues, and pull -requests. +The release workflow uses the `RELEASE_PLEASE_TOKEN` repository secret so its +pull requests trigger required CI. Use a fine-grained personal access token +limited to this repository with read and write access to contents, issues, and +pull requests. ## Linters: defaults & overrides From 23a3e8a6424940cf3143ceeb5fc6b4ac729278e4 Mon Sep 17 00:00:00 2001 From: Yuriy Kirillov Date: Wed, 12 Aug 2026 13:14:44 +0200 Subject: [PATCH 5/5] ci: rename release token secret --- .github/workflows/release-please.yml | 2 +- AGENTS.md | 2 +- README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 52653bd..f40540f 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -19,7 +19,7 @@ jobs: - uses: googleapis/release-please-action@v5 id: release with: - token: ${{ secrets.RELEASE_PLEASE_TOKEN }} + token: ${{ secrets.RELEASE_TOKEN }} publish: needs: release diff --git a/AGENTS.md b/AGENTS.md index d4680bc..c4cce08 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -125,7 +125,7 @@ pull request to release. The workflow creates the version tag and GitHub Release, publishes the gem, and updates the floating major and minor tags. The workflow requires a fine-grained personal access token in the -`RELEASE_PLEASE_TOKEN` repository secret, limited to this repository with read +`RELEASE_TOKEN` repository secret, limited to this repository with read and write access to contents, issues, and pull requests. The token ensures release pull requests trigger required CI. diff --git a/README.md b/README.md index c87a9b9..2b60213 100644 --- a/README.md +++ b/README.md @@ -352,7 +352,7 @@ pull request to release. The merge creates the version tag and GitHub Release, publishes `rubykatzen-baseline` to RubyGems, and updates the floating major and minor tags used by consumers. -The release workflow uses the `RELEASE_PLEASE_TOKEN` repository secret so its +The release workflow uses the `RELEASE_TOKEN` repository secret so its pull requests trigger required CI. Use a fine-grained personal access token limited to this repository with read and write access to contents, issues, and pull requests.