From a2d63d3999f0af676df3776aee7484f214a72b11 Mon Sep 17 00:00:00 2001 From: Timur Olzhabayev Date: Fri, 8 May 2026 11:48:34 +0200 Subject: [PATCH] ci: migrate releases to release-please MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous release flow (do-release{,-mcp}.yml → push commit + tag to main from a workflow) is blocked by the org-level "Public repo required PRs" ruleset. Replace the bespoke machinery with release-please, matching the convention already used in grafana/plugin-ci-workflows and grafana/plugin-actions. - Add release-please-config.json with two packages (root → plugin-validator, mcp-package → mcp); bootstrap-sha pins the root changelog start to the v0.41.0 commit so the first release PR doesn't enumerate all of history. - Add .release-please-manifest.json with current versions. - Add .github/workflows/release-please.yml; on push to main it opens / updates per-package release PRs and on merge creates the tag + GitHub Release with the changelog body. - Update release.yml to trigger on plugin-validator/v[0-9]* tags and pass RELEASE_VERSION (semver only) into goreleaser. - Update .goreleaser.yaml archives.name_template to use RELEASE_VERSION; needed because the new tag contains a "/" which would otherwise pollute the default {{ .Version }} archive name. - Update package.json binWrapper urlTemplate for the new download path. - release-mcp.yml + .goreleaser.mcp.yaml unchanged: release-please produces mcp/v* tags that already match the existing trigger and MCP_VERSION extraction. - GoReleaser default release.mode is keep-existing, which preserves the body release-please writes on the GitHub Release. - Delete do-release.yml and do-release-mcp.yml. - Rewrite HOW_TO_RELEASE.md for the conventional-commit-driven flow. Supersedes #570. --- .github/workflows/do-release-mcp.yml | 69 ---------------------------- .github/workflows/do-release.yml | 59 ------------------------ .github/workflows/release-please.yml | 53 +++++++++++++++++++++ .github/workflows/release.yml | 6 ++- .goreleaser.yaml | 5 +- .release-please-manifest.json | 4 ++ HOW_TO_RELEASE.md | 54 +++++++++++----------- package.json | 2 +- release-please-config.json | 32 +++++++++++++ 9 files changed, 124 insertions(+), 160 deletions(-) delete mode 100644 .github/workflows/do-release-mcp.yml delete mode 100644 .github/workflows/do-release.yml create mode 100644 .github/workflows/release-please.yml create mode 100644 .release-please-manifest.json create mode 100644 release-please-config.json diff --git a/.github/workflows/do-release-mcp.yml b/.github/workflows/do-release-mcp.yml deleted file mode 100644 index 019f51ed..00000000 --- a/.github/workflows/do-release-mcp.yml +++ /dev/null @@ -1,69 +0,0 @@ -name: Release MCP Server - -on: - workflow_dispatch: - inputs: - version: - description: "Semver type of new version (major / minor / patch)" - required: true - type: choice - options: - - patch - - minor - - major - -jobs: - release-mcp: - runs-on: ubuntu-latest - permissions: - contents: read - id-token: write - - steps: - - id: get-secrets - uses: grafana/shared-workflows/actions/get-vault-secrets@a37de51f3d713a30a9e4b21bcdfbd38170020593 # v1.3.0 - with: - repo_secrets: | - GITHUB_APP_ID=plugins-platform-bot-app:app_id - GITHUB_APP_PRIVATE_KEY=plugins-platform-bot-app:app_pem - export_env: false - - - name: Generate token - id: generate_token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 - with: - app-id: ${{ fromJSON(steps.get-secrets.outputs.secrets).GITHUB_APP_ID }} - private-key: ${{ fromJSON(steps.get-secrets.outputs.secrets).GITHUB_APP_PRIVATE_KEY }} - permission-contents: write - - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - token: ${{ steps.generate_token.outputs.token }} - persist-credentials: true - - - name: Setup Git - run: | - git config user.name 'grafana-plugins-platform-bot[bot]' - git config user.email '144369747+grafana-plugins-platform-bot[bot]@users.noreply.github.com' - - - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 - with: - node-version: "24" - - - name: Bump MCP version and create tag - run: | - cd mcp-package - npm version ${INPUT_VERSION} --no-git-tag-version - NEW_VERSION=$(jq -r .version package.json) - cd .. - - git add mcp-package/package.json - git commit -m "chore(mcp): release v${NEW_VERSION}" - git tag "mcp/v${NEW_VERSION}" - env: - INPUT_VERSION: ${{ github.event.inputs.version }} - - - name: Push changes and tag - run: | - git push origin main - git push origin --tags diff --git a/.github/workflows/do-release.yml b/.github/workflows/do-release.yml deleted file mode 100644 index e31cfd88..00000000 --- a/.github/workflows/do-release.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Bump Version and release - -on: - workflow_dispatch: - inputs: - version: - description: "Semver type of new version (major / minor / patch)" - required: true - type: choice - options: - - patch - - minor - - major - -jobs: - bump-version: - runs-on: ubuntu-arm64-small - permissions: - contents: read - id-token: write - - steps: - - id: get-secrets - uses: grafana/shared-workflows/actions/get-vault-secrets@a37de51f3d713a30a9e4b21bcdfbd38170020593 # v1.3.0 - with: - repo_secrets: | - GITHUB_APP_ID=plugins-platform-bot-app:app_id - GITHUB_APP_PRIVATE_KEY=plugins-platform-bot-app:app_pem - export_env: false - - - name: Generate token - id: generate_token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 - with: - app-id: ${{ fromJSON(steps.get-secrets.outputs.secrets).GITHUB_APP_ID }} - private-key: ${{ fromJSON(steps.get-secrets.outputs.secrets).GITHUB_APP_PRIVATE_KEY }} - permission-contents: write - - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - token: ${{ steps.generate_token.outputs.token }} - persist-credentials: true - - - name: Setup Git - run: | - git config user.name 'grafana-plugins-platform-bot[bot]' - git config user.email '144369747+grafana-plugins-platform-bot[bot]@users.noreply.github.com' - - - name: bump version - run: npm version ${INPUT_VERSION} - env: - INPUT_VERSION: ${{ github.event.inputs.version }} - GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} - - - name: Push latest version - run: git push origin main - - - name: Push tags - run: git push origin --tags diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 00000000..b2dae4e6 --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,53 @@ +# Creates release PRs, tags, and GitHub Releases for plugin-validator and the +# MCP server using release-please. Conventional commits on `main` drive the +# version bumps and changelog content. The post-tag publish workflows +# (release.yml, release-mcp.yml) handle GoReleaser builds, npm publishing, and +# Docker image push. +name: Release Please + +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + +jobs: + release-please: + runs-on: ubuntu-arm64-small + permissions: + contents: write # create releases and tags + pull-requests: write # create release PRs + id-token: write + steps: + - name: Get secrets from Vault + id: get-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@a37de51f3d713a30a9e4b21bcdfbd38170020593 # v1.3.0 + with: + repo_secrets: | + GITHUB_APP_ID=plugins-platform-bot-app:app_id + GITHUB_APP_PRIVATE_KEY=plugins-platform-bot-app:app_pem + export_env: false + + - name: Generate GitHub token + id: generate-github-token + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + with: + app-id: ${{ fromJSON(steps.get-secrets.outputs.secrets).GITHUB_APP_ID }} + private-key: ${{ fromJSON(steps.get-secrets.outputs.secrets).GITHUB_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 + with: + egress-policy: audit + + - uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4.4.1 + id: release + with: + config-file: release-please-config.json + manifest-file: .release-please-manifest.json + target-branch: ${{ github.ref_name }} + token: ${{ steps.generate-github-token.outputs.token }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2f47ccdb..3e45deb3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,7 +3,7 @@ name: Create release and publish to github, npm and docker hub on: # zizmor: ignore[cache-poisoning] push: tags: - - 'v[0-9]*' + - 'plugin-validator/v[0-9]*' jobs: release-to-github: @@ -39,6 +39,9 @@ jobs: private-key: ${{ fromJSON(steps.get-secrets.outputs.secrets).GITHUB_APP_PRIVATE_KEY }} permission-contents: write + - name: Extract release version + run: echo "RELEASE_VERSION=${GITHUB_REF_NAME#plugin-validator/v}" >> "$GITHUB_ENV" + - uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0 with: distribution: goreleaser @@ -46,6 +49,7 @@ jobs: args: release --clean env: GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} + RELEASE_VERSION: ${{ env.RELEASE_VERSION }} release-to-npm: runs-on: ubuntu-latest diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 10bf858f..fc3182dd 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -19,10 +19,11 @@ builds: - CGO_ENABLED=0 archives: - - formats: [ tar.gz ] + - formats: [tar.gz] + name_template: "plugin-validator_{{ .Env.RELEASE_VERSION }}_{{ .Os }}_{{ .Arch }}" format_overrides: - goos: windows - formats: [ zip ] + formats: [zip] checksum: name_template: "checksums.txt" diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 00000000..91c1df6f --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,4 @@ +{ + ".": "0.41.0", + "mcp-package": "0.1.2" +} diff --git a/HOW_TO_RELEASE.md b/HOW_TO_RELEASE.md index cadaed34..d8ab758a 100644 --- a/HOW_TO_RELEASE.md +++ b/HOW_TO_RELEASE.md @@ -1,49 +1,47 @@ # How to release a new version -**IMPORTANT:** Do not push release tags manually. The entire release process is automated via GitHub Actions. +**IMPORTANT:** Releases are driven by [release-please](https://github.com/googleapis/release-please-action) on top of [conventional commits](https://www.conventionalcommits.org/). You don't bump versions or push tags manually. -## Plugin Validator +## How it works -Use the ["Bump Version and release"](https://github.com/grafana/plugin-validator/actions/workflows/do-release.yml) workflow: +The [Release Please](https://github.com/grafana/plugin-validator/actions/workflows/release-please.yml) workflow runs on every push to `main`. It looks at the conventional commits since the last release and maintains an open release PR per package (one for the validator, one for the MCP server). -1. Click **"Run workflow"** -2. Keep branch: `main` -3. Select the semver type: `patch`, `minor`, or `major` -4. Click **"Run workflow"** +When you merge a release PR: -### Which semver type to choose? +1. release-please creates the version tag and a GitHub Release with the changelog body. +2. The tag push triggers the corresponding publish workflow. -- **patch** -- bug fixes, dependency updates, documentation changes -- **minor** -- new features (e.g. adding a new validator) -- **major** -- breaking changes to the CLI interface, output format, or public API +| Package | Tag format | Triggered workflow | Publishes to | +| --- | --- | --- | --- | +| Plugin Validator (root) | `plugin-validator/v*` | [Create release and publish](https://github.com/grafana/plugin-validator/actions/workflows/release.yml) | GitHub Release assets (GoReleaser), npm `@grafana/plugin-validator`, Docker `grafana/plugin-validator-cli` | +| MCP Server | `mcp/v*` | [Create MCP release and publish](https://github.com/grafana/plugin-validator/actions/workflows/release-mcp.yml) | GitHub Release assets (GoReleaser), npm `@grafana/plugin-validator-mcp` | -### What happens after the workflow runs? +GoReleaser runs after release-please with the default `release.mode: keep-existing`, so it preserves the changelog body release-please put on the release and only attaches the binary assets. -The workflow bumps the version in `package.json`, commits to `main`, and pushes a `v*` tag. The tag push automatically triggers the ["Create release and publish"](https://github.com/grafana/plugin-validator/actions/workflows/release.yml) workflow, which: +## Cutting a release -1. Builds binaries via GoReleaser (Linux, Windows, Darwin) and creates a **GitHub Release** -2. Publishes to **npm** (`@grafana/plugin-validator`) -3. Pushes a **Docker image** to `grafana/plugin-validator-cli` (tagged with the version and `latest`) +1. Make sure your changes have landed on `main` with conventional commit messages (`feat:`, `fix:`, `chore:`, etc.). The bump type follows the commit types: `feat` → minor, `fix` → patch, `feat!` / `BREAKING CHANGE` → major. +2. Open the release PR for the package you want to ship (titled `chore(plugin-validator): release X.Y.Z` or `chore(mcp): release X.Y.Z`). Review the proposed changelog and version bump. +3. Squash-merge the release PR. The publish workflow runs automatically. -## MCP Server +If no release PR exists, the **Release Please** workflow can also be triggered manually from the Actions tab — it'll open one based on the current state of `main`. -The MCP server has its own, independent release track. Use the ["Release MCP Server"](https://github.com/grafana/plugin-validator/actions/workflows/do-release-mcp.yml) workflow: +## Conventional commit cheat sheet -1. Click **"Run workflow"** -2. Keep branch: `main` -3. Select the semver type: `patch`, `minor`, or `major` -4. Click **"Run workflow"** +Use these prefixes; release-please groups them in the changelog: -This bumps the version in `mcp-package/package.json`, commits to `main`, and pushes a `mcp/v*` tag. The tag push automatically triggers the ["Create MCP release and publish"](https://github.com/grafana/plugin-validator/actions/workflows/release-mcp.yml) workflow, which: +- `feat:` -- new feature (minor bump) +- `fix:` -- bug fix (patch bump) +- `feat!:` / footer `BREAKING CHANGE:` -- breaking change (major bump) +- `docs:`, `style:`, `refactor:`, `perf:`, `test:`, `build:`, `ci:`, `chore:`, `revert:` -- recorded in the changelog without changing the version (unless paired with `!` or `BREAKING CHANGE`) -1. Builds binaries via GoReleaser (Linux, Windows, Darwin -- both amd64 and arm64) and creates a **GitHub Release** -2. Publishes to **npm** (`@grafana/plugin-validator-mcp`) +For MCP-server-only changes, scope your commit (e.g. `feat(mcp): add new tool`) and modify files under `mcp-package/` -- release-please uses paths to decide which package's version to bump. ## Permissions > [!NOTE] -> For security purposes, external contributors don't usually have permissions to create new releases. +> External contributors don't usually have permission to merge release PRs. Ask a maintainer. -## GitHub or NPM tokens expiration +## Token expirations -GitHub and NPM tokens are required to auto-publish. These tokens eventually expire. If there's an error pushing tags to GitHub or updates to npm, review the token expiration. See the [secrets usage table](https://grafana.github.io/grafana-catalog-team/secrets-rotation/secrets-usage-table/) for details on which secrets are used and who owns them. +The publish workflows use the `plugins-platform-bot-app` GitHub App and an npm token; both can expire. If a publish step fails with an auth error, check the [secrets usage table](https://grafana.github.io/grafana-catalog-team/secrets-rotation/secrets-usage-table/). diff --git a/package.json b/package.json index f7cd6a36..292e9b3d 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "bin": "./bin/index.js", "binWrapper": { "name": "plugincheck2", - "urlTemplate": "https://github.com/grafana/plugin-validator/releases/download/v{{version}}/plugin-validator_{{version}}_{{platform}}_{{arch}}.tar.gz" + "urlTemplate": "https://github.com/grafana/plugin-validator/releases/download/plugin-validator/v{{version}}/plugin-validator_{{version}}_{{platform}}_{{arch}}.tar.gz" }, "dependencies": { "tar": "^7.4.3" diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 00000000..03833f77 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,32 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "changelog-sections": [ + { "section": "🎉 Features", "type": "feat" }, + { "section": "🐛 Bug Fixes", "type": "fix" }, + { "section": "📝 Documentation", "type": "docs" }, + { "section": "💄 Styles", "type": "style" }, + { "section": "♻️ Code Refactoring", "type": "refactor" }, + { "section": "⚡ Performance Improvements", "type": "perf" }, + { "section": "✅ Tests", "type": "test" }, + { "section": "🏗️ Builds", "type": "build" }, + { "section": "🤖 Continuous Integrations", "type": "ci" }, + { "section": "🔧 Chores", "type": "chore" }, + { "section": "⏪ Reverts", "type": "revert" } + ], + "draft-pull-request": false, + "include-v-in-tag": true, + "packages": { + ".": { + "package-name": "plugin-validator", + "release-type": "node", + "exclude-paths": ["mcp-package"], + "bootstrap-sha": "627dd9babe0a4244226ed98208aeccbd319a572f" + }, + "mcp-package": { + "package-name": "mcp", + "release-type": "node" + } + }, + "tag-separator": "/", + "separate-pull-requests": true +}