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:
kaitranntt committed 2025-11-30 18:56:02 -05:00
1 parent 72c2b08715
commit d3d96371de
13 files changed
+1737 -142

No files matched your search

+39 -15
View File
@@ -1,12 +1,46 @@
#!/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
# Version is maintained in: VERSION, package.json, installers/*
# Releases are now automated via semantic-release:
# - 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
# 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)"
CCS_DIR="$(dirname "$SCRIPT_DIR")"
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 ""
# Auto-confirm in non-interactive mode (CI, piped, etc.)
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
# Already confirmed above in deprecation warning
# Update VERSION file
echo "$NEW_VERSION" > "$VERSION_FILE"
+41
View 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');
}
}
};