jekyll-theme-zer0 0.7.2 → 0.10.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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +272 -3
  3. data/README.md +83 -21
  4. data/_data/README.md +4 -5
  5. data/_includes/README.md +1 -1
  6. data/_includes/stats/README.md +14 -2
  7. data/_layouts/README.md +3 -3
  8. data/_plugins/preview_image_generator.rb +258 -0
  9. data/_plugins/theme_version.rb +88 -0
  10. data/_sass/core/_theme.scss +4 -1
  11. data/assets/images/previews/git-workflow-best-practices-for-modern-teams.png +0 -0
  12. data/scripts/README.md +443 -0
  13. data/scripts/analyze-commits.sh +313 -0
  14. data/scripts/bin/build +115 -0
  15. data/scripts/bin/release +240 -0
  16. data/scripts/bin/test +203 -0
  17. data/scripts/build +115 -0
  18. data/scripts/example-usage.sh +102 -0
  19. data/scripts/features/generate-preview-images +846 -0
  20. data/scripts/features/install-preview-generator +531 -0
  21. data/scripts/features/preview_generator.py +646 -0
  22. data/scripts/fix-markdown-format.sh +265 -0
  23. data/scripts/generate-preview-images.sh +791 -0
  24. data/scripts/install-preview-generator.sh +531 -0
  25. data/scripts/lib/README.md +291 -0
  26. data/scripts/lib/changelog.sh +313 -0
  27. data/scripts/lib/common.sh +154 -0
  28. data/scripts/lib/gem.sh +226 -0
  29. data/scripts/lib/git.sh +205 -0
  30. data/scripts/lib/preview_generator.py +646 -0
  31. data/scripts/lib/test/run_tests.sh +140 -0
  32. data/scripts/lib/test/test_changelog.sh +87 -0
  33. data/scripts/lib/test/test_gem.sh +68 -0
  34. data/scripts/lib/test/test_git.sh +82 -0
  35. data/scripts/lib/test/test_validation.sh +72 -0
  36. data/scripts/lib/test/test_version.sh +96 -0
  37. data/scripts/lib/validation.sh +139 -0
  38. data/scripts/lib/version.sh +178 -0
  39. data/scripts/release +240 -0
  40. data/scripts/setup.sh +155 -0
  41. data/scripts/test/integration/auto-version +243 -0
  42. data/scripts/test/integration/mermaid +252 -0
  43. data/scripts/test/lib/run_tests.sh +151 -0
  44. data/scripts/test/lib/test_changelog.sh +90 -0
  45. data/scripts/test/lib/test_gem.sh +71 -0
  46. data/scripts/test/lib/test_git.sh +85 -0
  47. data/scripts/test/lib/test_validation.sh +75 -0
  48. data/scripts/test/lib/test_version.sh +101 -0
  49. data/scripts/test/theme/validate +120 -0
  50. data/scripts/test-auto-version.sh +260 -0
  51. data/scripts/test-mermaid.sh +251 -0
  52. data/scripts/test.sh +156 -0
  53. data/scripts/utils/analyze-commits +300 -0
  54. data/scripts/utils/fix-markdown +251 -0
  55. data/scripts/utils/setup +137 -0
  56. data/scripts/version.sh +178 -0
  57. metadata +50 -2
@@ -0,0 +1,291 @@
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
+
13
+ Core utilities used by all other libraries.
14
+
15
+ **Functions:**
16
+
17
+ - `log()`, `info()`, `warn()`, `error()` - Colored logging
18
+ - `confirm()` - User confirmation prompts
19
+ - `dry_run_exec()` - Dry run wrapper for commands
20
+ - `require_command()`, `require_file()` - Dependency validation
21
+ - `get_repo_root()` - Find repository root directory
22
+
23
+ **Usage:**
24
+
25
+ ```bash
26
+ source "$(dirname "$0")/lib/common.sh"
27
+ log "Starting process..."
28
+ ```
29
+
30
+ ### 🔍 `validation.sh` - Environment Validation
31
+
32
+ Validates environment, dependencies, and prerequisites.
33
+
34
+ **Functions:**
35
+
36
+ - `validate_git_repo()` - Verify git repository
37
+ - `validate_clean_working_dir()` - Check for uncommitted changes
38
+ - `validate_required_files()` - Check required files exist
39
+ - `validate_dependencies()` - Verify all commands available
40
+ - `validate_rubygems_auth()` - Check RubyGems credentials
41
+ - `validate_environment()` - Comprehensive validation
42
+
43
+ **Usage:**
44
+
45
+ ```bash
46
+ source "$(dirname "$0")/lib/validation.sh"
47
+ validate_environment false false # skip_publish=false, require_gh=false
48
+ ```
49
+
50
+ ### 📝 `version.sh` - Version Management
51
+
52
+ Read, calculate, and update semantic versions.
53
+
54
+ **Functions:**
55
+
56
+ - `get_current_version()` - Read version from version.rb
57
+ - `calculate_new_version()` - Calculate new version (major/minor/patch)
58
+ - `update_version_files()` - Update all version files
59
+ - `validate_version_format()` - Validate semver format
60
+ - `version_less_than()` - Compare two versions
61
+
62
+ **Usage:**
63
+
64
+ ```bash
65
+ source "$(dirname "$0")/lib/version.sh"
66
+
67
+ current=$(get_current_version)
68
+ new=$(calculate_new_version "$current" "minor")
69
+ update_version_files "$new"
70
+ ```
71
+
72
+ ### 📋 `changelog.sh` - Changelog Generation
73
+
74
+ Generate changelogs from conventional commits.
75
+
76
+ **Functions:**
77
+
78
+ - `generate_changelog()` - Generate changelog for version
79
+ - `categorize_commit()` - Categorize commit by type
80
+ - `clean_commit_message()` - Clean conventional commit prefixes
81
+ - `extract_release_notes()` - Extract notes for specific version
82
+
83
+ **Commit Categories:**
84
+
85
+ - `feat:` → Added
86
+ - `fix:` → Fixed
87
+ - `BREAKING:` → Breaking Changes
88
+ - `docs:`, `chore:`, `refactor:` → Changed
89
+ - `remove:` → Removed
90
+ - `security:` → Security
91
+
92
+ **Usage:**
93
+
94
+ ```bash
95
+ source "$(dirname "$0")/lib/changelog.sh"
96
+
97
+ generate_changelog "1.2.0" "v1.1.0" "HEAD"
98
+ ```
99
+
100
+ ### 🔄 `git.sh` - Git Operations
101
+
102
+ Git commits, tags, and repository operations.
103
+
104
+ **Functions:**
105
+
106
+ - `get_last_version_tag()` - Find last version tag
107
+ - `commit_and_tag()` - Create release commit and tag
108
+ - `push_changes()` - Push to remote with tags
109
+ - `get_commits_between()` - Get commits in range
110
+ - `get_repo_info()` - Extract owner/repo from URL
111
+
112
+ **Usage:**
113
+
114
+ ```bash
115
+ source "$(dirname "$0")/lib/git.sh"
116
+
117
+ commit_and_tag "1.2.0"
118
+ push_changes "origin" "main"
119
+ ```
120
+
121
+ ### 💎 `gem.sh` - Gem Operations
122
+
123
+ Build, test, publish, and release gems.
124
+
125
+ **Functions:**
126
+
127
+ - `build_gem()` - Build the gem package
128
+ - `publish_gem()` - Publish to RubyGems
129
+ - `create_github_release()` - Create GitHub release
130
+ - `run_tests()` - Execute test suite
131
+ - `gem_version_exists()` - Check if version exists on RubyGems
132
+
133
+ **Usage:**
134
+
135
+ ```bash
136
+ source "$(dirname "$0")/lib/gem.sh"
137
+
138
+ build_gem "1.2.0"
139
+ run_tests
140
+ publish_gem "1.2.0"
141
+ create_github_release "1.2.0"
142
+ ```
143
+
144
+ ## Testing
145
+
146
+ Each library has comprehensive unit tests in `test/`.
147
+
148
+ ### Run All Tests
149
+
150
+ ```bash
151
+ ./scripts/lib/test/run_tests.sh
152
+ ```
153
+
154
+ ### Run Individual Tests
155
+
156
+ ```bash
157
+ ./scripts/lib/test/test_version.sh
158
+ ./scripts/lib/test/test_changelog.sh
159
+ ./scripts/lib/test/test_git.sh
160
+ ```
161
+
162
+ ### Test Coverage
163
+
164
+ - ✅ Version calculations and validation
165
+ - ✅ Changelog generation and categorization
166
+ - ✅ Git operations and tag management
167
+ - ✅ Environment validation
168
+ - ✅ Gem build and publish workflows
169
+
170
+ ## Environment Variables
171
+
172
+ Control library behavior with environment variables:
173
+
174
+ ```bash
175
+ # Dry run mode (no actual changes)
176
+ DRY_RUN=true
177
+
178
+ # Non-interactive mode (auto-confirm prompts)
179
+ INTERACTIVE=false
180
+
181
+ # Verbose debug output
182
+ VERBOSE=true
183
+ ```
184
+
185
+ ## Example: Custom Release Script
186
+
187
+ ```bash
188
+ #!/bin/bash
189
+ set -euo pipefail
190
+
191
+ # Source libraries
192
+ LIB_DIR="$(dirname "$0")/lib"
193
+ source "$LIB_DIR/common.sh"
194
+ source "$LIB_DIR/validation.sh"
195
+ source "$LIB_DIR/version.sh"
196
+ source "$LIB_DIR/changelog.sh"
197
+ source "$LIB_DIR/git.sh"
198
+ source "$LIB_DIR/gem.sh"
199
+
200
+ # Main workflow
201
+ main() {
202
+ print_header "Custom Release"
203
+
204
+ # Validate
205
+ validate_environment
206
+
207
+ # Version
208
+ local current=$(get_current_version)
209
+ local new=$(calculate_new_version "$current" "patch")
210
+ update_version_files "$new"
211
+
212
+ # Changelog
213
+ generate_changelog "$new"
214
+
215
+ # Build & Test
216
+ build_gem "$new"
217
+ run_tests
218
+
219
+ # Commit & Tag
220
+ commit_and_tag "$new"
221
+
222
+ # Publish
223
+ publish_gem "$new"
224
+ create_github_release "$new"
225
+ push_changes
226
+
227
+ success "Release $new complete!"
228
+ }
229
+
230
+ main "$@"
231
+ ```
232
+
233
+ ## Architecture Benefits
234
+
235
+ ### ✅ Modularity
236
+
237
+ Each library has ONE responsibility - easy to understand and modify.
238
+
239
+ ### ✅ Testability
240
+
241
+ Small, focused functions can be unit tested independently.
242
+
243
+ ### ✅ Reusability
244
+
245
+ Libraries can be used in different scripts or GitHub Actions.
246
+
247
+ ### ✅ Maintainability
248
+
249
+ Changes isolated to specific files - less ripple effect.
250
+
251
+ ### ✅ Clarity
252
+
253
+ Functions have clear names and single purposes.
254
+
255
+ ## Migrating Old Scripts
256
+
257
+ Old monolithic scripts (`gem-publish.sh`, `release.sh`, `build.sh`) can now be:
258
+
259
+ 1. **Deprecated** with warnings pointing to new libraries
260
+ 2. **Replaced** with thin wrappers using libraries
261
+ 3. **Removed** once adoption is confirmed
262
+
263
+ Example deprecation wrapper:
264
+
265
+ ```bash
266
+ #!/bin/bash
267
+ echo "⚠️ WARNING: This script is deprecated"
268
+ echo " Use: ./scripts/release"
269
+ exec "$(dirname "$0")/release" "$@"
270
+ ```
271
+
272
+ ## Contributing
273
+
274
+ When adding new functionality:
275
+
276
+ 1. **Choose the right library** - or create a new one if needed
277
+ 2. **Write tests first** - add tests to `test/test_*.sh`
278
+ 3. **Keep functions small** - one function, one purpose
279
+ 4. **Document thoroughly** - update this README
280
+ 5. **Test in isolation** - each library should work standalone
281
+
282
+ ## Questions?
283
+
284
+ - See `docs/RELEASE_WORKFLOW_IMPROVEMENTS.md` for the full refactoring plan
285
+ - Check existing library code for patterns and examples
286
+ - Run tests to ensure everything still works
287
+
288
+ ---
289
+
290
+ **Phase 1 Complete** ✅
291
+ 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