diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md index a05c2f9b..5df0febd 100644 --- a/.claude/skills/release/SKILL.md +++ b/.claude/skills/release/SKILL.md @@ -1,27 +1,55 @@ --- name: release description: Prepare a codegraph release — bump versions, update CHANGELOG, ROADMAP, BACKLOG, README, create PR -argument-hint: +argument-hint: "[version e.g. 3.1.1] (optional — auto-detects from commits)" allowed-tools: Bash, Read, Write, Edit, Glob, Grep, Agent --- -# Release v$ARGUMENTS +# Release -You are preparing a release for `@optave/codegraph` version **$ARGUMENTS**. +You are preparing a release for `@optave/codegraph`. + +**Version argument:** `$ARGUMENTS` +- If a version was provided (e.g. `3.1.1`), use it as the target version. +- If no version was provided (empty or blank `$ARGUMENTS`), you will auto-detect it in Step 1b. --- -## Step 1: Gather context +## Step 1a: Gather context Run these in parallel: -1. `git log --oneline v..HEAD` — all commits since the last release tag +1. `git log --oneline v..HEAD` — all commits since the last release tag (use `git describe --tags --match "v*" --abbrev=0` to find the previous tag) 2. Read `CHANGELOG.md` (first 80 lines) — understand the format 3. Read `package.json` — current version 4. `git describe --tags --match "v*" --abbrev=0` — find the previous stable release tag +## Step 1b: Determine version (if not provided) + +If `$ARGUMENTS` is empty or blank, determine the semver bump from the commits gathered in Step 1a. + +Scan **every commit message** between the last tag and HEAD. Apply these rules in priority order: + +| Condition | Bump | +|-----------|------| +| Any commit has a `BREAKING CHANGE:` or `BREAKING-CHANGE:` footer, **or** uses the `!` suffix (e.g. `feat!:`, `fix!:`, `refactor!:`) | **major** | +| Any commit uses `feat:` or `feat(scope):` | **minor** | +| Everything else (`fix:`, `refactor:`, `perf:`, `chore:`, `docs:`, `test:`, `ci:`, etc.) | **patch** | + +Given the current version `MAJOR.MINOR.PATCH` from `package.json`, compute the new version: +- **major** → `(MAJOR+1).0.0` +- **minor** → `MAJOR.(MINOR+1).0` +- **patch** → `MAJOR.MINOR.(PATCH+1)` + +Print the detected bump reason and the resolved version, e.g.: +> Detected **minor** bump (found `feat:` commits). Version: 3.1.0 → **3.2.0** + +Use the resolved version as `VERSION` for all subsequent steps. + +If `$ARGUMENTS` was provided, use it directly as `VERSION`. + ## Step 2: Bump version in package.json -Edit `package.json` to set `"version": "$ARGUMENTS"`. +Edit `package.json` to set `"version": "VERSION"`. **Do NOT bump:** - `crates/codegraph-core/Cargo.toml` — synced automatically by `scripts/sync-native-versions.js` during the publish workflow @@ -104,16 +132,16 @@ Run `grep` to confirm the new version appears in `package-lock.json` and that al ## Step 8: Create branch, commit, push, PR -1. Create branch: `git checkout -b release/$ARGUMENTS` +1. Create branch: `git checkout -b release/VERSION` 2. Stage only the files you changed: `CHANGELOG.md`, `package.json`, `package-lock.json`, `docs/roadmap/ROADMAP.md`, `docs/roadmap/BACKLOG.md` if changed, `README.md` if changed -3. Commit: `chore: release v$ARGUMENTS` -4. Push: `git push -u origin release/$ARGUMENTS` +3. Commit: `chore: release vVERSION` +4. Push: `git push -u origin release/VERSION` 5. Create PR: ``` -gh pr create --title "chore: release v$ARGUMENTS" --body "$(cat <<'EOF' +gh pr create --title "chore: release vVERSION" --body "$(cat <<'EOF' ## Summary -- Bump version to $ARGUMENTS +- Bump version to VERSION - Add CHANGELOG entry for all commits since previous release - Update ROADMAP progress diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index fea76d8d..9b253139 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -81,7 +81,7 @@ jobs: IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT" fi if [ "$COMMITS" -eq 0 ]; then - VERSION="${MAJOR}.${MINOR}.${PATCH}" + VERSION="${MAJOR}.${MINOR}.${PATCH}-dev.0" else VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))-dev.${COMMITS}" fi diff --git a/scripts/bench-version.js b/scripts/bench-version.js index cf2430ca..105aea03 100644 --- a/scripts/bench-version.js +++ b/scripts/bench-version.js @@ -5,7 +5,7 @@ * 1. `git describe --tags --match "v*" --abbrev=0` → find nearest release tag * 2. `git rev-list ..HEAD --count` → count commits since that tag * - * - If HEAD is exactly tagged (0 commits): returns "3.1.5" + * - If HEAD is exactly tagged (0 commits): returns "3.1.5-dev.0" * - Otherwise: returns "3.1.6-dev.12" (NEXT_PATCH-dev.COMMIT_COUNT) * This keeps dev versions in the correct semver range between the * current release and the next, avoiding inflated patch numbers. @@ -36,8 +36,8 @@ export function getBenchmarkVersion(pkgVersion, cwd) { const [, major, minor, patch] = m; - // Exact tag (0 commits since tag): return clean release version - if (commits === 0) return `${major}.${minor}.${patch}`; + // Exact tag (0 commits since tag): still mark as dev to avoid confusion with stable + if (commits === 0) return `${major}.${minor}.${patch}-dev.0`; // Dev build: MAJOR.MINOR.(PATCH+1)-dev.COMMITS const nextPatch = Number(patch) + 1; @@ -46,11 +46,17 @@ export function getBenchmarkVersion(pkgVersion, cwd) { /* git not available or no tags */ } - // Fallback: no git or no tags — match publish.yml's no-tags behavior (COMMITS=1) + // Fallback: no git or no tags — try to get a unique SHA so repeated runs + // don't collide in benchmark reports (which deduplicate by version) const parts = pkgVersion.split('.'); if (parts.length === 3) { const [major, minor, patch] = parts; - return `${major}.${minor}.${Number(patch) + 1}-dev.1`; + try { + const hash = execFileSync('git', ['rev-parse', '--short', 'HEAD'], { cwd, ...GIT_OPTS }).trim(); + return `${major}.${minor}.${Number(patch) + 1}-dev.${hash}`; + } catch { + return `${major}.${minor}.${Number(patch) + 1}-dev`; + } } return `${pkgVersion}-dev`; }