444 lines
19 KiB
YAML
444 lines
19 KiB
YAML
name: Module Init, HTML and Lighthouse Check
|
||
|
||
permissions:
|
||
contents: read
|
||
id-token: write
|
||
pull-requests: write
|
||
checks: write
|
||
statuses: write
|
||
|
||
on:
|
||
push:
|
||
branches: [ main ]
|
||
pull_request:
|
||
branches: [ main ]
|
||
|
||
jobs:
|
||
test-module-init:
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- name: Setup Hugo
|
||
uses: peaceiris/actions-hugo@v3
|
||
with:
|
||
hugo-version: ${{ vars.HUGO_VERSION || 'latest' }}
|
||
extended: true
|
||
|
||
- name: Create test site
|
||
run: |
|
||
hugo new site test-site
|
||
cd test-site
|
||
|
||
- name: Initialize Hugo module
|
||
working-directory: ./test-site
|
||
run: |
|
||
hugo mod init github.com/zetxek/test-site
|
||
hugo mod get -u github.com/zetxek/adritian-free-hugo-theme
|
||
|
||
- name: Configure module ref
|
||
if: github.ref != 'refs/heads/main'
|
||
working-directory: ./test-site
|
||
run: |
|
||
# Use the branch that initiated the PR
|
||
branch_name=${GITHUB_HEAD_REF}
|
||
echo "Using branch: ${branch_name}"
|
||
hugo mod get -u github.com/zetxek/adritian-free-hugo-theme@${branch_name}
|
||
|
||
- name: Configure Hugo module in hugo.toml
|
||
working-directory: ./test-site
|
||
run: |
|
||
echo '[module]
|
||
[[module.imports]]
|
||
path = "github.com/zetxek/adritian-free-hugo-theme"' > hugo.toml
|
||
|
||
- name: Validate Hugo module configuration
|
||
working-directory: ./test-site
|
||
run: |
|
||
hugo mod verify
|
||
hugo mod graph
|
||
|
||
- name: Upload test site
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: test-site
|
||
path: test-site
|
||
retention-days: 1
|
||
|
||
build-site:
|
||
needs: test-module-init
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- name: Setup Hugo
|
||
uses: peaceiris/actions-hugo@v3
|
||
with:
|
||
hugo-version: 'latest'
|
||
extended: true
|
||
|
||
- name: Download test site
|
||
uses: actions/download-artifact@v4
|
||
with:
|
||
name: test-site
|
||
path: test-site
|
||
|
||
- name: Pack dependencies
|
||
working-directory: ./test-site
|
||
run: |
|
||
hugo mod npm pack
|
||
|
||
- name: (debug) Cat package.json
|
||
working-directory: ./test-site
|
||
run: |
|
||
cat package.json
|
||
|
||
- name: Npm install (bootstrap)
|
||
working-directory: ./test-site
|
||
run: |
|
||
npm install
|
||
|
||
- name: (debug) ls node_modules
|
||
working-directory: ./test-site
|
||
run: |
|
||
ls node_modules
|
||
|
||
- name: Run helper script (copy config and content)
|
||
working-directory: ./test-site
|
||
run: |
|
||
./node_modules/@zetxek/adritian-theme-helper/dist/scripts/download-content.js
|
||
|
||
- name: Run build
|
||
working-directory: ./test-site
|
||
run: hugo
|
||
|
||
- name: Upload built site
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: built-site
|
||
path: test-site/public
|
||
retention-days: 1
|
||
|
||
validate-html:
|
||
needs: build-site
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- name: Download built site
|
||
uses: actions/download-artifact@v4
|
||
with:
|
||
name: built-site
|
||
path: ./public
|
||
|
||
- name: Print index.html
|
||
working-directory: ./public
|
||
run: |
|
||
cat index.html
|
||
|
||
# Validate HTML syntax with https://github.com/marketplace/actions/html5-validator
|
||
- name: Validate HTML syntax
|
||
uses: Cyb3r-Jak3/html5validator-action@v7.2.0
|
||
with:
|
||
root: ./public
|
||
skip_git_check: true
|
||
|
||
lighthouse-check:
|
||
needs: build-site
|
||
runs-on: ubuntu-latest
|
||
permissions:
|
||
contents: write
|
||
pull-requests: write
|
||
steps:
|
||
- name: Checkout repository
|
||
uses: actions/checkout@v4
|
||
|
||
- name: Download built site
|
||
uses: actions/download-artifact@v4
|
||
with:
|
||
name: built-site
|
||
path: ./public
|
||
|
||
- name: Create output directory
|
||
run: mkdir -p ${{ github.workspace }}/tmp/artifacts
|
||
|
||
- name: Start local server
|
||
run: |
|
||
npx http-server ./public -p 8080 &
|
||
SERVER_PID=$!
|
||
echo "Started http-server with PID: $SERVER_PID"
|
||
|
||
# Wait for server to be ready (max 30 seconds)
|
||
for i in {1..30}; do
|
||
if curl -s http://localhost:8080 > /dev/null 2>&1; then
|
||
echo "✅ Server is ready and responding"
|
||
break
|
||
fi
|
||
echo "Waiting for server to start... ($i/30)"
|
||
sleep 1
|
||
done
|
||
|
||
# Verify server is actually responding
|
||
if ! curl -s http://localhost:8080 > /dev/null 2>&1; then
|
||
echo "❌ Server failed to start properly"
|
||
exit 1
|
||
fi
|
||
|
||
echo "Server is ready, proceeding with Lighthouse check"
|
||
|
||
- name: Lighthouse Check
|
||
id: lighthouse
|
||
uses: foo-software/lighthouse-check-action@v12.0.1
|
||
with:
|
||
device: 'all'
|
||
gitHubAccessToken: ${{ secrets.GITHUB_TOKEN }}
|
||
prCommentEnabled: false
|
||
urls: 'http://localhost:8080'
|
||
outputDirectory: ${{ github.workspace }}/tmp/artifacts
|
||
|
||
- name: Extract and save Lighthouse scores
|
||
id: extract-scores
|
||
run: |
|
||
# Extract scores from Lighthouse reports
|
||
mkdir -p .github
|
||
|
||
results_file="${{ github.workspace }}/tmp/artifacts/results.json"
|
||
|
||
if [ ! -f "$results_file" ]; then
|
||
echo "❌ Error: results.json not found"
|
||
echo '{"timestamp":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","commit":"${{ github.sha }}","scores":{}}' > current-scores.json
|
||
exit 0
|
||
fi
|
||
|
||
echo "✅ Found results.json, extracting scores..."
|
||
|
||
echo "{" > current-scores.json
|
||
echo ' "timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'",' >> current-scores.json
|
||
echo ' "commit": "${{ github.sha }}",' >> current-scores.json
|
||
echo ' "scores": {' >> current-scores.json
|
||
|
||
# The results.json from lighthouse-check-action contains an array of results
|
||
# Scores are already 0-100, not 0-1
|
||
num_results=$(jq 'length' "$results_file")
|
||
|
||
first_device=true
|
||
|
||
# Loop through results and organize by emulatedFormFactor
|
||
for i in $(seq 0 $((num_results - 1))); do
|
||
form_factor=$(jq -r ".[$i].emulatedFormFactor // \"unknown\"" "$results_file")
|
||
|
||
perf=$(jq -r ".[$i].scores.performance // 0" "$results_file")
|
||
a11y=$(jq -r ".[$i].scores.accessibility // 0" "$results_file")
|
||
bp=$(jq -r ".[$i].scores.bestPractices // 0" "$results_file")
|
||
seo=$(jq -r ".[$i].scores.seo // 0" "$results_file")
|
||
|
||
echo " 📱 $form_factor: Performance=$perf, Accessibility=$a11y, Best Practices=$bp, SEO=$seo"
|
||
|
||
if [ "$form_factor" = "mobile" ] || [ "$form_factor" = "desktop" ]; then
|
||
if [ "$first_device" = false ]; then
|
||
echo "," >> current-scores.json
|
||
fi
|
||
first_device=false
|
||
|
||
echo " \"$form_factor\": {" >> current-scores.json
|
||
echo " \"performance\": $perf," >> current-scores.json
|
||
echo " \"accessibility\": $a11y," >> current-scores.json
|
||
echo " \"best-practices\": $bp," >> current-scores.json
|
||
echo " \"seo\": $seo" >> current-scores.json
|
||
echo -n " }" >> current-scores.json
|
||
fi
|
||
done
|
||
|
||
echo "" >> current-scores.json
|
||
echo " }" >> current-scores.json
|
||
echo "}" >> current-scores.json
|
||
|
||
echo "✅ Scores extracted successfully"
|
||
|
||
- name: Load baseline scores from lighthouse-data branch
|
||
id: load-baseline
|
||
if: github.event_name == 'pull_request'
|
||
run: |
|
||
# Try to fetch baseline scores from lighthouse-data branch
|
||
if git show origin/lighthouse-data:.github/lighthouse-scores.json > baseline-scores.json 2>/dev/null; then
|
||
echo "baseline_exists=true" >> $GITHUB_OUTPUT
|
||
baseline_commit=$(jq -r '.commit // ""' baseline-scores.json)
|
||
echo "✅ Baseline scores loaded from lighthouse-data branch (commit: ${baseline_commit:0:7})"
|
||
else
|
||
echo "baseline_exists=false" >> $GITHUB_OUTPUT
|
||
echo "ℹ️ No baseline found - this will be the first baseline after merge"
|
||
# Create empty baseline
|
||
echo '{"commit":"","scores":{}}' > baseline-scores.json
|
||
fi
|
||
|
||
- name: Format Lighthouse results with comparison
|
||
id: format-results
|
||
run: |
|
||
# Function to get score emoji
|
||
get_emoji() {
|
||
score=$1
|
||
# Handle empty or invalid scores
|
||
if [ -z "$score" ] || [ "$score" = "0" ] || [ "$score" = "null" ]; then
|
||
echo "⚪"
|
||
return
|
||
fi
|
||
if (( $(echo "$score >= 90" | bc -l) )); then
|
||
echo "🟢"
|
||
elif (( $(echo "$score >= 50" | bc -l) )); then
|
||
echo "🟠"
|
||
else
|
||
echo "🔴"
|
||
fi
|
||
}
|
||
|
||
# Function to get delta indicator
|
||
get_delta() {
|
||
current=$1
|
||
baseline=$2
|
||
if [ "$baseline" = "null" ] || [ -z "$baseline" ] || [ "$baseline" = "0" ]; then
|
||
echo ""
|
||
return
|
||
fi
|
||
delta=$((current - baseline))
|
||
if [ $delta -gt 0 ]; then
|
||
echo "(↑ +$delta)"
|
||
elif [ $delta -lt 0 ]; then
|
||
echo "(↓ $delta)"
|
||
else
|
||
echo "(→)"
|
||
fi
|
||
}
|
||
|
||
# Build URLs
|
||
commit_sha="${{ github.sha }}"
|
||
commit_url="https://github.com/${{ github.repository }}/commit/$commit_sha"
|
||
artifacts_url="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||
|
||
echo "## 🔦 Lighthouse Performance Report" > comment.md
|
||
echo "" >> comment.md
|
||
echo "**Test Results for commit:** [\`${commit_sha:0:7}\`]($commit_url)" >> comment.md
|
||
echo "" >> comment.md
|
||
|
||
# Check if we have baseline scores
|
||
has_baseline=false
|
||
if [ -f "baseline-scores.json" ]; then
|
||
baseline_commit=$(jq -r '.commit // ""' baseline-scores.json)
|
||
if [ -n "$baseline_commit" ] && [ "$baseline_commit" != "null" ]; then
|
||
has_baseline=true
|
||
baseline_commit_url="https://github.com/${{ github.repository }}/commit/$baseline_commit"
|
||
echo "_Comparing against baseline from lighthouse-data branch (commit: [\`${baseline_commit:0:7}\`]($baseline_commit_url))_" >> comment.md
|
||
echo "" >> comment.md
|
||
fi
|
||
fi
|
||
|
||
# Check if current scores exist
|
||
if [ ! -f "current-scores.json" ]; then
|
||
echo "❌ **Error:** No Lighthouse scores were generated." >> comment.md
|
||
echo "" >> comment.md
|
||
echo "Check the workflow logs for more details." >> comment.md
|
||
exit 0
|
||
fi
|
||
|
||
# Parse and format the results
|
||
found_scores=false
|
||
for device in "mobile" "desktop"; do
|
||
current_perf=$(jq -r ".scores.$device.performance // 0" current-scores.json)
|
||
current_a11y=$(jq -r ".scores.$device.accessibility // 0" current-scores.json)
|
||
current_bp=$(jq -r ".scores.$device[\"best-practices\"] // 0" current-scores.json)
|
||
current_seo=$(jq -r ".scores.$device.seo // 0" current-scores.json)
|
||
|
||
# Check if we have any real scores (not just 0)
|
||
if [ "$current_perf" != "0" ] || [ "$current_a11y" != "0" ] || [ "$current_bp" != "0" ] || [ "$current_seo" != "0" ]; then
|
||
found_scores=true
|
||
echo "### ${device^} Results" >> comment.md
|
||
echo "" >> comment.md
|
||
|
||
if [ "$has_baseline" = true ]; then
|
||
echo "| Category | Score | Change |" >> comment.md
|
||
echo "|----------|-------|--------|" >> comment.md
|
||
|
||
baseline_perf=$(jq -r ".scores.$device.performance // 0" baseline-scores.json)
|
||
baseline_a11y=$(jq -r ".scores.$device.accessibility // 0" baseline-scores.json)
|
||
baseline_bp=$(jq -r ".scores.$device[\"best-practices\"] // 0" baseline-scores.json)
|
||
baseline_seo=$(jq -r ".scores.$device.seo // 0" baseline-scores.json)
|
||
|
||
echo "| Performance | $(get_emoji $current_perf) $current_perf | $(get_delta $current_perf $baseline_perf) |" >> comment.md
|
||
echo "| Accessibility | $(get_emoji $current_a11y) $current_a11y | $(get_delta $current_a11y $baseline_a11y) |" >> comment.md
|
||
echo "| Best Practices | $(get_emoji $current_bp) $current_bp | $(get_delta $current_bp $baseline_bp) |" >> comment.md
|
||
echo "| SEO | $(get_emoji $current_seo) $current_seo | $(get_delta $current_seo $baseline_seo) |" >> comment.md
|
||
else
|
||
echo "| Category | Score |" >> comment.md
|
||
echo "|----------|-------|" >> comment.md
|
||
|
||
echo "| Performance | $(get_emoji $current_perf) $current_perf |" >> comment.md
|
||
echo "| Accessibility | $(get_emoji $current_a11y) $current_a11y |" >> comment.md
|
||
echo "| Best Practices | $(get_emoji $current_bp) $current_bp |" >> comment.md
|
||
echo "| SEO | $(get_emoji $current_seo) $current_seo |" >> comment.md
|
||
fi
|
||
echo "" >> comment.md
|
||
fi
|
||
done
|
||
|
||
if [ "$found_scores" = false ]; then
|
||
echo "⚠️ **Warning:** No Lighthouse scores were found in the reports." >> comment.md
|
||
echo "" >> comment.md
|
||
echo "This may be because:" >> comment.md
|
||
echo "- The Lighthouse action did not generate reports" >> comment.md
|
||
echo "- The report files have an unexpected format" >> comment.md
|
||
echo "" >> comment.md
|
||
fi
|
||
|
||
echo "📊 **[View full detailed reports in workflow artifacts]($artifacts_url)**" >> comment.md
|
||
echo "" >> comment.md
|
||
echo "_Report generated at $(date)_" >> comment.md
|
||
|
||
- name: Update PR comment
|
||
uses: marocchino/sticky-pull-request-comment@v2
|
||
if: github.event_name == 'pull_request'
|
||
with:
|
||
header: lighthouse-report
|
||
path: comment.md
|
||
|
||
- name: Upload Lighthouse report
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: lighthouse-report
|
||
path: ${{ github.workspace }}/tmp/artifacts
|
||
|
||
- name: Save baseline scores to lighthouse-data branch
|
||
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
||
run: |
|
||
# Copy scores to temp location before branch switching
|
||
# (git clean will remove it from the working directory)
|
||
TEMP_SCORES="/tmp/lighthouse-scores-${{ github.sha }}.json"
|
||
cp "${{ github.workspace }}/current-scores.json" "$TEMP_SCORES"
|
||
|
||
# Configure git
|
||
git config user.name "github-actions[bot]"
|
||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||
|
||
# Try to fetch and checkout the lighthouse-data branch
|
||
if git fetch origin lighthouse-data 2>/dev/null && git checkout lighthouse-data 2>/dev/null; then
|
||
echo "✅ Checked out existing lighthouse-data branch"
|
||
else
|
||
echo "Creating new lighthouse-data orphan branch"
|
||
git checkout --orphan lighthouse-data
|
||
git rm -rf . 2>/dev/null || true
|
||
git clean -fd 2>/dev/null || true
|
||
fi
|
||
|
||
# Copy the lighthouse scores file from temp location
|
||
mkdir -p .github
|
||
cp "$TEMP_SCORES" .github/lighthouse-scores.json
|
||
|
||
# Commit and push if there are changes
|
||
git add .github/lighthouse-scores.json
|
||
if git diff --staged --quiet 2>/dev/null; then
|
||
echo "No changes to lighthouse scores"
|
||
else
|
||
git commit -m "chore: update lighthouse baseline scores [skip ci]"
|
||
git push origin lighthouse-data
|
||
echo "✅ Lighthouse baseline scores updated in lighthouse-data branch"
|
||
fi
|
||
|
||
# Clean up temp file
|
||
rm -f "$TEMP_SCORES"
|