Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
53 changes: 42 additions & 11 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -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-<version>.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:
Expand All @@ -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:
Expand All @@ -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

Expand All @@ -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
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 14 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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-<version>.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

Expand Down
26 changes: 20 additions & 6 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand All @@ -65,5 +76,8 @@ To verify the packaged gem without publishing:
bundle exec rake build # writes pkg/activeadmin_mcp-<version>.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.
Loading