From e74f4d1fa4c01c6e8e9bc94eb7f4280f2f091bcc Mon Sep 17 00:00:00 2001 From: ACQ Build Date: Sat, 13 Jun 2026 12:07:16 -0500 Subject: [PATCH] ci: add plugin-release-guard + RELEASE.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fail any PR to main that changes plugins/apso/ without bumping the version, or whose plugin.json and marketplace.json versions disagree — the failure mode that stranded the domain-events skill (a skill merged with no version bump never reaches `claude plugin update`). RELEASE.md documents the release + update flow. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/plugin-release-guard.yml | 59 ++++++++++++++++++++++ RELEASE.md | 35 +++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 .github/workflows/plugin-release-guard.yml create mode 100644 RELEASE.md diff --git a/.github/workflows/plugin-release-guard.yml b/.github/workflows/plugin-release-guard.yml new file mode 100644 index 0000000..21412c4 --- /dev/null +++ b/.github/workflows/plugin-release-guard.yml @@ -0,0 +1,59 @@ +name: plugin-release-guard + +# Fails a PR that changes the published plugin without bumping the version, +# or whose plugin.json / marketplace.json versions disagree. This is the guard +# for the failure that stranded the domain-events skill: a skill merged without +# a version bump never reaches marketplace installs (`claude plugin update` +# only pulls a higher advertised version). + +on: + pull_request: + branches: [main] + +jobs: + release-guard: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # need the base ref to diff and read its versions + + - name: Check version agreement + bump-on-change + env: + BASE: ${{ github.event.pull_request.base.ref }} + run: | + set -euo pipefail + PLUGIN_MANIFEST="plugins/apso/.claude-plugin/plugin.json" + MARKET_MANIFEST=".claude-plugin/marketplace.json" + + plugin_ver=$(jq -r '.version' "$PLUGIN_MANIFEST") + market_ver=$(jq -r '.plugins[] | select(.name=="apso") | .version' "$MARKET_MANIFEST") + + # 1. The two manifests must advertise the same version. + if [ "$plugin_ver" != "$market_ver" ]; then + echo "::error::Version mismatch: plugin.json=$plugin_ver but marketplace.json=$market_ver. They must agree." + exit 1 + fi + + # 2. Version must be valid semver. + if ! echo "$plugin_ver" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'; then + echo "::error::Version '$plugin_ver' is not valid semver (X.Y.Z)." + exit 1 + fi + + git fetch --quiet origin "$BASE" + base_ver=$(git show "origin/$BASE:$PLUGIN_MANIFEST" | jq -r '.version') + + # 3. If anything under the published plugin changed, the version must move. + changed=$(git diff --name-only "origin/$BASE...HEAD" -- plugins/apso/ || true) + if [ -n "$changed" ]; then + echo "Published plugin files changed in this PR:" + echo "$changed" | sed 's/^/ /' + if [ "$plugin_ver" = "$base_ver" ]; then + echo "::error::plugins/apso/ changed but version is still $base_ver. Bump it in $PLUGIN_MANIFEST and the apso entry in $MARKET_MANIFEST (e.g. \`claude plugin tag\` validates agreement)." + exit 1 + fi + echo "OK: version bumped $base_ver -> $plugin_ver" + else + echo "OK: no published plugin changes; version bump not required." + fi diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 0000000..7a1839e --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,35 @@ +# Releasing the Apso plugin + +The plugin is distributed through this repo as a Claude Code / Cowork marketplace. +Marketplace installs only pull a release when the advertised **version increases**, so +**every change to `plugins/apso/` must bump the version.** CI (`plugin-release-guard`) +enforces this on PRs to `main`. + +## Checklist + +1. **PR straight to `main`** (no long-lived branches — that's what stranded the + `domain-events` skill). +2. **Bump the version** in both files, keeping them identical: + - `plugins/apso/.claude-plugin/plugin.json` → `version` + - `.claude-plugin/marketplace.json` → the `apso` entry's `version` + - semver: new skill / feature = minor (`0.1.0 → 0.2.0`); fix = patch. +3. **Validate locally:** `claude plugin validate .` +4. **Merge to `main`.** +5. **Tag the release (optional but recommended):** + `claude plugin tag --push -m "apso %s"` — creates `apso--v`, validating that + `plugin.json` and the marketplace entry agree. + +## How deployed installs update + +Marketplace installs via the Claude Code CLI: + +```bash +claude plugin marketplace update apso # refresh the catalog from this repo +claude plugin update apso@apso # pull the new version (restart to apply) +``` + +Org/team installs that set `"autoUpdate": true` on the marketplace in +`.claude/settings.json` (`extraKnownMarketplaces`) refresh automatically at session start. + +Cowork installs from an uploaded `.plugin` zip do **not** auto-update — rebuild the zip from +`plugins/apso/` and re-upload.