Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
{
"name": "superpowers",
"description": "Core skills library for Claude Code: TDD, debugging, collaboration patterns, and proven techniques",
"version": "5.6.0",
"version": "5.7.0",
"source": "./",
"author": {
"name": "Jesse Vincent",
Expand Down
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "superpowers",
"description": "Core skills library for Claude Code: TDD, debugging, collaboration patterns, and proven techniques",
"version": "5.6.0",
"version": "5.7.0",
"author": {
"name": "Jesse Vincent",
"email": "jesse@fsck.com"
Expand Down
2 changes: 1 addition & 1 deletion .cursor-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "superpowers",
"displayName": "Superpowers",
"description": "Core skills library: TDD, debugging, collaboration patterns, and proven techniques",
"version": "5.6.0",
"version": "5.7.0",
"author": {
"name": "Jesse Vincent",
"email": "jesse@fsck.com"
Expand Down
40 changes: 40 additions & 0 deletions .github/workflows/version-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Version Consistency Check

on:
push:
paths:
- '.claude-plugin/plugin.json'
- '.claude-plugin/marketplace.json'
- '.cursor-plugin/plugin.json'
- 'scripts/bump-version.sh'
- 'tests/version-check.sh'
- '.github/workflows/version-check.yml'
tags:
- 'v*.*.*'
pull_request:
paths:
- '.claude-plugin/plugin.json'
- '.claude-plugin/marketplace.json'
- '.cursor-plugin/plugin.json'
- 'scripts/bump-version.sh'
- 'tests/version-check.sh'
- '.github/workflows/version-check.yml'
workflow_dispatch:

permissions:
contents: read

jobs:
version-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Check version consistency across plugin manifests
run: bash tests/version-check.sh

- name: Check version matches latest git tag (on tag push)
if: startsWith(github.ref, 'refs/tags/')
run: bash tests/version-check.sh --check-tag
82 changes: 82 additions & 0 deletions scripts/bump-version.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/usr/bin/env bash
# bump-version.sh — Update the version string across all plugin manifests.
#
# Usage:
# scripts/bump-version.sh <new-version>
# scripts/bump-version.sh <old-version> <new-version>
#
# When called with one argument the script detects the current version from
# .claude-plugin/plugin.json and replaces it everywhere.
# When called with two arguments the first is the version to replace (useful
# when the files are already inconsistent).
#
# Version files managed by this script:
# .claude-plugin/plugin.json
# .claude-plugin/marketplace.json
# .cursor-plugin/plugin.json

set -euo pipefail

REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"

VERSION_FILES=(
".claude-plugin/plugin.json"
".claude-plugin/marketplace.json"
".cursor-plugin/plugin.json"
)

usage() {
echo "Usage: $0 <new-version>" >&2
echo " $0 <old-version> <new-version>" >&2
exit 1
}

if [[ $# -eq 1 ]]; then
NEW_VERSION="$1"
# Detect the current version from the primary file
PRIMARY="${REPO_ROOT}/${VERSION_FILES[0]}"
OLD_VERSION=$(grep '"version"' "$PRIMARY" | head -1 | sed 's/.*"version": *"\([^"]*\)".*/\1/' || true)
if [[ -z "$OLD_VERSION" ]]; then
echo "ERROR: Could not detect current version from $PRIMARY" >&2
exit 1
fi
elif [[ $# -eq 2 ]]; then
OLD_VERSION="$1"
NEW_VERSION="$2"
else
usage
fi

# Validate version looks like semver (N.N.N)
if ! echo "$NEW_VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "ERROR: Version '$NEW_VERSION' does not look like semver (e.g. 5.7.0)" >&2
exit 1
fi

echo "Bumping version: $OLD_VERSION → $NEW_VERSION"

ERRORS=0
for rel_path in "${VERSION_FILES[@]}"; do
abs_path="${REPO_ROOT}/${rel_path}"
if [[ ! -f "$abs_path" ]]; then
echo "WARNING: $rel_path not found, skipping" >&2
continue
fi
if ! grep -qF "\"version\": \"${OLD_VERSION}\"" "$abs_path"; then
echo "ERROR: version string '${OLD_VERSION}' not found in $rel_path; cannot update" >&2
ERRORS=$((ERRORS + 1))
continue
fi
sed -i.bak "s/\"version\": \"${OLD_VERSION}\"/\"version\": \"${NEW_VERSION}\"/" "$abs_path" && rm -f "${abs_path}.bak"
echo " Updated $rel_path"
done

if [[ $ERRORS -gt 0 ]]; then
echo "FAIL: $ERRORS file(s) could not be updated." >&2
exit 1
fi

echo "Done. Remember to:"
echo " 1. Update RELEASE-NOTES.md with a new ## v${NEW_VERSION} section."
echo " 2. Commit the changes: git commit -am \"chore: bump version to ${NEW_VERSION}\""
echo " 3. Tag the release: git tag v${NEW_VERSION}"
84 changes: 84 additions & 0 deletions tests/version-check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/env bash
# version-check.sh — Validate that all plugin manifests declare the same version.
#
# Optionally validates against the latest git tag when --check-tag is passed.
#
# Usage:
# tests/version-check.sh # consistency check only
# tests/version-check.sh --check-tag # also verify versions match latest git tag
#
# Exit codes:
# 0 All version files are consistent (and match the git tag if --check-tag).
# 1 One or more version files disagree.

set -euo pipefail

REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"

CHECK_TAG=false
if [[ "${1:-}" == "--check-tag" ]]; then
CHECK_TAG=true
fi

VERSION_FILES=(
".claude-plugin/plugin.json"
".claude-plugin/marketplace.json"
".cursor-plugin/plugin.json"
)

extract_version() {
grep '"version"' "$1" | head -1 | sed 's/.*"version": *"\([^"]*\)".*/\1/' || true
}

ERRORS=0
FIRST_VERSION=""
FIRST_FILE=""

for rel_path in "${VERSION_FILES[@]}"; do
abs_path="${REPO_ROOT}/${rel_path}"
if [[ ! -f "$abs_path" ]]; then
echo "ERROR: $rel_path not found" >&2
ERRORS=$((ERRORS + 1))
continue
fi
ver=$(extract_version "$abs_path")
if [[ -z "$ver" ]]; then
echo "ERROR: Could not read version from $rel_path" >&2
ERRORS=$((ERRORS + 1))
continue
fi
if [[ -z "$FIRST_VERSION" ]]; then
FIRST_VERSION="$ver"
FIRST_FILE="$rel_path"
elif [[ "$ver" != "$FIRST_VERSION" ]]; then
echo "ERROR: Version mismatch: $rel_path has '$ver' but $FIRST_FILE has '$FIRST_VERSION'" >&2
ERRORS=$((ERRORS + 1))
fi
done

if [[ $ERRORS -gt 0 ]]; then
echo ""
echo "FAIL: Version files are inconsistent. Run scripts/bump-version.sh <new-version> to fix." >&2
exit 1
fi

echo "OK: All version files agree on version $FIRST_VERSION"

if [[ "$CHECK_TAG" == true ]]; then
# Get the tag that points exactly at HEAD (e.g. when triggered by a tag push).
# Falls back gracefully if HEAD is not tagged.
HEAD_TAG=$(git -C "$REPO_ROOT" describe --tags --exact-match HEAD 2>/dev/null || true)
if [[ -z "$HEAD_TAG" ]]; then
echo "WARNING: HEAD is not at a semver tag; skipping tag check." >&2
elif ! echo "$HEAD_TAG" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "WARNING: HEAD tag '$HEAD_TAG' is not a semver tag; skipping tag check." >&2
else
TAG_VERSION="${HEAD_TAG#v}"
if [[ "$FIRST_VERSION" != "$TAG_VERSION" ]]; then
echo "ERROR: Plugin version '$FIRST_VERSION' does not match HEAD tag '$HEAD_TAG'." >&2
echo " Run: scripts/bump-version.sh ${TAG_VERSION}" >&2
Comment on lines +67 to +79
exit 1
fi
echo "OK: Plugin version $FIRST_VERSION matches HEAD tag $HEAD_TAG"
fi
fi
Loading