jekyll-theme-zer0 0.8.1 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +201 -4
- data/README.md +58 -25
- data/_data/README.md +4 -5
- data/_includes/README.md +1 -1
- data/_includes/stats/README.md +14 -2
- data/_layouts/README.md +3 -3
- data/_sass/core/_theme.scss +4 -1
- data/scripts/bin/build +115 -0
- data/scripts/bin/release +240 -0
- data/scripts/bin/test +203 -0
- data/scripts/features/generate-preview-images +846 -0
- data/scripts/features/install-preview-generator +531 -0
- data/scripts/features/preview_generator.py +646 -0
- data/scripts/generate-preview-images.sh +38 -93
- data/scripts/lib/README.md +35 -7
- data/scripts/test/integration/auto-version +243 -0
- data/scripts/test/integration/mermaid +252 -0
- data/scripts/test/lib/run_tests.sh +151 -0
- data/scripts/test/lib/test_changelog.sh +90 -0
- data/scripts/test/lib/test_gem.sh +71 -0
- data/scripts/test/lib/test_git.sh +85 -0
- data/scripts/test/lib/test_validation.sh +75 -0
- data/scripts/test/lib/test_version.sh +101 -0
- data/scripts/test/theme/validate +120 -0
- data/scripts/utils/analyze-commits +300 -0
- data/scripts/utils/fix-markdown +251 -0
- data/scripts/utils/setup +137 -0
- data/scripts/version.sh +26 -0
- metadata +20 -8
- data/scripts/build.sh +0 -33
- data/scripts/build.sh.legacy +0 -174
- data/scripts/gem-publish.sh +0 -42
- data/scripts/gem-publish.sh.legacy +0 -700
- data/scripts/release.sh +0 -33
- data/scripts/release.sh.legacy +0 -342
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Unit tests for validation.sh library
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
LIB_DIR="$(cd "$SCRIPT_DIR/../../lib" && pwd)"
|
|
7
|
+
|
|
8
|
+
# Set up test environment
|
|
9
|
+
export DRY_RUN=true
|
|
10
|
+
export INTERACTIVE=false
|
|
11
|
+
export VERBOSE=false
|
|
12
|
+
|
|
13
|
+
# Source the library
|
|
14
|
+
source "$LIB_DIR/validation.sh"
|
|
15
|
+
|
|
16
|
+
# Disable errexit for test assertions
|
|
17
|
+
set +e
|
|
18
|
+
|
|
19
|
+
print_suite_header "validation.sh"
|
|
20
|
+
|
|
21
|
+
# Test: validate_git_repo (should pass since we're in a git repo)
|
|
22
|
+
echo "Testing validate_git_repo..."
|
|
23
|
+
|
|
24
|
+
if validate_git_repo 2>/dev/null; then
|
|
25
|
+
assert_true "true" "Detects git repository"
|
|
26
|
+
else
|
|
27
|
+
assert_true "false" "Detects git repository"
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# Test: validate_required_files
|
|
31
|
+
echo -e "\nTesting validate_required_files..."
|
|
32
|
+
|
|
33
|
+
# This will fail if we're not in the project root, but that's expected for tests
|
|
34
|
+
if validate_required_files 2>/dev/null; then
|
|
35
|
+
assert_true "true" "Required files exist"
|
|
36
|
+
else
|
|
37
|
+
# If files don't exist (running from test dir), that's ok for unit tests
|
|
38
|
+
echo -e "${YELLOW}ℹ${NC} Required files check skipped (not in project root)"
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# Test: command_exists (from common.sh)
|
|
42
|
+
echo -e "\nTesting command_exists..."
|
|
43
|
+
|
|
44
|
+
if command_exists "git"; then
|
|
45
|
+
assert_true "true" "Git command exists"
|
|
46
|
+
else
|
|
47
|
+
assert_true "false" "Git command exists"
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
if command_exists "bash"; then
|
|
51
|
+
assert_true "true" "Bash command exists"
|
|
52
|
+
else
|
|
53
|
+
assert_true "false" "Bash command exists"
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
if command_exists "nonexistent_command_xyz"; then
|
|
57
|
+
assert_false "true" "Nonexistent command not found"
|
|
58
|
+
else
|
|
59
|
+
assert_false "false" "Nonexistent command not found"
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# Test: validate_dependencies
|
|
63
|
+
echo -e "\nTesting validate_dependencies..."
|
|
64
|
+
|
|
65
|
+
# Test that common dependencies are found
|
|
66
|
+
common_commands=("git" "ruby" "bundle" "jq")
|
|
67
|
+
for cmd in "${common_commands[@]}"; do
|
|
68
|
+
if command_exists "$cmd"; then
|
|
69
|
+
assert_true "true" "Required command exists: $cmd"
|
|
70
|
+
else
|
|
71
|
+
echo -e "${YELLOW}ℹ${NC} Optional command not found: $cmd (skipping)"
|
|
72
|
+
fi
|
|
73
|
+
done
|
|
74
|
+
|
|
75
|
+
echo -e "\n${GREEN}validation.sh tests complete${NC}"
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Unit tests for version.sh library
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
LIB_DIR="$(cd "$SCRIPT_DIR/../../lib" && pwd)"
|
|
7
|
+
|
|
8
|
+
# Set up test environment
|
|
9
|
+
export DRY_RUN=true
|
|
10
|
+
export INTERACTIVE=false
|
|
11
|
+
export VERBOSE=false
|
|
12
|
+
|
|
13
|
+
# Source the library
|
|
14
|
+
source "$LIB_DIR/version.sh"
|
|
15
|
+
|
|
16
|
+
# Disable errexit for test assertions (sourcing common.sh enables it)
|
|
17
|
+
set +e
|
|
18
|
+
|
|
19
|
+
print_suite_header "version.sh"
|
|
20
|
+
|
|
21
|
+
# Test: validate_version_format
|
|
22
|
+
echo "Testing validate_version_format..."
|
|
23
|
+
|
|
24
|
+
valid_versions=("0.0.1" "1.0.0" "10.20.30" "999.999.999")
|
|
25
|
+
for version in "${valid_versions[@]}"; do
|
|
26
|
+
# Run in subshell because error() calls exit 1
|
|
27
|
+
if (validate_version_format "$version" 2>/dev/null); then
|
|
28
|
+
assert_true "true" "Valid version format accepted: $version"
|
|
29
|
+
else
|
|
30
|
+
assert_true "false" "Valid version format accepted: $version"
|
|
31
|
+
fi
|
|
32
|
+
done
|
|
33
|
+
|
|
34
|
+
invalid_versions=("1.0" "1.0.0.0" "v1.0.0" "1.0.0-beta" "abc.def.ghi")
|
|
35
|
+
for version in "${invalid_versions[@]}"; do
|
|
36
|
+
# Run in subshell because error() calls exit 1
|
|
37
|
+
if (validate_version_format "$version" 2>/dev/null); then
|
|
38
|
+
assert_false "true" "Invalid version format rejected: $version"
|
|
39
|
+
else
|
|
40
|
+
assert_false "false" "Invalid version format rejected: $version"
|
|
41
|
+
fi
|
|
42
|
+
done
|
|
43
|
+
|
|
44
|
+
# Test: calculate_new_version
|
|
45
|
+
echo -e "\nTesting calculate_new_version..."
|
|
46
|
+
|
|
47
|
+
result=$(calculate_new_version "1.2.3" "patch")
|
|
48
|
+
assert_equals "1.2.4" "$result" "Patch bump: 1.2.3 → 1.2.4"
|
|
49
|
+
|
|
50
|
+
result=$(calculate_new_version "1.2.3" "minor")
|
|
51
|
+
assert_equals "1.3.0" "$result" "Minor bump: 1.2.3 → 1.3.0"
|
|
52
|
+
|
|
53
|
+
result=$(calculate_new_version "1.2.3" "major")
|
|
54
|
+
assert_equals "2.0.0" "$result" "Major bump: 1.2.3 → 2.0.0"
|
|
55
|
+
|
|
56
|
+
result=$(calculate_new_version "0.0.9" "patch")
|
|
57
|
+
assert_equals "0.0.10" "$result" "Patch bump with digit rollover: 0.0.9 → 0.0.10"
|
|
58
|
+
|
|
59
|
+
result=$(calculate_new_version "0.9.9" "minor")
|
|
60
|
+
assert_equals "0.10.0" "$result" "Minor bump resets patch: 0.9.9 → 0.10.0"
|
|
61
|
+
|
|
62
|
+
result=$(calculate_new_version "9.9.9" "major")
|
|
63
|
+
assert_equals "10.0.0" "$result" "Major bump resets minor and patch: 9.9.9 → 10.0.0"
|
|
64
|
+
|
|
65
|
+
# Test: version_less_than
|
|
66
|
+
echo -e "\nTesting version_less_than..."
|
|
67
|
+
|
|
68
|
+
if version_less_than "1.0.0" "2.0.0"; then
|
|
69
|
+
assert_true "true" "1.0.0 < 2.0.0"
|
|
70
|
+
else
|
|
71
|
+
assert_true "false" "1.0.0 < 2.0.0"
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
if version_less_than "1.2.3" "1.2.4"; then
|
|
75
|
+
assert_true "true" "1.2.3 < 1.2.4"
|
|
76
|
+
else
|
|
77
|
+
assert_true "false" "1.2.3 < 1.2.4"
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
if version_less_than "2.0.0" "1.0.0"; then
|
|
81
|
+
assert_false "true" "2.0.0 not < 1.0.0"
|
|
82
|
+
else
|
|
83
|
+
assert_false "false" "2.0.0 not < 1.0.0"
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
if version_less_than "1.2.3" "1.2.3"; then
|
|
87
|
+
assert_false "true" "1.2.3 not < 1.2.3 (equal)"
|
|
88
|
+
else
|
|
89
|
+
assert_false "false" "1.2.3 not < 1.2.3 (equal)"
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
# Test: get_version_from_tag
|
|
93
|
+
echo -e "\nTesting get_version_from_tag..."
|
|
94
|
+
|
|
95
|
+
result=$(get_version_from_tag "v1.2.3")
|
|
96
|
+
assert_equals "1.2.3" "$result" "Remove 'v' prefix: v1.2.3 → 1.2.3"
|
|
97
|
+
|
|
98
|
+
result=$(get_version_from_tag "1.2.3")
|
|
99
|
+
assert_equals "1.2.3" "$result" "No prefix to remove: 1.2.3 → 1.2.3"
|
|
100
|
+
|
|
101
|
+
echo -e "\n${GREEN}version.sh tests complete${NC}"
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Theme validation tests for zer0-mistakes Jekyll theme
|
|
4
|
+
# Usage: ./scripts/test/theme/validate
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
# Get script and library directories
|
|
9
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
|
+
SCRIPTS_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
11
|
+
LIB_DIR="$SCRIPTS_ROOT/lib"
|
|
12
|
+
PROJECT_ROOT="$(cd "$SCRIPTS_ROOT/.." && pwd)"
|
|
13
|
+
|
|
14
|
+
# Source common library for logging and utilities
|
|
15
|
+
source "$LIB_DIR/common.sh"
|
|
16
|
+
|
|
17
|
+
# Test counter
|
|
18
|
+
TESTS_RUN=0
|
|
19
|
+
TESTS_PASSED=0
|
|
20
|
+
|
|
21
|
+
run_test() {
|
|
22
|
+
local test_name="$1"
|
|
23
|
+
local test_command="$2"
|
|
24
|
+
|
|
25
|
+
((TESTS_RUN++)) || true
|
|
26
|
+
|
|
27
|
+
step "Running: $test_name"
|
|
28
|
+
|
|
29
|
+
if [[ "${VERBOSE:-false}" == "true" ]]; then
|
|
30
|
+
debug "Command: $test_command"
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
if eval "$test_command" > /dev/null 2>&1; then
|
|
34
|
+
success "$test_name"
|
|
35
|
+
((TESTS_PASSED++)) || true
|
|
36
|
+
else
|
|
37
|
+
warn "$test_name - FAILED"
|
|
38
|
+
if [[ "${VERBOSE:-false}" == "true" ]]; then
|
|
39
|
+
echo "Command output:"
|
|
40
|
+
eval "$test_command" 2>&1 || true
|
|
41
|
+
fi
|
|
42
|
+
fi
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
log "Running theme validation tests for zer0-mistakes..."
|
|
46
|
+
|
|
47
|
+
cd "$PROJECT_ROOT"
|
|
48
|
+
|
|
49
|
+
# Test 1: Validate package.json
|
|
50
|
+
run_test "Validate package.json syntax" "jq empty package.json"
|
|
51
|
+
|
|
52
|
+
# Test 2: Validate package.json version
|
|
53
|
+
run_test "Validate package.json version format" "jq -r '.version' package.json | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$'"
|
|
54
|
+
|
|
55
|
+
# Test 3: Validate gemspec syntax
|
|
56
|
+
run_test "Validate gemspec syntax" "ruby -c jekyll-theme-zer0.gemspec"
|
|
57
|
+
|
|
58
|
+
# Test 4: Build gem (test build)
|
|
59
|
+
run_test "Test gem build" "gem build jekyll-theme-zer0.gemspec"
|
|
60
|
+
|
|
61
|
+
# Test 5: Check for required files
|
|
62
|
+
run_test "Check README.md exists" "test -f README.md"
|
|
63
|
+
run_test "Check LICENSE exists" "test -f LICENSE"
|
|
64
|
+
run_test "Check _layouts directory exists" "test -d _layouts"
|
|
65
|
+
run_test "Check _includes directory exists" "test -d _includes"
|
|
66
|
+
run_test "Check _sass directory exists" "test -d _sass"
|
|
67
|
+
run_test "Check assets directory exists" "test -d assets"
|
|
68
|
+
|
|
69
|
+
# Test 6: Validate YAML front matter in layouts
|
|
70
|
+
if [[ -d "_layouts" ]]; then
|
|
71
|
+
for layout in _layouts/*.html; do
|
|
72
|
+
if [[ -f "$layout" ]]; then
|
|
73
|
+
layout_name=$(basename "$layout")
|
|
74
|
+
run_test "Validate front matter in $layout_name" "head -10 '$layout' | grep -q -- '---'"
|
|
75
|
+
fi
|
|
76
|
+
done
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
# Test 7: Check for common Jekyll requirements
|
|
80
|
+
run_test "Check Jekyll dependency in gemspec" "grep -q 'jekyll' jekyll-theme-zer0.gemspec"
|
|
81
|
+
|
|
82
|
+
# Test 8: Validate version consistency
|
|
83
|
+
PACKAGE_VERSION=$(jq -r '.version' package.json)
|
|
84
|
+
if [[ -f "jekyll-theme-zer0-${PACKAGE_VERSION}.gem" ]]; then
|
|
85
|
+
run_test "Version consistency check" "test -f jekyll-theme-zer0-${PACKAGE_VERSION}.gem"
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
# Test 9: Check scripts are executable
|
|
89
|
+
if [[ -d "scripts/bin" ]]; then
|
|
90
|
+
for script in scripts/bin/*; do
|
|
91
|
+
if [[ -f "$script" ]]; then
|
|
92
|
+
script_name=$(basename "$script")
|
|
93
|
+
run_test "Check $script_name is executable" "test -x '$script'"
|
|
94
|
+
fi
|
|
95
|
+
done
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
# Test 10: Validate bundle install
|
|
99
|
+
run_test "Test bundle install" "bundle install --quiet"
|
|
100
|
+
|
|
101
|
+
# Clean up test gem file
|
|
102
|
+
rm -f jekyll-theme-zer0-*.gem 2>/dev/null || true
|
|
103
|
+
|
|
104
|
+
# Test results
|
|
105
|
+
echo ""
|
|
106
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
107
|
+
info "Test Results"
|
|
108
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
109
|
+
echo " Tests run: $TESTS_RUN"
|
|
110
|
+
echo " Tests passed: $TESTS_PASSED"
|
|
111
|
+
echo " Tests failed: $((TESTS_RUN - TESTS_PASSED))"
|
|
112
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
113
|
+
|
|
114
|
+
if [[ $TESTS_PASSED -eq $TESTS_RUN ]]; then
|
|
115
|
+
success "All tests passed!"
|
|
116
|
+
exit 0
|
|
117
|
+
else
|
|
118
|
+
error "Some tests failed!"
|
|
119
|
+
exit 1
|
|
120
|
+
fi
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Commit Analysis Script for Automated Version Bumping
|
|
4
|
+
# Analyzes git commits to determine appropriate semantic version bump
|
|
5
|
+
# Usage: ./scripts/utils/analyze-commits [commit-range]
|
|
6
|
+
# Output: patch|minor|major|none
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
# Get script and library directories
|
|
11
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
+
LIB_DIR="$SCRIPT_DIR/../lib"
|
|
13
|
+
|
|
14
|
+
# Source common library for logging and utilities
|
|
15
|
+
source "$LIB_DIR/common.sh"
|
|
16
|
+
|
|
17
|
+
# Default values
|
|
18
|
+
COMMIT_RANGE="${1:-HEAD~1..HEAD}"
|
|
19
|
+
DEBUG=${DEBUG:-false}
|
|
20
|
+
|
|
21
|
+
# Override debug function to use DEBUG flag
|
|
22
|
+
log_debug() {
|
|
23
|
+
if [[ "$DEBUG" == "true" ]]; then
|
|
24
|
+
debug "$1"
|
|
25
|
+
fi
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# Function to analyze individual commit
|
|
29
|
+
analyze_commit() {
|
|
30
|
+
local commit_hash="$1"
|
|
31
|
+
local commit_message
|
|
32
|
+
local commit_files
|
|
33
|
+
local bump_level="none"
|
|
34
|
+
|
|
35
|
+
commit_message=$(git log --format="%s%n%b" -n 1 "$commit_hash")
|
|
36
|
+
commit_files=$(git diff-tree --no-commit-id --name-only -r "$commit_hash" 2>/dev/null || echo "")
|
|
37
|
+
|
|
38
|
+
log_debug "Analyzing commit: $commit_hash"
|
|
39
|
+
log_debug "Message: $(echo "$commit_message" | head -1)"
|
|
40
|
+
|
|
41
|
+
# Check for breaking changes (MAJOR)
|
|
42
|
+
if echo "$commit_message" | grep -qi "BREAKING CHANGE\|breaking:"; then
|
|
43
|
+
echo "major"
|
|
44
|
+
return 0
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Check for major version indicators in commit message
|
|
48
|
+
if echo "$commit_message" | grep -qE "^(major|MAJOR|breaking|BREAKING)[\s:]"; then
|
|
49
|
+
echo "major"
|
|
50
|
+
return 0
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# Check commit message patterns for conventional commits
|
|
54
|
+
local subject_line=$(echo "$commit_message" | head -1)
|
|
55
|
+
|
|
56
|
+
# MAJOR changes
|
|
57
|
+
if echo "$subject_line" | grep -qE "^(revert|remove|delete)[\s:].*[Bb]reaking"; then
|
|
58
|
+
echo "major"
|
|
59
|
+
return 0
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# MINOR changes (new features)
|
|
63
|
+
if echo "$subject_line" | grep -qE "^(feat|feature|add|new)[\s:]"; then
|
|
64
|
+
echo "minor"
|
|
65
|
+
return 0
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# MINOR changes - significant additions
|
|
69
|
+
if echo "$subject_line" | grep -qE "^(enhance|improve|update)[\s:].*[Ff]eature"; then
|
|
70
|
+
echo "minor"
|
|
71
|
+
return 0
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
# PATCH changes (bug fixes, small improvements)
|
|
75
|
+
if echo "$subject_line" | grep -qE "^(fix|bug|patch|hotfix|chore|docs|style|refactor|test|perf)[\s:]"; then
|
|
76
|
+
echo "patch"
|
|
77
|
+
return 0
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# PATCH changes - maintenance and small improvements
|
|
81
|
+
if echo "$subject_line" | grep -qE "^(update|improve|enhance|optimize|clean)[\s:]"; then
|
|
82
|
+
echo "patch"
|
|
83
|
+
return 0
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# File-based analysis for changes without conventional commit messages
|
|
87
|
+
if [[ -n "$commit_files" ]]; then
|
|
88
|
+
local critical_files=0
|
|
89
|
+
local feature_files=0
|
|
90
|
+
local patch_files=0
|
|
91
|
+
|
|
92
|
+
while IFS= read -r file; do
|
|
93
|
+
[[ -z "$file" ]] && continue
|
|
94
|
+
|
|
95
|
+
log_debug "Analyzing file: $file"
|
|
96
|
+
|
|
97
|
+
# Critical/breaking change files (MAJOR)
|
|
98
|
+
if echo "$file" | grep -qE "(Gemfile|gemspec|_config\.yml|docker-compose\.yml|Dockerfile)$"; then
|
|
99
|
+
((critical_files++))
|
|
100
|
+
log_debug "Critical file detected: $file"
|
|
101
|
+
|
|
102
|
+
# Feature files (MINOR)
|
|
103
|
+
elif echo "$file" | grep -qE "(_layouts/|_includes/|assets/|pages/.*\.md$|\.rb$|\.js$)"; then
|
|
104
|
+
((feature_files++))
|
|
105
|
+
log_debug "Feature file detected: $file"
|
|
106
|
+
|
|
107
|
+
# Documentation and minor files (PATCH)
|
|
108
|
+
elif echo "$file" | grep -qE "(README|CHANGELOG|\.md$|\.txt$|\.yml$|\.yaml$|test/)"; then
|
|
109
|
+
((patch_files++))
|
|
110
|
+
log_debug "Patch file detected: $file"
|
|
111
|
+
fi
|
|
112
|
+
done <<< "$commit_files"
|
|
113
|
+
|
|
114
|
+
# Determine bump level based on file analysis
|
|
115
|
+
if [[ $critical_files -gt 0 ]]; then
|
|
116
|
+
# But only if there are significant changes to critical files
|
|
117
|
+
local lines_changed=$(git diff --shortstat "$commit_hash^" "$commit_hash" 2>/dev/null | grep -oE '[0-9]+ insertion|[0-9]+ deletion' | grep -oE '[0-9]+' | paste -sd+ | bc 2>/dev/null || echo "0")
|
|
118
|
+
if [[ $lines_changed -gt 10 ]]; then
|
|
119
|
+
echo "minor" # Significant changes to critical files = minor
|
|
120
|
+
return 0
|
|
121
|
+
else
|
|
122
|
+
echo "patch" # Small changes to critical files = patch
|
|
123
|
+
return 0
|
|
124
|
+
fi
|
|
125
|
+
elif [[ $feature_files -gt 0 ]]; then
|
|
126
|
+
echo "patch" # Changes to feature files without explicit feat: = patch
|
|
127
|
+
return 0
|
|
128
|
+
elif [[ $patch_files -gt 0 ]]; then
|
|
129
|
+
echo "patch"
|
|
130
|
+
return 0
|
|
131
|
+
fi
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
# If we reach here, no clear classification - default to patch for any real changes
|
|
135
|
+
if [[ -n "$commit_files" ]]; then
|
|
136
|
+
echo "patch"
|
|
137
|
+
else
|
|
138
|
+
echo "none"
|
|
139
|
+
fi
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
# Function to determine highest bump level from multiple commits
|
|
143
|
+
determine_overall_bump() {
|
|
144
|
+
local commits=("$@")
|
|
145
|
+
local highest_bump="none"
|
|
146
|
+
local patch_count=0
|
|
147
|
+
local minor_count=0
|
|
148
|
+
local major_count=0
|
|
149
|
+
|
|
150
|
+
log_info "Analyzing ${#commits[@]} commits for version bump determination"
|
|
151
|
+
|
|
152
|
+
for commit in "${commits[@]}"; do
|
|
153
|
+
local bump_level
|
|
154
|
+
bump_level=$(analyze_commit "$commit")
|
|
155
|
+
|
|
156
|
+
log_debug "Commit $commit: $bump_level"
|
|
157
|
+
|
|
158
|
+
case "$bump_level" in
|
|
159
|
+
"major")
|
|
160
|
+
((major_count++))
|
|
161
|
+
highest_bump="major"
|
|
162
|
+
;;
|
|
163
|
+
"minor")
|
|
164
|
+
((minor_count++))
|
|
165
|
+
if [[ "$highest_bump" != "major" ]]; then
|
|
166
|
+
highest_bump="minor"
|
|
167
|
+
fi
|
|
168
|
+
;;
|
|
169
|
+
"patch")
|
|
170
|
+
((patch_count++))
|
|
171
|
+
if [[ "$highest_bump" == "none" ]]; then
|
|
172
|
+
highest_bump="patch"
|
|
173
|
+
fi
|
|
174
|
+
;;
|
|
175
|
+
esac
|
|
176
|
+
done
|
|
177
|
+
|
|
178
|
+
log_info "Bump analysis summary:"
|
|
179
|
+
log_info " - Major changes: $major_count"
|
|
180
|
+
log_info " - Minor changes: $minor_count"
|
|
181
|
+
log_info " - Patch changes: $patch_count"
|
|
182
|
+
log_info " - Overall recommendation: $highest_bump"
|
|
183
|
+
|
|
184
|
+
echo "$highest_bump"
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
# Main execution
|
|
188
|
+
main() {
|
|
189
|
+
local commit_range="$COMMIT_RANGE"
|
|
190
|
+
|
|
191
|
+
log_info "Analyzing commits in range: $commit_range"
|
|
192
|
+
|
|
193
|
+
# Validate git repository
|
|
194
|
+
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
|
195
|
+
log_error "Not in a git repository"
|
|
196
|
+
exit 1
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
# Get list of commits in range
|
|
200
|
+
local commits
|
|
201
|
+
if ! commits=($(git rev-list --reverse "$commit_range" 2>/dev/null)); then
|
|
202
|
+
log_warning "No commits found in range: $commit_range"
|
|
203
|
+
echo "none"
|
|
204
|
+
exit 0
|
|
205
|
+
fi
|
|
206
|
+
|
|
207
|
+
if [[ ${#commits[@]} -eq 0 ]]; then
|
|
208
|
+
log_warning "No commits to analyze"
|
|
209
|
+
echo "none"
|
|
210
|
+
exit 0
|
|
211
|
+
fi
|
|
212
|
+
|
|
213
|
+
# Filter out merge commits and automated commits
|
|
214
|
+
local filtered_commits=()
|
|
215
|
+
for commit in "${commits[@]}"; do
|
|
216
|
+
local commit_subject
|
|
217
|
+
commit_subject=$(git log --format="%s" -n 1 "$commit")
|
|
218
|
+
|
|
219
|
+
# Skip merge commits
|
|
220
|
+
if echo "$commit_subject" | grep -qE "^Merge (branch|pull request|remote-tracking branch)"; then
|
|
221
|
+
log_debug "Skipping merge commit: $commit"
|
|
222
|
+
continue
|
|
223
|
+
fi
|
|
224
|
+
|
|
225
|
+
# Skip automated commits (version bumps, changelog updates)
|
|
226
|
+
if echo "$commit_subject" | grep -qE "^(chore: bump version|chore: update changelog|Automated|Auto-update)"; then
|
|
227
|
+
log_debug "Skipping automated commit: $commit"
|
|
228
|
+
continue
|
|
229
|
+
fi
|
|
230
|
+
|
|
231
|
+
# Skip commits that only modify ignored files
|
|
232
|
+
local commit_files
|
|
233
|
+
commit_files=$(git diff-tree --no-commit-id --name-only -r "$commit" 2>/dev/null || echo "")
|
|
234
|
+
|
|
235
|
+
local has_significant_files=false
|
|
236
|
+
while IFS= read -r file; do
|
|
237
|
+
[[ -z "$file" ]] && continue
|
|
238
|
+
|
|
239
|
+
# Skip if only changelog, version files, or workflow files changed
|
|
240
|
+
if ! echo "$file" | grep -qE "^(CHANGELOG\.md|lib/.*version\.rb|package\.json|\.github/workflows/)$"; then
|
|
241
|
+
has_significant_files=true
|
|
242
|
+
break
|
|
243
|
+
fi
|
|
244
|
+
done <<< "$commit_files"
|
|
245
|
+
|
|
246
|
+
if [[ "$has_significant_files" == "true" ]]; then
|
|
247
|
+
filtered_commits+=("$commit")
|
|
248
|
+
else
|
|
249
|
+
log_debug "Skipping commit with only version/changelog files: $commit"
|
|
250
|
+
fi
|
|
251
|
+
done
|
|
252
|
+
|
|
253
|
+
if [[ ${#filtered_commits[@]} -eq 0 ]]; then
|
|
254
|
+
log_info "No significant commits found after filtering"
|
|
255
|
+
echo "none"
|
|
256
|
+
exit 0
|
|
257
|
+
fi
|
|
258
|
+
|
|
259
|
+
log_info "Analyzing ${#filtered_commits[@]} significant commits (filtered from ${#commits[@]} total)"
|
|
260
|
+
|
|
261
|
+
# Determine the appropriate version bump
|
|
262
|
+
determine_overall_bump "${filtered_commits[@]}"
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
# Show usage if requested
|
|
266
|
+
if [[ "${1:-}" == "--help" ]] || [[ "${1:-}" == "-h" ]]; then
|
|
267
|
+
cat << EOF
|
|
268
|
+
Commit Analysis Script for Automated Version Bumping
|
|
269
|
+
|
|
270
|
+
USAGE:
|
|
271
|
+
$0 [commit-range]
|
|
272
|
+
|
|
273
|
+
ARGUMENTS:
|
|
274
|
+
commit-range Git commit range to analyze (default: HEAD~1..HEAD)
|
|
275
|
+
|
|
276
|
+
OUTPUT:
|
|
277
|
+
patch Bug fixes, documentation, small improvements
|
|
278
|
+
minor New features, enhancements
|
|
279
|
+
major Breaking changes, major refactors
|
|
280
|
+
none No version bump needed
|
|
281
|
+
|
|
282
|
+
EXAMPLES:
|
|
283
|
+
$0 # Analyze last commit
|
|
284
|
+
$0 HEAD~5..HEAD # Analyze last 5 commits
|
|
285
|
+
$0 v1.0.0..HEAD # Analyze since last tag
|
|
286
|
+
|
|
287
|
+
ENVIRONMENT:
|
|
288
|
+
DEBUG=true # Enable debug output
|
|
289
|
+
|
|
290
|
+
CONVENTIONAL COMMIT PATTERNS:
|
|
291
|
+
feat:, feature:, add: → minor
|
|
292
|
+
fix:, bug:, patch: → patch
|
|
293
|
+
BREAKING CHANGE, breaking: → major
|
|
294
|
+
chore:, docs:, style: → patch
|
|
295
|
+
EOF
|
|
296
|
+
exit 0
|
|
297
|
+
fi
|
|
298
|
+
|
|
299
|
+
# Execute main function
|
|
300
|
+
main "$@"
|