Skip to content

Bump version to 2.1.2 #20

Bump version to 2.1.2

Bump version to 2.1.2 #20

Workflow file for this run

name: Release
# Triggered when a version tag is pushed (e.g. v1.1.0). The job:
# 1. Verifies all plugin manifests carry the same version as the tag.
# 2. Validates every JSON manifest parses.
# 3. Extracts the matching CHANGELOG section and creates a GitHub Release
# with notes + install snippets + a compare link to the previous tag.
on:
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+"
- "v[0-9]+.[0-9]+.[0-9]+-*"
permissions:
contents: write
jobs:
release:
name: Validate and publish
runs-on: ubuntu-latest
steps:
- name: Checkout (with full history for compare links)
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Resolve tag and version
id: version
run: |
TAG="${GITHUB_REF#refs/tags/}"
VERSION="${TAG#v}"
PREVIOUS_TAG="$(git describe --tags --abbrev=0 "${TAG}^" 2>/dev/null || true)"
{
echo "tag=$TAG"
echo "version=$VERSION"
echo "previous_tag=$PREVIOUS_TAG"
} >> "$GITHUB_OUTPUT"
echo "Releasing $TAG (version $VERSION, previous tag: ${PREVIOUS_TAG:-none})"
- name: Validate manifest version alignment
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
set -euo pipefail
fail=0
check() {
local file="$1" path="$2" actual
actual="$(jq -r "$path" "$file")"
if [ "$actual" != "$VERSION" ]; then
echo "::error file=$file::version $actual does not match tag $VERSION"
fail=1
else
echo "✓ $file ($path) = $actual"
fi
}
check plugins/cpln/.claude-plugin/plugin.json '.version'
check .claude-plugin/marketplace.json '.plugins[0].version'
check plugins/cpln/.codex-plugin/plugin.json '.version'
check plugins/cpln/.cursor-plugin/plugin.json '.version'
check .cursor-plugin/marketplace.json '.metadata.version'
check .cursor-plugin/marketplace.json '.plugins[0].version'
check plugins/cpln/plugin.json '.version'
[ "$fail" -eq 0 ] || exit 1
- name: Validate JSON syntax
run: |
jq empty \
.claude-plugin/marketplace.json \
.agents/plugins/marketplace.json \
.cursor-plugin/marketplace.json \
plugins/cpln/plugin.json \
plugins/cpln/mcp_config.json \
plugins/cpln/.claude-plugin/plugin.json \
plugins/cpln/.codex-plugin/plugin.json \
plugins/cpln/.mcp.json \
plugins/cpln/.cursor-plugin/plugin.json \
plugins/cpln/.cursor-plugin/mcp.json \
plugins/cpln/.claude-mcp.json \
plugins/cpln/hooks/hooks.json
- name: Extract release notes from CHANGELOG
id: notes
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
set -euo pipefail
# Grab everything between "## [VERSION]" and the next top-level
# version header (## [...]) or end of file.
notes="$(awk -v ver="$VERSION" '
BEGIN { found = 0 }
/^## \[/ {
if (found) exit
if ($0 ~ "\\[" ver "\\]") { found = 1; next }
}
found { print }
' CHANGELOG.md)"
# Trim leading/trailing blank lines.
notes="$(printf "%s" "$notes" | awk 'NF { hit = 1 } hit { print }' | awk 'BEGIN { RS = ""; ORS = "\n" } { print }')"
if [ -z "$notes" ]; then
echo "::error::CHANGELOG.md has no section for version $VERSION"
exit 1
fi
{
echo "notes<<RELEASE_NOTES_EOF"
printf "%s\n" "$notes"
echo "RELEASE_NOTES_EOF"
} >> "$GITHUB_OUTPUT"
- name: Assemble release body
env:
VERSION: ${{ steps.version.outputs.version }}
TAG: ${{ steps.version.outputs.tag }}
PREVIOUS_TAG: ${{ steps.version.outputs.previous_tag }}
NOTES: ${{ steps.notes.outputs.notes }}
REPO: ${{ github.repository }}
run: |
set -euo pipefail
{
printf '# Control Plane AI Plugin\n\n'
printf '## What changed\n\n'
printf '%s\n' "$NOTES"
printf '\n---\n\n'
printf '## Update an existing install\n\n'
printf 'If you already have the plugin installed, use the per-client commands below to pull this release. None of these clients auto-update third-party plugins by default.\n\n'
printf '**Claude Code**\n\n'
printf '```text\n'
printf '/plugin marketplace update controlplane\n'
printf '/reload-plugins\n'
printf '```\n\n'
printf 'To opt into auto-updates for future releases, run `/plugin`, open the **Marketplaces** tab, select **Control Plane**, and choose **Enable auto-update**.\n\n'
printf '**Codex**\n\n'
printf '```bash\n'
printf 'codex plugin marketplace upgrade controlplane\n'
printf '```\n\n'
printf 'Restart Codex so the upgraded plugin manifest is picked up.\n\n'
printf '**Antigravity CLI**\n\n'
printf 'Antigravity has no dedicated update command — reinstall to pull the latest:\n\n'
printf '```bash\n'
printf 'agy plugin uninstall cpln\n'
printf 'agy plugin install https://github.com/%s/plugins/cpln\n' "$REPO"
printf '```\n\n'
printf '## First-time install\n\n'
printf '**Claude Code**\n\n'
printf '```text\n'
printf '/plugin marketplace add https://github.com/%s.git\n' "$REPO"
printf '/plugin install cpln@controlplane\n'
printf '/reload-plugins\n'
printf '```\n\n'
printf '**Codex**\n\n'
printf '```bash\n'
printf 'codex plugin marketplace add https://github.com/%s.git\n' "$REPO"
printf '```\n\n'
printf 'Then start Codex, open `/plugins`, and install `cpln` from the Control Plane marketplace.\n\n'
printf '**Antigravity CLI**\n\n'
printf '```bash\n'
printf 'agy plugin install https://github.com/%s/plugins/cpln\n' "$REPO"
printf '```\n\n'
printf '**Generic MCP client** — point it at `https://mcp.cpln.io/mcp`. OAuth 2.1 sign-in handles authorization on first use. See README for the config snippet.\n\n'
if [ -n "$PREVIOUS_TAG" ]; then
printf -- '---\n\n'
printf '**Full changelog**: https://github.com/%s/compare/%s...%s\n' "$REPO" "$PREVIOUS_TAG" "$TAG"
fi
} > release-notes.md
echo "::group::Assembled release body"
cat release-notes.md
echo "::endgroup::"
- name: Create GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.version.outputs.tag }}
VERSION: ${{ steps.version.outputs.version }}
run: |
set -euo pipefail
# Skip pre-releases? Treat any tag that contains a "-" as pre-release.
PRERELEASE_FLAG=""
case "$TAG" in
*-*) PRERELEASE_FLAG="--prerelease" ;;
esac
gh release create "$TAG" \
--title "$TAG" \
--notes-file release-notes.md \
$PRERELEASE_FLAG