claude-code-orchestrator-kit 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/database/workers/api-builder.md +155 -0
- package/.claude/agents/database/workers/database-architect.md +193 -0
- package/.claude/agents/database/workers/supabase-auditor.md +1070 -0
- package/.claude/agents/development/workers/code-reviewer.md +968 -0
- package/.claude/agents/development/workers/cost-calculator-specialist.md +683 -0
- package/.claude/agents/development/workers/llm-service-specialist.md +999 -0
- package/.claude/agents/development/workers/skill-builder-v2.md +480 -0
- package/.claude/agents/development/workers/typescript-types-specialist.md +649 -0
- package/.claude/agents/development/workers/utility-builder.md +582 -0
- package/.claude/agents/documentation/workers/technical-writer.md +152 -0
- package/.claude/agents/frontend/workers/fullstack-nextjs-specialist.md +206 -0
- package/.claude/agents/frontend/workers/visual-effects-creator.md +159 -0
- package/.claude/agents/health/orchestrators/bug-orchestrator.md +1045 -0
- package/.claude/agents/health/orchestrators/dead-code-orchestrator.md +1045 -0
- package/.claude/agents/health/orchestrators/dependency-orchestrator.md +1045 -0
- package/.claude/agents/health/orchestrators/security-orchestrator.md +1045 -0
- package/.claude/agents/health/workers/bug-fixer.md +525 -0
- package/.claude/agents/health/workers/bug-hunter.md +649 -0
- package/.claude/agents/health/workers/dead-code-hunter.md +446 -0
- package/.claude/agents/health/workers/dead-code-remover.md +437 -0
- package/.claude/agents/health/workers/dependency-auditor.md +379 -0
- package/.claude/agents/health/workers/dependency-updater.md +436 -0
- package/.claude/agents/health/workers/security-scanner.md +700 -0
- package/.claude/agents/health/workers/vulnerability-fixer.md +524 -0
- package/.claude/agents/infrastructure/workers/infrastructure-specialist.md +156 -0
- package/.claude/agents/infrastructure/workers/orchestration-logic-specialist.md +1260 -0
- package/.claude/agents/infrastructure/workers/qdrant-specialist.md +503 -0
- package/.claude/agents/infrastructure/workers/quality-validator-specialist.md +984 -0
- package/.claude/agents/meta/workers/meta-agent-v3.md +503 -0
- package/.claude/agents/research/workers/problem-investigator.md +507 -0
- package/.claude/agents/research/workers/research-specialist.md +423 -0
- package/.claude/agents/testing/workers/accessibility-tester.md +813 -0
- package/.claude/agents/testing/workers/integration-tester.md +188 -0
- package/.claude/agents/testing/workers/mobile-fixes-implementer.md +252 -0
- package/.claude/agents/testing/workers/mobile-responsiveness-tester.md +180 -0
- package/.claude/agents/testing/workers/performance-optimizer.md +262 -0
- package/.claude/agents/testing/workers/test-writer.md +800 -0
- package/.claude/commands/health-bugs.md +297 -0
- package/.claude/commands/health-cleanup.md +297 -0
- package/.claude/commands/health-deps.md +297 -0
- package/.claude/commands/health-metrics.md +747 -0
- package/.claude/commands/health-security.md +297 -0
- package/.claude/commands/push.md +21 -0
- package/.claude/commands/speckit.analyze.md +184 -0
- package/.claude/commands/speckit.checklist.md +294 -0
- package/.claude/commands/speckit.clarify.md +178 -0
- package/.claude/commands/speckit.constitution.md +78 -0
- package/.claude/commands/speckit.implement.md +182 -0
- package/.claude/commands/speckit.plan.md +87 -0
- package/.claude/commands/speckit.specify.md +250 -0
- package/.claude/commands/speckit.tasks.md +137 -0
- package/.claude/commands/translate-doc.md +95 -0
- package/.claude/commands/worktree-cleanup.md +382 -0
- package/.claude/commands/worktree-create.md +287 -0
- package/.claude/commands/worktree-list.md +239 -0
- package/.claude/commands/worktree-remove.md +339 -0
- package/.claude/schemas/base-plan.schema.json +82 -0
- package/.claude/schemas/bug-plan.schema.json +71 -0
- package/.claude/schemas/dead-code-plan.schema.json +71 -0
- package/.claude/schemas/dependency-plan.schema.json +74 -0
- package/.claude/schemas/security-plan.schema.json +71 -0
- package/.claude/scripts/gates/check-bundle-size.sh +47 -0
- package/.claude/scripts/gates/check-coverage.sh +67 -0
- package/.claude/scripts/gates/check-security.sh +46 -0
- package/.claude/scripts/release.sh +740 -0
- package/.claude/settings.local.json +21 -0
- package/.claude/settings.local.json.example +20 -0
- package/.claude/skills/calculate-priority-score/SKILL.md +229 -0
- package/.claude/skills/calculate-priority-score/scoring-matrix.json +83 -0
- package/.claude/skills/extract-version/SKILL.md +228 -0
- package/.claude/skills/format-commit-message/SKILL.md +189 -0
- package/.claude/skills/format-commit-message/template.md +64 -0
- package/.claude/skills/format-markdown-table/SKILL.md +202 -0
- package/.claude/skills/format-markdown-table/examples.md +84 -0
- package/.claude/skills/format-todo-list/SKILL.md +222 -0
- package/.claude/skills/format-todo-list/template.json +30 -0
- package/.claude/skills/generate-changelog/SKILL.md +258 -0
- package/.claude/skills/generate-changelog/commit-mapping.json +47 -0
- package/.claude/skills/generate-report-header/SKILL.md +228 -0
- package/.claude/skills/generate-report-header/template.md +66 -0
- package/.claude/skills/parse-error-logs/SKILL.md +286 -0
- package/.claude/skills/parse-error-logs/patterns.json +26 -0
- package/.claude/skills/parse-git-status/SKILL.md +164 -0
- package/.claude/skills/parse-package-json/SKILL.md +151 -0
- package/.claude/skills/parse-package-json/schema.json +43 -0
- package/.claude/skills/render-template/SKILL.md +245 -0
- package/.claude/skills/rollback-changes/SKILL.md +582 -0
- package/.claude/skills/rollback-changes/changes-log-schema.json +101 -0
- package/.claude/skills/run-quality-gate/SKILL.md +404 -0
- package/.claude/skills/run-quality-gate/gate-mappings.json +97 -0
- package/.claude/skills/validate-plan-file/SKILL.md +327 -0
- package/.claude/skills/validate-plan-file/schema.json +35 -0
- package/.claude/skills/validate-report-file/SKILL.md +256 -0
- package/.claude/skills/validate-report-file/schema.json +67 -0
- package/.env.example +49 -0
- package/.github/BRANCH_PROTECTION.md +137 -0
- package/.github/workflows/build.yml +70 -0
- package/.github/workflows/claude-code-review.yml +255 -0
- package/.github/workflows/claude.yml +79 -0
- package/.github/workflows/deploy-staging.yml +90 -0
- package/.github/workflows/test.yml +104 -0
- package/.gitignore +116 -0
- package/CLAUDE.md +137 -0
- package/LICENSE +72 -0
- package/README.md +1098 -0
- package/docs/ARCHITECTURE.md +746 -0
- package/docs/Agents Ecosystem/AGENT-ORCHESTRATION.md +568 -0
- package/docs/Agents Ecosystem/AI-AGENT-ECOSYSTEM-README.md +658 -0
- package/docs/Agents Ecosystem/ARCHITECTURE.md +606 -0
- package/docs/Agents Ecosystem/QUALITY-GATES-SPECIFICATION.md +1315 -0
- package/docs/Agents Ecosystem/REPORT-TEMPLATE-STANDARD.md +1324 -0
- package/docs/Agents Ecosystem/spec-kit-comprehensive-updates.md +478 -0
- package/docs/FAQ.md +572 -0
- package/docs/MIGRATION-GUIDE.md +542 -0
- package/docs/PERFORMANCE-OPTIMIZATION.md +494 -0
- package/docs/ROADMAP.md +439 -0
- package/docs/TUTORIAL-CUSTOM-AGENTS.md +2041 -0
- package/docs/USE-CASES.md +706 -0
- package/index.js +96 -0
- package/mcp/.mcp.base.json +21 -0
- package/mcp/.mcp.frontend.json +29 -0
- package/mcp/.mcp.full.json +67 -0
- package/mcp/.mcp.local.example.json +7 -0
- package/mcp/.mcp.local.json +7 -0
- package/mcp/.mcp.n8n.json +45 -0
- package/mcp/.mcp.supabase-full.json +35 -0
- package/mcp/.mcp.supabase-only.json +28 -0
- package/package.json +78 -0
- package/postinstall.js +71 -0
- package/switch-mcp.sh +101 -0
|
@@ -0,0 +1,740 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Release Automation Script
|
|
4
|
+
# Automated release management with version bumping and changelog generation
|
|
5
|
+
#
|
|
6
|
+
# Features:
|
|
7
|
+
# - Auto-syncs package.json versions with latest git tag (prevents version conflicts)
|
|
8
|
+
# - Auto-detects version bump from conventional commits
|
|
9
|
+
# - Generates CHANGELOG.md entries
|
|
10
|
+
# - Rollback support for failed releases
|
|
11
|
+
#
|
|
12
|
+
# Usage: ./release.sh [patch|minor|major] [--yes]
|
|
13
|
+
# Leave empty for auto-detection from conventional commits
|
|
14
|
+
# --yes: Skip confirmation prompt (for automation)
|
|
15
|
+
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
|
|
18
|
+
# === CONFIGURATION ===
|
|
19
|
+
readonly DATE=$(date +%Y-%m-%d)
|
|
20
|
+
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
21
|
+
readonly PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
22
|
+
|
|
23
|
+
# Colors for output
|
|
24
|
+
readonly RED='\033[0;31m'
|
|
25
|
+
readonly GREEN='\033[0;32m'
|
|
26
|
+
readonly YELLOW='\033[1;33m'
|
|
27
|
+
readonly BLUE='\033[0;34m'
|
|
28
|
+
readonly NC='\033[0m' # No Color
|
|
29
|
+
|
|
30
|
+
# State tracking for rollback
|
|
31
|
+
CREATED_COMMIT=""
|
|
32
|
+
CREATED_TAG=""
|
|
33
|
+
STASH_CREATED=""
|
|
34
|
+
declare -a MODIFIED_FILES=()
|
|
35
|
+
|
|
36
|
+
# Commit categorization arrays
|
|
37
|
+
declare -a ALL_COMMITS=()
|
|
38
|
+
declare -a FEATURES=()
|
|
39
|
+
declare -a FIXES=()
|
|
40
|
+
declare -a BREAKING_CHANGES=()
|
|
41
|
+
declare -a REFACTORS=()
|
|
42
|
+
declare -a PERF=()
|
|
43
|
+
declare -a OTHER_CHANGES=()
|
|
44
|
+
|
|
45
|
+
# === UTILITY FUNCTIONS ===
|
|
46
|
+
|
|
47
|
+
log_info() {
|
|
48
|
+
echo -e "${BLUE}ℹ️ $*${NC}"
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
log_success() {
|
|
52
|
+
echo -e "${GREEN}✅ $*${NC}"
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
log_warning() {
|
|
56
|
+
echo -e "${YELLOW}⚠️ $*${NC}"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
log_error() {
|
|
60
|
+
echo -e "${RED}❌ $*${NC}" >&2
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# === CLEANUP AND ROLLBACK ===
|
|
64
|
+
|
|
65
|
+
cleanup() {
|
|
66
|
+
local exit_code=$?
|
|
67
|
+
|
|
68
|
+
if [ $exit_code -ne 0 ]; then
|
|
69
|
+
echo ""
|
|
70
|
+
log_error "Error occurred during release process"
|
|
71
|
+
echo ""
|
|
72
|
+
log_warning "Rolling back changes..."
|
|
73
|
+
|
|
74
|
+
# Delete tag if created
|
|
75
|
+
if [ -n "$CREATED_TAG" ]; then
|
|
76
|
+
git tag -d "$CREATED_TAG" 2>/dev/null || true
|
|
77
|
+
log_success "Deleted tag $CREATED_TAG"
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# Rollback commit using reset --soft to preserve working directory
|
|
81
|
+
if [ -n "$CREATED_COMMIT" ]; then
|
|
82
|
+
git reset --soft HEAD~1 2>/dev/null || true
|
|
83
|
+
log_success "Rolled back commit (working directory preserved)"
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# Restore modified files only (not untracked files)
|
|
87
|
+
if [ ${#MODIFIED_FILES[@]} -gt 0 ]; then
|
|
88
|
+
git restore --staged "${MODIFIED_FILES[@]}" 2>/dev/null || true
|
|
89
|
+
git restore "${MODIFIED_FILES[@]}" 2>/dev/null || true
|
|
90
|
+
log_success "Restored modified package.json files"
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# Restore stash if we created one
|
|
94
|
+
if [ -n "$STASH_CREATED" ]; then
|
|
95
|
+
if git stash list | grep -q "release.sh: temporary stash"; then
|
|
96
|
+
git stash pop >/dev/null 2>&1 || {
|
|
97
|
+
log_warning "Stash pop failed with conflicts. Use 'git stash list' to see stashes."
|
|
98
|
+
log_info "Manually restore with: git stash pop"
|
|
99
|
+
}
|
|
100
|
+
fi
|
|
101
|
+
log_success "Stashed changes restored (or available in stash list)"
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
echo ""
|
|
105
|
+
log_info "Rollback complete. Repository state restored."
|
|
106
|
+
echo ""
|
|
107
|
+
exit $exit_code
|
|
108
|
+
fi
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
trap cleanup EXIT
|
|
112
|
+
|
|
113
|
+
# === PRE-FLIGHT CHECKS ===
|
|
114
|
+
|
|
115
|
+
run_preflight_checks() {
|
|
116
|
+
log_info "Running pre-flight checks..."
|
|
117
|
+
echo ""
|
|
118
|
+
|
|
119
|
+
# Check if we're in the project root
|
|
120
|
+
if [ ! -f "$PROJECT_ROOT/package.json" ]; then
|
|
121
|
+
log_error "Not in project root. Could not find package.json"
|
|
122
|
+
exit 1
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
# Check if on a branch (not detached HEAD)
|
|
126
|
+
BRANCH=$(git branch --show-current)
|
|
127
|
+
if [ -z "$BRANCH" ]; then
|
|
128
|
+
log_error "You are in detached HEAD state"
|
|
129
|
+
echo "Checkout a branch first:"
|
|
130
|
+
echo " git checkout main"
|
|
131
|
+
exit 1
|
|
132
|
+
fi
|
|
133
|
+
log_success "On branch: $BRANCH"
|
|
134
|
+
|
|
135
|
+
# Stash uncommitted changes to preserve user work
|
|
136
|
+
if ! git diff-index --quiet HEAD -- 2>/dev/null; then
|
|
137
|
+
log_warning "Uncommitted changes detected. Stashing for safety..."
|
|
138
|
+
git stash push -m "release.sh: temporary stash $(date +%Y%m%d-%H%M%S)" >/dev/null 2>&1 || {
|
|
139
|
+
log_error "Failed to stash changes. Please commit or stash manually."
|
|
140
|
+
exit 1
|
|
141
|
+
}
|
|
142
|
+
STASH_CREATED="true"
|
|
143
|
+
log_success "Changes stashed (will be restored on exit)"
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
# Check if remote is configured
|
|
147
|
+
if ! git remote -v | grep -q origin; then
|
|
148
|
+
log_error "No remote 'origin' configured"
|
|
149
|
+
exit 1
|
|
150
|
+
fi
|
|
151
|
+
log_success "Remote configured"
|
|
152
|
+
|
|
153
|
+
# Check for Node.js
|
|
154
|
+
if ! command -v node &> /dev/null; then
|
|
155
|
+
log_error "Node.js is not installed"
|
|
156
|
+
exit 1
|
|
157
|
+
fi
|
|
158
|
+
log_success "Node.js available"
|
|
159
|
+
|
|
160
|
+
# Get current version
|
|
161
|
+
CURRENT_VERSION=$(node -p "require('$PROJECT_ROOT/package.json').version")
|
|
162
|
+
if [ -z "$CURRENT_VERSION" ]; then
|
|
163
|
+
log_error "Could not read current version from package.json"
|
|
164
|
+
exit 1
|
|
165
|
+
fi
|
|
166
|
+
log_success "Current version: $CURRENT_VERSION"
|
|
167
|
+
|
|
168
|
+
# Get last git tag (across all branches using --all)
|
|
169
|
+
LAST_TAG=$(git tag --sort=-version:refname | head -n 1 || echo "")
|
|
170
|
+
if [ -z "$LAST_TAG" ]; then
|
|
171
|
+
log_warning "No previous git tags found (first release)"
|
|
172
|
+
LAST_TAG="HEAD~999999" # Get all commits
|
|
173
|
+
COMMITS_RANGE="HEAD"
|
|
174
|
+
else
|
|
175
|
+
log_success "Last tag: $LAST_TAG"
|
|
176
|
+
COMMITS_RANGE="${LAST_TAG}..HEAD"
|
|
177
|
+
|
|
178
|
+
# Sync package.json version with git tag if needed
|
|
179
|
+
TAG_VERSION="${LAST_TAG#v}" # Remove 'v' prefix
|
|
180
|
+
if [ "$CURRENT_VERSION" != "$TAG_VERSION" ]; then
|
|
181
|
+
log_warning "Version mismatch: package.json ($CURRENT_VERSION) != tag ($TAG_VERSION)"
|
|
182
|
+
log_info "Syncing package.json versions to $TAG_VERSION..."
|
|
183
|
+
|
|
184
|
+
# Find and update all package.json files
|
|
185
|
+
find "$PROJECT_ROOT" -name "package.json" -not -path "*/node_modules/*" -print0 | while IFS= read -r -d '' pkg_file; do
|
|
186
|
+
if grep -q "\"version\"" "$pkg_file"; then
|
|
187
|
+
sed -i "s/\"version\": \"[^\"]*\"/\"version\": \"$TAG_VERSION\"/" "$pkg_file"
|
|
188
|
+
MODIFIED_FILES+=("$pkg_file")
|
|
189
|
+
fi
|
|
190
|
+
done
|
|
191
|
+
|
|
192
|
+
CURRENT_VERSION="$TAG_VERSION"
|
|
193
|
+
log_success "Synced all package.json files to version $TAG_VERSION"
|
|
194
|
+
fi
|
|
195
|
+
fi
|
|
196
|
+
|
|
197
|
+
# Check for commits since last tag
|
|
198
|
+
COMMITS_COUNT=$(git rev-list $COMMITS_RANGE --count 2>/dev/null || echo "0")
|
|
199
|
+
if [ "$COMMITS_COUNT" -eq 0 ]; then
|
|
200
|
+
log_error "No commits since last release ($LAST_TAG)"
|
|
201
|
+
echo "Nothing to release!"
|
|
202
|
+
exit 1
|
|
203
|
+
fi
|
|
204
|
+
log_success "Found $COMMITS_COUNT commits since last release"
|
|
205
|
+
|
|
206
|
+
echo ""
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
# === COMMIT PARSING ===
|
|
210
|
+
|
|
211
|
+
parse_commits() {
|
|
212
|
+
log_info "Analyzing commits since ${LAST_TAG:-start}..."
|
|
213
|
+
echo ""
|
|
214
|
+
|
|
215
|
+
# Get all commits with hash
|
|
216
|
+
while IFS= read -r line; do
|
|
217
|
+
if [ -n "$line" ]; then
|
|
218
|
+
ALL_COMMITS+=("$line")
|
|
219
|
+
fi
|
|
220
|
+
done < <(git log --format="%h %s" $COMMITS_RANGE)
|
|
221
|
+
|
|
222
|
+
# Parse and categorize each commit
|
|
223
|
+
# Define regex patterns as variables for proper bash regex matching
|
|
224
|
+
local breaking_pattern='^[a-z]+(\([^)]+\))?!:'
|
|
225
|
+
local feat_pattern='^feat(\([^)]+\))?:'
|
|
226
|
+
local fix_pattern='^fix(\([^)]+\))?:'
|
|
227
|
+
local refactor_pattern='^refactor(\([^)]+\))?:'
|
|
228
|
+
local perf_pattern='^perf(\([^)]+\))?:'
|
|
229
|
+
|
|
230
|
+
for commit in "${ALL_COMMITS[@]}"; do
|
|
231
|
+
local hash=$(echo "$commit" | awk '{print $1}')
|
|
232
|
+
local message=$(echo "$commit" | cut -d' ' -f2-)
|
|
233
|
+
|
|
234
|
+
# Check for breaking changes
|
|
235
|
+
if [[ "$message" =~ $breaking_pattern ]] || echo "$message" | grep -q "BREAKING CHANGE:"; then
|
|
236
|
+
BREAKING_CHANGES+=("$commit")
|
|
237
|
+
# Check for features
|
|
238
|
+
elif [[ "$message" =~ $feat_pattern ]]; then
|
|
239
|
+
FEATURES+=("$commit")
|
|
240
|
+
# Check for fixes
|
|
241
|
+
elif [[ "$message" =~ $fix_pattern ]]; then
|
|
242
|
+
FIXES+=("$commit")
|
|
243
|
+
# Check for refactors
|
|
244
|
+
elif [[ "$message" =~ $refactor_pattern ]]; then
|
|
245
|
+
REFACTORS+=("$commit")
|
|
246
|
+
# Check for performance improvements
|
|
247
|
+
elif [[ "$message" =~ $perf_pattern ]]; then
|
|
248
|
+
PERF+=("$commit")
|
|
249
|
+
# Everything else
|
|
250
|
+
else
|
|
251
|
+
OTHER_CHANGES+=("$commit")
|
|
252
|
+
fi
|
|
253
|
+
done
|
|
254
|
+
|
|
255
|
+
# Display commit summary
|
|
256
|
+
log_info "Commit summary:"
|
|
257
|
+
[ ${#BREAKING_CHANGES[@]} -gt 0 ] && echo " 🔥 ${#BREAKING_CHANGES[@]} breaking changes"
|
|
258
|
+
[ ${#FEATURES[@]} -gt 0 ] && echo " ✨ ${#FEATURES[@]} features"
|
|
259
|
+
[ ${#FIXES[@]} -gt 0 ] && echo " 🐛 ${#FIXES[@]} bug fixes"
|
|
260
|
+
[ ${#REFACTORS[@]} -gt 0 ] && echo " ♻️ ${#REFACTORS[@]} refactors"
|
|
261
|
+
[ ${#PERF[@]} -gt 0 ] && echo " ⚡ ${#PERF[@]} performance improvements"
|
|
262
|
+
[ ${#OTHER_CHANGES[@]} -gt 0 ] && echo " 📝 ${#OTHER_CHANGES[@]} other changes"
|
|
263
|
+
echo ""
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
# === VERSION BUMP DETECTION ===
|
|
267
|
+
|
|
268
|
+
detect_version_bump() {
|
|
269
|
+
local provided_bump="$1"
|
|
270
|
+
|
|
271
|
+
# If bump type provided, validate and use it
|
|
272
|
+
if [ -n "$provided_bump" ]; then
|
|
273
|
+
if [[ ! "$provided_bump" =~ ^(patch|minor|major)$ ]]; then
|
|
274
|
+
log_error "Invalid version bump type: $provided_bump"
|
|
275
|
+
echo "Usage: ./release.sh [patch|minor|major]"
|
|
276
|
+
exit 1
|
|
277
|
+
fi
|
|
278
|
+
BUMP_TYPE="$provided_bump"
|
|
279
|
+
AUTO_DETECT_REASON="Manually specified"
|
|
280
|
+
log_info "Using manual version bump: $BUMP_TYPE"
|
|
281
|
+
else
|
|
282
|
+
# Auto-detect from commits
|
|
283
|
+
if [ ${#BREAKING_CHANGES[@]} -gt 0 ]; then
|
|
284
|
+
BUMP_TYPE="major"
|
|
285
|
+
AUTO_DETECT_REASON="Found ${#BREAKING_CHANGES[@]} breaking change(s)"
|
|
286
|
+
elif [ ${#FEATURES[@]} -gt 0 ]; then
|
|
287
|
+
BUMP_TYPE="minor"
|
|
288
|
+
AUTO_DETECT_REASON="Found ${#FEATURES[@]} new feature(s)"
|
|
289
|
+
elif [ ${#FIXES[@]} -gt 0 ]; then
|
|
290
|
+
BUMP_TYPE="patch"
|
|
291
|
+
AUTO_DETECT_REASON="Found ${#FIXES[@]} bug fix(es)"
|
|
292
|
+
else
|
|
293
|
+
BUMP_TYPE="patch"
|
|
294
|
+
AUTO_DETECT_REASON="Default (no conventional commits detected)"
|
|
295
|
+
fi
|
|
296
|
+
log_success "Auto-detected version bump: $BUMP_TYPE ($AUTO_DETECT_REASON)"
|
|
297
|
+
fi
|
|
298
|
+
echo ""
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
# === VERSION CALCULATION ===
|
|
302
|
+
|
|
303
|
+
calculate_new_version() {
|
|
304
|
+
local current="$CURRENT_VERSION"
|
|
305
|
+
local IFS='.'
|
|
306
|
+
read -ra parts <<< "$current"
|
|
307
|
+
|
|
308
|
+
local major="${parts[0]}"
|
|
309
|
+
local minor="${parts[1]}"
|
|
310
|
+
local patch="${parts[2]}"
|
|
311
|
+
|
|
312
|
+
case "$BUMP_TYPE" in
|
|
313
|
+
major)
|
|
314
|
+
major=$((major + 1))
|
|
315
|
+
minor=0
|
|
316
|
+
patch=0
|
|
317
|
+
;;
|
|
318
|
+
minor)
|
|
319
|
+
minor=$((minor + 1))
|
|
320
|
+
patch=0
|
|
321
|
+
;;
|
|
322
|
+
patch)
|
|
323
|
+
patch=$((patch + 1))
|
|
324
|
+
;;
|
|
325
|
+
esac
|
|
326
|
+
|
|
327
|
+
NEW_VERSION="$major.$minor.$patch"
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
# === CHANGELOG GENERATION ===
|
|
331
|
+
|
|
332
|
+
generate_changelog_entry() {
|
|
333
|
+
local version="$1"
|
|
334
|
+
local date="$2"
|
|
335
|
+
|
|
336
|
+
cat << EOF
|
|
337
|
+
## [$version] - $date
|
|
338
|
+
|
|
339
|
+
EOF
|
|
340
|
+
|
|
341
|
+
# Added section (features)
|
|
342
|
+
if [ ${#FEATURES[@]} -gt 0 ]; then
|
|
343
|
+
echo "### Added"
|
|
344
|
+
for commit in "${FEATURES[@]}"; do
|
|
345
|
+
format_changelog_line "$commit"
|
|
346
|
+
done
|
|
347
|
+
echo ""
|
|
348
|
+
fi
|
|
349
|
+
|
|
350
|
+
# Changed section (breaking, refactor, perf)
|
|
351
|
+
if [ ${#BREAKING_CHANGES[@]} -gt 0 ] || [ ${#REFACTORS[@]} -gt 0 ] || [ ${#PERF[@]} -gt 0 ]; then
|
|
352
|
+
echo "### Changed"
|
|
353
|
+
for commit in "${BREAKING_CHANGES[@]}"; do
|
|
354
|
+
format_changelog_line "$commit" "⚠️ BREAKING: "
|
|
355
|
+
done
|
|
356
|
+
for commit in "${REFACTORS[@]}"; do
|
|
357
|
+
format_changelog_line "$commit"
|
|
358
|
+
done
|
|
359
|
+
for commit in "${PERF[@]}"; do
|
|
360
|
+
format_changelog_line "$commit"
|
|
361
|
+
done
|
|
362
|
+
echo ""
|
|
363
|
+
fi
|
|
364
|
+
|
|
365
|
+
# Fixed section
|
|
366
|
+
if [ ${#FIXES[@]} -gt 0 ]; then
|
|
367
|
+
echo "### Fixed"
|
|
368
|
+
for commit in "${FIXES[@]}"; do
|
|
369
|
+
format_changelog_line "$commit"
|
|
370
|
+
done
|
|
371
|
+
echo ""
|
|
372
|
+
fi
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
format_changelog_line() {
|
|
376
|
+
local commit="$1"
|
|
377
|
+
local prefix="${2:-}"
|
|
378
|
+
|
|
379
|
+
local hash=$(echo "$commit" | awk '{print $1}')
|
|
380
|
+
local message=$(echo "$commit" | cut -d' ' -f2-)
|
|
381
|
+
|
|
382
|
+
# Extract scope if present: "type(scope): message" -> "**scope**: message"
|
|
383
|
+
local scope_pattern='^[a-z]+(\(([^)]+)\))?!?:[ ]+(.+)$'
|
|
384
|
+
if [[ "$message" =~ $scope_pattern ]]; then
|
|
385
|
+
local scope="${BASH_REMATCH[2]}"
|
|
386
|
+
local msg="${BASH_REMATCH[3]}"
|
|
387
|
+
|
|
388
|
+
if [ -n "$scope" ]; then
|
|
389
|
+
echo "- ${prefix}**${scope}**: ${msg} (${hash})"
|
|
390
|
+
else
|
|
391
|
+
echo "- ${prefix}${msg} (${hash})"
|
|
392
|
+
fi
|
|
393
|
+
else
|
|
394
|
+
# Not a conventional commit, use as-is
|
|
395
|
+
echo "- ${prefix}${message} (${hash})"
|
|
396
|
+
fi
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
# === PACKAGE.JSON UPDATES ===
|
|
400
|
+
|
|
401
|
+
update_package_files() {
|
|
402
|
+
local version="$1"
|
|
403
|
+
|
|
404
|
+
log_info "Updating package.json files..."
|
|
405
|
+
echo ""
|
|
406
|
+
|
|
407
|
+
# Find all package.json files
|
|
408
|
+
local package_files=$(find "$PROJECT_ROOT" -name "package.json" \
|
|
409
|
+
-not -path "*/node_modules/*" \
|
|
410
|
+
-not -path "*/.next/*" \
|
|
411
|
+
-not -path "*/dist/*" \
|
|
412
|
+
-not -path "*/.turbo/*" \
|
|
413
|
+
-not -path "*/build/*")
|
|
414
|
+
|
|
415
|
+
while IFS= read -r pkg; do
|
|
416
|
+
if [ -n "$pkg" ]; then
|
|
417
|
+
# Track for rollback
|
|
418
|
+
MODIFIED_FILES+=("$pkg")
|
|
419
|
+
|
|
420
|
+
# Update version using Node.js for proper JSON handling
|
|
421
|
+
node -e "
|
|
422
|
+
const fs = require('fs');
|
|
423
|
+
const path = '$pkg';
|
|
424
|
+
const data = JSON.parse(fs.readFileSync(path, 'utf-8'));
|
|
425
|
+
data.version = '$version';
|
|
426
|
+
fs.writeFileSync(path, JSON.stringify(data, null, 2) + '\n');
|
|
427
|
+
" || {
|
|
428
|
+
log_error "Failed to update $pkg"
|
|
429
|
+
exit 1
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
# Show relative path
|
|
433
|
+
local rel_path="${pkg#$PROJECT_ROOT/}"
|
|
434
|
+
echo " ✓ $rel_path"
|
|
435
|
+
fi
|
|
436
|
+
done <<< "$package_files"
|
|
437
|
+
|
|
438
|
+
echo ""
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
# === CHANGELOG UPDATE ===
|
|
442
|
+
|
|
443
|
+
update_changelog() {
|
|
444
|
+
local version="$1"
|
|
445
|
+
local date="$2"
|
|
446
|
+
|
|
447
|
+
log_info "Updating CHANGELOG.md..."
|
|
448
|
+
|
|
449
|
+
local changelog_file="$PROJECT_ROOT/CHANGELOG.md"
|
|
450
|
+
|
|
451
|
+
# Track for rollback
|
|
452
|
+
MODIFIED_FILES+=("$changelog_file")
|
|
453
|
+
|
|
454
|
+
# Generate new entry
|
|
455
|
+
local new_entry=$(generate_changelog_entry "$version" "$date")
|
|
456
|
+
|
|
457
|
+
# Read existing changelog
|
|
458
|
+
if [ -f "$changelog_file" ]; then
|
|
459
|
+
local existing_content=$(<"$changelog_file")
|
|
460
|
+
|
|
461
|
+
# Insert new entry after [Unreleased] section
|
|
462
|
+
if echo "$existing_content" | grep -q "## \[Unreleased\]"; then
|
|
463
|
+
# Find the line number of [Unreleased]
|
|
464
|
+
local unreleased_line=$(echo "$existing_content" | grep -n "## \[Unreleased\]" | head -1 | cut -d: -f1)
|
|
465
|
+
|
|
466
|
+
# Insert after [Unreleased] and its blank line
|
|
467
|
+
{
|
|
468
|
+
echo "$existing_content" | head -n $((unreleased_line))
|
|
469
|
+
echo ""
|
|
470
|
+
echo "$new_entry"
|
|
471
|
+
echo "$existing_content" | tail -n +$((unreleased_line + 1))
|
|
472
|
+
} > "$changelog_file"
|
|
473
|
+
else
|
|
474
|
+
# No [Unreleased] section, insert at the beginning after header
|
|
475
|
+
{
|
|
476
|
+
echo "$existing_content" | head -n 6
|
|
477
|
+
echo ""
|
|
478
|
+
echo "$new_entry"
|
|
479
|
+
echo "$existing_content" | tail -n +7
|
|
480
|
+
} > "$changelog_file"
|
|
481
|
+
fi
|
|
482
|
+
else
|
|
483
|
+
# Create new CHANGELOG.md
|
|
484
|
+
cat > "$changelog_file" << EOF
|
|
485
|
+
# Changelog
|
|
486
|
+
|
|
487
|
+
All notable changes to this project will be documented in this file.
|
|
488
|
+
|
|
489
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
490
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
491
|
+
|
|
492
|
+
## [Unreleased]
|
|
493
|
+
|
|
494
|
+
$new_entry
|
|
495
|
+
EOF
|
|
496
|
+
fi
|
|
497
|
+
|
|
498
|
+
log_success "CHANGELOG.md updated"
|
|
499
|
+
echo ""
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
# === PREVIEW ===
|
|
503
|
+
|
|
504
|
+
show_preview() {
|
|
505
|
+
cat << EOF
|
|
506
|
+
═══════════════════════════════════════════════════════════
|
|
507
|
+
RELEASE PREVIEW
|
|
508
|
+
═══════════════════════════════════════════════════════════
|
|
509
|
+
|
|
510
|
+
📌 Version: $CURRENT_VERSION → $NEW_VERSION (${BUMP_TYPE^^})
|
|
511
|
+
Reason: $AUTO_DETECT_REASON
|
|
512
|
+
|
|
513
|
+
📊 Commits included: ${#ALL_COMMITS[@]}
|
|
514
|
+
EOF
|
|
515
|
+
|
|
516
|
+
[ ${#BREAKING_CHANGES[@]} -gt 0 ] && echo " 🔥 ${#BREAKING_CHANGES[@]} breaking changes"
|
|
517
|
+
[ ${#FEATURES[@]} -gt 0 ] && echo " ✨ ${#FEATURES[@]} features"
|
|
518
|
+
[ ${#FIXES[@]} -gt 0 ] && echo " 🐛 ${#FIXES[@]} bug fixes"
|
|
519
|
+
[ ${#REFACTORS[@]} -gt 0 ] && echo " ♻️ ${#REFACTORS[@]} refactors"
|
|
520
|
+
[ ${#PERF[@]} -gt 0 ] && echo " ⚡ ${#PERF[@]} performance improvements"
|
|
521
|
+
[ ${#OTHER_CHANGES[@]} -gt 0 ] && echo " 📝 ${#OTHER_CHANGES[@]} other changes"
|
|
522
|
+
|
|
523
|
+
cat << EOF
|
|
524
|
+
|
|
525
|
+
📦 Package Updates:
|
|
526
|
+
EOF
|
|
527
|
+
|
|
528
|
+
find "$PROJECT_ROOT" -name "package.json" \
|
|
529
|
+
-not -path "*/node_modules/*" \
|
|
530
|
+
-not -path "*/.next/*" \
|
|
531
|
+
-not -path "*/dist/*" \
|
|
532
|
+
-not -path "*/.turbo/*" \
|
|
533
|
+
-not -path "*/build/*" | while read -r pkg; do
|
|
534
|
+
local rel_path="${pkg#$PROJECT_ROOT/}"
|
|
535
|
+
echo " ✓ $rel_path"
|
|
536
|
+
done
|
|
537
|
+
|
|
538
|
+
cat << EOF
|
|
539
|
+
|
|
540
|
+
📄 CHANGELOG.md Entry:
|
|
541
|
+
───────────────────────────────────────────────────────────
|
|
542
|
+
$(generate_changelog_entry "$NEW_VERSION" "$DATE")───────────────────────────────────────────────────────────
|
|
543
|
+
|
|
544
|
+
💬 Git Commit Message:
|
|
545
|
+
───────────────────────────────────────────────────────────
|
|
546
|
+
chore(release): v$NEW_VERSION
|
|
547
|
+
|
|
548
|
+
Release version $NEW_VERSION with ${#FEATURES[@]} features and ${#FIXES[@]} fixes
|
|
549
|
+
|
|
550
|
+
Includes commits from ${LAST_TAG:-start} to HEAD
|
|
551
|
+
|
|
552
|
+
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
|
553
|
+
|
|
554
|
+
Co-Authored-By: Claude <noreply@anthropic.com>
|
|
555
|
+
───────────────────────────────────────────────────────────
|
|
556
|
+
|
|
557
|
+
🏷️ Git Tag: v$NEW_VERSION
|
|
558
|
+
🌿 Branch: $BRANCH
|
|
559
|
+
|
|
560
|
+
═══════════════════════════════════════════════════════════
|
|
561
|
+
EOF
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
# === USER CONFIRMATION ===
|
|
565
|
+
|
|
566
|
+
get_user_confirmation() {
|
|
567
|
+
local auto_confirm="$1"
|
|
568
|
+
|
|
569
|
+
echo ""
|
|
570
|
+
|
|
571
|
+
# Skip confirmation if --yes flag provided
|
|
572
|
+
if [ "$auto_confirm" = "true" ]; then
|
|
573
|
+
log_info "Auto-confirming release (--yes flag provided)"
|
|
574
|
+
echo ""
|
|
575
|
+
return 0
|
|
576
|
+
fi
|
|
577
|
+
|
|
578
|
+
read -p "Proceed with release? [Y/n]: " confirm
|
|
579
|
+
|
|
580
|
+
if [[ ! "$confirm" =~ ^[Yy]?$ ]]; then
|
|
581
|
+
log_warning "Release cancelled by user"
|
|
582
|
+
exit 0
|
|
583
|
+
fi
|
|
584
|
+
|
|
585
|
+
echo ""
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
# === EXECUTE RELEASE ===
|
|
589
|
+
|
|
590
|
+
execute_release() {
|
|
591
|
+
log_info "Executing release..."
|
|
592
|
+
echo ""
|
|
593
|
+
|
|
594
|
+
# Stage all changes
|
|
595
|
+
log_info "Staging changes..."
|
|
596
|
+
git add -A
|
|
597
|
+
|
|
598
|
+
# Create commit
|
|
599
|
+
log_info "Creating release commit..."
|
|
600
|
+
git commit -m "chore(release): v$NEW_VERSION
|
|
601
|
+
|
|
602
|
+
Release version $NEW_VERSION with ${#FEATURES[@]} features and ${#FIXES[@]} fixes
|
|
603
|
+
|
|
604
|
+
Includes commits from ${LAST_TAG:-start} to HEAD
|
|
605
|
+
|
|
606
|
+
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
|
607
|
+
|
|
608
|
+
Co-Authored-By: Claude <noreply@anthropic.com>" || {
|
|
609
|
+
log_error "Failed to create commit"
|
|
610
|
+
exit 1
|
|
611
|
+
}
|
|
612
|
+
CREATED_COMMIT="true"
|
|
613
|
+
log_success "Commit created"
|
|
614
|
+
|
|
615
|
+
# Create tag (check if exists first)
|
|
616
|
+
log_info "Creating git tag..."
|
|
617
|
+
|
|
618
|
+
# Check if tag already exists
|
|
619
|
+
if git rev-parse "v$NEW_VERSION" >/dev/null 2>&1; then
|
|
620
|
+
log_error "Tag v$NEW_VERSION already exists!"
|
|
621
|
+
echo ""
|
|
622
|
+
log_info "Existing tags:"
|
|
623
|
+
git tag --sort=-version:refname | head -n 10
|
|
624
|
+
echo ""
|
|
625
|
+
log_info "Suggested actions:"
|
|
626
|
+
echo " 1. Delete existing tag: git tag -d v$NEW_VERSION && git push origin :refs/tags/v$NEW_VERSION"
|
|
627
|
+
echo " 2. Use different version: ./release.sh [patch|minor|major]"
|
|
628
|
+
exit 1
|
|
629
|
+
fi
|
|
630
|
+
|
|
631
|
+
local tag_message="Release v$NEW_VERSION
|
|
632
|
+
|
|
633
|
+
$(generate_changelog_entry "$NEW_VERSION" "$DATE")"
|
|
634
|
+
|
|
635
|
+
git tag -a "v$NEW_VERSION" -m "$tag_message" || {
|
|
636
|
+
log_error "Failed to create tag"
|
|
637
|
+
exit 1
|
|
638
|
+
}
|
|
639
|
+
CREATED_TAG="v$NEW_VERSION"
|
|
640
|
+
log_success "Tag v$NEW_VERSION created"
|
|
641
|
+
|
|
642
|
+
# Push to remote
|
|
643
|
+
log_info "Pushing to remote..."
|
|
644
|
+
git push origin "$BRANCH" --follow-tags || {
|
|
645
|
+
log_error "Failed to push to remote"
|
|
646
|
+
echo ""
|
|
647
|
+
log_warning "Your changes are committed locally but push failed."
|
|
648
|
+
echo ""
|
|
649
|
+
echo "To retry push:"
|
|
650
|
+
echo " git push origin $BRANCH --follow-tags"
|
|
651
|
+
echo ""
|
|
652
|
+
echo "To rollback:"
|
|
653
|
+
echo " git reset --hard HEAD~1"
|
|
654
|
+
echo " git tag -d v$NEW_VERSION"
|
|
655
|
+
echo ""
|
|
656
|
+
exit 1
|
|
657
|
+
}
|
|
658
|
+
log_success "Pushed to origin/$BRANCH"
|
|
659
|
+
|
|
660
|
+
echo ""
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
# === MAIN ===
|
|
664
|
+
|
|
665
|
+
main() {
|
|
666
|
+
cd "$PROJECT_ROOT"
|
|
667
|
+
|
|
668
|
+
echo ""
|
|
669
|
+
echo "╔═══════════════════════════════════════════════════════════╗"
|
|
670
|
+
echo "║ Release Automation ║"
|
|
671
|
+
echo "╚═══════════════════════════════════════════════════════════╝"
|
|
672
|
+
echo ""
|
|
673
|
+
|
|
674
|
+
# Parse arguments
|
|
675
|
+
local bump_arg=""
|
|
676
|
+
local auto_confirm="false"
|
|
677
|
+
|
|
678
|
+
for arg in "$@"; do
|
|
679
|
+
case "$arg" in
|
|
680
|
+
--yes|-y)
|
|
681
|
+
auto_confirm="true"
|
|
682
|
+
;;
|
|
683
|
+
patch|minor|major)
|
|
684
|
+
bump_arg="$arg"
|
|
685
|
+
;;
|
|
686
|
+
*)
|
|
687
|
+
log_error "Unknown argument: $arg"
|
|
688
|
+
echo "Usage: $0 [patch|minor|major] [--yes]"
|
|
689
|
+
exit 1
|
|
690
|
+
;;
|
|
691
|
+
esac
|
|
692
|
+
done
|
|
693
|
+
|
|
694
|
+
# Run workflow
|
|
695
|
+
run_preflight_checks
|
|
696
|
+
parse_commits
|
|
697
|
+
detect_version_bump "$bump_arg"
|
|
698
|
+
calculate_new_version
|
|
699
|
+
|
|
700
|
+
# Show preview
|
|
701
|
+
show_preview
|
|
702
|
+
get_user_confirmation "$auto_confirm"
|
|
703
|
+
|
|
704
|
+
# Execute release
|
|
705
|
+
update_package_files "$NEW_VERSION"
|
|
706
|
+
update_changelog "$NEW_VERSION" "$DATE"
|
|
707
|
+
execute_release
|
|
708
|
+
|
|
709
|
+
# Success! Restore stash if created
|
|
710
|
+
if [ -n "$STASH_CREATED" ]; then
|
|
711
|
+
log_info "Restoring stashed changes..."
|
|
712
|
+
if git stash list | grep -q "release.sh: temporary stash"; then
|
|
713
|
+
git stash pop >/dev/null 2>&1 || {
|
|
714
|
+
log_warning "Stash pop had conflicts. Check 'git stash list'"
|
|
715
|
+
log_info "Manually restore with: git stash pop"
|
|
716
|
+
}
|
|
717
|
+
fi
|
|
718
|
+
fi
|
|
719
|
+
|
|
720
|
+
echo ""
|
|
721
|
+
echo "╔═══════════════════════════════════════════════════════════╗"
|
|
722
|
+
echo "║ RELEASE SUCCESSFUL! 🎉 ║"
|
|
723
|
+
echo "╚═══════════════════════════════════════════════════════════╝"
|
|
724
|
+
echo ""
|
|
725
|
+
log_success "Released v$NEW_VERSION"
|
|
726
|
+
log_success "Tag: v$NEW_VERSION"
|
|
727
|
+
log_success "Branch: $BRANCH"
|
|
728
|
+
if [ -n "$STASH_CREATED" ]; then
|
|
729
|
+
log_success "Stashed changes restored"
|
|
730
|
+
fi
|
|
731
|
+
echo ""
|
|
732
|
+
log_info "Next steps:"
|
|
733
|
+
echo " • Verify release on GitHub: git remote -v"
|
|
734
|
+
echo " • Create GitHub Release from tag (optional)"
|
|
735
|
+
echo " • Notify team if applicable"
|
|
736
|
+
echo ""
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
# Run main function
|
|
740
|
+
main "$@"
|