Skip to content

Update External Plugins #14

Update External Plugins

Update External Plugins #14

name: Update External Plugins
# Auto-bump external-plugin pins (source.ref + mirrored version) when an
# upstream publishes a newer release. Opens a reviewable chore(...) PR;
# manifest-only chore commits do not trigger an agent-plugins release.
on:
schedule:
- cron: '17 6 * * *' # daily, 06:17 UTC
workflow_dispatch:
permissions:
contents: write
pull-requests: write
concurrency:
group: update-external-plugins
cancel-in-progress: false
jobs:
update:
name: Resolve and pin latest external releases
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Update external plugin pins
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: python3 .github/scripts/update_external_plugins.py
- name: Validate manifest sync
run: |
python3 << 'EOF'
import json, re, sys
claude = json.load(open('.claude-plugin/marketplace.json'))
agents = json.load(open('.agents/plugins/marketplace.json'))
a_by_name = {p.get('name'): p for p in agents.get('plugins', [])}
def norm(url):
m = re.match(r'^git@github\.com:(.+?)(?:\.git)?$', (url or '').strip())
if m:
return m.group(1).lower()
m = re.match(r'^https://github\.com/(.+?)(?:\.git)?$',
(url or '').strip())
return m.group(1).lower() if m else (url or '').strip().lower()
errors = []
for e in claude.get('plugins', []):
s = e.get('source')
if not isinstance(s, dict) or s.get('source') != 'github':
continue
name, repo = e.get('name'), s.get('repo', '')
ref, ver = s.get('ref', ''), e.get('version')
if ver is not None and ref != f"v{ver}":
errors.append(f"{name}: ref {ref!r} != v+version {ver!r}")
a = a_by_name.get(name)
if a is None:
errors.append(f"{name}: missing from .agents manifest")
continue
a_src = a.get('source', {})
if norm(a_src.get('url', '')) != repo.lower():
errors.append(f"{name}: .agents repo mismatch")
if a_src.get('ref', '') != ref:
errors.append(
f"{name}: ref mismatch claude {ref!r} vs "
f".agents {a_src.get('ref','')!r}")
if errors:
print("\n".join(f"❌ {x}" for x in errors))
sys.exit(1)
print("✅ external manifests in sync")
EOF
- name: Open PR
id: cpr
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
branch: automation/external-plugin-updates
base: master
commit-message: 'chore(external-plugins): update external plugin pins'
title: 'chore(external-plugins): update external plugin pins'
body: |
Automated external-plugin pin update.
Bumps `source.ref` in both manifests (and the mirrored
`version` in `.claude-plugin/marketplace.json`) to the latest
eligible upstream release. Manifest-only `chore:` change - no
agent-plugins release is triggered.
Review the diff against the upstream changelog before merging.
delete-branch: true
add-paths: |
.claude-plugin/marketplace.json
.agents/plugins/marketplace.json
- name: Auto-merge
# The inline "Validate manifest sync" step above is the gate: if it
# fails the workflow stops and no PR exists. Manifest-only chore PR
# -> no agent-plugins release. Squash-merge immediately.
if: >-
${{ steps.cpr.outputs.pull-request-number != '' &&
(steps.cpr.outputs.pull-request-operation == 'created' ||
steps.cpr.outputs.pull-request-operation == 'updated') }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR: ${{ steps.cpr.outputs.pull-request-number }}
run: |
gh pr merge "$PR" --squash --delete-branch \
--subject 'chore(external-plugins): update external plugin pins'