Skip to content

Merge pull request #156 from prgrms-web-devcourse-final-project/154-f… #317

Merge pull request #156 from prgrms-web-devcourse-final-project/154-f…

Merge pull request #156 from prgrms-web-devcourse-final-project/154-f… #317

Workflow file for this run

name: Lighthouse CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
permissions:
contents: read
pull-requests: write
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
- name: Configure npm registry
run: |
echo "Configuring npm to use public registry"
npm config set registry https://registry.npmjs.org/
- name: Clear npm cache
run: npm cache clean --force
- name: Remove package-lock.json
run: rm -f package-lock.json
- name: Install dependencies
run: npm install --registry=https://registry.npmjs.org/
env:
NODE_AUTH_TOKEN: ''
- name: Build project
run: npm run lint
- name: Build project
run: npm run build
- name: Run Lighthouse CI
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
NODE_TLS_REJECT_UNAUTHORIZED: '0' # SSL 검증 비활성화
run: |
npm install -g @lhci/cli@0.12.x
lhci autorun || echo "Lighthouse CI failed, but continuing"
- name: Upload Lighthouse results
uses: actions/upload-artifact@v4
with:
name: lighthouse-results
path: .lighthouseci
include-hidden-files: true
- name: Format lighthouse score
id: format_lighthouse_score
uses: actions/github-script@v3
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
const fs = require('fs')
const REPORT_PATH = './lhci_reports/manifest.json'
const SCORE_EMOJIS = {
HIGH: '🟢',
MEDIUM: '🟠',
LOW: '🔴',
}
const getScoreEmoji = score => {
if (score >= 90) return SCORE_EMOJIS.HIGH
if (score >= 50) return SCORE_EMOJIS.MEDIUM
return SCORE_EMOJIS.LOW
}
const formatScore = score => Math.round(score * 100)
const generateReportForUrl = report => {
const {
url,
details: { audits },
summary,
} = report
const sections = [
`## ${url}`,
generateMetricsTable(summary),
generateDetailedMetricsTable(audits),
generateSecurityTable(audits),
]
return sections.join('\n\n')
}
const generateMetricsTable = summary => {
return [
'### Performance Metrics',
'| Category | Score |',
'| --- | --- |',
`| Performance | ${getScoreEmoji(summary.performance)} ${summary.performance} |`,
`| Accessibility | ${getScoreEmoji(summary.accessibility)} ${summary.accessibility} |`,
`| Best Practices | ${getScoreEmoji(summary['best-practices'])} ${summary['best-practices']} |`,
`| SEO | ${getScoreEmoji(summary.seo)} ${summary.seo} |`,
].join('\n')
}
const generateDetailedMetricsTable = audits => {
return [
'### Detailed Metrics',
'| Metric | Value |',
'| --- | --- |',
`| First Contentful Paint | ${getScoreEmoji(audits['first-contentful-paint'].score * 100)} ${audits['first-contentful-paint'].displayValue} |`,
`| Largest Contentful Paint | ${getScoreEmoji(audits['largest-contentful-paint'].score * 100)} ${audits['largest-contentful-paint'].displayValue} |`,
`| Total Blocking Time | ${getScoreEmoji(audits['total-blocking-time'].score * 100)} ${audits['total-blocking-time'].displayValue} |`,
`| Cumulative Layout Shift | ${getScoreEmoji(audits['cumulative-layout-shift'].score * 100)} ${audits['cumulative-layout-shift'].displayValue} |`,
`| Speed Index | ${getScoreEmoji(audits['speed-index'].score * 100)} ${audits['speed-index'].displayValue} |`,
].join('\n')
}
const generateSecurityTable = audits => {
return [
'### Security Checks',
'| Check | Status | Details |',
'| --- | --- | --- |',
`| CSP-XSS | ${audits['csp-xss'] ? (audits['csp-xss'].score === 1 ? '✅' : '⚠️') : '❓'} | ${audits['csp-xss']?.title || 'Not available'} |`,
].join('\n')
}
const generateLighthouseReports = () => {
if (!fs.existsSync(REPORT_PATH)) {
console.error('No Lighthouse report found')
return null
}
try {
const results = JSON.parse(fs.readFileSync(REPORT_PATH))
const medianResults = results.filter(entry => entry.isRepresentativeRun)
const reports = medianResults.map(result => ({
url: result.url,
details: JSON.parse(fs.readFileSync(result.jsonPath)),
summary: Object.fromEntries(Object.entries(result.summary).map(([key, value]) => [key, formatScore(value)])),
}))
return ['# ⚡ Lighthouse Reports', ...reports.map(report => generateReportForUrl(report))].join('\n\n')
} catch (error) {
console.error('Error generating Lighthouse reports:', error)
return null
}
}
const comments = generateLighthouseReports()
if (comments) {
core.setOutput('comments', comments)
}
- name: Comment PR
if: github.event_name == 'pull_request'
uses: unsplash/comment-on-pr@v1.3.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
msg: ${{ steps.format_lighthouse_score.outputs.comments}}