Skip to content

Commit 699cefc

Browse files
authored
Merge pull request #14 from oposs/feat/skill-moved-out
The skill has moved out; this is now only the action
2 parents 8b432c6 + 6e9ec46 commit 699cefc

32 files changed

Lines changed: 65 additions & 8005 deletions

.claude-plugin/plugin.json

Lines changed: 0 additions & 8 deletions
This file was deleted.

.github/workflows/create-release-pr.yml

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -260,37 +260,6 @@ jobs:
260260
echo "Updated CHANGES.md for version ${VERSION_NO_V}"
261261
echo "::endgroup::"
262262
263-
- name: Sync Claude plugin version
264-
env:
265-
VERSION_NO_V: ${{ steps.generate_version.outputs.version_no_v }}
266-
run: |
267-
set -euo pipefail
268-
echo "::group::Syncing .claude-plugin/plugin.json"
269-
270-
MANIFEST=.claude-plugin/plugin.json
271-
272-
# Claude caches a plugin under its resolved version, so a skill change only
273-
# reaches users once this version moves. Keeping it equal to the release tag
274-
# makes shipping a skill fix just a matter of cutting a release. The publish
275-
# workflow also reads this file to decide what to tag.
276-
if [[ ! -f "$MANIFEST" ]]; then
277-
echo "::error title=Missing plugin manifest::$MANIFEST not found - Claude plugin users would silently miss this release"
278-
exit 1
279-
fi
280-
281-
OLD_VERSION=$(jq -r '.version // "unset"' "$MANIFEST")
282-
jq --indent 2 --arg v "$VERSION_NO_V" '.version = $v' "$MANIFEST" > "$MANIFEST.tmp"
283-
mv "$MANIFEST.tmp" "$MANIFEST"
284-
285-
NEW_VERSION=$(jq -r '.version' "$MANIFEST")
286-
if [[ "$NEW_VERSION" != "$VERSION_NO_V" ]]; then
287-
echo "::error title=Version sync failed::expected ${VERSION_NO_V}, got ${NEW_VERSION}"
288-
exit 1
289-
fi
290-
291-
echo "::notice title=Plugin Version Synced::.claude-plugin/plugin.json ${OLD_VERSION} -> ${NEW_VERSION}"
292-
echo "::endgroup::"
293-
294263
- name: Open release PR
295264
env:
296265
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -306,10 +275,10 @@ jobs:
306275
git config --local user.name "GitHub Action"
307276
308277
git checkout -b "$BRANCH"
309-
git add CHANGES.md .claude-plugin/plugin.json
278+
git add CHANGES.md
310279
311280
if git diff --cached --quiet; then
312-
echo "::error title=Nothing to release::CHANGES.md and plugin.json are unchanged - is the Unreleased section empty?"
281+
echo "::error title=Nothing to release::CHANGES.md is unchanged - is the Unreleased section empty?"
313282
exit 1
314283
fi
315284
@@ -327,7 +296,6 @@ jobs:
327296
"Prepared automatically by the **Create release PR** workflow." \
328297
"" \
329298
"- rolls the \`CHANGES.md\` \`[Unreleased]\` section into \`## ${VERSION_NO_V} - ${RELEASE_DATE}\`" \
330-
"- syncs \`.claude-plugin/plugin.json\` to \`${VERSION_NO_V}\`" \
331299
"" \
332300
"Review the changelog, then merge. Merging triggers **Release publisher**, which tags \`${VERSION}\`, moves \`@v${VERSION_NO_V%%.*}\` and publishes the GitHub release." \
333301
"" \

.github/workflows/release-publisher.yml

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
name: Release publisher
22

33
# Second half of the release. "Create release PR" prepares a PR; merging it lands the
4-
# rolled CHANGES.md and the synced plugin.json on main, and that merge triggers this.
4+
# rolled CHANGES.md on main, and that merge triggers this.
55
#
66
# main is protected by a ruleset and GITHUB_TOKEN cannot be granted a bypass (the bypass
77
# list takes users, teams and GitHub Apps -- not the built-in Actions token). So nothing
88
# here pushes to a branch. Tagging is unaffected: the ruleset targets branches, and tags
99
# are a separate ref namespace.
1010
#
11-
# The version is read from .claude-plugin/plugin.json rather than passed between jobs,
11+
# The version is read from CHANGES.md rather than passed between jobs,
1212
# which makes this idempotent: if that version is already tagged there is nothing to do.
1313
# A re-run or an unrelated push to main is therefore harmless.
1414
#
1515
# There is deliberately no workflow_dispatch. Publishing should be a consequence of
1616
# merging a release PR, never something anyone starts by hand. Combined with the branch
1717
# ruleset, which forbids pushing to main directly, that leaves exactly one way to reach
18-
# this workflow: merge a PR that changes plugin.json. Recovery does not need a manual
18+
# this workflow: merge a PR that changes CHANGES.md. Recovery does not need a manual
1919
# trigger either -- a failed run can be re-run from the Actions UI, and because the
2020
# version comes from the repository rather than from run inputs, the re-run does exactly
2121
# what the original attempt would have done.
@@ -24,7 +24,7 @@ on:
2424
push:
2525
branches: [main]
2626
paths:
27-
- '.claude-plugin/plugin.json'
27+
- 'CHANGES.md'
2828

2929
concurrency:
3030
group: release-publisher
@@ -50,9 +50,13 @@ jobs:
5050
set -euo pipefail
5151
echo "::group::Determining version"
5252
53-
VERSION_NO_V=$(jq -r '.version' .claude-plugin/plugin.json)
54-
if [[ -z "$VERSION_NO_V" || "$VERSION_NO_V" == "null" ]]; then
55-
echo "::error title=No version::.claude-plugin/plugin.json has no version"
53+
# CHANGES.md is the source of truth. The first heading carrying a
54+
# semver is the newest released section; `## [Unreleased]` has no
55+
# digits and is skipped, so an unreleased-only changelog matches
56+
# nothing and this fails rather than tagging something arbitrary.
57+
VERSION_NO_V=$(grep -m1 -oP '^##\s+\Kv?[0-9]+\.[0-9]+\.[0-9]+' CHANGES.md || true)
58+
if [[ -z "$VERSION_NO_V" ]]; then
59+
echo "::error title=No version::CHANGES.md has no released version section"
5660
exit 1
5761
fi
5862
VERSION="v${VERSION_NO_V}"

.gitignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
!/.github
88
!/.mkp-builder.ini.example
99
!/.clinerules
10-
!/skills
11-
!/.claude-plugin
1210
*~
1311
*.swp
1412
*.bak

CHANGES.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Changed
11+
12+
- The Checkmk plugin development skill has moved to its own repository,
13+
`oposs/cmk-oposs-plugin`. This repository is now only the packaging action. Holding both
14+
behind one version number misfired in both directions: v2.2.2 changed five action files
15+
and no skill files while telling every skill user their skill had updated, and v2.3.0
16+
changed only skill files while cutting an action release whose code was byte-identical.
17+
- The release version is read from `CHANGES.md` instead of `.claude-plugin/plugin.json`,
18+
which went with the skill. A GitHub Action is not a Claude plugin and has no reason to
19+
carry a plugin manifest.
20+
1021
### New
1122

1223
### Changed

README.md

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,39 +18,25 @@ A reusable GitHub Action for building Checkmk MKP (Monitoring Konfiguration Pack
1818
- 🧹 **Clean**: No permanent changes to your repository
1919
-**Fast**: Downloads build tools on-demand, no bloated containers
2020

21-
## Claude Code Plugin: Checkmk Plugin Development Guide
21+
## Related projects
2222

23-
This repo includes a comprehensive Checkmk 2.3.x plugin development guide
24-
as a [Claude Code](https://claude.com/claude-code) skill. Once installed,
25-
Claude will automatically use the guide whenever you ask it to build, upgrade,
26-
or package Checkmk plugins.
23+
This repository is **only** the packaging action. Two companions do the rest:
2724

28-
### Install
25+
- **[`oposs/cmk-oposs-plugin`](https://github.com/oposs/cmk-oposs-plugin)** — the Claude Code
26+
skill for building, upgrading and packaging Checkmk 2.3.x plugins. It used to live here,
27+
which meant a skill change cut a release of this action and an action change told skill
28+
users their skill had updated. Install it with:
2929

30-
Add the OPOSS marketplace and install the plugin:
30+
```
31+
/plugin marketplace add oposs/claude-plugins
32+
/plugin install cmk-oposs-plugin
33+
```
3134

32-
```
33-
/plugin marketplace add oposs/claude-plugins
34-
/plugin install cmk-oposs-plugin@oposs-plugins
35-
```
36-
37-
### What it covers
38-
39-
- Agent-based check plugins, SNMP plugins, special agents
40-
- Metrics, graphing, and perfometers
41-
- Rulesets and GUI configuration
42-
- Bakery integration for automatic agent deployment
43-
- Notification plugins (Slack, Teams, webhooks, etc.)
44-
- Metric migration (renaming metrics while preserving RRD history)
45-
- MKP packaging with the `oposs/mkp-builder` GitHub Action
46-
47-
### Example prompts
35+
- **[`oposs/repo-infra`](https://github.com/oposs/repo-infra)** — release flow, changelog
36+
handling, branch protection and CI. It detects a Checkmk plugin from `.mkp-builder.ini`
37+
and generates a `ci.yml` that calls this action.
4838

49-
- *"Build an SNMP plugin to monitor my Liebert UPS"*
50-
- *"Upgrade my old v1 check plugin to the v2 API"*
51-
- *"Create a notification plugin that sends alerts to Teams"*
52-
- *"Add graphs and configurable thresholds to my plugin"*
53-
- *"Package my plugin as an MKP with a GitHub Actions release workflow"*
39+
The seam between the three is one line: `uses: oposs/mkp-builder@v2`.
5440

5541
## Quick Start
5642

RELEASING.md

Lines changed: 27 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,44 @@
11
# Releasing
22

3-
This repo ships two things, released **together** from a single version stream:
3+
This repo ships one thing: the **`mkp-builder` GitHub Action**. Consumers pin
4+
`oposs/mkp-builder@v2`.
45

5-
- **The `mkp-builder` GitHub Action** — consumers pin `oposs/mkp-builder@v2`.
6-
- **The `cmk-oposs-plugin` Claude Code plugin** — the `skills/` tree, versioned by
7-
`.claude-plugin/plugin.json`.
6+
(The `cmk-oposs-plugin` Claude Code skill used to be released from here too. It now lives
7+
in [`oposs/cmk-oposs-plugin`](https://github.com/oposs/cmk-oposs-plugin) with its own
8+
version stream, because one number could not honestly describe both.)
89

910
## The version stream
1011

11-
Git tags (`v2.2.0`, …) with a moving major tag (`@v2`) drive both. A release runs in two
12-
halves, because `main` is protected — see [Why a PR](#why-a-pr) below.
12+
Git tags (`v2.2.0`, …) with a moving major tag (`@v2`). A release runs in two halves,
13+
because `main` is protected — see [Why a PR](#why-a-pr) below.
1314

1415
**1. Run the `Create release PR` workflow** (`workflow_dispatch`, bugfix/feature/major). It:
1516

1617
1. computes the next version from the latest tag, and refuses if that tag already exists,
1718
2. rolls the `CHANGES.md` `## [Unreleased]` section into a dated version section,
18-
3. **rewrites `.claude-plugin/plugin.json` to the same version**,
19-
4. pushes a `release/vX.Y.Z` branch and opens a PR.
19+
3. pushes a `release/vX.Y.Z` branch and opens a PR.
2020

2121
Nothing is tagged or published yet. Closing the PR cancels the release.
2222

2323
**2. Review the changelog and merge the PR.** That triggers `Release publisher`, which
24-
reads the version back out of `plugin.json`, tags it, moves `@vX`, and publishes the
25-
GitHub release with the notes from `CHANGES.md`.
24+
reads the version back out of `CHANGES.md`, tags it, moves `@vX`, and publishes the GitHub
25+
release with the notes from that section.
26+
27+
`CHANGES.md` is the single source of truth for the version. The first `##` heading carrying
28+
a semver is the newest release; `## [Unreleased]` has no digits and is skipped, so a
29+
changelog with nothing released matches nothing and the publisher fails rather than tagging
30+
something arbitrary.
2631

2732
`Release publisher` has **no manual trigger, by design** — publishing should be a
2833
consequence of merging a release PR, never something anyone starts by hand. With `main`
29-
protected against direct pushes, that leaves exactly one route to a release: merge a PR
30-
that changes `plugin.json`. If a run fails, re-run it from the Actions UI; the version
31-
comes from the repository rather than from run inputs, so a re-run does exactly what the
32-
original attempt would have.
34+
protected against direct pushes, that leaves exactly one route to a release. If a run
35+
fails, re-run it from the Actions UI; the version comes from the repository rather than
36+
from run inputs, so a re-run does exactly what the original attempt would have.
3337

34-
Publishing is also idempotent: if the version in `plugin.json` is already tagged it does
35-
nothing, so re-runs and unrelated pushes to `main` are harmless.
38+
Publishing is idempotent: if the newest version in `CHANGES.md` is already tagged it does
39+
nothing. That matters more now than it used to, because the publisher triggers on any push
40+
to `main` that touches `CHANGES.md` — including an ordinary PR that only adds an
41+
`[Unreleased]` entry. Such a push resolves to the already-tagged newest release and stops.
3642

3743
### Why a PR
3844

@@ -44,36 +50,10 @@ release through a PR instead needs no credential and works with the protection r
4450
than around it. Tagging is unaffected: the ruleset targets branches, and tags live in a
4551
separate ref namespace.
4652

47-
`plugin.json` remains the single source of truth for the plugin version — Claude Code
48-
resolves `plugin.json → marketplace entry → commit SHA`, and `plugin.json` wins. The
49-
marketplace entry in [`oposs/claude-plugins`](https://github.com/oposs/claude-plugins)
50-
therefore carries **no** `version` field for this plugin (a stale duplicate silently
51-
masks the real one). The workflow keeps `plugin.json` equal to the tag so the two
52-
version lines cannot drift apart.
53-
54-
### Releasing a plugin or skill change
53+
### Releasing an action change
5554

56-
1. Make the change under `skills/` and merge it to `main`.
55+
1. Make the change to `action.yml` or `mkp-builder.py`, add a `CHANGES.md` entry under
56+
`## [Unreleased]`, and merge to `main`.
5757
2. Run the **Create release PR** workflow, then merge the release PR it opens.
58-
`plugin.json` is bumped for you — there is no hand-written bump commit any more.
59-
3. Users run `/plugin marketplace update` then `/plugin update cmk-oposs-plugin`.
60-
61-
Nothing else is needed here. Claude only re-resolves plugin versions when it re-fetches
62-
the marketplace, so the marketplace repository has to move as well — but that is handled
63-
from the other side: [`oposs/claude-plugins`](https://github.com/oposs/claude-plugins)
64-
runs an hourly **Track plugin versions** workflow that reads this repository's
65-
`plugin.json` and commits when the version changes.
66-
67-
That direction was chosen deliberately. A push from here would need a credential for
68-
another repository (the org restricts fine-grained PATs, leaving a deploy key or a
69-
GitHub App). Polling from the marketplace needs no credential at all, covers every
70-
plugin instead of only those wired up to push, and still catches a version bumped
71-
outside the release workflow.
72-
73-
> **Why the version bump matters.** Claude caches a plugin under its resolved version and
74-
> reads the skill from `~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/`.
75-
> Merging a skill fix without moving the version ships nothing — every Claude keeps
76-
> reading the old cached copy, with no error and no signal that anything is stale. While
77-
> this was a manual step it was missed often enough that an installed copy was found 10
78-
> commits and three merged skill fixes behind `main`. That is why step 3 of the release
79-
> workflow now enforces it.
58+
59+
Consumers pinning `@v2` pick it up as soon as the major tag moves. Nothing else is needed.

0 commit comments

Comments
 (0)