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
125 changes: 99 additions & 26 deletions .claude/commands/release.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,112 @@
# Release

This command manages releases for the React SDK.
Prepares a release PR for the React SDK using `release-it`.

## Automatic Versioning and Changelog
## How it works

Package versions and changelog are now automatically updated when PRs are merged to `main`, based on the PR title:
`release-it` reads all commits since the last release, groups them by conventional commit type, proposes the next semver version, and then:

**During 0.x.x (pre-1.0 development):**
1. Bumps `package.json` to the new version
2. Updates `package-lock.json`
3. Prepends a new section to `CHANGELOG.md`
4. Commits all three files as `chore: release <version>`
5. Checks out a `chore/release-<version>` branch automatically

- `feat:` → MINOR bump (0.1.0 → 0.2.0) + "Features & Enhancements" changelog entry
- `fix:` → PATCH bump (0.1.0 → 0.1.1) + "Fixes" changelog entry
- `feat!:` or `fix!:` → MINOR bump\* (0.1.0 → 0.2.0) + "Breaking Changes" changelog entry
- Other types (`docs`, `chore`, etc.) → no version bump + "Chores & Maintenance" changelog entry
Version bump rules (during 0.x.x):

\*Per semver spec, breaking changes bump MINOR during 0.x.x since the API is unstable.
- `feat` → MINOR (0.1.0 → 0.2.0)
- `fix` → PATCH (0.1.0 → 0.1.1)
- `feat!` / `fix!` (breaking) → MINOR (0.1.0 → 0.2.0) — enforced via `preMajor: true` in `.release-it.json`; remove that flag when intentionally releasing 1.0.0
- `docs`, `chore`, `build`, etc. → no version bump

## Publishing a Release
## Dry run

After PRs are merged and versions are bumped:
If `--dry-run` is passed to this skill, run:

1. Verify the current version in `package.json` is correct
2. Verify `CHANGELOG.md` has been updated with recent changes
3. Run the `Publish to NPM` GitHub action at https://github.com/Gusto/embedded-react-sdk/actions/workflows/publish.yaml
4. Click `Run workflow` to publish to NPM
```bash
npm run release -- --dry-run
```

## Manual Release (if needed)
Capture the full terminal output. Extract the proposed version and the generated changelog block (everything between `Changelog:` and the next blank section), then apply the curation rules below to produce a polished preview. Present:

If automatic versioning didn't trigger or you need to manually adjust:
1. The target version (e.g. `0.44.2`)
2. The curated changelog section as it would appear in `CHANGELOG.md`

1. Examine the git history and find the most recent release commit
2. Calculate the next semantic version based on the conventional commit prefixes:
- `feat` commits → MINOR bump
- `fix` commits → PATCH bump
- Breaking changes (with `!`) → MINOR bump (during 0.x.x)
3. Update `package.json` to use the calculated version
4. Run `npm install` from the root directory after updating `package.json`
5. Update `CHANGELOG.md` with the commit descriptions as presently organized in that file. Note any breaking changes consistent with what is already in the changelog
6. After changes are verified, ask if user would like to commit those changes. If they do, create a git commit formatted as "chore: release <version-number>"
Do not make any file changes or git operations in dry-run mode.

## Steps

1. Fetch latest remote state (no branch switch needed):
```bash
git fetch origin
```
2. Run `npm run release -- --ci`
- The version is auto-detected from commits (see version bump rules above)
- To override: `npm run release -- --ci --increment=<version>` (e.g. `--increment=0.45.0`)
3. `release-it` will create and check out `chore/release-<version>` branched from `origin/main` automatically — no manual branch setup needed
4. **Review and improve the generated changelog** (see below)
5. After `release-it` finishes and the changelog is polished, push the branch and open a PR:
```bash
git push -u origin chore/release-<version>
gh pr create --title "chore: release <version>"
```
6. Once the PR is merged, trigger the [Publish to NPM](https://github.com/Gusto/embedded-react-sdk/actions/workflows/publish.yaml) GitHub action

## Changelog curation (step 4)

After `release-it` commits, the generated `CHANGELOG.md` entry is a mechanical draft — correct but not consumer-friendly. Before pushing, rewrite it to match the style of existing entries.

Read only the new section from `CHANGELOG.md` — it always starts at line 3 and ends just before the next `## ` header. Start with `Read` using `limit: 60`; if the second `## ` header has not yet appeared, read another 60 lines at a time until it does. Do not read the whole file. Then amend the release commit with an improved version.

### Formatting rules

Match the style of existing entries in `CHANGELOG.md`:

- Blank line after the version header (`## 0.44.2`) and after each section header (`### Fixes`)
- Use `-` bullets, not `*`
- No commit hash links (e.g. `([bb3913c](...))`) — remove them
- Keep PR links (e.g. `([#1774](...))`) if present — they are useful
- Use backticks for component names, hook names, prop names, type names, and npm packages

### Content rules

Write for SDK consumers (partners integrating the SDK), not for internal contributors:

- **Features**: Describe what the feature enables from the consumer's perspective. If a new component or hook is exported, name it.
- **Fixes**: Describe the user-visible symptom that was fixed, not the internal cause.
- **Breaking changes**: Always include a before/after code example showing the migration path. Name the old and new APIs explicitly.
- **Chores & Maintenance**: Collapse multiple dependency bumps into a single grouped line (e.g. "Bump dev dependencies (`vitest`, `typescript-eslint`, `@playwright/test`)"). Only call out a dep bump individually if it has meaningful consumer impact.
- Omit entries that have zero consumer relevance (internal tooling, CI config, docs-only changes).

### Example transformation

Generated:

```
### Fixes

* **SDK-828:** utc roundtrip bug in date picker field ([#1767](...)) ([82b158b](...))
* prevent skeleton/gap pop-in for empty TransitionPayrollAlert ([#1773](...)) ([8601f51](...))
```

After curation:

```
### Fixes

- Fix UTC roundtrip bug in date picker field where dates near midnight would shift by one day ([#1767](...))
- Prevent layout shift (skeleton flash) when `TransitionPayrollAlert` has no content to display ([#1773](...))
```

### Amending the commit

Once the changelog is edited to your satisfaction:

```bash
git add CHANGELOG.md
git commit --amend --no-edit
```

## Alternatively: trigger from CI

The [Prepare Release](https://github.com/Gusto/embedded-react-sdk/actions/workflows/prepare-release.yaml) workflow accepts a `workflow_dispatch` trigger. Run it from the GitHub Actions UI — it auto-detects the version from commits, creates the branch, and opens a PR without any local setup. The changelog curation step still applies — edit `CHANGELOG.md` and push an additional commit to the PR branch before merging.
116 changes: 0 additions & 116 deletions .github/workflows/auto-version.yaml

This file was deleted.

71 changes: 9 additions & 62 deletions .github/workflows/pr-title-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,66 +22,13 @@ jobs:
with:
node-version-file: '.nvmrc'

- name: Check PR title follows conventional commits
uses: actions/github-script@v7
with:
script: |
// Import the shared versioning logic
const { parseConventionalCommit, getVersionBumpType, VALID_TYPES } = await import('${{ github.workspace }}/build/versioning/conventionalCommits.js');

const title = context.payload.pull_request.title;
const parsed = parseConventionalCommit(title);

if (!parsed.isValid) {
const errorMessage = `
❌ **PR title does not follow conventional commits format**

Your title: \`${title}\`
${parsed.error ? `\nError: ${parsed.error}` : ''}

**Expected format:** \`type(optional-scope): description\`

**Valid types and their semver impact (0.x.x pre-release):**
| Type | Version Bump | Description |
|------|--------------|-------------|
| \`feat\` | MINOR | New features |
| \`fix\` | PATCH | Bug fixes |
| \`feat!\` or \`fix!\` | MINOR* | Breaking changes (note the \`!\`) |
| \`docs\` | none | Documentation |
| \`chore\` | none | Maintenance |
| \`refactor\` | none | Code refactoring |
| \`test\` | none | Tests |
| \`ci\` | none | CI changes |
| \`style\` | none | Code style |
| \`perf\` | none | Performance |
| \`build\` | none | Build system |
| \`revert\` | none | Reverts |
- name: Install commitlint
run: |
CLI=$(node -p "require('./package.json').devDependencies['@commitlint/cli']")
CONFIG=$(node -p "require('./package.json').devDependencies['@commitlint/config-conventional']")
npm install --no-save "@commitlint/cli@$CLI" "@commitlint/config-conventional@$CONFIG"

*Per semver spec, breaking changes bump MINOR during 0.x.x (pre-1.0)

**Note:** Types must be lowercase (e.g., \`feat\`, not \`FEAT\`)

**Examples:**
- \`feat: add new component\`
- \`fix: resolve validation issue\`
- \`feat(SDK-123): add payroll alerts\`
- \`feat!: redesign JSX component props\` (breaking change)
- \`chore: update dependencies\`
`;

core.setFailed(errorMessage);
} else {
const bumpType = getVersionBumpType(parsed, true); // true = pre-release (0.x.x)

let versionBump = 'none';
if (parsed.isBreaking) {
versionBump = 'MINOR (breaking change during 0.x.x)';
} else if (bumpType === 'minor') {
versionBump = 'MINOR';
} else if (bumpType === 'patch') {
versionBump = 'PATCH';
}

console.log(`✅ PR title is valid: "${title}"`);
console.log(`📦 Version bump on merge: ${versionBump}`);
}
- name: Check PR title follows conventional commits
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: echo "$PR_TITLE" | npx commitlint
65 changes: 65 additions & 0 deletions .github/workflows/prepare-release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: Prepare Release

on:
workflow_dispatch:
inputs:
version:
description: 'Target version (e.g. 1.2.3). Leave blank to auto-detect from commits.'
required: false
type: string

permissions:
contents: write
pull-requests: write

jobs:
prepare-release:
name: Prepare Release PR
runs-on:
group: gusto-ubuntu-default
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: '.nvmrc'

- name: Configure Git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

- name: Install dependencies
run: npm ci

- name: Run release-it
env:
RELEASE_VERSION: ${{ inputs.version }}
run: |
if [ -n "$RELEASE_VERSION" ]; then
if ! echo "$RELEASE_VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?(\+[a-zA-Z0-9.]+)?$'; then
echo "Error: Invalid version '$RELEASE_VERSION'. Expected semver (e.g. 0.45.0)."
exit 1
fi
npx release-it "$RELEASE_VERSION" --ci
else
npx release-it --ci
fi

- name: Push branch and open PR
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
BRANCH=$(git branch --show-current)
VERSION=$(node -p "require('./package.json').version")
git push -u origin "$BRANCH"
gh pr create \
--title "chore: release $VERSION" \
--body "Automated release PR for v$VERSION. Review the changelog and merge to publish." \
--base main \
--head "$BRANCH"
Loading
Loading