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
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: npm
- run: npm ci
- run: npm run build
- run: npm test
120 changes: 120 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
name: Release

on:
workflow_dispatch:
inputs:
version:
description: 'Release version (e.g., 1.3.0 or 2.0.0-beta.1)'
required: true
type: string

permissions:
contents: write
id-token: write

jobs:
release:
runs-on: ubuntu-latest
steps:
# ── Setup ──────────────────────────────────
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
registry-url: https://registry.npmjs.org

- run: npm ci

# ── Validate ───────────────────────────────
- name: Validate version format
run: |
if ! echo "${{ inputs.version }}" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*)?$'; then
echo "::error::Invalid version format. Use semver (e.g., 1.3.0 or 2.0.0-beta.1)"
exit 1
fi

- name: Check tag doesn't exist
run: |
if git rev-parse "v${{ inputs.version }}" >/dev/null 2>&1; then
echo "::error::Tag v${{ inputs.version }} already exists"
exit 1
fi

- name: Check [Unreleased] section exists
run: |
if ! grep -q '## \[Unreleased\]' CHANGELOG.md; then
echo "::error::CHANGELOG.md has no [Unreleased] section"
exit 1
fi

# ── Bump version ───────────────────────────
- name: Bump version in all files
run: npx tsx scripts/bump-version.ts ${{ inputs.version }} > release-notes.md

# ── Build & Test ───────────────────────────
- run: npm run build
- run: npm test

- name: Verify CLI version output
run: |
ACTUAL=$(node dist/cli.js --version)
if [ "$ACTUAL" != "${{ inputs.version }}" ]; then
echo "::error::CLI reports $ACTUAL, expected ${{ inputs.version }}"
exit 1
fi

# ── Commit & Tag ───────────────────────────
- name: Configure git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

- name: Commit version bump
run: |
git add -A
git commit -m "chore: bump version to ${{ inputs.version }}"
git push origin main

- name: Create and push tag
run: |
git tag -a "v${{ inputs.version }}" -m "Version ${{ inputs.version }}"
git push origin "v${{ inputs.version }}"

# ── Publish to npm ─────────────────────────
- name: Determine npm tag
id: npm-tag
run: |
if echo "${{ inputs.version }}" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "tag=latest" >> "$GITHUB_OUTPUT"
else
PRE=$(echo "${{ inputs.version }}" | sed 's/^[0-9]*\.[0-9]*\.[0-9]*-//' | sed 's/\..*//')
echo "tag=$PRE" >> "$GITHUB_OUTPUT"
fi

- name: Publish to npm
run: npm publish --provenance --tag ${{ steps.npm-tag.outputs.tag }}
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

# ── GitHub Release ─────────────────────────
- name: Create GitHub release
run: |
PRERELEASE_FLAG=""
if echo "${{ inputs.version }}" | grep -qE '-'; then
PRERELEASE_FLAG="--prerelease"
fi
gh release create "v${{ inputs.version }}" \
--title "v${{ inputs.version }}" \
--notes-file release-notes.md \
$PRERELEASE_FLAG
env:
GH_TOKEN: ${{ github.token }}

# ── Restore [Unreleased] ───────────────────
- name: Restore [Unreleased] section
run: |
sed -i "s/^## \[${{ inputs.version }}\]/## [Unreleased]\n\n---\n\n## [${{ inputs.version }}]/" CHANGELOG.md
git add CHANGELOG.md
git commit -m "chore: restore [Unreleased] section"
git push origin main
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ All notable changes to DevFlow will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

---

## [1.2.0] - 2026-03-05

### Added
Expand Down
181 changes: 85 additions & 96 deletions docs/reference/release-process.md
Original file line number Diff line number Diff line change
@@ -1,132 +1,121 @@
# Release Process

Full runbook for creating new DevFlow Kit releases.
One-click releases via GitHub Actions. The developer chooses the version; CI handles everything else.

## 1. Prepare the Release
## Prerequisites (One-Time Setup)

**Update Version** in `package.json`:
- Patch (x.y.Z): Bug fixes, docs, minor tweaks, internal refactoring
- Minor (x.Y.0): New features, commands, CLI options (backwards compatible)
- Major (X.0.0): Breaking changes, removed/renamed commands
1. **Create npm access token** — npmjs.com → Access Tokens → Granular Access Token
- Package: `devflow-kit` only
- Permissions: Read and Write
- Expiration: No expiration (recommended for CI)

2. **Add GitHub secret** — Repo Settings → Secrets → Actions → `NPM_TOKEN`

3. **Allow CI to push to main** — Repo Settings → Rules → Rulesets → add "GitHub Actions" to bypass actors

## During Development

Update `CHANGELOG.md` `[Unreleased]` section in each PR:

**Update CHANGELOG.md:**
```markdown
## [x.y.z] - YYYY-MM-DD
## [Unreleased]

### Added
- New features

### Changed
- Modified functionality
- New feature description

### Fixed
- Bug fixes

### Documentation
- Doc improvements
- Bug fix description

---
[x.y.z]: https://github.com/dean0x/devflow/releases/tag/vx.y.z
```

## 2. Build and Test
## Creating a Release

```bash
npm run build
node dist/cli.js --version # Verify new version
node dist/cli.js init # Test installation
npm pack --dry-run # Verify package contents
```
1. Go to **GitHub Actions** → **Release** workflow
2. Click **Run workflow**
3. Enter version (e.g., `1.3.0`) — strict semver, no `v` prefix
4. Click **Run workflow**

## 3. Commit Version Bump
Done. npm package, git tag, and GitHub release are all created automatically.

```bash
git add package.json package-lock.json CHANGELOG.md && \
git commit -m "chore: bump version to x.y.z

- Update package.json to x.y.z
- Add CHANGELOG entry for vx.y.z
- Document [summary of changes]"
## What CI Does

git push origin main
```

## 4. Publish to npm

```bash
npm publish
npm view devflow-kit version # Verify
validate version format
→ check tag doesn't exist
→ check [Unreleased] section exists
→ bump version in 21 files (package.json, plugin.json x17, marketplace.json, CHANGELOG.md)
→ sync package-lock.json
→ build
→ test
→ verify CLI --version output
→ commit "chore: bump version to X.Y.Z"
→ push to main
→ create + push git tag vX.Y.Z
→ npm publish --provenance
→ create GitHub release with extracted notes
→ restore [Unreleased] section + commit + push
```

## 5. Create Git Tag and GitHub Release
## Manual Fallback

```bash
git tag -a vx.y.z -m "Version x.y.z - [Brief Description]

- Key change 1
- Key change 2
- Key change 3"

git push origin vx.y.z
```
If CI is unavailable, release manually:

```bash
gh release create vx.y.z \
--title "vx.y.z - [Release Title]" \
--notes "$(cat <<'EOF'
# DevFlow Kit vx.y.z

[Brief description]

## Highlights
- Key improvement 1
- Key improvement 2

## Changes
# 1. Bump all version files
npm run version:bump -- 1.3.0 > release-notes.md

### Added
- New features
# 2. Build and test
npm run build && npm test

### Changed
- Modified functionality
# 3. Commit and push
git add -A
git commit -m "chore: bump version to 1.3.0"
git push origin main

### Fixed
- Bug fixes
# 4. Tag
git tag -a v1.3.0 -m "Version 1.3.0"
git push origin v1.3.0

## Installation
# 5. Publish
npm publish

\`\`\`bash
npx devflow-kit init
\`\`\`
# 6. GitHub release
gh release create v1.3.0 --title "v1.3.0" --notes-file release-notes.md

## Links
- npm: https://www.npmjs.com/package/devflow-kit
- Changelog: https://github.com/dean0x/devflow/blob/main/CHANGELOG.md
EOF
)"
# 7. Restore [Unreleased]
# Add back the [Unreleased] section above the new version in CHANGELOG.md
git add CHANGELOG.md
git commit -m "chore: restore [Unreleased] section"
git push origin main
```

## 6. Verify Release
## Troubleshooting

```bash
npm view devflow-kit
gh release view vx.y.z
npx devflow-kit@latest init
```
| Issue | Fix |
|-------|-----|
| "Tag already exists" | Tag was created but release failed. Delete tag: `git push --delete origin v1.3.0 && git tag -d v1.3.0`, then re-run. |
| "No [Unreleased] section" | CHANGELOG.md is missing the `## [Unreleased]` header. Add it manually above the latest version. |
| npm publish fails (401) | `NPM_TOKEN` secret expired or missing. Generate a new token and update the secret. |
| npm publish fails (403) | Token doesn't have write access to `devflow-kit`. Regenerate with correct package scope. |
| CLI version mismatch | Build output doesn't match expected version. Check that `package.json` was updated correctly. |
| Push to main rejected | GitHub Actions bot not in ruleset bypass list. Update branch protection rules. |

## Release Checklist

- [ ] Version bumped in package.json
- [ ] CHANGELOG.md updated
- [ ] All plugin.json files updated to match
- [ ] marketplace.json updated to match
- [ ] `npm run build` succeeds
- [ ] `npm test` passes
- [ ] `npm pack --dry-run` looks clean (no .map files, no build scripts)
- [ ] Local testing passed
- [ ] Version bump committed and pushed
- [ ] Published to npm
- [ ] Git tag created and pushed
- [ ] GitHub release created
- [ ] npm shows correct version
- [ ] `npx devflow-kit init` works
Items marked with **[auto]** are handled by CI:

- [ ] CHANGELOG.md `[Unreleased]` section has content
- [x] **[auto]** Version bumped in package.json
- [x] **[auto]** package-lock.json synced
- [x] **[auto]** All 17 plugin.json files updated
- [x] **[auto]** marketplace.json updated
- [x] **[auto]** CHANGELOG.md dated and linked
- [x] **[auto]** Build succeeds
- [x] **[auto]** Tests pass
- [x] **[auto]** CLI `--version` matches
- [x] **[auto]** Committed and pushed to main
- [x] **[auto]** Git tag created
- [x] **[auto]** Published to npm with provenance
- [x] **[auto]** GitHub release created
- [x] **[auto]** `[Unreleased]` section restored
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"dev": "tsc --watch",
"cli": "node dist/cli.js",
"prepublishOnly": "npm run build",
"version:bump": "npx tsx scripts/bump-version.ts",
"test": "vitest run",
"test:watch": "vitest"
},
Expand Down
Loading