diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf603b1..917ccde 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: - name: Set up Node uses: actions/setup-node@v4 with: - node-version: "22" + node-version: "24" - name: Run proxy tests run: npm test diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4838af8..a73543c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,11 +1,13 @@ name: Release # Publishes the gem to RubyGems.org via Trusted Publishing (OIDC) whenever a -# GitHub Release is published. The release tag (e.g. v0.1.1) is the source of -# truth for the version: the workflow writes it into lib/activeadmin_mcp/version.rb, -# builds and publishes the gem, then commits the version bump back to the -# default branch. No API key is stored — RubyGems verifies this workflow via -# OIDC. See RELEASING.md for the one-time RubyGems setup. +# GitHub Release is published, and attaches the Claude Desktop bundle to the +# release. The release tag (e.g. v0.1.1) is the source of truth for the +# version: the workflow writes it into lib/activeadmin_mcp/version.rb, +# mcpb/manifest.json and mcpb/package.json, publishes the gem, uploads +# activeadmin-mcp-.mcpb as a release asset, then commits the version +# bumps back to the default branch. No API key is stored — RubyGems verifies +# this workflow via OIDC. See RELEASING.md for the one-time RubyGems setup. on: release: @@ -19,7 +21,7 @@ jobs: runs-on: ubuntu-latest environment: rubygems permissions: - contents: write # commit the version bump back to the default branch + contents: write # upload the release asset, commit the version bumps back id-token: write # exchange the OIDC token with RubyGems steps: @@ -45,15 +47,35 @@ jobs: ruby-version: "4.0.7" bundler-cache: true - - name: Write version file + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: "24" + + - name: Write version files run: | version="${{ steps.version.outputs.version }}" sed -i -E "s/VERSION = \".*\"/VERSION = \"${version}\"/" lib/activeadmin_mcp/version.rb grep -q "VERSION = \"${version}\"" lib/activeadmin_mcp/version.rb + # The leading quote in the pattern keeps this off "manifest_version". + for json in mcpb/manifest.json mcpb/package.json; do + sed -i -E "s/^(\s*)\"version\": \".*\",$/\\1\"version\": \"${version}\",/" "$json" + grep -q "\"version\": \"${version}\"," "$json" + done - name: Run specs run: bundle exec rspec + - name: Run proxy tests + run: npm test + working-directory: mcpb + + - name: Pack the bundle + run: | + version="${{ steps.version.outputs.version }}" + npx --yes @anthropic-ai/mcpb pack . "../activeadmin-mcp-${version}.mcpb" + working-directory: mcpb + - name: Configure RubyGems credentials (OIDC) uses: rubygems/configure-rubygems-credentials@v2.1.0 @@ -63,15 +85,24 @@ jobs: gem build activeadmin_mcp.gemspec gem push "activeadmin_mcp-${version}.gem" - - name: Commit version bump to default branch + - name: Attach the bundle to the release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + version="${{ steps.version.outputs.version }}" + gh release upload "${{ github.event.release.tag_name }}" \ + "activeadmin-mcp-${version}.mcpb" --clobber + + - name: Commit version bumps to default branch run: | version="${{ steps.version.outputs.version }}" - if git diff --quiet -- lib/activeadmin_mcp/version.rb; then - echo "version.rb already at ${version}; nothing to commit." + versioned_files="lib/activeadmin_mcp/version.rb mcpb/manifest.json mcpb/package.json" + if git diff --quiet -- $versioned_files; then + echo "version files already at ${version}; nothing to commit." else git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add lib/activeadmin_mcp/version.rb + git add $versioned_files git commit -m "Bump version to ${version}" git push origin "HEAD:${{ github.event.repository.default_branch }}" fi diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c18156..a7a3b3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,8 +36,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 underlying exception message is written to the log instead of being sent to the MCP client, where it could disclose SQL, table names or file paths. +- Publishing a GitHub Release now attaches the Claude Desktop bundle to it as + `activeadmin-mcp-X.Y.Z.mcpb`, so installing the bundle no longer means + digging a build artifact out of the Actions tab. + ### Changed +- The release tag is now the source of truth for the bundle's version too: the + release workflow writes it into `mcpb/manifest.json` and `mcpb/package.json` + before packing, and commits the bump back alongside `version.rb`. The gem and + the bundle can no longer drift apart, and nobody has to remember the manual + bump the README used to ask for. + - **Breaking:** the minimum supported Ruby is now 4.0 and the minimum Rails is 7.2, and ActiveAdmin is constrained to `~> 3.5`. Applications outside those must stay on the previous release until they upgrade. Rails 7.2 is the oldest diff --git a/README.md b/README.md index 7eb1a6d..6f04a97 100644 --- a/README.md +++ b/README.md @@ -324,16 +324,20 @@ npx @anthropic-ai/mcpb pack . ../activeadmin-mcp.mcpb ``` That writes `activeadmin-mcp.mcpb` (a zip of `manifest.json`, `package.json` and -`server/index.js`) to the repository root, ready to distribute. Bump `version` -in **both** `mcpb/manifest.json` and `mcpb/package.json` before packing a -release — Claude Desktop uses the manifest version to detect upgrades. - -CI packs the bundle on every push and attaches it as a build artifact, so you -can also download a build from the Actions tab rather than packing it yourself. - -Distribute the file however suits you: an internal file share, a GitHub release -asset, or an S3 bucket. Anyone with the file can install it, but it is inert -without a token. +`server/index.js`) to the repository root. You do not bump the bundle's +`version` by hand: publishing a GitHub Release writes the release tag into +`mcpb/manifest.json` and `mcpb/package.json`, packs the bundle from that, and +attaches `activeadmin-mcp-.mcpb` to the release — so the bundle +version Claude Desktop uses to detect upgrades always matches the gem version. +See [RELEASING.md](RELEASING.md). + +So the bundle for any released version is on that release's page, and CI packs +the bundle on every push and attaches it as a build artifact if you want an +unreleased build from the Actions tab. + +Distribute the file however suits you — pointing colleagues at the release +asset, an internal file share, or an S3 bucket. Anyone with the file can +install it, but it is inert without a token. ### Installing diff --git a/RELEASING.md b/RELEASING.md index 3208af2..ba607f5 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -8,12 +8,21 @@ Releases are driven by **GitHub Releases**. Publishing a release with a `vX.Y.Z` tag triggers `.github/workflows/release.yml`, which: 1. Derives the version from the release tag. -2. Writes it into `lib/activeadmin_mcp/version.rb`. -3. Runs the specs, then builds and publishes the gem to RubyGems via OIDC. -4. Commits the version bump back to the default branch. +2. Writes it into `lib/activeadmin_mcp/version.rb`, `mcpb/manifest.json` and + `mcpb/package.json`. +3. Runs the specs and the MCPB proxy tests, and packs the Claude Desktop bundle. +4. Builds and publishes the gem to RubyGems via OIDC. +5. Uploads `activeadmin-mcp-X.Y.Z.mcpb` as an asset on the release. +6. Commits the version bumps back to the default branch. The release tag is the single source of truth for the version — you do not edit -`version.rb` by hand. +`version.rb` or the bundle's `version` fields by hand, and the gem and the +bundle always carry the same version. + +The bundle is packed before the gem is pushed, so a broken bundle stops the +release rather than following a published gem. Uploading the asset uses +`--clobber`, so re-running the workflow on the same release replaces the asset +rather than failing. ## One-time setup (RubyGems side) @@ -48,8 +57,10 @@ regular trusted publisher automatically — no further RubyGems setup is needed. notes, and click **Publish release**. Publishing the release triggers `.github/workflows/release.yml`, which bumps -`version.rb` to match the tag, runs the specs, publishes the gem to RubyGems via -OIDC, and commits the version bump back to `main`. +`version.rb` and the bundle manifests to match the tag, runs the specs, +publishes the gem to RubyGems via OIDC, attaches the +`activeadmin-mcp-X.Y.Z.mcpb` bundle to the release, and commits the version +bumps back to `main`. > **Branch protection:** the workflow pushes the version-bump commit to the > default branch using the built-in `GITHUB_TOKEN`. If `main` requires pull @@ -65,5 +76,8 @@ To verify the packaged gem without publishing: bundle exec rake build # writes pkg/activeadmin_mcp-.gem ``` +To verify the Claude Desktop bundle without publishing, see +[Building the bundle](README.md#building-the-bundle) in the README. + Do **not** run `rake release` locally — publishing happens only through the tagged CI workflow.