Skip to content

Commit 2275e8b

Browse files
authored
Merge pull request #78 from jrjohnson/diff-tweaks
Improve Internal Diffs
2 parents 4aebb16 + f89e0eb commit 2275e8b

3 files changed

Lines changed: 188 additions & 25 deletions

File tree

.github/workflows/ci.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,29 @@ jobs:
8888
- uses: actions/upload-artifact@v6
8989
with:
9090
path: results
91+
92+
cache-baseline:
93+
name: Cache Baseline
94+
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
95+
runs-on: ubuntu-latest
96+
steps:
97+
- uses: actions/checkout@v6
98+
- name: Cache Screenshots
99+
id: screenshot-cache
100+
uses: actions/cache@v5
101+
with:
102+
path: build
103+
key: screenshots-${{ github.sha }}
104+
- uses: pnpm/action-setup@v4
105+
- uses: actions/setup-node@v6
106+
with:
107+
node-version: 24
108+
cache: pnpm
109+
- name: Install Dependencies
110+
if: steps.screenshot-cache.outputs.cache-hit != 'true'
111+
run: |
112+
pnpm install
113+
pnpm exec playwright install --with-deps
114+
- name: Capture Screenshots
115+
if: steps.screenshot-cache.outputs.cache-hit != 'true'
116+
run: pnpm run test:screenshots

.github/workflows/visual-diff.yml

Lines changed: 90 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
name: Visual Diff
22

33
on:
4-
pull_request: {}
4+
pull_request:
5+
types: [labeled, unlabeled, opened, synchronize, reopened]
56

67
concurrency:
78
group: visual-diff-${{ github.head_ref || github.ref }}
@@ -11,39 +12,71 @@ jobs:
1112
baseline:
1213
name: Baseline
1314
runs-on: ubuntu-latest
15+
outputs:
16+
ref: ${{ steps.ref.outputs.ref }}
1417
steps:
1518
- uses: actions/checkout@v6
1619
with:
1720
ref: ${{ github.base_ref }}
21+
- id: ref
22+
run: |
23+
echo "$(git rev-parse HEAD)"
24+
echo "ref=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
25+
- name: Cache Screenshots
26+
id: screenshot-cache
27+
uses: actions/cache@v5
28+
with:
29+
path: build
30+
key: screenshots-${{ steps.ref.outputs.ref }}
1831
- uses: pnpm/action-setup@v4
1932
- uses: actions/setup-node@v6
2033
with:
2134
node-version: 24
2235
cache: pnpm
23-
- run: pnpm install
24-
- run: pnpm exec playwright install --with-deps
36+
- name: Install Dependencies
37+
if: steps.screenshot-cache.outputs.cache-hit != 'true'
38+
run: |
39+
pnpm install
40+
pnpm exec playwright install --with-deps
2541
- name: Capture Screenshots
42+
if: steps.screenshot-cache.outputs.cache-hit != 'true'
2643
run: pnpm run test:screenshots
27-
- uses: actions/upload-artifact@v6
44+
- uses: actions/upload-artifact@v7
2845
with:
2946
name: baseline
3047
path: build
3148

3249
candidate:
3350
name: Candidate
3451
runs-on: ubuntu-latest
52+
outputs:
53+
ref: ${{ steps.ref.outputs.ref }}
3554
steps:
3655
- uses: actions/checkout@v6
56+
- id: ref
57+
run: |
58+
echo "$(git rev-parse HEAD)"
59+
echo "ref=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
60+
- name: Cache Screenshots
61+
id: screenshot-cache
62+
uses: actions/cache@v5
63+
with:
64+
path: build
65+
key: screenshots-${{ steps.ref.outputs.ref }}-${{ matrix.name }}
3766
- uses: pnpm/action-setup@v4
3867
- uses: actions/setup-node@v6
3968
with:
4069
node-version: 24
4170
cache: pnpm
42-
- run: pnpm install
43-
- run: pnpm exec playwright install --with-deps
71+
- name: Install Dependencies
72+
if: steps.screenshot-cache.outputs.cache-hit != 'true'
73+
run: |
74+
pnpm install
75+
pnpm exec playwright install --with-deps
4476
- name: Capture Screenshots
45-
run: pnpm run test:screenshots
46-
- uses: actions/upload-artifact@v6
77+
if: steps.screenshot-cache.outputs.cache-hit != 'true'
78+
run: pnpm test:screenshots
79+
- uses: actions/upload-artifact@v7
4780
with:
4881
name: candidate
4982
path: build
@@ -54,35 +87,68 @@ jobs:
5487
runs-on: ubuntu-latest
5588
env:
5689
OUTPUT_DIR: visual-diff-${{ github.event.pull_request.number }}
90+
BASELINE_REF: ${{needs.baseline.outputs.ref}}
91+
CANDIDATE_REF: ${{needs.candidate.outputs.ref}}
5792
steps:
5893
- uses: actions/checkout@v6
59-
- uses: actions/download-artifact@v7
60-
- run: ls -lh baseline candidate
61-
- uses: pnpm/action-setup@v4
6294
- uses: actions/setup-node@v6
6395
with:
6496
node-version: 24
65-
- run: pnpm install
66-
- run: pnpm build
97+
- uses: pnpm/action-setup@v4
98+
- name: Restore Comparison Cache
99+
id: comparison-cache
100+
uses: actions/cache/restore@v5
101+
with:
102+
path: |
103+
${{ env.OUTPUT_DIR }}
104+
results
105+
key: comparison-${{ env.BASELINE_REF }}-${{ env.CANDIDATE_REF }}
106+
- uses: actions/download-artifact@v8
107+
if: steps.comparison-cache.outputs.cache-hit != 'true'
108+
- run: ls -lh baseline candidate 2>/dev/null || true
67109
- run: |
68110
mkdir -p ./results
69111
echo ${{ github.event.pull_request.number }} > ./results/pr_number
112+
- run: pnpm install
113+
if: steps.comparison-cache.outputs.cache-hit != 'true'
114+
- run: pnpm build
115+
if: steps.comparison-cache.outputs.cache-hit != 'true'
70116
- name: Create Visual Diff
71-
run: node ./dist/bin/visual-differ.js baseline candidate ${{ env.OUTPUT_DIR }} > results/visual-diff.txt
72-
- if: always()
73-
run: cp ${{ env.OUTPUT_DIR }}/report.md results/
74-
- if: always()
75-
run: cat results/report.md results/visual-diff.txt
76-
- uses: actions/upload-artifact@v6
117+
if: steps.comparison-cache.outputs.cache-hit != 'true'
118+
run: |
119+
set +e
120+
node ./dist/bin/visual-differ.js --threshold=0.1 baseline candidate ${{ env.OUTPUT_DIR }} > results/visual-diff.txt
121+
exit_code=$?
122+
echo $exit_code > ./results/exit_code
123+
cp ${{ env.OUTPUT_DIR }}/report.md results/
124+
cat results/report.md results/visual-diff.txt
125+
- uses: actions/upload-artifact@v7
77126
id: upload-output
78-
if: always()
79127
with:
80128
name: ${{ env.OUTPUT_DIR }}
81129
path: ${{ env.OUTPUT_DIR }}
82-
- if: always()
83-
run: echo ${{ steps.upload-output.outputs.artifact-url }} > ./results/artifact_url
84-
- uses: actions/upload-artifact@v6
85-
if: always()
130+
- run: |
131+
echo ${{ steps.upload-output.outputs.artifact-url }} > ./results/artifact_url
132+
echo ${{ contains(github.event.pull_request.labels.*.name, 'approve visual diff') }} > ./results/approved
133+
- name: Save Comparison Cache
134+
if: steps.comparison-cache.outputs.cache-hit != 'true'
135+
uses: actions/cache/save@v5
136+
with:
137+
path: |
138+
${{ env.OUTPUT_DIR }}
139+
results
140+
key: ${{ steps.comparison-cache.outputs.cache-primary-key }}
141+
- uses: actions/upload-artifact@v7
86142
with:
87143
name: results
88144
path: results
145+
- name: Check Status
146+
run: |
147+
exitCode=$(cat ./results/exit_code)
148+
approved=$(cat ./results/approved)
149+
echo "visual-differ exit code: $exitCode"
150+
echo "approve visual diff label present: $approved"
151+
if [ "$exitCode" != "0" ] && [ "$approved" != "true" ]; then
152+
echo "Visual diff detected and 'approve visual diff' label not present."
153+
exit 1
154+
fi

.github/workflows/visual_diff_results.yml

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,39 @@ on:
77
- completed
88

99
jobs:
10+
check-artifact:
11+
name: Check for results artifact
12+
runs-on: ubuntu-latest
13+
permissions:
14+
actions: read
15+
outputs:
16+
found: ${{ steps.check.outputs.found }}
17+
steps:
18+
- name: Check for "results" artifact
19+
id: check
20+
uses: actions/github-script@v8
21+
with:
22+
github-token: ${{ secrets.GITHUB_TOKEN }}
23+
script: |
24+
const allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
25+
owner: context.repo.owner,
26+
repo: context.repo.repo,
27+
run_id: context.payload.workflow_run.id,
28+
});
29+
30+
const match = allArtifacts.data.artifacts.find(a => a.name === 'results');
31+
32+
if (match) {
33+
core.setOutput('found', 'true');
34+
} else {
35+
core.setOutput('found', 'false');
36+
console.log('No artifact uploaded.');
37+
}
38+
1039
comment:
1140
name: Comment on Pull Request
41+
needs: check-artifact
42+
if: ${{ needs.check-artifact.outputs.found == 'true' }}
1243
runs-on: ubuntu-latest
1344
permissions:
1445
actions: read
@@ -23,27 +54,33 @@ jobs:
2354
script: |
2455
const fs = require('fs');
2556
const path = require('path');
57+
2658
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
2759
owner: context.repo.owner,
2860
repo: context.repo.repo,
2961
run_id: context.payload.workflow_run.id,
3062
});
63+
3164
let matchArtifact = allArtifacts.data.artifacts.find((artifact) => {
3265
return artifact.name === 'results';
3366
});
67+
3468
let download = await github.rest.actions.downloadArtifact({
3569
owner: context.repo.owner,
3670
repo: context.repo.repo,
3771
artifact_id: matchArtifact.id,
3872
archive_format: 'zip',
3973
});
74+
4075
const temp = '${{ runner.temp }}/artifacts';
4176
if (!fs.existsSync(temp)){
4277
fs.mkdirSync(temp);
4378
}
4479
fs.writeFileSync(path.join(temp, 'results.zip'), Buffer.from(download.data));
80+
4581
- name: Unzip artifact
4682
run: unzip "${{ runner.temp }}/artifacts/results.zip" -d "${{ runner.temp }}/artifacts"
83+
4784
- name: Extract Details
4885
uses: actions/github-script@v8
4986
id: data
@@ -52,49 +89,83 @@ jobs:
5289
const fs = require('fs');
5390
const path = require('path');
5491
const temp = '${{ runner.temp }}/artifacts';
92+
5593
const report = fs.readFileSync(path.join(temp, 'report.md'), "utf8");
5694
const issue_number = Number(fs.readFileSync(path.join(temp, 'pr_number')));
95+
const exit_code = Number(fs.readFileSync(path.join(temp, 'exit_code')));
96+
const approved = fs.readFileSync(path.join(temp, 'approved'), "utf8");
5797
const url = fs.readFileSync(path.join(temp, 'artifact_url'), "utf8");
5898
59-
return { report, issue_number, url };
99+
const rhett = { report, issue_number, exit_code, approved, url };
100+
console.log(rhett);
101+
return rhett;
102+
60103
- name: Decode Output
61104
id: decoded
62105
run: |
63106
JSON='${{ steps.data.outputs.result }}'
64107
65108
ISSUE_NUMBER="$(echo "$JSON" | jq -r '.issue_number')"
109+
EXIT_CODE="$(echo "$JSON" | jq -r '.exit_code')"
110+
APPROVED="$(echo "$JSON" | jq -r '.approved')"
66111
URL="$(echo "$JSON" | jq -r '.url')"
67112
REPORT="$(echo "$JSON" | jq -r '.report')"
68113
69114
{
70115
echo "issue_number=$ISSUE_NUMBER"
116+
echo "exit_code=$EXIT_CODE"
117+
echo "approved=$APPROVED"
71118
echo "url=$URL"
72119
echo "report<<EOF"
73120
echo "$REPORT"
74121
echo "EOF"
75122
} >> "$GITHUB_OUTPUT"
123+
76124
- name: Show Output
77125
run: |
78126
echo "issue_number:"
79127
echo "${{ steps.decoded.outputs.issue_number }}"
80128
129+
echo "exit_code:"
130+
echo "${{ steps.decoded.outputs.exit_code }}"
131+
132+
echo "approved:"
133+
echo "${{ steps.decoded.outputs.approved }}"
134+
81135
echo "url:"
82136
echo "${{ steps.decoded.outputs.url }}"
83137
84138
echo "report:"
85139
echo "${{ steps.decoded.outputs.report }}"
140+
141+
- name: Approval Results
142+
id: approval-results
143+
run: |
144+
exitCode="${{ steps.decoded.outputs.exit_code }}"
145+
approved="${{ steps.decoded.outputs.approved }}"
146+
echo "visual-differ exit code: $exitCode"
147+
echo "approve visual diff label present: $approved"
148+
if [ "$exitCode" != "0" ] && [ "$approved" == "true" ]; then
149+
APPROVAL_RESULTS="### ✅ Visual Diff Approved"
150+
else
151+
APPROVAL_RESULTS=""
152+
fi
153+
echo "results=$APPROVAL_RESULTS" >> "$GITHUB_OUTPUT"
154+
86155
- name: Find Previous Comment
87156
uses: peter-evans/find-comment@v4
88157
id: find
89158
with:
90159
issue-number: ${{ steps.decoded.outputs.issue_number }}
91160
body-includes: ${{ env.MARKER }}
161+
92162
- name: Create or Update Comment
93163
uses: peter-evans/create-or-update-comment@v5
94164
with:
95165
comment-id: ${{ steps.find.outputs.comment-id }}
96166
issue-number: ${{ steps.decoded.outputs.issue_number }}
97167
body: |
168+
${{ steps.approval-results.outputs.results }}
98169
${{ steps.decoded.outputs.report }}
99170
100171
Download the [results](${{ steps.decoded.outputs.url }}).

0 commit comments

Comments
 (0)