Skip to content

Commit 423bcd8

Browse files
authored
feat: Add lighthouse to build workflow (#402)
1 parent fb5d35c commit 423bcd8

File tree

9 files changed

+2344
-14
lines changed

9 files changed

+2344
-14
lines changed

.github/workflows/build-push.yml

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ on:
88
env:
99
REPO_OWNER: "nginx"
1010
REPO_NAME: "documentation"
11+
FRONT_DOOR_USERNAME: ${{ secrets.FRONT_DOOR_USERNAME }}
12+
FRONT_DOOR_PASSWORD: ${{ secrets.FRONT_DOOR_PASSWORD }}
13+
GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }}
14+
MAIN_REPORT_ARTIFACT_NAME: "lighthouse-reports-main"
1115

1216
jobs:
1317
deploy-example-site:
@@ -24,4 +28,97 @@ jobs:
2428
auto_deploy_env: "prod"
2529
secrets:
2630
AZURE_CREDENTIALS: ${{secrets.AZURE_CREDENTIALS_DOCS}}
27-
AZURE_KEY_VAULT: ${{secrets.AZURE_KEY_VAULT_DOCS}}
31+
AZURE_KEY_VAULT: ${{secrets.AZURE_KEY_VAULT_DOCS}}
32+
lighthouseci:
33+
if: github.event.pull_request
34+
needs: deploy-example-site
35+
runs-on: ubuntu-22.04
36+
steps:
37+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
38+
with:
39+
ref: ${{ github.event.workflow_run.head_branch }}
40+
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
41+
with:
42+
node-version: 22
43+
- name: Installing packages
44+
run: cd performance && npm ci
45+
- name: Generating lighthouse reports for PR...
46+
env:
47+
REPORT_NAME: "pr"
48+
run: |
49+
node performance/lighthouse-script.js
50+
- name: Retrieve the latest artifact ID for lighthouse report main
51+
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
52+
id: fetch-artifact-id
53+
with:
54+
script: |
55+
const { owner, repo } = context.issue;
56+
const response = await github.rest.actions.listArtifactsForRepo({
57+
owner,
58+
repo,
59+
});
60+
61+
const artifactName = process.env.MAIN_REPORT_ARTIFACT_NAME;
62+
const filteredArtifacts = response.data.artifacts.filter(a => a.name === artifactName);
63+
64+
if(filteredArtifacts.length === 0) {
65+
return core.setFailed(`Error: Not able to find artifact with name ${artifactName}`);
66+
}
67+
68+
const latestArtifact = filteredArtifacts.sort((a, b) => new Date(b.created_at) - new Date(a.created_at))[0];
69+
console.log(`Success! Found the latest artifact with name ${artifactName}, with artifact id ${latestArtifact.id} | workflow id ${latestArtifact.workflow_run.id}`);
70+
71+
const download = await github.rest.actions.downloadArtifact({
72+
owner: owner,
73+
repo: repo,
74+
artifact_id: latestArtifact.id,
75+
archive_format: 'zip',
76+
});
77+
78+
const fs = require('fs');
79+
fs.writeFileSync('./artifact-lighthouse-report-main.zip', Buffer.from(download.data));
80+
81+
result-encoding: string
82+
- name: Unzip the artifact
83+
run: |
84+
unzip ./artifact-lighthouse-report-main.zip
85+
ls -al
86+
- name: Move the main report artifact to same directory as pr report
87+
run: |
88+
mv main-report.json ./lighthouse-reports
89+
- name: Compare the artifacts for negative differences in performance
90+
continue-on-error: true
91+
run: |
92+
FIELDS=("performance" "accessibility")
93+
TOLERANCE=0.05
94+
FLOOR_VALUE=0.90
95+
for FIELD in "${FIELDS[@]}"; do
96+
PR_VALUE=$(cat lighthouse-reports/pr-report.json | jq -r ".categories.$FIELD.score")
97+
MAIN_VALUE=$(cat lighthouse-reports/main-report.json | jq -r ".categories.$FIELD.score")
98+
echo "$FIELD: PR - $PR_VALUE | Main - $MAIN_VALUE"
99+
100+
if (( $(echo "$PR_VALUE < $FLOOR_VALUE" | bc -l) )); then
101+
echo "Error: $FIELD score in PR ($PR_VALUE) is less than accepted value ($FLOOR_VALUE)"
102+
exit 1
103+
fi
104+
105+
if [ $FIELD = "performance" ]; then
106+
LOWER_BOUND=$(echo "$MAIN_VALUE - $TOLERANCE" | bc)
107+
UPPER_BOUND=$(echo "$MAIN_VALUE + $TOLERANCE" | bc)
108+
if (( $(echo "$PR_VALUE < $LOWER_BOUND" | bc -l) || $(echo "$PR_VALUE > $UPPER_BOUND" | bc -l) )); then
109+
echo "Error: $FIELD score in PR ($PR_VALUE) is less than in MAIN ($MAIN_VALUE)"
110+
exit 1
111+
fi
112+
else
113+
if (( $(echo "$PR_VALUE < $MAIN_VALUE" | bc -l) )); then
114+
echo "Error: $FIELD score in PR ($PR_VALUE) is less than in MAIN ($MAIN_VALUE)"
115+
exit 1
116+
fi
117+
fi
118+
done
119+
- uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
120+
if: ${{ !cancelled() }}
121+
with:
122+
name: lighthouse-reports-pr
123+
path: lighthouse-reports/pr-report.json
124+
retention-days: 3

.github/workflows/lighthouse.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: Update lighthouse report artifact for main branch
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
workflow_dispatch:
8+
9+
env:
10+
OWNER: nginxinc
11+
REPO: nginx-hugo-theme
12+
FRONT_DOOR_USERNAME: ${{ secrets.FRONT_DOOR_USERNAME }}
13+
FRONT_DOOR_PASSWORD: ${{ secrets.FRONT_DOOR_PASSWORD }}
14+
jobs:
15+
generate-lighthouse-report:
16+
runs-on: ubuntu-22.04
17+
steps:
18+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
19+
with:
20+
ref: ${{ github.event.workflow_run.head_branch }}
21+
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
22+
with:
23+
node-version: 18
24+
- name: Installing packages
25+
run: cd performance && npm ci
26+
- name: Get PR number being merged
27+
env:
28+
PAGE: 1
29+
PER_PAGE: 10
30+
run: |
31+
RESPONSE=$(curl -L \
32+
-X GET \
33+
-H "Accept: application/vnd.github+json" \
34+
-H "X-GitHub-Api-Version: 2022-11-28" \
35+
-s "https://api.github.com/repos/${{ env.OWNER }}/${{ env.REPO }}/pulls?state=closed&direction=desc&page=${{ env.PAGE }}&per_page=${{ env.PER_PAGE }}")
36+
LATEST_PR_NUMBER=$(echo "$RESPONSE" | jq -r '.[] | select(.merged_at != null) | .number' | head -1)
37+
echo "GITHUB_PR_NUMBER=$LATEST_PR_NUMBER" >> $GITHUB_ENV
38+
- name: Generating lighthouse reports for main...
39+
env:
40+
REPORT_NAME: "main"
41+
GITHUB_PR_NUMBER: ${{ env.GITHUB_PR_NUMBER }}
42+
run: |
43+
node performance/lighthouse-script.js
44+
- uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
45+
with:
46+
name: lighthouse-reports-main
47+
path: lighthouse-reports/main-report.json
48+
retention-days: 30

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,6 @@ exampleSite/hugo
3737

3838
# Biome
3939
biome.rb
40+
41+
# Local Lighthouse artifacts
42+
*/lighthouse-reports

performance/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### Important Information
2+
3+
This folder is only to be run in the CI/CD system. Should not be ran locally!

performance/lighthouse-script.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
const puppeteer = require('puppeteer');
2+
const fs = require('fs');
3+
4+
const PORT = 8041;
5+
const REPORT_NAME = process.env.REPORT_NAME;
6+
const PR_NUMBER = process.env.GITHUB_PR_NUMBER;
7+
const OUTPUT_DIR = './lighthouse-reports';
8+
9+
const signIntoFrontDoor = async (browser, env) => {
10+
const page = await browser.newPage();
11+
try {
12+
await page.authenticate({
13+
username: process.env.FRONT_DOOR_USERNAME,
14+
password: process.env.FRONT_DOOR_PASSWORD,
15+
});
16+
await page.goto(env['url']);
17+
await page.waitForSelector('.grid-container');
18+
console.log('Logged in...');
19+
await page.close();
20+
} catch (error) {
21+
console.log('Unable to log in or not needed...', error);
22+
}
23+
};
24+
25+
const generateLighthouseReport = async (env) => {
26+
const OUTPUT_FILE = `${env['title']}-report.json`;
27+
const lighthouse = (await import('lighthouse')).default;
28+
console.log(`Running Lighthouse for ${env['title']}...`);
29+
const result = await lighthouse(env['url'], { port: PORT });
30+
fs.writeFileSync(`${OUTPUT_DIR}/${OUTPUT_FILE}`, result.report);
31+
console.log(`Generated report for ${env['title']}...`);
32+
};
33+
34+
const launchBrowser = async () => {
35+
return await puppeteer.launch({
36+
args: [`--remote-debugging-port=${PORT}`],
37+
headless: true,
38+
});
39+
};
40+
41+
(async () => {
42+
const browser = await launchBrowser();
43+
if (!fs.existsSync(OUTPUT_DIR)) {
44+
fs.mkdirSync(OUTPUT_DIR);
45+
}
46+
47+
const environment = {
48+
title: `${REPORT_NAME}`,
49+
url: `https://frontdoor-test-docs.nginx.com/previews/nginx-hugo-theme/${PR_NUMBER}/`,
50+
};
51+
52+
console.log(`Running on ${environment.url}...`);
53+
await signIntoFrontDoor(browser, environment);
54+
await generateLighthouseReport(environment);
55+
56+
browser.close();
57+
})();

0 commit comments

Comments
 (0)