This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Vercel Preview URL Lighthouse Audit | |
on: | |
push: | |
branches: | |
- main | |
jobs: | |
generate_lighthouse_audit: | |
timeout-minutes: 5 | |
runs-on: ubuntu-latest | |
permissions: | |
pull-requests: write | |
contents: write | |
actions: read | |
steps: | |
- name: Retrieve Vercel Project and Team Info | |
id: fetch_vercel_info | |
env: | |
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} | |
run: | | |
# Extract the repository name | |
project_name="${GITHUB_REPOSITORY##*/}" | |
echo "Using project name: $project_name" | |
# Fetch team info first | |
teams_response=$(curl -s \ | |
-H "Authorization: Bearer $VERCEL_TOKEN" \ | |
"https://api.vercel.com/v2/teams") | |
# Get the team ID for darkroom-engineering | |
team_id=$(echo "$teams_response" | jq -r '.teams[] | select(.slug=="darkroom-engineering") | .id') | |
echo "Retrieved Team ID: $team_id" | |
# Fetch projects using team ID | |
response=$(curl -s \ | |
-H "Authorization: Bearer $VERCEL_TOKEN" \ | |
-H "Content-Type: application/json" \ | |
"https://api.vercel.com/v9/projects?teamId=$team_id") | |
# Get project ID for matching project name | |
project_id=$(echo "$response" | jq -r ".projects[] | select(.name==\"$project_name\") | .id") | |
echo "Retrieved Project ID: $project_id" | |
# Set environment variables | |
echo "VERCEL_TEAM_ID=$team_id" >> $GITHUB_ENV | |
echo "VERCEL_PROJECT_ID=$project_id" >> $GITHUB_ENV | |
- name: Capture Vercel preview URL | |
id: vercel_preview_url | |
uses: zentered/[email protected] | |
env: | |
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} | |
with: | |
vercel_team_id: ${{ env.VERCEL_TEAM_ID }} | |
vercel_project_id: ${{ env.VERCEL_PROJECT_ID }} | |
- name: Wait for Vercel Deployment | |
uses: UnlyEd/[email protected] | |
id: await-vercel | |
env: | |
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} | |
with: | |
deployment-url: "${{ steps.vercel_preview_url.outputs.preview_url }}" | |
timeout: 300 # 5 minutes timeout | |
poll-interval: 10 | |
continue-on-error: true # Allow the workflow to continue even if deployment fails | |
- name: Check deployment status | |
id: deployment_check | |
run: | | |
if [ "${{ steps.await-vercel.outcome }}" == "failure" ]; then | |
echo "deployment_failed=true" >> $GITHUB_OUTPUT | |
echo "error_message=⚠️ Vercel deployment failed or timed out. Lighthouse audit could not be performed." >> $GITHUB_OUTPUT | |
else | |
echo "deployment_failed=false" >> $GITHUB_OUTPUT | |
fi | |
- uses: actions/checkout@v4 | |
if: steps.deployment_check.outputs.deployment_failed != 'true' | |
- name: Audit preview URL with Lighthouse | |
id: lighthouse_audit | |
if: steps.deployment_check.outputs.deployment_failed != 'true' | |
uses: treosh/lighthouse-ci-action@v12 | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
with: | |
urls: | | |
"https://${{ steps.vercel_preview_url.outputs.preview_url }}" | |
uploadArtifacts: true | |
temporaryPublicStorage: true | |
- name: Format lighthouse score | |
id: format_lighthouse_score | |
uses: actions/github-script@v7 | |
with: | |
github-token: ${{ secrets.GITHUB_TOKEN }} | |
script: | | |
try { | |
// Check if deployment failed | |
const deploymentFailed = '${{ steps.deployment_check.outputs.deployment_failed }}' === 'true'; | |
if (deploymentFailed) { | |
core.setOutput("comment", '${{ steps.deployment_check.outputs.error_message }}'); | |
return; | |
} | |
// Add debug logging to see the raw output | |
console.log('Raw lighthouse audit output:', JSON.stringify(${{ steps.lighthouse_audit.outputs.manifest }}, null, 2)); | |
console.log('Raw links output:', JSON.stringify(${{ steps.lighthouse_audit.outputs.links }}, null, 2)); | |
// Parse manifest with error checking | |
let manifest; | |
try { | |
manifest = JSON.parse(JSON.stringify(${{ steps.lighthouse_audit.outputs.manifest }})); | |
} catch (e) { | |
throw new Error(`Failed to parse manifest: ${e.message}`); | |
} | |
// Validate manifest structure | |
if (!manifest || !Array.isArray(manifest) || manifest.length === 0) { | |
throw new Error('Lighthouse manifest is invalid or empty'); | |
} | |
// Get the first (and presumably only) result | |
const result = manifest[0].summary; | |
if (!result) { | |
console.log('Full manifest structure:', JSON.stringify(manifest, null, 2)); | |
throw new Error('Lighthouse summary is undefined in manifest'); | |
} | |
// Parse and validate links | |
let links; | |
try { | |
links = JSON.parse(JSON.stringify(${{ steps.lighthouse_audit.outputs.links }})); | |
} catch (e) { | |
throw new Error(`Failed to parse links: ${e.message}`); | |
} | |
if (!links) { | |
throw new Error('Lighthouse links are undefined'); | |
} | |
if (Object.keys(links).length === 0) { | |
throw new Error('Lighthouse links object is empty'); | |
} | |
// Helper functions | |
const formatResult = (res) => { | |
if (typeof res !== 'number') { | |
console.log(`Warning: Invalid result value: ${res}`); | |
return 0; | |
} | |
return Math.round(res * 100); | |
}; | |
const score = (res) => { | |
const numericRes = typeof res === 'number' ? res : 0; | |
return numericRes >= 0.9 ? '🟢' : numericRes >= 0.5 ? '🟠' : '🔴'; | |
}; | |
// Generate report | |
const reportUrl = manifest[0].url || Object.values(links)[0]; | |
const formattedScores = [ | |
`⚡️ [Lighthouse report](${Object.values(links)[0]}) for the changes in this commit:`, | |
'', | |
`${score(result.performance)} Performance: ${formatResult(result.performance)}`, | |
`${score(result.accessibility)} Accessibility: ${formatResult(result.accessibility)}`, | |
`${score(result['best-practices'])} Best practices: ${formatResult(result['best-practices'])}`, | |
`${score(result.seo)} SEO: ${formatResult(result.seo)}`, | |
'', | |
`*Lighthouse ran on [${reportUrl}](${reportUrl})*` | |
].join('\n'); | |
// Set output | |
core.setOutput("comment", formattedScores); | |
} catch (error) { | |
// Enhanced error logging | |
console.error('Full error:', error); | |
console.error('Error stack:', error.stack); | |
// Set a user-friendly error message as the comment | |
const errorMessage = '⚠️ Failed to generate Lighthouse report. Please check the workflow logs for more details.'; | |
core.setOutput("comment", errorMessage); | |
core.setFailed(`Formatting failed: ${error.message}`); | |
} | |
- name: Create commit comment | |
uses: peter-evans/commit-comment@v3 | |
with: | |
token: ${{ secrets.GITHUB_TOKEN }} | |
body: ${{ steps.format_lighthouse_score.outputs.comment }} |