--- description: Comprehensive release preparation workflow including quality gates, security audit, changelog generation, version bumping, tagging, and publishing args: - name: --patch description: Create patch release (bug fixes, 1.0.0 → 1.0.1) required: false - name: --minor description: Create minor release (new features, 1.0.0 → 1.1.0) required: false - name: --major description: Create major release (breaking changes, 1.0.0 → 2.0.0) required: false - name: --skip-audit description: Skip security audit (not recommended for production) required: false --- ## Goal Orchestrate a complete release workflow with quality gates, version bumping, and publishing automation. ## User Input ```text $ARGUMENTS ``` --- ## What This Command Does `/specswarm:release` orchestrates a complete release workflow: 1. **Pre-Release Validation** - Checks git status, branch protection, uncommitted changes 2. **Quality Gates** - Runs tests, linting, type checking, build verification 3. **Security Audit** - Optional security scan (recommended for production releases) 4. **Version Bumping** - Semantic versioning (patch/minor/major) 5. **Changelog Generation** - Auto-generates changelog from git commits 6. **Git Tagging** - Creates annotated git tags 7. **Artifact Building** - Builds production-ready artifacts 8. **Publishing** - Optional npm publish and GitHub release creation --- ## When to Use This Command - Preparing production releases - Creating new package versions - Publishing to npm registry - Creating GitHub releases - After completing a feature milestone - Before deploying to production --- ## Prerequisites - Git repository with clean working directory - On a release-ready branch (main/master or release branch) - All tests passing - package.json exists (for version bumping) - npm credentials configured (if publishing to npm) - GitHub CLI installed (if creating GitHub releases) --- ## Usage ```bash /specswarm:release ``` **Options** (via interactive prompts): - Release type (patch/minor/major) - Run security audit (yes/no) - Publish to npm (yes/no) - Create GitHub release (yes/no) - Push to remote (yes/no) --- ## Output 1. Updated `package.json` with new version 2. Generated/updated `CHANGELOG.md` 3. Git tag (e.g., `v1.2.3`) 4. Built artifacts (in `dist/` or `build/`) 5. Optional: Published npm package 6. Optional: GitHub release with notes --- ## Implementation ```bash #!/bin/bash set -euo pipefail # ============================================================================ # RELEASE COMMAND - v3.1.0 # ============================================================================ echo "📦 SpecSwarm Release Workflow" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" # ============================================================================ # CONFIGURATION # ============================================================================ RELEASE_TYPE="patch" RUN_SECURITY_AUDIT="no" PUBLISH_TO_NPM="no" CREATE_GITHUB_RELEASE="no" PUSH_TO_REMOTE="yes" DRY_RUN="no" CURRENT_VERSION="" NEW_VERSION="" CHANGELOG_FILE="CHANGELOG.md" TAG_NAME="" # ============================================================================ # HELPER FUNCTIONS # ============================================================================ log_section() { echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "$1" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" } log_check() { local check_name=$1 local status=$2 local message=$3 if [ "$status" = "pass" ]; then echo "✅ $check_name: $message" elif [ "$status" = "warn" ]; then echo "⚠️ $check_name: $message" else echo "❌ $check_name: $message" fi } semver_bump() { local version=$1 local bump_type=$2 # Parse version (e.g., "1.2.3" -> major=1, minor=2, patch=3) local major minor patch IFS='.' read -r major minor patch <<< "${version#v}" case "$bump_type" in major) major=$((major + 1)) minor=0 patch=0 ;; minor) minor=$((minor + 1)) patch=0 ;; patch) patch=$((patch + 1)) ;; esac echo "${major}.${minor}.${patch}" } generate_changelog_entry() { local version=$1 local previous_tag=$2 local date=$(date +%Y-%m-%d) echo "## [$version] - $date" echo "" # Get commits since last tag if [ -n "$previous_tag" ]; then commits=$(git log "$previous_tag..HEAD" --pretty=format:"- %s" --no-merges 2>/dev/null || echo "- Initial release") else commits=$(git log --pretty=format:"- %s" --no-merges 2>/dev/null || echo "- Initial release") fi # Categorize commits echo "### Added" echo "$commits" | grep -i "^- \(feat\|add\|new\)" || echo "- No new features" echo "" echo "### Fixed" echo "$commits" | grep -i "^- \(fix\|bugfix\|patch\)" || echo "- No bug fixes" echo "" echo "### Changed" echo "$commits" | grep -i "^- \(update\|change\|refactor\|improve\)" || echo "- No changes" echo "" echo "### Deprecated" echo "- None" echo "" echo "### Removed" echo "- None" echo "" echo "### Security" echo "- Security audit passed" echo "" } # ============================================================================ # PREFLIGHT CHECKS # ============================================================================ log_section "Preflight Checks" # Check 1: Git repository if ! git rev-parse --git-dir > /dev/null 2>&1; then log_check "Git Repository" "fail" "Not in a git repository" exit 1 fi log_check "Git Repository" "pass" "Valid git repository" # Check 2: Clean working directory DIRTY=$(git status --porcelain 2>/dev/null) if [ -n "$DIRTY" ]; then log_check "Working Directory" "fail" "Uncommitted changes detected" echo "" git status --short echo "" echo "Please commit or stash changes before releasing" exit 1 fi log_check "Working Directory" "pass" "Clean working directory" # Check 3: Current branch CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) log_check "Current Branch" "pass" "$CURRENT_BRANCH" RELEASE_BRANCHES=("main" "master" "develop" "release") IS_RELEASE_BRANCH=false for branch in "${RELEASE_BRANCHES[@]}"; do if [ "$CURRENT_BRANCH" = "$branch" ] || [[ "$CURRENT_BRANCH" == release/* ]]; then IS_RELEASE_BRANCH=true break fi done if [ "$IS_RELEASE_BRANCH" = false ]; then log_check "Branch Validation" "warn" "Not on a typical release branch (main/master/develop/release/*)" echo " Current branch: $CURRENT_BRANCH" echo " You may want to merge to a release branch first" fi # Check 4: package.json exists if [ ! -f "package.json" ]; then log_check "package.json" "fail" "No package.json found" echo " Release command requires package.json for version management" exit 1 fi log_check "package.json" "pass" "Found package.json" # Check 5: Get current version CURRENT_VERSION=$(jq -r '.version' package.json) if [ "$CURRENT_VERSION" = "null" ] || [ -z "$CURRENT_VERSION" ]; then log_check "Version" "fail" "No version in package.json" exit 1 fi log_check "Current Version" "pass" "$CURRENT_VERSION" # Check 6: Remote repository if git remote -v | grep -q "origin"; then REMOTE_URL=$(git remote get-url origin) log_check "Remote Repository" "pass" "$REMOTE_URL" HAS_REMOTE=true else log_check "Remote Repository" "warn" "No remote 'origin' configured" HAS_REMOTE=false fi # Check 7: GitHub CLI (for GitHub releases) if command -v gh > /dev/null 2>&1; then log_check "GitHub CLI" "pass" "gh available" HAS_GH_CLI=true else log_check "GitHub CLI" "warn" "gh not installed (GitHub releases disabled)" HAS_GH_CLI=false fi # ============================================================================ # QUALITY GATES # ============================================================================ log_section "Quality Gates" # Check for quality-standards.md QUALITY_GATES_ENFORCED=false if [ -f ".specswarm/quality-standards.md" ]; then log_check "Quality Standards" "pass" "Found quality-standards.md" QUALITY_GATES_ENFORCED=true # Extract quality gate settings if grep -q "enforce_gates: true" .specswarm/quality-standards.md 2>/dev/null; then echo " Quality gates are enforced" fi else log_check "Quality Standards" "warn" "No quality-standards.md (run /specswarm:init)" fi # Run tests if available if jq -e '.scripts.test' package.json > /dev/null 2>&1; then echo "" echo "Running tests..." if npm test 2>&1 | tee /tmp/test-output.txt; then log_check "Tests" "pass" "All tests passed" else log_check "Tests" "fail" "Tests failed" echo "" echo "Cannot release with failing tests" exit 1 fi else log_check "Tests" "warn" "No test script defined" fi # Run linting if available if jq -e '.scripts.lint' package.json > /dev/null 2>&1; then echo "" echo "Running linter..." if npm run lint 2>&1 | tee /tmp/lint-output.txt; then log_check "Linting" "pass" "Linting passed" else log_check "Linting" "fail" "Linting failed" echo "" echo "Cannot release with linting errors" exit 1 fi else log_check "Linting" "warn" "No lint script defined" fi # Run type checking if available (TypeScript) if jq -e '.scripts["type-check"]' package.json > /dev/null 2>&1; then echo "" echo "Running type check..." if npm run type-check 2>&1 | tee /tmp/typecheck-output.txt; then log_check "Type Check" "pass" "Type checking passed" else log_check "Type Check" "fail" "Type errors found" echo "" echo "Cannot release with type errors" exit 1 fi else log_check "Type Check" "warn" "No type-check script defined" fi # Run build if available if jq -e '.scripts.build' package.json > /dev/null 2>&1; then echo "" echo "Running build..." if npm run build 2>&1 | tee /tmp/build-output.txt; then log_check "Build" "pass" "Build successful" else log_check "Build" "fail" "Build failed" echo "" echo "Cannot release with build errors" exit 1 fi else log_check "Build" "warn" "No build script defined" fi echo "" echo "✅ All quality gates passed" # ============================================================================ # INTERACTIVE CONFIGURATION # ============================================================================ log_section "Release Configuration" # Question 1: Release type cat << 'EOF_QUESTION_1' | claude --question { "questions": [ { "question": "What type of release is this?", "header": "Release type", "multiSelect": false, "options": [ { "label": "Patch (bug fixes)", "description": "Patch release - bug fixes and minor updates (1.0.0 → 1.0.1)" }, { "label": "Minor (new features)", "description": "Minor release - new features, backward compatible (1.0.0 → 1.1.0)" }, { "label": "Major (breaking changes)", "description": "Major release - breaking changes or major features (1.0.0 → 2.0.0)" } ] } ] } EOF_QUESTION_1 # Parse release type if echo "$CLAUDE_ANSWERS" | jq -e '.["Release type"] == "Minor (new features)"' > /dev/null 2>&1; then RELEASE_TYPE="minor" elif echo "$CLAUDE_ANSWERS" | jq -e '.["Release type"] == "Major (breaking changes)"' > /dev/null 2>&1; then RELEASE_TYPE="major" else RELEASE_TYPE="patch" fi # Calculate new version NEW_VERSION=$(semver_bump "$CURRENT_VERSION" "$RELEASE_TYPE") TAG_NAME="v${NEW_VERSION}" echo "Current version: $CURRENT_VERSION" echo "New version: $NEW_VERSION ($RELEASE_TYPE)" echo "Tag name: $TAG_NAME" # Question 2: Security audit cat << 'EOF_QUESTION_2' | claude --question { "questions": [ { "question": "Run security audit before releasing?", "header": "Security", "multiSelect": false, "options": [ { "label": "Yes, run audit", "description": "Recommended for production releases - scans dependencies and code" }, { "label": "No, skip audit", "description": "Skip security audit (not recommended for production)" } ] } ] } EOF_QUESTION_2 # Parse security audit answer if echo "$CLAUDE_ANSWERS" | jq -e '.["Security"] == "Yes, run audit"' > /dev/null 2>&1; then RUN_SECURITY_AUDIT="yes" fi echo "Security audit: $RUN_SECURITY_AUDIT" # Question 3: Publishing options PUBLISH_OPTIONS=() # Always ask about npm publishing PUBLISH_OPTIONS+='{ "label": "Publish to npm", "description": "Publish package to npm registry (requires npm credentials)" }' # Add GitHub release option if gh CLI is available if [ "$HAS_GH_CLI" = true ]; then PUBLISH_OPTIONS+='{ "label": "Create GitHub release", "description": "Create GitHub release with auto-generated notes" }' fi # Add push to remote option if remote exists if [ "$HAS_REMOTE" = true ]; then PUBLISH_OPTIONS+='{ "label": "Push to remote", "description": "Push commits and tags to remote repository" }' fi # Build JSON array for options PUBLISH_OPTIONS_JSON=$(printf '%s\n' "${PUBLISH_OPTIONS[@]}" | jq -s .) cat << EOF_QUESTION_3 | claude --question { "questions": [ { "question": "Select publishing and deployment options:", "header": "Publishing", "multiSelect": true, "options": $PUBLISH_OPTIONS_JSON } ] } EOF_QUESTION_3 # Parse publishing options if echo "$CLAUDE_ANSWERS" | jq -e '.["Publishing"]' | grep -q "Publish to npm"; then PUBLISH_TO_NPM="yes" fi if echo "$CLAUDE_ANSWERS" | jq -e '.["Publishing"]' | grep -q "Create GitHub release"; then CREATE_GITHUB_RELEASE="yes" fi if echo "$CLAUDE_ANSWERS" | jq -e '.["Publishing"]' | grep -q "Push to remote"; then PUSH_TO_REMOTE="yes" else PUSH_TO_REMOTE="no" fi echo "Publish to npm: $PUBLISH_TO_NPM" echo "Create GitHub release: $CREATE_GITHUB_RELEASE" echo "Push to remote: $PUSH_TO_REMOTE" # ============================================================================ # SECURITY AUDIT (OPTIONAL) # ============================================================================ if [ "$RUN_SECURITY_AUDIT" = "yes" ]; then log_section "Security Audit" echo "Running security audit..." # Note: This would call /specswarm:security-audit in production # For now, we'll run npm audit directly if command -v npm > /dev/null 2>&1; then if npm audit --audit-level=moderate 2>&1 | tee /tmp/audit-output.txt; then log_check "Security Audit" "pass" "No moderate+ vulnerabilities" else log_check "Security Audit" "fail" "Vulnerabilities found" echo "" echo "Security audit failed. Fix vulnerabilities before releasing." echo "Run: npm audit fix" exit 1 fi else log_check "Security Audit" "warn" "npm not available, skipping audit" fi fi # ============================================================================ # VERSION BUMPING # ============================================================================ log_section "Version Bumping" echo "Updating version in package.json..." # Update package.json version jq --arg version "$NEW_VERSION" '.version = $version' package.json > package.json.tmp mv package.json.tmp package.json log_check "package.json" "pass" "Updated to $NEW_VERSION" # Update package-lock.json if it exists if [ -f "package-lock.json" ]; then jq --arg version "$NEW_VERSION" '.version = $version' package-lock.json > package-lock.json.tmp mv package-lock.json.tmp package-lock.json log_check "package-lock.json" "pass" "Updated to $NEW_VERSION" fi # ============================================================================ # CHANGELOG GENERATION # ============================================================================ log_section "Changelog Generation" # Get previous tag PREVIOUS_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") if [ -n "$PREVIOUS_TAG" ]; then echo "Previous tag: $PREVIOUS_TAG" else echo "No previous tags found (first release)" fi # Generate changelog entry CHANGELOG_ENTRY=$(generate_changelog_entry "$NEW_VERSION" "$PREVIOUS_TAG") # Update or create CHANGELOG.md if [ -f "$CHANGELOG_FILE" ]; then echo "Updating $CHANGELOG_FILE..." # Prepend new entry to existing changelog { echo "# Changelog" echo "" echo "$CHANGELOG_ENTRY" echo "" tail -n +2 "$CHANGELOG_FILE" } > "${CHANGELOG_FILE}.tmp" mv "${CHANGELOG_FILE}.tmp" "$CHANGELOG_FILE" else echo "Creating $CHANGELOG_FILE..." cat > "$CHANGELOG_FILE" << EOF # Changelog All notable changes to this project 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). $CHANGELOG_ENTRY EOF fi log_check "Changelog" "pass" "Updated $CHANGELOG_FILE" # ============================================================================ # GIT COMMIT & TAG # ============================================================================ log_section "Git Commit & Tag" # Stage changes git add package.json package-lock.json "$CHANGELOG_FILE" 2>/dev/null || true # Create commit COMMIT_MESSAGE="chore(release): v${NEW_VERSION} 🚀 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude " git commit -m "$COMMIT_MESSAGE" log_check "Git Commit" "pass" "Created release commit" # Create annotated tag TAG_MESSAGE="Release v${NEW_VERSION} $(echo "$CHANGELOG_ENTRY" | head -n 30) 🚀 Generated with [Claude Code](https://claude.com/claude-code)" git tag -a "$TAG_NAME" -m "$TAG_MESSAGE" log_check "Git Tag" "pass" "Created tag $TAG_NAME" # ============================================================================ # BUILD ARTIFACTS # ============================================================================ if jq -e '.scripts.build' package.json > /dev/null 2>&1; then log_section "Building Artifacts" echo "Running production build..." if npm run build; then log_check "Build" "pass" "Production artifacts built" else log_check "Build" "fail" "Build failed" echo "" echo "Build failed after version bump. Please fix and retry." exit 1 fi fi # ============================================================================ # PUSH TO REMOTE # ============================================================================ if [ "$PUSH_TO_REMOTE" = "yes" ] && [ "$HAS_REMOTE" = true ]; then log_section "Push to Remote" echo "Pushing to remote..." git push origin "$CURRENT_BRANCH" log_check "Push Branch" "pass" "Pushed $CURRENT_BRANCH" git push origin "$TAG_NAME" log_check "Push Tag" "pass" "Pushed $TAG_NAME" fi # ============================================================================ # NPM PUBLISHING # ============================================================================ if [ "$PUBLISH_TO_NPM" = "yes" ]; then log_section "NPM Publishing" echo "Publishing to npm..." # Check if logged in to npm if npm whoami > /dev/null 2>&1; then NPM_USER=$(npm whoami) echo "Logged in as: $NPM_USER" # Publish to npm if npm publish; then log_check "npm publish" "pass" "Published to npm registry" else log_check "npm publish" "fail" "npm publish failed" echo "" echo "npm publish failed. Check credentials and package.json configuration." exit 1 fi else log_check "npm auth" "fail" "Not logged in to npm" echo "" echo "Please run: npm login" exit 1 fi fi # ============================================================================ # GITHUB RELEASE # ============================================================================ if [ "$CREATE_GITHUB_RELEASE" = "yes" ] && [ "$HAS_GH_CLI" = true ]; then log_section "GitHub Release" echo "Creating GitHub release..." # Create release notes RELEASE_NOTES=$(cat << EOF $CHANGELOG_ENTRY --- **Full Changelog**: https://github.com/$(git remote get-url origin | sed 's/.*github.com[:/]\(.*\)\.git/\1/')/compare/${PREVIOUS_TAG}...${TAG_NAME} 🚀 Generated with [Claude Code](https://claude.com/claude-code) EOF ) # Create GitHub release if gh release create "$TAG_NAME" \ --title "Release $TAG_NAME" \ --notes "$RELEASE_NOTES"; then log_check "GitHub Release" "pass" "Created release $TAG_NAME" else log_check "GitHub Release" "fail" "Failed to create GitHub release" echo "" echo "GitHub release creation failed. Check gh CLI authentication." fi fi # ============================================================================ # SUMMARY # ============================================================================ log_section "Release Complete" echo "🎉 Successfully released version $NEW_VERSION!" echo "" echo "Summary:" echo " Previous version: $CURRENT_VERSION" echo " New version: $NEW_VERSION" echo " Release type: $RELEASE_TYPE" echo " Git tag: $TAG_NAME" echo " Branch: $CURRENT_BRANCH" echo "" if [ "$PUBLISH_TO_NPM" = "yes" ]; then echo " 📦 Published to npm: https://www.npmjs.com/package/$(jq -r '.name' package.json)" fi if [ "$CREATE_GITHUB_RELEASE" = "yes" ]; then REPO_URL=$(git remote get-url origin | sed 's/\.git$//' | sed 's/git@github.com:/https:\/\/github.com\//') echo " 🚀 GitHub release: ${REPO_URL}/releases/tag/${TAG_NAME}" fi echo "" echo "Next steps:" if [ "$PUSH_TO_REMOTE" = "no" ]; then echo " - Push changes: git push origin $CURRENT_BRANCH --tags" fi if [ "$PUBLISH_TO_NPM" = "no" ]; then echo " - Publish to npm: npm publish" fi echo " - Deploy to production environments" echo " - Update documentation if needed" echo " - Notify stakeholders of the release" echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" ``` --- ## Examples ### Example 1: Patch Release (Bug Fixes) ```bash /specswarm:release # Select: Patch (bug fixes) # Select: Yes, run audit # Select: Push to remote # Result: v1.0.1 released with security audit ``` ### Example 2: Minor Release (New Features) ```bash /specswarm:release # Select: Minor (new features) # Select: Yes, run audit # Select: Publish to npm, Create GitHub release, Push to remote # Result: v1.1.0 published to npm and GitHub ``` ### Example 3: Major Release (Breaking Changes) ```bash /specswarm:release # Select: Major (breaking changes) # Select: Yes, run audit # Select: All publishing options # Result: v2.0.0 with full publishing workflow ``` --- ## Release Checklist The command automatically handles: - ✅ Verify clean working directory - ✅ Run all tests - ✅ Run linting - ✅ Run type checking (if TypeScript) - ✅ Run production build - ✅ Optional security audit - ✅ Bump version in package.json - ✅ Update/create CHANGELOG.md - ✅ Create git commit - ✅ Create annotated git tag - ✅ Push to remote - ✅ Optional: Publish to npm - ✅ Optional: Create GitHub release --- ## Semantic Versioning The command follows [Semantic Versioning](https://semver.org/): - **Patch** (1.0.0 → 1.0.1): Bug fixes, minor updates - **Minor** (1.0.0 → 1.1.0): New features, backward compatible - **Major** (1.0.0 → 2.0.0): Breaking changes --- ## Changelog Format Generated changelogs follow [Keep a Changelog](https://keepachangelog.com/) format: ```markdown ## [1.2.0] - 2025-01-15 ### Added - feat: new user dashboard - add: dark mode support ### Fixed - fix: authentication bug - bugfix: memory leak ### Changed - update: API endpoint structure - refactor: database queries ``` --- ## CI/CD Integration Integrate with GitHub Actions: ```yaml # .github/workflows/release.yml name: Release on: workflow_dispatch: inputs: release_type: description: 'Release type' required: true type: choice options: - patch - minor - major jobs: release: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - run: /specswarm:release env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ``` --- ## Notes - **Protected Branches**: Some quality gates may be skipped on feature branches - **npm Credentials**: Run `npm login` before publishing to npm - **GitHub Releases**: Requires `gh` CLI and authentication - **Rollback**: Use `/specswarm:rollback` if release fails - **Dry Run**: The command always shows what it will do before making changes --- ## Troubleshooting **Build Fails**: - Check build scripts in package.json - Verify all dependencies are installed - Review build logs in `/tmp/build-output.txt` **npm Publish Fails**: - Verify npm login: `npm whoami` - Check package name availability - Ensure version hasn't been published before **GitHub Release Fails**: - Verify gh CLI: `gh auth status` - Check repository permissions - Ensure remote URL is correct --- ## See Also - `/specswarm:security-audit` - Run security audit separately - `/specswarm:rollback` - Rollback failed releases - `/specswarm:analyze-quality` - Check quality before releasing - `/specswarm:ship` - Complete feature workflow with merge --- **Version**: 3.1.0 **Category**: Release Management **Estimated Time**: 3-10 minutes (depending on build and publishing)