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
16171 . computes the next version from the latest tag, and refuses if that tag already exists,
17182 . 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
2121Nothing 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
2833consequence 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
4450than around it. Tagging is unaffected: the ruleset targets branches, and tags live in a
4551separate 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 ` .
57572 . 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