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"