Skip to content

Commit c11e241

Browse files
author
ebembi-crdb
committed
Add error classification to Jekyll build retry logic
Implement smart error classification to only retry transient network errors, preventing unnecessary retries on permanent build failures that waste 4-5 minutes. Changes: - Capture Jekyll build output to log files for error analysis - Classify errors into retryable (network/DNS/SSL) vs permanent (Liquid syntax, missing files) - Return different exit codes: 0=success, 1=permanent error, 2=transient error - Update retry logic to immediately fail on permanent errors - Only retry on actual network connectivity issues Benefits: - Eliminates 4-5 minute delays from retrying Liquid syntax errors - Eliminates delays from retrying missing file reference errors - Provides clear feedback explaining retry decisions - Conservative approach: unclassified errors default to non-retryable Addresses PR review feedback from #20409 to improve build experience for writers encountering permanent errors.
1 parent d263130 commit c11e241

File tree

1 file changed

+52
-16
lines changed

1 file changed

+52
-16
lines changed

src/current/netlify/build.sh

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -64,22 +64,46 @@ function log_attempt() {
6464

6565
function build_with_monitoring {
6666
local config=$1
67+
local build_log="build_${ATTEMPT_COUNT}.log"
68+
6769
echo "📝 Starting Jekyll build with config: $config"
6870
echo "⏰ Build start: $(date)"
71+
echo "📄 Build log: $build_log"
6972

70-
# Run Jekyll build with network monitoring
71-
bundle exec jekyll build --trace --config _config_base.yml,$config
72-
local build_result=$?
73-
74-
echo "⏰ Build end: $(date)"
75-
echo "📊 Build result: $build_result"
76-
77-
if [[ $build_result != 0 ]]; then
78-
echo "❌ Jekyll build failed with exit code: $build_result"
79-
return $build_result
80-
else
73+
# Capture Jekyll output for error analysis
74+
if bundle exec jekyll build --trace --config _config_base.yml,$config 2>&1 | tee "$build_log"; then
75+
echo "⏰ Build end: $(date)"
8176
echo "✅ Jekyll build completed successfully"
8277
return 0
78+
else
79+
local exit_code=$?
80+
echo "⏰ Build end: $(date)"
81+
echo "❌ Jekyll build failed with exit code: $exit_code"
82+
83+
# Analyze build log for error classification
84+
echo "🔍 Analyzing build errors for retry eligibility..."
85+
86+
# Check for transient network errors that should be retried
87+
if grep -qiE "(temporary failure in name resolution|connection refused|connection reset|SSL_connect|certificate verify failed|execution expired|timeout|network is unreachable|failed to open tcp connection|socketerror)" "$build_log"; then
88+
echo "🌐 Transient network error detected - eligible for retry"
89+
echo "📋 Network error details:"
90+
grep -iE "(temporary failure in name resolution|connection refused|connection reset|SSL_connect|certificate verify failed|execution expired|timeout|network is unreachable|failed to open tcp connection|socketerror)" "$build_log" | head -3
91+
return 2 # Retryable error
92+
fi
93+
94+
# Check for permanent errors that should NOT be retried
95+
if grep -qiE "(liquid.*syntax error|liquid error|argumenterror|no such file or directory|undefined method|unknown tag|was not properly terminated|missing file)" "$build_log"; then
96+
echo "🚫 Permanent build error detected - not retrying"
97+
echo "📋 Error details:"
98+
grep -iE "(liquid.*syntax error|liquid error|argumenterror|no such file or directory|undefined method|unknown tag|was not properly terminated|missing file)" "$build_log" | head -3
99+
return 1 # Non-retryable error
100+
fi
101+
102+
# If we can't classify the error, treat it as non-retryable to be safe
103+
echo "❓ Unclassified build error - treating as permanent (not retrying)"
104+
echo "📋 Last few lines of build log:"
105+
tail -5 "$build_log"
106+
return 1 # Non-retryable error by default
83107
fi
84108
}
85109

@@ -91,18 +115,30 @@ function build_with_retries {
91115
log_attempt $attempt $MAX_RETRIES
92116
ATTEMPT_COUNT=$attempt
93117

94-
if build_with_monitoring "$config"; then
118+
build_with_monitoring "$config"
119+
local result=$?
120+
121+
if [[ $result == 0 ]]; then
95122
echo "✅ Build succeeded on attempt ${attempt}/${MAX_RETRIES}"
96123
success=true
97124
break
98-
else
99-
echo "❌ Build failed on attempt ${attempt}/${MAX_RETRIES}"
125+
elif [[ $result == 1 ]]; then
126+
echo "❌ Build failed on attempt ${attempt}/${MAX_RETRIES} with permanent error"
127+
echo "🚫 Permanent error detected - failing immediately (no retry)"
128+
break # Don't retry permanent errors
129+
elif [[ $result == 2 ]]; then
130+
echo "❌ Build failed on attempt ${attempt}/${MAX_RETRIES} with transient error"
100131
if [[ $attempt -lt $MAX_RETRIES ]]; then
101132
local next_delay=$((BASE_RETRY_DELAY * (1 << (attempt - 1))))
102-
echo "🔄 Will retry in ${next_delay} seconds (exponential backoff)..."
133+
echo "🔄 Transient error - will retry in ${next_delay} seconds (exponential backoff)..."
103134
else
104-
echo "💀 All retry attempts exhausted"
135+
echo "💀 All retry attempts exhausted for transient error"
105136
fi
137+
else
138+
# Fallback for unexpected return codes
139+
echo "❌ Build failed on attempt ${attempt}/${MAX_RETRIES} with unexpected error code: $result"
140+
echo "⚠️ Treating as permanent error - not retrying"
141+
break
106142
fi
107143
done
108144

0 commit comments

Comments
 (0)