jekyll-theme-zer0 0.7.2 → 0.8.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +72 -0
- data/README.md +33 -4
- data/_plugins/preview_image_generator.rb +258 -0
- data/_plugins/theme_version.rb +88 -0
- data/assets/images/previews/git-workflow-best-practices-for-modern-teams.png +0 -0
- data/scripts/README.md +443 -0
- data/scripts/analyze-commits.sh +313 -0
- data/scripts/build +115 -0
- data/scripts/build.sh +33 -0
- data/scripts/build.sh.legacy +174 -0
- data/scripts/example-usage.sh +102 -0
- data/scripts/fix-markdown-format.sh +265 -0
- data/scripts/gem-publish.sh +42 -0
- data/scripts/gem-publish.sh.legacy +700 -0
- data/scripts/generate-preview-images.sh +846 -0
- data/scripts/install-preview-generator.sh +531 -0
- data/scripts/lib/README.md +263 -0
- data/scripts/lib/changelog.sh +313 -0
- data/scripts/lib/common.sh +154 -0
- data/scripts/lib/gem.sh +226 -0
- data/scripts/lib/git.sh +205 -0
- data/scripts/lib/preview_generator.py +646 -0
- data/scripts/lib/test/run_tests.sh +140 -0
- data/scripts/lib/test/test_changelog.sh +87 -0
- data/scripts/lib/test/test_gem.sh +68 -0
- data/scripts/lib/test/test_git.sh +82 -0
- data/scripts/lib/test/test_validation.sh +72 -0
- data/scripts/lib/test/test_version.sh +96 -0
- data/scripts/lib/validation.sh +139 -0
- data/scripts/lib/version.sh +178 -0
- data/scripts/release +240 -0
- data/scripts/release.sh +33 -0
- data/scripts/release.sh.legacy +342 -0
- data/scripts/setup.sh +155 -0
- data/scripts/test-auto-version.sh +260 -0
- data/scripts/test-mermaid.sh +251 -0
- data/scripts/test.sh +156 -0
- data/scripts/version.sh +152 -0
- metadata +37 -1
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# Release Script Libraries
|
|
2
|
+
|
|
3
|
+
Modular, tested, and reusable libraries for the zer0-mistakes release automation system.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This directory contains focused, single-responsibility libraries that power the release automation. Each library can be used independently or composed together for complex workflows.
|
|
8
|
+
|
|
9
|
+
## Libraries
|
|
10
|
+
|
|
11
|
+
### 📦 `common.sh` - Shared Utilities
|
|
12
|
+
Core utilities used by all other libraries.
|
|
13
|
+
|
|
14
|
+
**Functions:**
|
|
15
|
+
- `log()`, `info()`, `warn()`, `error()` - Colored logging
|
|
16
|
+
- `confirm()` - User confirmation prompts
|
|
17
|
+
- `dry_run_exec()` - Dry run wrapper for commands
|
|
18
|
+
- `require_command()`, `require_file()` - Dependency validation
|
|
19
|
+
- `get_repo_root()` - Find repository root directory
|
|
20
|
+
|
|
21
|
+
**Usage:**
|
|
22
|
+
```bash
|
|
23
|
+
source "$(dirname "$0")/lib/common.sh"
|
|
24
|
+
log "Starting process..."
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### 🔍 `validation.sh` - Environment Validation
|
|
28
|
+
Validates environment, dependencies, and prerequisites.
|
|
29
|
+
|
|
30
|
+
**Functions:**
|
|
31
|
+
- `validate_git_repo()` - Verify git repository
|
|
32
|
+
- `validate_clean_working_dir()` - Check for uncommitted changes
|
|
33
|
+
- `validate_required_files()` - Check required files exist
|
|
34
|
+
- `validate_dependencies()` - Verify all commands available
|
|
35
|
+
- `validate_rubygems_auth()` - Check RubyGems credentials
|
|
36
|
+
- `validate_environment()` - Comprehensive validation
|
|
37
|
+
|
|
38
|
+
**Usage:**
|
|
39
|
+
```bash
|
|
40
|
+
source "$(dirname "$0")/lib/validation.sh"
|
|
41
|
+
validate_environment false false # skip_publish=false, require_gh=false
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 📝 `version.sh` - Version Management
|
|
45
|
+
Read, calculate, and update semantic versions.
|
|
46
|
+
|
|
47
|
+
**Functions:**
|
|
48
|
+
- `get_current_version()` - Read version from version.rb
|
|
49
|
+
- `calculate_new_version()` - Calculate new version (major/minor/patch)
|
|
50
|
+
- `update_version_files()` - Update all version files
|
|
51
|
+
- `validate_version_format()` - Validate semver format
|
|
52
|
+
- `version_less_than()` - Compare two versions
|
|
53
|
+
|
|
54
|
+
**Usage:**
|
|
55
|
+
```bash
|
|
56
|
+
source "$(dirname "$0")/lib/version.sh"
|
|
57
|
+
|
|
58
|
+
current=$(get_current_version)
|
|
59
|
+
new=$(calculate_new_version "$current" "minor")
|
|
60
|
+
update_version_files "$new"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 📋 `changelog.sh` - Changelog Generation
|
|
64
|
+
Generate changelogs from conventional commits.
|
|
65
|
+
|
|
66
|
+
**Functions:**
|
|
67
|
+
- `generate_changelog()` - Generate changelog for version
|
|
68
|
+
- `categorize_commit()` - Categorize commit by type
|
|
69
|
+
- `clean_commit_message()` - Clean conventional commit prefixes
|
|
70
|
+
- `extract_release_notes()` - Extract notes for specific version
|
|
71
|
+
|
|
72
|
+
**Commit Categories:**
|
|
73
|
+
- `feat:` → Added
|
|
74
|
+
- `fix:` → Fixed
|
|
75
|
+
- `BREAKING:` → Breaking Changes
|
|
76
|
+
- `docs:`, `chore:`, `refactor:` → Changed
|
|
77
|
+
- `remove:` → Removed
|
|
78
|
+
- `security:` → Security
|
|
79
|
+
|
|
80
|
+
**Usage:**
|
|
81
|
+
```bash
|
|
82
|
+
source "$(dirname "$0")/lib/changelog.sh"
|
|
83
|
+
|
|
84
|
+
generate_changelog "1.2.0" "v1.1.0" "HEAD"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 🔄 `git.sh` - Git Operations
|
|
88
|
+
Git commits, tags, and repository operations.
|
|
89
|
+
|
|
90
|
+
**Functions:**
|
|
91
|
+
- `get_last_version_tag()` - Find last version tag
|
|
92
|
+
- `commit_and_tag()` - Create release commit and tag
|
|
93
|
+
- `push_changes()` - Push to remote with tags
|
|
94
|
+
- `get_commits_between()` - Get commits in range
|
|
95
|
+
- `get_repo_info()` - Extract owner/repo from URL
|
|
96
|
+
|
|
97
|
+
**Usage:**
|
|
98
|
+
```bash
|
|
99
|
+
source "$(dirname "$0")/lib/git.sh"
|
|
100
|
+
|
|
101
|
+
commit_and_tag "1.2.0"
|
|
102
|
+
push_changes "origin" "main"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 💎 `gem.sh` - Gem Operations
|
|
106
|
+
Build, test, publish, and release gems.
|
|
107
|
+
|
|
108
|
+
**Functions:**
|
|
109
|
+
- `build_gem()` - Build the gem package
|
|
110
|
+
- `publish_gem()` - Publish to RubyGems
|
|
111
|
+
- `create_github_release()` - Create GitHub release
|
|
112
|
+
- `run_tests()` - Execute test suite
|
|
113
|
+
- `gem_version_exists()` - Check if version exists on RubyGems
|
|
114
|
+
|
|
115
|
+
**Usage:**
|
|
116
|
+
```bash
|
|
117
|
+
source "$(dirname "$0")/lib/gem.sh"
|
|
118
|
+
|
|
119
|
+
build_gem "1.2.0"
|
|
120
|
+
run_tests
|
|
121
|
+
publish_gem "1.2.0"
|
|
122
|
+
create_github_release "1.2.0"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Testing
|
|
126
|
+
|
|
127
|
+
Each library has comprehensive unit tests in `test/`.
|
|
128
|
+
|
|
129
|
+
### Run All Tests
|
|
130
|
+
```bash
|
|
131
|
+
./scripts/lib/test/run_tests.sh
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Run Individual Tests
|
|
135
|
+
```bash
|
|
136
|
+
./scripts/lib/test/test_version.sh
|
|
137
|
+
./scripts/lib/test/test_changelog.sh
|
|
138
|
+
./scripts/lib/test/test_git.sh
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Test Coverage
|
|
142
|
+
- ✅ Version calculations and validation
|
|
143
|
+
- ✅ Changelog generation and categorization
|
|
144
|
+
- ✅ Git operations and tag management
|
|
145
|
+
- ✅ Environment validation
|
|
146
|
+
- ✅ Gem build and publish workflows
|
|
147
|
+
|
|
148
|
+
## Environment Variables
|
|
149
|
+
|
|
150
|
+
Control library behavior with environment variables:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
# Dry run mode (no actual changes)
|
|
154
|
+
DRY_RUN=true
|
|
155
|
+
|
|
156
|
+
# Non-interactive mode (auto-confirm prompts)
|
|
157
|
+
INTERACTIVE=false
|
|
158
|
+
|
|
159
|
+
# Verbose debug output
|
|
160
|
+
VERBOSE=true
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Example: Custom Release Script
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
#!/bin/bash
|
|
167
|
+
set -euo pipefail
|
|
168
|
+
|
|
169
|
+
# Source libraries
|
|
170
|
+
LIB_DIR="$(dirname "$0")/lib"
|
|
171
|
+
source "$LIB_DIR/common.sh"
|
|
172
|
+
source "$LIB_DIR/validation.sh"
|
|
173
|
+
source "$LIB_DIR/version.sh"
|
|
174
|
+
source "$LIB_DIR/changelog.sh"
|
|
175
|
+
source "$LIB_DIR/git.sh"
|
|
176
|
+
source "$LIB_DIR/gem.sh"
|
|
177
|
+
|
|
178
|
+
# Main workflow
|
|
179
|
+
main() {
|
|
180
|
+
print_header "Custom Release"
|
|
181
|
+
|
|
182
|
+
# Validate
|
|
183
|
+
validate_environment
|
|
184
|
+
|
|
185
|
+
# Version
|
|
186
|
+
local current=$(get_current_version)
|
|
187
|
+
local new=$(calculate_new_version "$current" "patch")
|
|
188
|
+
update_version_files "$new"
|
|
189
|
+
|
|
190
|
+
# Changelog
|
|
191
|
+
generate_changelog "$new"
|
|
192
|
+
|
|
193
|
+
# Build & Test
|
|
194
|
+
build_gem "$new"
|
|
195
|
+
run_tests
|
|
196
|
+
|
|
197
|
+
# Commit & Tag
|
|
198
|
+
commit_and_tag "$new"
|
|
199
|
+
|
|
200
|
+
# Publish
|
|
201
|
+
publish_gem "$new"
|
|
202
|
+
create_github_release "$new"
|
|
203
|
+
push_changes
|
|
204
|
+
|
|
205
|
+
success "Release $new complete!"
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
main "$@"
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Architecture Benefits
|
|
212
|
+
|
|
213
|
+
### ✅ Modularity
|
|
214
|
+
Each library has ONE responsibility - easy to understand and modify.
|
|
215
|
+
|
|
216
|
+
### ✅ Testability
|
|
217
|
+
Small, focused functions can be unit tested independently.
|
|
218
|
+
|
|
219
|
+
### ✅ Reusability
|
|
220
|
+
Libraries can be used in different scripts or GitHub Actions.
|
|
221
|
+
|
|
222
|
+
### ✅ Maintainability
|
|
223
|
+
Changes isolated to specific files - less ripple effect.
|
|
224
|
+
|
|
225
|
+
### ✅ Clarity
|
|
226
|
+
Functions have clear names and single purposes.
|
|
227
|
+
|
|
228
|
+
## Migrating Old Scripts
|
|
229
|
+
|
|
230
|
+
Old monolithic scripts (`gem-publish.sh`, `release.sh`, `build.sh`) can now be:
|
|
231
|
+
|
|
232
|
+
1. **Deprecated** with warnings pointing to new libraries
|
|
233
|
+
2. **Replaced** with thin wrappers using libraries
|
|
234
|
+
3. **Removed** once adoption is confirmed
|
|
235
|
+
|
|
236
|
+
Example deprecation wrapper:
|
|
237
|
+
```bash
|
|
238
|
+
#!/bin/bash
|
|
239
|
+
echo "⚠️ WARNING: This script is deprecated"
|
|
240
|
+
echo " Use: ./scripts/release"
|
|
241
|
+
exec "$(dirname "$0")/release" "$@"
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Contributing
|
|
245
|
+
|
|
246
|
+
When adding new functionality:
|
|
247
|
+
|
|
248
|
+
1. **Choose the right library** - or create a new one if needed
|
|
249
|
+
2. **Write tests first** - add tests to `test/test_*.sh`
|
|
250
|
+
3. **Keep functions small** - one function, one purpose
|
|
251
|
+
4. **Document thoroughly** - update this README
|
|
252
|
+
5. **Test in isolation** - each library should work standalone
|
|
253
|
+
|
|
254
|
+
## Questions?
|
|
255
|
+
|
|
256
|
+
- See `docs/RELEASE_WORKFLOW_IMPROVEMENTS.md` for the full refactoring plan
|
|
257
|
+
- Check existing library code for patterns and examples
|
|
258
|
+
- Run tests to ensure everything still works
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
**Phase 1 Complete** ✅
|
|
263
|
+
All libraries extracted with comprehensive test coverage.
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Changelog generation library for zer0-mistakes release scripts
|
|
4
|
+
# Provides automatic changelog generation from git commit history
|
|
5
|
+
|
|
6
|
+
# Check Bash version (need 4+ for associative arrays)
|
|
7
|
+
if [[ "${BASH_VERSINFO[0]}" -lt 4 ]]; then
|
|
8
|
+
echo "[ERROR] This script requires Bash 4.0 or higher (current: ${BASH_VERSION})" >&2
|
|
9
|
+
echo "[INFO] On macOS, install via: brew install bash" >&2
|
|
10
|
+
echo "[INFO] Then update scripts to use: #!/usr/local/bin/bash" >&2
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
# Source common utilities
|
|
15
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
16
|
+
source "$SCRIPT_DIR/common.sh"
|
|
17
|
+
source "$SCRIPT_DIR/git.sh"
|
|
18
|
+
|
|
19
|
+
# Constants (check if already defined)
|
|
20
|
+
if [[ -z "${CHANGELOG_FILE:-}" ]]; then
|
|
21
|
+
readonly CHANGELOG_FILE="CHANGELOG.md"
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# Categorize a single commit based on conventional commit format
|
|
25
|
+
categorize_commit() {
|
|
26
|
+
local commit_hash="$1"
|
|
27
|
+
local subject
|
|
28
|
+
|
|
29
|
+
subject=$(get_commit_subject "$commit_hash")
|
|
30
|
+
local subject_lower=$(echo "$subject" | tr '[:upper:]' '[:lower:]')
|
|
31
|
+
|
|
32
|
+
debug "Categorizing: $subject"
|
|
33
|
+
|
|
34
|
+
# Check commit message and body for breaking changes
|
|
35
|
+
local commit_full
|
|
36
|
+
commit_full=$(get_commit_message "$commit_hash")
|
|
37
|
+
|
|
38
|
+
if echo "$commit_full" | grep -qi "BREAKING CHANGE\|breaking:"; then
|
|
39
|
+
echo "breaking"
|
|
40
|
+
return 0
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# Conventional commit patterns
|
|
44
|
+
case "$subject_lower" in
|
|
45
|
+
feat:*|feature:*|add:*|new:*)
|
|
46
|
+
echo "added"
|
|
47
|
+
;;
|
|
48
|
+
fix:*|bugfix:*|bug:*|patch:*)
|
|
49
|
+
echo "fixed"
|
|
50
|
+
;;
|
|
51
|
+
perf:*|performance:*)
|
|
52
|
+
echo "changed"
|
|
53
|
+
;;
|
|
54
|
+
refactor:*)
|
|
55
|
+
echo "changed"
|
|
56
|
+
;;
|
|
57
|
+
style:*)
|
|
58
|
+
echo "changed"
|
|
59
|
+
;;
|
|
60
|
+
docs:*|doc:*)
|
|
61
|
+
echo "changed"
|
|
62
|
+
;;
|
|
63
|
+
test:*)
|
|
64
|
+
echo "changed"
|
|
65
|
+
;;
|
|
66
|
+
chore:*)
|
|
67
|
+
echo "changed"
|
|
68
|
+
;;
|
|
69
|
+
ci:*)
|
|
70
|
+
echo "changed"
|
|
71
|
+
;;
|
|
72
|
+
build:*)
|
|
73
|
+
echo "changed"
|
|
74
|
+
;;
|
|
75
|
+
revert:*|remove:*|delete:*)
|
|
76
|
+
echo "removed"
|
|
77
|
+
;;
|
|
78
|
+
deprecate:*|deprecated:*)
|
|
79
|
+
echo "deprecated"
|
|
80
|
+
;;
|
|
81
|
+
security:*|sec:*)
|
|
82
|
+
echo "security"
|
|
83
|
+
;;
|
|
84
|
+
*)
|
|
85
|
+
echo "other"
|
|
86
|
+
;;
|
|
87
|
+
esac
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# Clean commit message for changelog
|
|
91
|
+
clean_commit_message() {
|
|
92
|
+
local subject="$1"
|
|
93
|
+
|
|
94
|
+
# Remove conventional commit prefix
|
|
95
|
+
subject=$(echo "$subject" | sed -E 's/^(feat|feature|fix|bugfix|bug|patch|perf|performance|refactor|style|docs|doc|test|chore|ci|build|revert|remove|delete|deprecate|deprecated|security|sec)(\([^)]*\))?:\s*//')
|
|
96
|
+
|
|
97
|
+
# Capitalize first letter
|
|
98
|
+
subject="$(echo "${subject:0:1}" | tr '[:lower:]' '[:upper:]')${subject:1}"
|
|
99
|
+
|
|
100
|
+
echo "$subject"
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# Generate changelog entry for a version
|
|
104
|
+
generate_changelog() {
|
|
105
|
+
local new_version="$1"
|
|
106
|
+
local from_ref="${2:-$(get_last_version_tag)}"
|
|
107
|
+
local to_ref="${3:-HEAD}"
|
|
108
|
+
|
|
109
|
+
step "Generating changelog for version $new_version..."
|
|
110
|
+
|
|
111
|
+
debug "Commit range: $from_ref..$to_ref"
|
|
112
|
+
|
|
113
|
+
# Get commits
|
|
114
|
+
local commits_raw
|
|
115
|
+
commits_raw=$(get_commits_between "$from_ref" "$to_ref")
|
|
116
|
+
|
|
117
|
+
if [[ -z "$commits_raw" ]]; then
|
|
118
|
+
warn "No commits found since $from_ref"
|
|
119
|
+
return 0
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
# Parse and categorize commits
|
|
123
|
+
declare -A categories
|
|
124
|
+
categories=(
|
|
125
|
+
["breaking"]=""
|
|
126
|
+
["added"]=""
|
|
127
|
+
["changed"]=""
|
|
128
|
+
["deprecated"]=""
|
|
129
|
+
["removed"]=""
|
|
130
|
+
["fixed"]=""
|
|
131
|
+
["security"]=""
|
|
132
|
+
["other"]=""
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
local commit_count=0
|
|
136
|
+
while IFS='|' read -r hash subject author date; do
|
|
137
|
+
[[ -z "$hash" ]] && continue
|
|
138
|
+
|
|
139
|
+
((commit_count++))
|
|
140
|
+
|
|
141
|
+
# Skip merge commits
|
|
142
|
+
if echo "$subject" | grep -qE "^Merge (branch|pull request|remote-tracking branch)"; then
|
|
143
|
+
debug "Skipping merge commit: $hash"
|
|
144
|
+
continue
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
# Skip automated version/changelog commits
|
|
148
|
+
if echo "$subject" | grep -qE "^(chore: (bump version|release version|update changelog)|Automated|Auto-update)"; then
|
|
149
|
+
debug "Skipping automated commit: $hash"
|
|
150
|
+
continue
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
# Check if commit only modified version/changelog files
|
|
154
|
+
local files
|
|
155
|
+
files=$(get_commit_files "$hash")
|
|
156
|
+
local has_significant_files=false
|
|
157
|
+
|
|
158
|
+
while IFS= read -r file; do
|
|
159
|
+
[[ -z "$file" ]] && continue
|
|
160
|
+
|
|
161
|
+
if ! echo "$file" | grep -qE "^(CHANGELOG\.md|lib/.*version\.rb|package\.json|\.github/workflows/)$"; then
|
|
162
|
+
has_significant_files=true
|
|
163
|
+
break
|
|
164
|
+
fi
|
|
165
|
+
done <<< "$files"
|
|
166
|
+
|
|
167
|
+
if [[ "$has_significant_files" == "false" ]]; then
|
|
168
|
+
debug "Skipping commit with only version/changelog files: $hash"
|
|
169
|
+
continue
|
|
170
|
+
fi
|
|
171
|
+
|
|
172
|
+
# Categorize and store
|
|
173
|
+
local category
|
|
174
|
+
category=$(categorize_commit "$hash")
|
|
175
|
+
|
|
176
|
+
local clean_msg
|
|
177
|
+
clean_msg=$(clean_commit_message "$subject")
|
|
178
|
+
|
|
179
|
+
if [[ -n "${categories[$category]}" ]]; then
|
|
180
|
+
categories[$category]+=$'\n'
|
|
181
|
+
fi
|
|
182
|
+
categories[$category]+="- $clean_msg"
|
|
183
|
+
|
|
184
|
+
done <<< "$commits_raw"
|
|
185
|
+
|
|
186
|
+
info "Analyzed $commit_count commits"
|
|
187
|
+
|
|
188
|
+
# Build changelog entry
|
|
189
|
+
local changelog_entry=""
|
|
190
|
+
local date
|
|
191
|
+
date=$(date +"%Y-%m-%d")
|
|
192
|
+
|
|
193
|
+
changelog_entry+="## [$new_version] - $date"$'\n\n'
|
|
194
|
+
|
|
195
|
+
# Add sections in order
|
|
196
|
+
for category in "breaking" "added" "changed" "deprecated" "removed" "fixed" "security" "other"; do
|
|
197
|
+
if [[ -n "${categories[$category]}" ]]; then
|
|
198
|
+
case "$category" in
|
|
199
|
+
"breaking")
|
|
200
|
+
changelog_entry+="### ⚠️ BREAKING CHANGES"$'\n'
|
|
201
|
+
;;
|
|
202
|
+
"added")
|
|
203
|
+
changelog_entry+="### Added"$'\n'
|
|
204
|
+
;;
|
|
205
|
+
"changed")
|
|
206
|
+
changelog_entry+="### Changed"$'\n'
|
|
207
|
+
;;
|
|
208
|
+
"deprecated")
|
|
209
|
+
changelog_entry+="### Deprecated"$'\n'
|
|
210
|
+
;;
|
|
211
|
+
"removed")
|
|
212
|
+
changelog_entry+="### Removed"$'\n'
|
|
213
|
+
;;
|
|
214
|
+
"fixed")
|
|
215
|
+
changelog_entry+="### Fixed"$'\n'
|
|
216
|
+
;;
|
|
217
|
+
"security")
|
|
218
|
+
changelog_entry+="### Security"$'\n'
|
|
219
|
+
;;
|
|
220
|
+
"other")
|
|
221
|
+
changelog_entry+="### Other"$'\n'
|
|
222
|
+
;;
|
|
223
|
+
esac
|
|
224
|
+
|
|
225
|
+
changelog_entry+="${categories[$category]}"$'\n\n'
|
|
226
|
+
fi
|
|
227
|
+
done
|
|
228
|
+
|
|
229
|
+
# Preview changelog
|
|
230
|
+
info "Changelog preview:"
|
|
231
|
+
echo -e "${PURPLE}${changelog_entry}${NC}" | head -30
|
|
232
|
+
|
|
233
|
+
# Update CHANGELOG.md
|
|
234
|
+
if [[ "$DRY_RUN" == "true" ]]; then
|
|
235
|
+
info "[DRY RUN] Would update $CHANGELOG_FILE"
|
|
236
|
+
return 0
|
|
237
|
+
fi
|
|
238
|
+
|
|
239
|
+
if [[ "$INTERACTIVE" == "true" ]]; then
|
|
240
|
+
if ! confirm "Add this changelog entry to $CHANGELOG_FILE?"; then
|
|
241
|
+
error "Changelog generation cancelled by user"
|
|
242
|
+
fi
|
|
243
|
+
fi
|
|
244
|
+
|
|
245
|
+
update_changelog_file "$changelog_entry"
|
|
246
|
+
|
|
247
|
+
success "Changelog generated for version $new_version"
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
# Update the CHANGELOG.md file with new entry
|
|
251
|
+
update_changelog_file() {
|
|
252
|
+
local entry="$1"
|
|
253
|
+
|
|
254
|
+
debug "Updating $CHANGELOG_FILE..."
|
|
255
|
+
|
|
256
|
+
if [[ ! -f "$CHANGELOG_FILE" ]]; then
|
|
257
|
+
warn "$CHANGELOG_FILE not found, creating new one"
|
|
258
|
+
echo "# Changelog" > "$CHANGELOG_FILE"
|
|
259
|
+
echo "" >> "$CHANGELOG_FILE"
|
|
260
|
+
echo "All notable changes to this project will be documented in this file." >> "$CHANGELOG_FILE"
|
|
261
|
+
echo "" >> "$CHANGELOG_FILE"
|
|
262
|
+
echo "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," >> "$CHANGELOG_FILE"
|
|
263
|
+
echo "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." >> "$CHANGELOG_FILE"
|
|
264
|
+
echo "" >> "$CHANGELOG_FILE"
|
|
265
|
+
fi
|
|
266
|
+
|
|
267
|
+
# Create backup
|
|
268
|
+
cp "$CHANGELOG_FILE" "${CHANGELOG_FILE}.bak"
|
|
269
|
+
|
|
270
|
+
# Insert new entry after header (preserve first line)
|
|
271
|
+
{
|
|
272
|
+
head -n 1 "$CHANGELOG_FILE"
|
|
273
|
+
echo ""
|
|
274
|
+
echo "$entry"
|
|
275
|
+
tail -n +2 "$CHANGELOG_FILE"
|
|
276
|
+
} > "${CHANGELOG_FILE}.tmp"
|
|
277
|
+
|
|
278
|
+
mv "${CHANGELOG_FILE}.tmp" "$CHANGELOG_FILE"
|
|
279
|
+
rm -f "${CHANGELOG_FILE}.bak"
|
|
280
|
+
|
|
281
|
+
debug "✓ Updated $CHANGELOG_FILE"
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
# Extract release notes for a specific version from CHANGELOG.md
|
|
285
|
+
extract_release_notes() {
|
|
286
|
+
local version="$1"
|
|
287
|
+
|
|
288
|
+
debug "Extracting release notes for version $version..."
|
|
289
|
+
|
|
290
|
+
if [[ ! -f "$CHANGELOG_FILE" ]]; then
|
|
291
|
+
warn "No CHANGELOG.md found"
|
|
292
|
+
return 1
|
|
293
|
+
fi
|
|
294
|
+
|
|
295
|
+
# Extract section between version header and next version header
|
|
296
|
+
awk -v version="$version" '
|
|
297
|
+
/^## \[/ {
|
|
298
|
+
if (found) exit
|
|
299
|
+
if ($0 ~ "\\[" version "\\]") {
|
|
300
|
+
found=1
|
|
301
|
+
next
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
found && !/^## \[/ { print }
|
|
305
|
+
' "$CHANGELOG_FILE"
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
# Export functions
|
|
309
|
+
export -f categorize_commit
|
|
310
|
+
export -f clean_commit_message
|
|
311
|
+
export -f generate_changelog
|
|
312
|
+
export -f update_changelog_file
|
|
313
|
+
export -f extract_release_notes
|