Skip to content

Latest commit

 

History

History
160 lines (120 loc) · 7.51 KB

File metadata and controls

160 lines (120 loc) · 7.51 KB

Versioning & Release Strategy

GSD follows Semantic Versioning 2.0.0 with three release tiers mapped to npm dist-tags.

Release Tiers

Tier What ships Version format npm tag Branch Install
Patch Bug fixes only 1.27.1 latest hotfix/1.27.1 npx @opengsd/gsd-core@latest
Minor Fixes + enhancements 1.28.0 latest (after RC) release/1.28.0 npx @opengsd/gsd-core@next (RC)
Major Fixes + enhancements + features 2.0.0 latest (after beta) release/2.0.0 npx @opengsd/gsd-core@next (beta)

npm Dist-Tags

Only two tags, following Angular/Next.js convention:

Tag Meaning Installed by
latest Stable production release npm install @opengsd/gsd-core (default)
next Pre-release (RC or beta) npm install @opengsd/gsd-core@next (opt-in)

The version string (-rc.1 vs -beta.1) communicates stability level. Users never get pre-releases unless they explicitly opt in.

Semver Rules

Increment When Examples
PATCH (1.27.x) Bug fixes, typo corrections, test additions Hook filter fix, config corruption fix
MINOR (1.x.0) Non-breaking enhancements, new commands, new runtime support New workflow command, discuss-mode feature
MAJOR (x.0.0) Breaking changes to config format, CLI flags, or runtime API; new features that alter existing behavior Removing a command, changing config schema

Pre-Release Version Progression

Major and minor releases use different pre-release types:

Minor: 1.28.0-rc.1  →  1.28.0-rc.2  →  1.28.0
Major: 2.0.0-beta.1 →  2.0.0-beta.2 →  2.0.0
  • beta (major releases only): Feature-complete but not fully tested. API mostly stable. Used for major releases to signal a longer testing cycle.
  • rc (minor releases only): Production-ready candidate. Only critical fixes expected.
  • Each version uses one pre-release type throughout its cycle. The rc action in the release workflow automatically selects the correct type based on the version.

Branch Structure

main                              ← stable, always deployable
  │
  ├── hotfix/1.27.1               ← patch: cherry-pick fix from main, publish to latest
  │
  ├── release/1.28.0              ← minor: accumulate fixes + enhancements, RC cycle
  │     ├── v1.28.0-rc.1          ← tag: published to next
  │     └── v1.28.0               ← tag: promoted to latest
  │
  ├── release/2.0.0               ← major: features + breaking changes, beta cycle
  │     ├── v2.0.0-beta.1         ← tag: published to next
  │     ├── v2.0.0-beta.2         ← tag: published to next
  │     └── v2.0.0                ← tag: promoted to latest
  │
  ├── fix/1200-bug-description    ← bug fix branch (merges to main)
  ├── feat/925-feature-name       ← feature branch (merges to main)
  └── chore/1206-maintenance      ← maintenance branch (merges to main)

Release Workflows

Patch Release (Hotfix)

For fixes that need to ship without waiting for the next minor.

A hotfix vX.YY.Z cumulatively includes everything in vX.YY.{Z-1} plus every fix:/chore: commit landed on main since that base. The base tag is the anchor — git cherry $BASE_TAG main reveals exactly which commits are still unshipped, and the new vX.YY.Z tag becomes the next hotfix's base, so the cycle is self-documenting.

How to dispatch a hotfix

Hotfixes are dispatched via the Release workflow (release.yml) with a patch version (X.Y.Z). There is no separate hotfix workflow.

  1. Trigger release.yml with action=create, version=1.27.1, auto_cherry_pick=true (default).
    • Workflow detects BASE_TAG = highest v1.27.* < v1.27.1 (so 1.27.1 branches from v1.27.0; 1.27.2 would branch from v1.27.1).
    • Branches hotfix/1.27.1 from BASE_TAG.
    • Auto-cherry-picks every fix:/chore: commit on origin/main not already in the base, oldest-first. Patch-equivalents are skipped via git cherry. feat:/refactor: are never auto-included.
    • On conflict the workflow halts with the offending SHA. Resolve manually on the branch, then re-run finalize with auto_cherry_pick=false.
    • Bumps package.json (and sdk/package.json), pushes the branch, and lists every included SHA in the run summary.
  2. (Optional) push additional manual commits to hotfix/1.27.1.
  3. Trigger release.yml with action=finalize. The workflow:
    • Runs install-smoke cross-platform gate.
    • Runs full test suite + coverage.
    • Builds SDK, bundles sdk-bundle/gsd-sdk.tgz inside the CC tarball.
    • Tags v1.27.1, publishes to @latest, re-points @next → v1.27.1.
    • Opens merge-back PR against main.

Minor Release (Standard Cycle)

For accumulated fixes and enhancements.

  1. Trigger release.yml with action create and version (e.g., 1.28.0)
  2. Workflow creates release/1.28.0 branch from main, bumps package.json
  3. Trigger release.yml with action rc to publish 1.28.0-rc.1 to next
  4. Test the RC: npx @opengsd/gsd-core@next
  5. If issues found: fix on release branch, publish rc.2, rc.3, etc.
  6. Trigger release.yml with action finalize — publishes 1.28.0 to latest
  7. Merge release branch to main

Major Release

Same as minor but uses -beta.N instead of -rc.N, signaling a longer testing cycle.

  1. Trigger release.yml with action create and version (e.g., 2.0.0)
  2. Trigger release.yml with action rc to publish 2.0.0-beta.1 to next
  3. If issues found: fix on release branch, publish beta.2, beta.3, etc.
  4. Trigger release.yml with action finalize -- publishes 2.0.0 to latest
  5. Merge release branch to main

Conventional Commits

Branch names map to commit types:

Branch prefix Commit type Version bump
fix/ fix: PATCH
feat/ feat: MINOR
hotfix/ fix: PATCH (immediate)
chore/ chore: none
docs/ docs: none
refactor/ refactor: none

Manifest Version Sync

Certain runtime-integration manifests carry a version field that must always match package.json:

  • .claude-plugin/plugin.json — Claude Code plugin manifest (issue #766)
  • gemini-extension.json — Gemini CLI extension manifest (issue #775)
  • .claude-plugin/marketplace.json — Claude plugin marketplace manifest; its version lives at plugins[0].version and is stamped via a nested versionKey descriptor (issue #1855)

The version npm lifecycle script (scripts/sync-manifest-versions.cjs --stage) stamps these files automatically on every npm version call, and stages them so they are included in the release commit alongside package.json.

To add a new manifest that must track the package version, register its path (and, if its version field is not top-level, its dotted versionKey) in the VERSIONED_MANIFESTS array in scripts/sync-manifest-versions.cjs. A regression test (tests/issue-844-manifest-version-sync.test.cjs) enforces this: it scans all committed JSON files for a matching version field and fails if any are missing from the registry.

Publishing Commands (Reference)

# Stable release (sets latest tag automatically)
npm publish

# Pre-release (must use --tag to avoid overwriting latest)
npm publish --tag next

# Verify what latest and next point to
npm dist-tag ls @opengsd/gsd-core