mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-02 16:19:27 +00:00
feat(release): implement semantic versioning automation with conventional commits
- Add semantic-release for automated npm publishing - Add commitlint + husky for commit message enforcement - Add professional branching strategy (beta → main workflow) - Create .releaserc.json with multi-branch config - Create sync-version-plugin.cjs for VERSION file sync - Update release.yml workflow (Node.js 22 required) - Update CLAUDE.md and CONTRIBUTING.md with new workflow - Deprecate manual bump-version.sh script
This commit is contained in:
@@ -0,0 +1,51 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- beta
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '22'
|
||||||
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|
||||||
|
- name: Setup Bun
|
||||||
|
uses: oven-sh/setup-bun@v2
|
||||||
|
with:
|
||||||
|
bun-version: latest
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: bun install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Build package
|
||||||
|
run: bun run build
|
||||||
|
|
||||||
|
- name: Validate (typecheck + lint + tests)
|
||||||
|
run: bun run validate
|
||||||
|
|
||||||
|
- name: Release
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
run: bunx semantic-release
|
||||||
Executable
+1
@@ -0,0 +1 @@
|
|||||||
|
bunx --no-install commitlint --edit "$1"
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Run validation before commit (typecheck + lint + format + tests)
|
||||||
|
bun run validate
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"branches": [
|
||||||
|
"main",
|
||||||
|
{ "name": "beta", "prerelease": true }
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"@semantic-release/commit-analyzer",
|
||||||
|
"@semantic-release/release-notes-generator",
|
||||||
|
["@semantic-release/changelog", {
|
||||||
|
"changelogFile": "CHANGELOG.md"
|
||||||
|
}],
|
||||||
|
"./scripts/sync-version-plugin.cjs",
|
||||||
|
["@semantic-release/npm", {
|
||||||
|
"npmPublish": true
|
||||||
|
}],
|
||||||
|
["@semantic-release/git", {
|
||||||
|
"assets": ["CHANGELOG.md", "package.json", "VERSION", "installers/install.sh", "installers/install.ps1"],
|
||||||
|
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
||||||
|
}],
|
||||||
|
["@semantic-release/github", {
|
||||||
|
"successComment": false,
|
||||||
|
"failComment": false
|
||||||
|
}]
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -113,6 +113,182 @@ Windows fallback: Copies if symlinks unavailable
|
|||||||
- TTY detect before colors, respect NO_COLOR
|
- TTY detect before colors, respect NO_COLOR
|
||||||
- Box borders for errors: ╔═╗║╚╝
|
- Box borders for errors: ╔═╗║╚╝
|
||||||
|
|
||||||
|
## Conventional Commits (MANDATORY)
|
||||||
|
|
||||||
|
**ALL commits MUST follow conventional commit format. Non-compliant commits are rejected by husky.**
|
||||||
|
|
||||||
|
### Commit Format
|
||||||
|
```
|
||||||
|
<type>(<scope>): <description>
|
||||||
|
|
||||||
|
[optional body]
|
||||||
|
|
||||||
|
[optional footer]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Commit Types (determines version bump)
|
||||||
|
| Type | Version Bump | Use For |
|
||||||
|
|------|--------------|---------|
|
||||||
|
| `feat:` | MINOR | New features |
|
||||||
|
| `fix:` | PATCH | Bug fixes |
|
||||||
|
| `perf:` | PATCH | Performance improvements |
|
||||||
|
| `feat!:` | MAJOR | Breaking changes |
|
||||||
|
| `docs:` | None | Documentation only |
|
||||||
|
| `style:` | None | Formatting, no code change |
|
||||||
|
| `refactor:` | None | Code restructure |
|
||||||
|
| `test:` | None | Adding tests |
|
||||||
|
| `chore:` | None | Maintenance |
|
||||||
|
| `ci:` | None | CI/CD changes |
|
||||||
|
| `build:` | None | Build system |
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
```bash
|
||||||
|
# Good - will be accepted
|
||||||
|
git commit -m "feat(cliproxy): add OAuth token refresh"
|
||||||
|
git commit -m "fix(doctor): handle missing config gracefully"
|
||||||
|
git commit -m "feat!: remove deprecated GLMT proxy" # BREAKING CHANGE
|
||||||
|
|
||||||
|
# Bad - will be REJECTED
|
||||||
|
git commit -m "added new feature"
|
||||||
|
git commit -m "Fixed bug"
|
||||||
|
git commit -m "WIP"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Branching Strategy (FOLLOW STRICTLY)
|
||||||
|
|
||||||
|
### Branch Hierarchy
|
||||||
|
|
||||||
|
```
|
||||||
|
main (production) ← beta (integration) ← feat/* | fix/* | docs/*
|
||||||
|
↑ ↑
|
||||||
|
│ └── All development merges here FIRST
|
||||||
|
│
|
||||||
|
└── Only receives: (1) Tested code from beta, (2) Hotfixes
|
||||||
|
```
|
||||||
|
|
||||||
|
### Branch Types
|
||||||
|
|
||||||
|
| Branch | Purpose | Merges From | Releases To |
|
||||||
|
|--------|---------|-------------|-------------|
|
||||||
|
| `main` | Production-ready | `beta`, `hotfix/*` | npm `@latest` |
|
||||||
|
| `beta` | Integration/staging | `feat/*`, `fix/*`, `docs/*` | npm `@beta` |
|
||||||
|
| `feat/*` | New features | - | → `beta` |
|
||||||
|
| `fix/*` | Bug fixes | - | → `beta` |
|
||||||
|
| `docs/*` | Documentation | - | → `beta` |
|
||||||
|
| `hotfix/*` | Critical production fixes | - | → `main` directly |
|
||||||
|
|
||||||
|
### Branch Naming Convention
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>/<short-description>
|
||||||
|
|
||||||
|
# Examples:
|
||||||
|
feat/oauth-token-refresh
|
||||||
|
fix/doctor-missing-config
|
||||||
|
docs/update-installation-guide
|
||||||
|
hotfix/critical-auth-bug
|
||||||
|
```
|
||||||
|
|
||||||
|
### Standard Development Workflow (Features/Fixes → Beta → Main)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. ALWAYS start from beta (integration branch)
|
||||||
|
git checkout beta
|
||||||
|
git pull origin beta
|
||||||
|
|
||||||
|
# 2. Create feature branch FROM BETA
|
||||||
|
git checkout -b feat/my-feature
|
||||||
|
|
||||||
|
# 3. Make changes with conventional commits
|
||||||
|
git commit -m "feat(scope): add new feature"
|
||||||
|
git commit -m "test(scope): add unit tests"
|
||||||
|
|
||||||
|
# 4. Push and create PR to BETA (not main!)
|
||||||
|
git push -u origin feat/my-feature
|
||||||
|
gh pr create --base beta --title "feat(scope): add new feature"
|
||||||
|
# → Merge triggers npm @beta release for testing
|
||||||
|
|
||||||
|
# 5. After testing in beta, promote to main
|
||||||
|
git checkout beta
|
||||||
|
gh pr create --base main --title "chore: promote beta to main"
|
||||||
|
# → Merge triggers npm @latest release
|
||||||
|
|
||||||
|
# 6. Clean up
|
||||||
|
git checkout beta
|
||||||
|
git pull origin beta
|
||||||
|
git branch -d feat/my-feature
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hotfix Workflow (Critical Production Fixes Only)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Start from main (production)
|
||||||
|
git checkout main
|
||||||
|
git pull origin main
|
||||||
|
|
||||||
|
# 2. Create hotfix branch
|
||||||
|
git checkout -b hotfix/critical-bug
|
||||||
|
|
||||||
|
# 3. Fix and commit
|
||||||
|
git commit -m "fix: critical authentication bypass"
|
||||||
|
|
||||||
|
# 4. PR directly to main (skip beta)
|
||||||
|
gh pr create --base main --title "fix: critical authentication bypass"
|
||||||
|
# → Merge triggers immediate npm @latest release
|
||||||
|
|
||||||
|
# 5. Sync hotfix back to beta
|
||||||
|
git checkout beta
|
||||||
|
git pull origin beta
|
||||||
|
git merge main
|
||||||
|
git push origin beta
|
||||||
|
|
||||||
|
# 6. Clean up
|
||||||
|
git branch -d hotfix/critical-bug
|
||||||
|
```
|
||||||
|
|
||||||
|
### Keeping Beta in Sync with Main
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# After any main release, sync to beta
|
||||||
|
git checkout beta
|
||||||
|
git pull origin beta
|
||||||
|
git merge main
|
||||||
|
git push origin beta
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rules
|
||||||
|
|
||||||
|
1. **NEVER commit directly to `main` or `beta`** - always use PRs
|
||||||
|
2. **ALWAYS create feature branches from `beta`** (not main)
|
||||||
|
3. **Only `hotfix/*` branches go directly to `main`**
|
||||||
|
4. **`beta` must always be up-to-date with `main`**
|
||||||
|
5. **Delete feature branches after merge**
|
||||||
|
6. **Test in `@beta` before promoting to `@latest`**
|
||||||
|
|
||||||
|
## Automated Releases (DO NOT MANUALLY TAG)
|
||||||
|
|
||||||
|
**Releases are FULLY AUTOMATED via semantic-release. NEVER manually bump versions or create tags.**
|
||||||
|
|
||||||
|
### Release Channels
|
||||||
|
| Branch | npm Tag | When |
|
||||||
|
|--------|---------|------|
|
||||||
|
| `main` | `@latest` | Merge PR to main |
|
||||||
|
| `beta` | `@beta` | Push to beta branch |
|
||||||
|
|
||||||
|
### What CI Does Automatically
|
||||||
|
1. Analyzes commits since last release
|
||||||
|
2. Determines version bump from commit types
|
||||||
|
3. Updates `CHANGELOG.md`, `VERSION`, `package.json`, installers
|
||||||
|
4. Creates git tag
|
||||||
|
5. Publishes to npm
|
||||||
|
6. Creates GitHub release
|
||||||
|
|
||||||
|
**NEVER DO:**
|
||||||
|
- `./scripts/bump-version.sh` (deprecated, emergency only)
|
||||||
|
- `git tag vX.Y.Z` (tags are auto-created)
|
||||||
|
- Manual `npm publish` (CI handles it)
|
||||||
|
- Commit directly to `main`
|
||||||
|
|
||||||
## Development Workflows
|
## Development Workflows
|
||||||
|
|
||||||
### Testing (REQUIRED before PR)
|
### Testing (REQUIRED before PR)
|
||||||
@@ -123,11 +299,6 @@ bun run test:native # Native install tests
|
|||||||
bun run test:unit # Unit tests
|
bun run test:unit # Unit tests
|
||||||
```
|
```
|
||||||
|
|
||||||
### Version Management
|
|
||||||
```bash
|
|
||||||
./scripts/bump-version.sh [major|minor|patch] # Updates VERSION, sync-version.js
|
|
||||||
```
|
|
||||||
|
|
||||||
### Local Development
|
### Local Development
|
||||||
```bash
|
```bash
|
||||||
./scripts/dev-install.sh # Build, pack, install globally
|
./scripts/dev-install.sh # Build, pack, install globally
|
||||||
@@ -143,18 +314,22 @@ rm -rf ~/.ccs # Clean environment
|
|||||||
4. Add unit tests (`tests/unit/**/*.test.js`)
|
4. Add unit tests (`tests/unit/**/*.test.js`)
|
||||||
5. Run `bun run validate`
|
5. Run `bun run validate`
|
||||||
6. Update README.md if user-facing
|
6. Update README.md if user-facing
|
||||||
|
7. **Commit with**: `git commit -m "feat(scope): description"`
|
||||||
|
|
||||||
### Bug Fix Checklist
|
### Bug Fix Checklist
|
||||||
1. Add regression test first
|
1. Add regression test first
|
||||||
2. Fix in TypeScript (or native scripts if bootstrap-related)
|
2. Fix in TypeScript (or native scripts if bootstrap-related)
|
||||||
3. Run `bun run validate`
|
3. Run `bun run validate`
|
||||||
|
4. **Commit with**: `git commit -m "fix(scope): description"`
|
||||||
|
|
||||||
## Pre-PR Checklist (MANDATORY)
|
## Pre-PR Checklist (MANDATORY)
|
||||||
|
|
||||||
- [ ] `bun run validate` passes (typecheck + lint:fix + format:check + tests)
|
- [ ] `bun run validate` passes (typecheck + lint:fix + format:check + tests)
|
||||||
|
- [ ] All commits follow conventional format (`feat:`, `fix:`, etc.)
|
||||||
- [ ] `--help` updated and consistent across src/ccs.ts, lib/ccs, lib/ccs.ps1
|
- [ ] `--help` updated and consistent across src/ccs.ts, lib/ccs, lib/ccs.ps1
|
||||||
- [ ] ASCII only (NO emojis), NO_COLOR respected
|
- [ ] ASCII only (NO emojis), NO_COLOR respected
|
||||||
- [ ] Idempotent install, concurrent sessions work, instance isolation maintained
|
- [ ] Idempotent install, concurrent sessions work, instance isolation maintained
|
||||||
|
- [ ] **DO NOT** manually bump version or create tags
|
||||||
|
|
||||||
## Error Handling Principles
|
## Error Handling Principles
|
||||||
|
|
||||||
|
|||||||
+178
-20
@@ -94,24 +94,131 @@ Test on all platforms before submitting PR:
|
|||||||
ccs --invalid-flag # Should pass through to Claude
|
ccs --invalid-flag # Should pass through to Claude
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Branching Strategy
|
||||||
|
|
||||||
|
#### Branch Hierarchy
|
||||||
|
|
||||||
|
```
|
||||||
|
main (production) ← beta (integration) ← feat/* | fix/* | docs/*
|
||||||
|
↑ ↑
|
||||||
|
│ └── All contributions merge here FIRST
|
||||||
|
│
|
||||||
|
└── Only: tested beta code OR hotfix/*
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Branch Types
|
||||||
|
|
||||||
|
| Branch | Purpose | PRs Target | Releases To |
|
||||||
|
|--------|---------|------------|-------------|
|
||||||
|
| `main` | Production | From `beta` only | npm `@latest` |
|
||||||
|
| `beta` | Integration/testing | From `feat/*`, `fix/*` | npm `@beta` |
|
||||||
|
| `feat/*` | New features | → `beta` | - |
|
||||||
|
| `fix/*` | Bug fixes | → `beta` | - |
|
||||||
|
| `docs/*` | Documentation | → `beta` | - |
|
||||||
|
| `hotfix/*` | Critical fixes | → `main` directly | npm `@latest` |
|
||||||
|
|
||||||
|
#### Branch Naming Convention
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>/<short-description>
|
||||||
|
|
||||||
|
# Examples:
|
||||||
|
feat/oauth-token-refresh
|
||||||
|
fix/doctor-missing-config
|
||||||
|
docs/update-installation-guide
|
||||||
|
hotfix/critical-security-fix
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Development Workflow (Contributors)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Fork and clone
|
||||||
|
git clone https://github.com/YOUR_USERNAME/ccs.git
|
||||||
|
cd ccs
|
||||||
|
|
||||||
|
# 2. Add upstream remote
|
||||||
|
git remote add upstream https://github.com/kaitranntt/ccs.git
|
||||||
|
|
||||||
|
# 3. ALWAYS start from latest BETA (not main!)
|
||||||
|
git checkout beta
|
||||||
|
git pull upstream beta
|
||||||
|
|
||||||
|
# 4. Create feature branch FROM BETA
|
||||||
|
git checkout -b feat/my-feature # for features
|
||||||
|
git checkout -b fix/bug-name # for bug fixes
|
||||||
|
git checkout -b docs/update-readme # for documentation
|
||||||
|
|
||||||
|
# 5. Make changes with conventional commits
|
||||||
|
git commit -m "feat(scope): add new feature"
|
||||||
|
|
||||||
|
# 6. Push to your fork
|
||||||
|
git push -u origin feat/my-feature
|
||||||
|
|
||||||
|
# 7. Create PR targeting BETA (not main!)
|
||||||
|
gh pr create --base beta --title "feat(scope): add new feature"
|
||||||
|
# → After merge, your changes release to npm @beta for testing
|
||||||
|
|
||||||
|
# 8. Maintainers will promote tested beta to main
|
||||||
|
# → This triggers npm @latest release
|
||||||
|
|
||||||
|
# 9. After PR merged, clean up
|
||||||
|
git checkout beta
|
||||||
|
git pull upstream beta
|
||||||
|
git branch -d feat/my-feature
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Hotfix Workflow (Critical Production Fixes Only)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Only for critical bugs in production!
|
||||||
|
# 1. Start from main
|
||||||
|
git checkout main
|
||||||
|
git pull upstream main
|
||||||
|
|
||||||
|
# 2. Create hotfix branch
|
||||||
|
git checkout -b hotfix/critical-bug
|
||||||
|
|
||||||
|
# 3. Fix and commit
|
||||||
|
git commit -m "fix: critical security vulnerability"
|
||||||
|
|
||||||
|
# 4. PR directly to main (skip beta)
|
||||||
|
gh pr create --base main --title "fix: critical security vulnerability"
|
||||||
|
|
||||||
|
# 5. After merge, sync to beta
|
||||||
|
# (Maintainers will handle this)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Rules
|
||||||
|
|
||||||
|
- **NEVER** commit directly to `main` or `beta`
|
||||||
|
- **ALWAYS** create branches from `beta` (not main)
|
||||||
|
- **ALWAYS** target PRs to `beta` (not main)
|
||||||
|
- **ONLY** `hotfix/*` branches target `main` directly
|
||||||
|
- **DELETE** branches after merge
|
||||||
|
|
||||||
### Submission Process
|
### Submission Process
|
||||||
|
|
||||||
#### Before Submitting
|
#### Before Submitting
|
||||||
|
|
||||||
1. Fork the repository
|
1. Ensure branch is from `beta` (not main)
|
||||||
2. Create a feature branch: `git checkout -b feature-name`
|
2. Ensure branch follows naming: `feat/*`, `fix/*`, `docs/*`
|
||||||
3. Make your changes
|
3. Run `bun run validate` - must pass
|
||||||
4. Test on all platforms
|
4. Rebase on latest beta: `git rebase beta`
|
||||||
5. Ensure existing tests pass
|
5. Test on all platforms if possible
|
||||||
|
|
||||||
#### Pull Request Requirements
|
#### Pull Request Requirements
|
||||||
|
|
||||||
|
- **Target `beta` branch** (not main!) - unless hotfix
|
||||||
- Clear description of changes
|
- Clear description of changes
|
||||||
- Testing instructions if applicable
|
- Testing instructions if applicable
|
||||||
- Link to relevant issues
|
- Link to relevant issues
|
||||||
- Follow existing commit message style
|
- **All commits MUST follow conventional format** (enforced by husky)
|
||||||
|
- **Branch MUST follow naming convention** (`feat/*`, `fix/*`, etc.)
|
||||||
|
- Run `bun run validate` before submitting
|
||||||
|
|
||||||
#### Commit Message Style
|
#### Commit Message Style (MANDATORY)
|
||||||
|
|
||||||
|
**All commits MUST follow conventional commit format. Non-compliant commits are automatically rejected.**
|
||||||
|
|
||||||
```
|
```
|
||||||
type(scope): description
|
type(scope): description
|
||||||
@@ -121,11 +228,29 @@ type(scope): description
|
|||||||
[optional footer]
|
[optional footer]
|
||||||
```
|
```
|
||||||
|
|
||||||
Examples:
|
**Commit types that trigger releases:**
|
||||||
```
|
| Type | Version Bump |
|
||||||
fix(installer): handle git worktree detection
|
|------|--------------|
|
||||||
feat(config): support custom config location
|
| `feat:` | MINOR (5.0.2 → 5.1.0) |
|
||||||
docs(readme): update installation instructions
|
| `fix:` | PATCH (5.0.2 → 5.0.3) |
|
||||||
|
| `perf:` | PATCH |
|
||||||
|
| `feat!:` | MAJOR (5.0.2 → 6.0.0) |
|
||||||
|
|
||||||
|
**Commit types that DON'T trigger releases:**
|
||||||
|
`docs:`, `style:`, `refactor:`, `test:`, `chore:`, `ci:`, `build:`
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
```bash
|
||||||
|
# Good - will be accepted
|
||||||
|
git commit -m "fix(installer): handle git worktree detection"
|
||||||
|
git commit -m "feat(config): support custom config location"
|
||||||
|
git commit -m "docs(readme): update installation instructions"
|
||||||
|
git commit -m "feat!: remove deprecated API" # Breaking change
|
||||||
|
|
||||||
|
# Bad - will be REJECTED by husky
|
||||||
|
git commit -m "fixed bug"
|
||||||
|
git commit -m "WIP"
|
||||||
|
git commit -m "updated stuff"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Development Setup
|
### Development Setup
|
||||||
@@ -269,16 +394,49 @@ Be respectful, constructive, and focused on the project's philosophy of simplici
|
|||||||
- **[GitHub Issues](https://github.com/kaitranntt/ccs/issues)**: Track bugs, features, and discussions
|
- **[GitHub Issues](https://github.com/kaitranntt/ccs/issues)**: Track bugs, features, and discussions
|
||||||
- **[VERSION](./VERSION)**: Current version number
|
- **[VERSION](./VERSION)**: Current version number
|
||||||
|
|
||||||
## 🎯 Release Process
|
## 🎯 Release Process (FULLY AUTOMATED)
|
||||||
|
|
||||||
**For maintainers:**
|
**Releases are automated via semantic-release. DO NOT manually bump versions or create tags.**
|
||||||
|
|
||||||
1. Bump version: `./scripts/bump-version.sh [major|minor|patch]`
|
### How Releases Work
|
||||||
2. Update CHANGELOG if applicable
|
|
||||||
3. Commit: `git commit -m "chore: bump version to X.Y.Z"`
|
1. **Write conventional commits** during development
|
||||||
4. Tag: `git tag vX.Y.Z`
|
2. **Merge PR to `main`** (or push to `beta`)
|
||||||
5. Push: `git push origin main && git push origin vX.Y.Z`
|
3. **CI automatically:**
|
||||||
6. GitHub Actions will automatically publish to npm
|
- Analyzes commits since last release
|
||||||
|
- Determines version bump from commit types
|
||||||
|
- Updates CHANGELOG.md, VERSION, package.json
|
||||||
|
- Creates git tag
|
||||||
|
- Publishes to npm
|
||||||
|
- Creates GitHub release
|
||||||
|
|
||||||
|
### Release Channels
|
||||||
|
|
||||||
|
| Branch | npm Tag | Use Case |
|
||||||
|
|--------|---------|----------|
|
||||||
|
| `main` | `@latest` | Stable production releases |
|
||||||
|
| `beta` | `@beta` | Pre-release testing |
|
||||||
|
|
||||||
|
### Workflow
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Stable release
|
||||||
|
git checkout -b feat/my-feature
|
||||||
|
git commit -m "feat: add new feature"
|
||||||
|
gh pr create --base main
|
||||||
|
# → Merge PR → CI auto-releases to npm @latest
|
||||||
|
|
||||||
|
# Beta release
|
||||||
|
git checkout beta
|
||||||
|
git merge feat/experimental
|
||||||
|
git push origin beta
|
||||||
|
# → CI auto-releases to npm @beta
|
||||||
|
```
|
||||||
|
|
||||||
|
**NEVER DO:**
|
||||||
|
- `./scripts/bump-version.sh` (deprecated, emergency only)
|
||||||
|
- `git tag vX.Y.Z` (tags are auto-created)
|
||||||
|
- Manual `npm publish` (CI handles it)
|
||||||
|
|
||||||
## 📄 License
|
## 📄 License
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// Commitlint configuration for conventional commits
|
||||||
|
// See: https://commitlint.js.org/
|
||||||
|
module.exports = {
|
||||||
|
extends: ['@commitlint/config-conventional'],
|
||||||
|
rules: {
|
||||||
|
// Allowed commit types (determines version bump)
|
||||||
|
'type-enum': [2, 'always', [
|
||||||
|
'feat', // New feature → MINOR
|
||||||
|
'fix', // Bug fix → PATCH
|
||||||
|
'docs', // Documentation only → no release
|
||||||
|
'style', // Formatting, no code change → no release
|
||||||
|
'refactor', // Code change, no feat/fix → no release
|
||||||
|
'perf', // Performance improvement → PATCH
|
||||||
|
'test', // Adding tests → no release
|
||||||
|
'chore', // Maintenance → no release
|
||||||
|
'ci', // CI/CD changes → no release
|
||||||
|
'build', // Build system → no release
|
||||||
|
'revert' // Revert commit → PATCH
|
||||||
|
]],
|
||||||
|
// Subject must be lowercase
|
||||||
|
'subject-case': [2, 'always', 'lower-case'],
|
||||||
|
// Max header length (type + scope + subject)
|
||||||
|
'header-max-length': [2, 'always', 100]
|
||||||
|
}
|
||||||
|
};
|
||||||
+139
-96
@@ -2,128 +2,171 @@
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
CCS uses a centralized version management system to ensure consistency across all components including npm package and shell installers.
|
CCS uses **semantic-release** for fully automated versioning and releases. Version numbers are determined automatically from conventional commit messages - no manual version bumping required.
|
||||||
|
|
||||||
## Version Locations
|
## Release Channels
|
||||||
|
|
||||||
The version number must be kept in sync across these files:
|
| Branch | npm Tag | Example | Description |
|
||||||
|
|--------|---------|---------|-------------|
|
||||||
|
| `main` | `@latest` | `5.1.0` | Stable production releases |
|
||||||
|
| `beta` | `@beta` | `5.1.0-beta.1` | Pre-release testing |
|
||||||
|
|
||||||
1. **`VERSION`** - Primary version file (read by shell scripts at runtime)
|
## How Releases Work
|
||||||
2. **`package.json`** - npm package version (for npm installations)
|
|
||||||
3. **`installers/install.sh`** - Hardcoded for standalone installations (`curl | bash`)
|
|
||||||
4. **`installers/install.ps1`** - Hardcoded for standalone installations (`irm | iex`)
|
|
||||||
|
|
||||||
## Why Multiple Version Locations?
|
### Automatic Release (Default)
|
||||||
|
|
||||||
### npm Package (`package.json`)
|
1. **Write conventional commits** during development
|
||||||
When users run `npm install -g @kaitranntt/ccs`, npm uses the version from `package.json` for package management and dependency resolution.
|
2. **Merge PR to `main`** (or push to `beta`)
|
||||||
|
3. **CI automatically**:
|
||||||
|
- Analyzes commits to determine version bump
|
||||||
|
- Updates `CHANGELOG.md`
|
||||||
|
- Updates `VERSION`, `package.json`, installers
|
||||||
|
- Creates git tag
|
||||||
|
- Publishes to npm
|
||||||
|
- Creates GitHub release
|
||||||
|
|
||||||
### Shell Installers (Hardcoded versions)
|
### Conventional Commits
|
||||||
When users run:
|
|
||||||
- `curl -fsSL ccs.kaitran.ca/install | bash`
|
|
||||||
- `irm ccs.kaitran.ca/install.ps1 | iex`
|
|
||||||
|
|
||||||
The installer script is downloaded and executed directly **without** other files. Therefore, installers must have a hardcoded version as fallback.
|
Version bump is determined by commit type:
|
||||||
|
|
||||||
### VERSION File (Runtime)
|
| Commit Type | Version Bump | Example |
|
||||||
For git-based installations or shell scripts, the VERSION file is read at runtime to display accurate version information, overriding hardcoded versions.
|
|-------------|--------------|---------|
|
||||||
|
| `feat:` | MINOR | `5.0.2` → `5.1.0` |
|
||||||
|
| `fix:` | PATCH | `5.0.2` → `5.0.3` |
|
||||||
|
| `perf:` | PATCH | `5.0.2` → `5.0.3` |
|
||||||
|
| `feat!:` or `BREAKING CHANGE:` | MAJOR | `5.0.2` → `6.0.0` |
|
||||||
|
| `docs:`, `style:`, `refactor:`, `test:`, `chore:`, `ci:` | No release | - |
|
||||||
|
|
||||||
## Updating Version
|
### Commit Format
|
||||||
|
|
||||||
### Automated Method (Recommended)
|
```
|
||||||
|
<type>(<scope>): <description>
|
||||||
|
|
||||||
Use the provided script to bump the version automatically:
|
[optional body]
|
||||||
|
|
||||||
|
[optional footer(s)]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
```bash
|
||||||
|
feat(cliproxy): add OAuth token refresh
|
||||||
|
fix(doctor): handle missing config gracefully
|
||||||
|
feat!: remove deprecated GLMT proxy
|
||||||
|
docs: update installation guide
|
||||||
|
```
|
||||||
|
|
||||||
|
## Workflow Examples
|
||||||
|
|
||||||
|
### Stable Release
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Work on feature branch
|
||||||
|
git checkout -b feat/new-feature
|
||||||
|
git commit -m "feat(scope): add new feature"
|
||||||
|
|
||||||
|
# 2. Open PR to main
|
||||||
|
gh pr create --base main
|
||||||
|
|
||||||
|
# 3. Merge PR → CI auto-releases to npm @latest
|
||||||
|
```
|
||||||
|
|
||||||
|
### Beta Release
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Switch to beta branch
|
||||||
|
git checkout beta
|
||||||
|
git merge feat/experimental
|
||||||
|
|
||||||
|
# 2. Push → CI auto-releases to npm @beta
|
||||||
|
git push origin beta
|
||||||
|
```
|
||||||
|
|
||||||
|
### Installing Different Channels
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Stable (default)
|
||||||
|
npm install -g @kaitranntt/ccs
|
||||||
|
|
||||||
|
# Beta
|
||||||
|
npm install -g @kaitranntt/ccs@beta
|
||||||
|
|
||||||
|
# Specific version
|
||||||
|
npm install -g @kaitranntt/ccs@5.1.0-beta.1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Version Files
|
||||||
|
|
||||||
|
These files are automatically synced by semantic-release:
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `VERSION` | Shell scripts, runtime display |
|
||||||
|
| `package.json` | npm package version |
|
||||||
|
| `installers/install.sh` | Standalone bash installer |
|
||||||
|
| `installers/install.ps1` | Standalone PowerShell installer |
|
||||||
|
| `CHANGELOG.md` | Auto-generated release notes |
|
||||||
|
|
||||||
|
## Local Commit Validation
|
||||||
|
|
||||||
|
Commits are validated locally via husky + commitlint:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# This will be rejected:
|
||||||
|
git commit -m "added new feature"
|
||||||
|
|
||||||
|
# This will pass:
|
||||||
|
git commit -m "feat: add new feature"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Emergency Manual Release
|
||||||
|
|
||||||
|
For emergencies only (e.g., CI broken, hotfix needed):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Bump patch version (2.1.1 -> 2.1.2)
|
|
||||||
./scripts/bump-version.sh patch
|
./scripts/bump-version.sh patch
|
||||||
|
git add -A
|
||||||
# Bump minor version (2.1.1 -> 2.2.0)
|
git commit -m "chore(release): emergency release"
|
||||||
./scripts/bump-version.sh minor
|
git push origin main
|
||||||
|
npm publish
|
||||||
# Bump major version (2.1.1 -> 3.0.0)
|
|
||||||
./scripts/bump-version.sh major
|
|
||||||
```
|
```
|
||||||
|
|
||||||
This updates:
|
## Tooling
|
||||||
- VERSION file
|
|
||||||
- package.json (npm package version)
|
|
||||||
- installers/install.sh (hardcoded version)
|
|
||||||
- installers/install.ps1 (hardcoded version)
|
|
||||||
|
|
||||||
### Manual Method
|
| Tool | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `semantic-release` | Automated versioning and publishing |
|
||||||
|
| `@semantic-release/changelog` | Auto-update CHANGELOG.md |
|
||||||
|
| `@semantic-release/git` | Commit version files back |
|
||||||
|
| `commitlint` | Validate commit message format |
|
||||||
|
| `husky` | Git hooks for local validation |
|
||||||
|
|
||||||
If updating manually, update version in ALL four locations:
|
## Configuration Files
|
||||||
|
|
||||||
1. **VERSION file**:
|
- `.releaserc.json` - semantic-release configuration
|
||||||
```bash
|
- `commitlint.config.cjs` - commit message rules
|
||||||
echo "2.4.6" > VERSION
|
- `.husky/commit-msg` - commit validation hook
|
||||||
```
|
- `.github/workflows/release.yml` - CI release workflow
|
||||||
|
|
||||||
2. **package.json** (line 4):
|
## Troubleshooting
|
||||||
```json
|
|
||||||
"version": "2.4.6",
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **installers/install.sh** (line ~34):
|
### Commit rejected by commitlint
|
||||||
```bash
|
|
||||||
CCS_VERSION="2.4.6"
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **installers/install.ps1** (line ~33):
|
|
||||||
```powershell
|
|
||||||
$CcsVersion = "2.4.6"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Release Checklist
|
|
||||||
|
|
||||||
When releasing a new version:
|
|
||||||
|
|
||||||
- [ ] Update version using `./scripts/bump-version.sh X.Y.Z`
|
|
||||||
- [ ] Review changes: `git diff`
|
|
||||||
- [ ] Run comprehensive tests: `npm test`
|
|
||||||
- [ ] ~~Test both installation methods if applicable~~ (Shell installers DEPRECATED)
|
|
||||||
- [ ] Update CHANGELOG.md with release notes
|
|
||||||
- [ ] Commit changes: `git commit -am "chore: bump version to X.Y.Z"`
|
|
||||||
- [ ] Push: `git push`
|
|
||||||
- [ ] ~~Verify CloudFlare worker serves updated installer~~ (Shell installers DEPRECATED)
|
|
||||||
- [ ] Publish to npm: `npm publish`
|
|
||||||
|
|
||||||
## Version Display
|
|
||||||
|
|
||||||
After installation, users can check version:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Shows CCS version (from VERSION file if available)
|
# Check what's wrong
|
||||||
ccs --version
|
bunx commitlint --edit
|
||||||
|
|
||||||
# Shows Claude CLI version
|
# Fix commit message format
|
||||||
ccs version
|
git commit --amend
|
||||||
```
|
```
|
||||||
|
|
||||||
## Semantic Versioning
|
### No release triggered
|
||||||
|
|
||||||
CCS follows [Semantic Versioning](https://semver.org/):
|
Check if commits include releasable types (`feat:`, `fix:`, `perf:`). Documentation-only commits (`docs:`) don't trigger releases.
|
||||||
|
|
||||||
- **MAJOR** (X.0.0): Breaking changes
|
### Beta out of sync with main
|
||||||
- **MINOR** (0.X.0): New features (backward compatible)
|
|
||||||
- **PATCH** (0.0.X): Bug fixes
|
|
||||||
|
|
||||||
Current version: **2.4.4**
|
```bash
|
||||||
- 2.4.0: Code simplification (38% reduction, 1,315→813 lines)
|
git checkout beta
|
||||||
- 2.4.1: Postinstall script improvements
|
git rebase main
|
||||||
- 2.4.2: Cross-compatibility testing framework
|
git push --force-with-lease origin beta
|
||||||
- 2.4.3: Performance optimizations
|
```
|
||||||
- 2.4.4: npm package testing enhancements
|
|
||||||
- 2.4.4: Documentation updates and bug fixes
|
|
||||||
|
|
||||||
## Version Detection Priority
|
|
||||||
|
|
||||||
Different installation methods display versions differently:
|
|
||||||
|
|
||||||
1. **Shell Installation**: Reads VERSION file at runtime
|
|
||||||
2. **npm Package**: Uses package.json version
|
|
||||||
3. **Git Installation**: VERSION file overrides installer versions
|
|
||||||
4. **Fallback**: Installer hardcoded version used if no VERSION file
|
|
||||||
|
|
||||||
All methods report the same version number when properly synchronized.
|
|
||||||
|
|||||||
+7
-1
@@ -66,7 +66,7 @@
|
|||||||
"test:native": "bash tests/native/unix/edge-cases.sh",
|
"test:native": "bash tests/native/unix/edge-cases.sh",
|
||||||
"prepublishOnly": "npm run validate && node scripts/sync-version.js",
|
"prepublishOnly": "npm run validate && node scripts/sync-version.js",
|
||||||
"prepack": "npm run validate && node scripts/sync-version.js",
|
"prepack": "npm run validate && node scripts/sync-version.js",
|
||||||
"prepare": "node scripts/check-executables.js",
|
"prepare": "husky",
|
||||||
"postinstall": "node scripts/postinstall.js"
|
"postinstall": "node scripts/postinstall.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -74,13 +74,19 @@
|
|||||||
"ora": "^9.0.0"
|
"ora": "^9.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@commitlint/cli": "^20.1.0",
|
||||||
|
"@commitlint/config-conventional": "^20.0.0",
|
||||||
|
"@semantic-release/changelog": "^6.0.3",
|
||||||
|
"@semantic-release/git": "^10.0.1",
|
||||||
"@types/node": "^20.19.25",
|
"@types/node": "^20.19.25",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.48.0",
|
"@typescript-eslint/eslint-plugin": "^8.48.0",
|
||||||
"@typescript-eslint/parser": "^8.48.0",
|
"@typescript-eslint/parser": "^8.48.0",
|
||||||
"eslint": "^9.39.1",
|
"eslint": "^9.39.1",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
|
"husky": "^9.1.7",
|
||||||
"mocha": "^11.7.5",
|
"mocha": "^11.7.5",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.6.2",
|
||||||
|
"semantic-release": "^25.0.2",
|
||||||
"typescript": "5.3"
|
"typescript": "5.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+39
-15
@@ -1,12 +1,46 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Bump CCS version
|
# =============================================================================
|
||||||
# Usage: ./scripts/bump-version.sh [major|minor|patch]
|
# DEPRECATED: This script is kept for emergency manual releases only.
|
||||||
#
|
#
|
||||||
# v4.5.0+: Bootstrap architecture - shell scripts delegate to Node.js
|
# Releases are now automated via semantic-release:
|
||||||
# Version is maintained in: VERSION, package.json, installers/*
|
# - Merge to main → stable release (npm @latest)
|
||||||
|
# - Merge to beta → beta release (npm @beta)
|
||||||
|
#
|
||||||
|
# Version is determined automatically from conventional commits:
|
||||||
|
# - feat: commit → MINOR bump (5.0.2 → 5.1.0)
|
||||||
|
# - fix: commit → PATCH bump (5.0.2 → 5.0.3)
|
||||||
|
# - feat!: commit → MAJOR bump (5.0.2 → 6.0.0)
|
||||||
|
#
|
||||||
|
# See: docs/version-management.md
|
||||||
|
# =============================================================================
|
||||||
|
#
|
||||||
|
# Legacy usage (emergency only): ./scripts/bump-version.sh [major|minor|patch]
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Show deprecation warning
|
||||||
|
echo "============================================================="
|
||||||
|
echo "[!] DEPRECATED: This script is for emergency use only."
|
||||||
|
echo ""
|
||||||
|
echo " Releases are now automated via semantic-release."
|
||||||
|
echo " Simply merge to 'main' or 'beta' branch."
|
||||||
|
echo ""
|
||||||
|
echo " Version is determined from conventional commits:"
|
||||||
|
echo " feat: commit -> MINOR (5.0.2 -> 5.1.0)"
|
||||||
|
echo " fix: commit -> PATCH (5.0.2 -> 5.0.3)"
|
||||||
|
echo " feat!: commit -> MAJOR (5.0.2 -> 6.0.0)"
|
||||||
|
echo "============================================================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Require explicit confirmation
|
||||||
|
read -p "Continue with manual bump? (y/N) " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
echo "Cancelled. Use conventional commits + merge to main instead."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
CCS_DIR="$(dirname "$SCRIPT_DIR")"
|
CCS_DIR="$(dirname "$SCRIPT_DIR")"
|
||||||
VERSION_FILE="$CCS_DIR/VERSION"
|
VERSION_FILE="$CCS_DIR/VERSION"
|
||||||
@@ -69,17 +103,7 @@ echo "Note: lib/ccs and lib/ccs.ps1 are now bootstraps"
|
|||||||
echo " (delegate to Node.js, no version hardcoded)"
|
echo " (delegate to Node.js, no version hardcoded)"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Auto-confirm in non-interactive mode (CI, piped, etc.)
|
# Already confirmed above in deprecation warning
|
||||||
if [[ ! -t 0 ]]; then
|
|
||||||
echo "[i] Non-interactive mode detected, proceeding..."
|
|
||||||
else
|
|
||||||
read -p "Continue? (y/N) " -n 1 -r
|
|
||||||
echo
|
|
||||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
||||||
echo "Cancelled."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Update VERSION file
|
# Update VERSION file
|
||||||
echo "$NEW_VERSION" > "$VERSION_FILE"
|
echo "$NEW_VERSION" > "$VERSION_FILE"
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* semantic-release plugin to sync VERSION file
|
||||||
|
*
|
||||||
|
* semantic-release updates package.json but not the VERSION file.
|
||||||
|
* This plugin keeps VERSION in sync for shell scripts and installers.
|
||||||
|
*/
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
/**
|
||||||
|
* Called during the prepare step before git commit
|
||||||
|
*/
|
||||||
|
prepare(_pluginConfig, context) {
|
||||||
|
const { nextRelease, logger } = context;
|
||||||
|
const versionFile = path.join(process.cwd(), 'VERSION');
|
||||||
|
|
||||||
|
// Write version without 'v' prefix (e.g., "5.1.0" not "v5.1.0")
|
||||||
|
const version = nextRelease.version;
|
||||||
|
fs.writeFileSync(versionFile, version + '\n');
|
||||||
|
logger.log('[sync-version-plugin] Updated VERSION file to %s', version);
|
||||||
|
|
||||||
|
// Also update installers for standalone installs
|
||||||
|
const installSh = path.join(process.cwd(), 'installers', 'install.sh');
|
||||||
|
const installPs1 = path.join(process.cwd(), 'installers', 'install.ps1');
|
||||||
|
|
||||||
|
if (fs.existsSync(installSh)) {
|
||||||
|
let content = fs.readFileSync(installSh, 'utf8');
|
||||||
|
content = content.replace(/^CCS_VERSION=".*"/m, `CCS_VERSION="${version}"`);
|
||||||
|
fs.writeFileSync(installSh, content);
|
||||||
|
logger.log('[sync-version-plugin] Updated installers/install.sh');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fs.existsSync(installPs1)) {
|
||||||
|
let content = fs.readFileSync(installPs1, 'utf8');
|
||||||
|
content = content.replace(/^\$CcsVersion = ".*"/m, `$CcsVersion = "${version}"`);
|
||||||
|
fs.writeFileSync(installPs1, content);
|
||||||
|
logger.log('[sync-version-plugin] Updated installers/install.ps1');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user