Skip to content

Releasing

Cut a release with no “what number is this” step — your commit messages decide the version. Ongoing merges don’t ship anything; you release deliberately, by merging one pull request when a batch of work is ready.

cckit follows Semantic Versioning driven by Conventional Commits. The version number is automatic — the commit messages since the last tag pick it — and the release is cut by release-please, which keeps a single Release PR open and up to date. Merging that PR is the release.

release-please reads every Conventional Commit since the last tag and picks the highest bump (the local cckit release fallback applies the same rules via scripts/lib/version-bump.sh):

CommitBumpExample
feat!: / type!: / a BREAKING CHANGE: footermajor1.4.2 → 2.0.0
feat:minor1.4.2 → 1.5.0
fix: / perf: / refactor: / revert: / anything elsepatch1.4.2 → 1.4.3
no commits since the tagnoneno release

Check what the next version would be at any time:

Terminal window
cckit release --next # or: scripts/lib/version-bump.sh --next

.github/workflows/release-please.yml runs on every push to main. It doesn’t ship anything on its own — it keeps a single Release PR open (titled chore(main): release X.Y.Z) and rewrites it as more work lands. So you can merge, merge, merge, then release once by merging that PR when the batch is ready.

  1. Work reaches main. Feature branches merge into develop; when you’re ready, develop merges into main via PR. (main is protected — releases only.)

  2. release-please opens/updates the Release PR — it computes the bump from the commits since the last tag, bumps all four version surfaces (package.json, docs-site/package.json, .claude-plugin/plugin.json, cckit.config.json) plus Formula/cckit.rb, and regenerates CHANGELOG.md. Review it whenever; nothing ships yet.

  3. Merge the Release PR. That merge is the release: release-please tags vX.Y.Z and cuts the GitHub Release, then the publish job pushes to npm and bumps the Homebrew tap automatically.

A batch with only chore/docs noise produces no Release PR (bump=none → nothing to release). Because the release is a single PR merge, two feature PRs merging at once can no longer race each other into a corrupted tag.

Publishing is automatic once the Release PR is merged — the publish job in release-please.yml runs npm publish and bumps the jeiemgi/homebrew-cckit tap (recomputing the formula sha256). Each channel is gated on a repo secret and simply skipped if the secret is absent:

  • NPM_TOKEN — npm Automation token (npmjs.com → Access Tokens → Automation).
  • Homebrew tap auth — either of:
    • GitHub App (preferred)HOMEBREW_APP_ID + HOMEBREW_APP_PRIVATE_KEY for an app with Contents: write installed on homebrew-cckit. The workflow mints a short-lived, tap-scoped token per run — nothing to expire or rotate by hand.
    • HOMEBREW_TAP_TOKEN — legacy fallback: a PAT with push access to the tap repo (contents:write). Used only when the app secrets are absent.

scripts/publish.sh (aliased as cckit release) still cuts a release from your machine if you ever need to bypass CI. It is dry-run by default — nothing changes until you add --publish.

Terminal window
cckit release X.Y.Z # DRY RUN — prints the full plan, changes nothing
cckit release X.Y.Z --publish # tag + GitHub release + Homebrew formula + npm (needs tokens)
  • Every commit on a PR uses a Conventional Commit subject: type(scope): summary.
  • Breaking changes use ! after the type/scope or a BREAKING CHANGE: footer — never a silent major.
  • One logical change per commit so the generated release notes read cleanly.

Independent, educational project — not affiliated with or endorsed by Anthropic. Claude and Claude Code are trademarks of Anthropic PBC. Disclaimer & trademarks ·

From Mexico with love by josegtz