Skip to content

Commit 38538ba

Browse files
committed
[WIP] Diff build folder
1 parent f39d525 commit 38538ba

3 files changed

Lines changed: 252 additions & 0 deletions

File tree

.github/workflows/build-diff.yaml

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
name: Build Diff
2+
3+
on:
4+
pull_request:
5+
workflow_dispatch:
6+
inputs:
7+
base_ref:
8+
description: Base ref to compare from
9+
required: false
10+
default: origin/main
11+
type: string
12+
head_ref:
13+
description: Head ref to compare to
14+
required: false
15+
default: HEAD
16+
type: string
17+
18+
permissions:
19+
contents: read
20+
pull-requests: write
21+
22+
jobs:
23+
generate-diff:
24+
name: Build diff
25+
runs-on: ubuntu-22.04
26+
if: ${{ github.event_name == 'workflow_dispatch' || (github.event.pull_request && !github.event.pull_request.head.repo.fork) }}
27+
28+
steps:
29+
- name: Checkout
30+
uses: actions/checkout@v6.0.2
31+
with:
32+
fetch-depth: 0
33+
34+
- name: Setup Node.js
35+
uses: actions/setup-node@v4.4.0
36+
with:
37+
cache: npm
38+
check-latest: true
39+
node-version-file: .nvmrc
40+
41+
- name: Generate diff
42+
run: |
43+
BASE_REF="${{ github.event_name == 'workflow_dispatch' && inputs.base_ref || github.event.pull_request.base.sha }}"
44+
HEAD_REF="${{ github.event_name == 'workflow_dispatch' && inputs.head_ref || github.event.pull_request.head.sha }}"
45+
46+
bash scripts/build-diff-ci.sh \
47+
"$BASE_REF" \
48+
"$HEAD_REF" \
49+
build.diff
50+
51+
- name: Upload diff artifact
52+
uses: actions/upload-artifact@v4
53+
with:
54+
name: compiled-build-diff
55+
path: build.diff
56+
if-no-files-found: ignore
57+
58+
- name: Comment on PR
59+
if: ${{ github.event_name == 'pull_request' }}
60+
uses: actions/github-script@v9.0.0
61+
with:
62+
script: |
63+
const path = require('path')
64+
const { addBuildDiffComment } = require(path.join(
65+
process.env.GITHUB_WORKSPACE,
66+
'scripts/build-diff-pr-comment.js'
67+
))
68+
69+
await addBuildDiffComment({
70+
github,
71+
context,
72+
htmlDiffPath: 'build-html.diff',
73+
cssDiffPath: 'build-css.diff',
74+
jsDiffPath: 'build-js.diff'
75+
})

scripts/build-diff-ci.sh

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
repo_root="$(git rev-parse --show-toplevel)"
5+
cd "$repo_root"
6+
7+
base_ref="${1:-origin/main}"
8+
head_ref="${2:-HEAD}"
9+
output_file="${3:-build.diff}"
10+
output_dir="$(dirname "$output_file")"
11+
html_diff_file="$output_dir/build-html.diff"
12+
css_diff_file="$output_dir/build-css.diff"
13+
js_diff_file="$output_dir/build-js.diff"
14+
15+
tmp_dir="$(mktemp -d "${TMPDIR:-/tmp}/build-diff-ci.XXXXXX")"
16+
base_build_dir="$tmp_dir/base-build"
17+
head_build_dir="$tmp_dir/head-build"
18+
original_ref="$(git rev-parse --verify HEAD)"
19+
20+
cleanup() {
21+
set +e
22+
git checkout --force "$original_ref" >/dev/null 2>&1
23+
rm -rf "$tmp_dir"
24+
}
25+
trap cleanup EXIT
26+
27+
echo "Checking out base ref: $base_ref"
28+
git checkout --force "$base_ref"
29+
30+
echo "Building base ref"
31+
npm ci
32+
npm run build
33+
cp -R build "$base_build_dir"
34+
35+
echo "Checking out head ref: $head_ref"
36+
git checkout --force "$head_ref"
37+
38+
echo "Building head ref"
39+
npm ci
40+
npm run build
41+
cp -R build "$head_build_dir"
42+
43+
echo "Diffing build folders"
44+
mkdir -p "$output_dir"
45+
diff -ruN "$base_build_dir" "$head_build_dir" > "$output_file" || true
46+
47+
echo "Splitting diff by file type"
48+
: > "$html_diff_file"
49+
: > "$css_diff_file"
50+
: > "$js_diff_file"
51+
52+
awk \
53+
-v html_out="$html_diff_file" \
54+
-v css_out="$css_diff_file" \
55+
-v js_out="$js_diff_file" '
56+
function target(path) {
57+
if (path ~ /\.html$/) return html_out
58+
if (path ~ /\.css$/) return css_out
59+
if (path ~ /\.(js|mjs|cjs)$/) return js_out
60+
return ""
61+
}
62+
63+
/^diff -ruN / {
64+
current_file = ""
65+
path = $NF
66+
sub(/^.*\/base-build\//, "", path)
67+
sub(/^.*\/head-build\//, "", path)
68+
current_file = target(path)
69+
}
70+
71+
{
72+
if (current_file != "") print >> current_file
73+
}
74+
' "$output_file"
75+
76+
echo "Saved diff to $output_file"
77+
echo "Saved HTML diff to $html_diff_file"
78+
echo "Saved CSS diff to $css_diff_file"
79+
echo "Saved JavaScript diff to $js_diff_file"

scripts/build-diff-pr-comment.js

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
const fs = require('fs')
2+
3+
const DIFF_TYPES = [
4+
{ key: 'html', label: 'HTML', marker: '<!-- build-diff-html -->' },
5+
{ key: 'css', label: 'CSS', marker: '<!-- build-diff-css -->' },
6+
{ key: 'js', label: 'JavaScript', marker: '<!-- build-diff-js -->' }
7+
]
8+
9+
function readText(filePath) {
10+
if (!fs.existsSync(filePath)) return ''
11+
return fs.readFileSync(filePath, 'utf8').trim()
12+
}
13+
14+
function buildCommentBody({ marker, label, raw, maxChars }) {
15+
const content = (raw || '').trim()
16+
17+
if (!content) {
18+
return [marker, `## ${label} build diff`, '', 'No changes detected.'].join(
19+
'\n'
20+
)
21+
}
22+
23+
const text =
24+
content.length > maxChars
25+
? `${content.slice(0, maxChars)}\n\n... output truncated`
26+
: content
27+
28+
return [
29+
marker,
30+
`## ${label} build diff`,
31+
'',
32+
'<details>',
33+
'<summary>Show diff</summary>',
34+
'',
35+
'```diff',
36+
text,
37+
'```',
38+
'</details>'
39+
].join('\n')
40+
}
41+
42+
async function addBuildDiffComment({
43+
github,
44+
context,
45+
htmlDiffPath = 'build-html.diff',
46+
cssDiffPath = 'build-css.diff',
47+
jsDiffPath = 'build-js.diff',
48+
maxChars = 20000
49+
}) {
50+
const split = {
51+
html: readText(htmlDiffPath),
52+
css: readText(cssDiffPath),
53+
js: readText(jsDiffPath)
54+
}
55+
const { owner, repo } = context.repo
56+
const issueNumber = context.issue.number
57+
58+
const comments = await github.paginate(github.rest.issues.listComments, {
59+
owner,
60+
repo,
61+
issue_number: issueNumber,
62+
per_page: 100
63+
})
64+
65+
for (const type of DIFF_TYPES) {
66+
const body = buildCommentBody({
67+
marker: type.marker,
68+
label: type.label,
69+
raw: split[type.key],
70+
maxChars
71+
})
72+
73+
const existing = comments.find(
74+
(comment) => comment.body && comment.body.includes(type.marker)
75+
)
76+
77+
if (existing) {
78+
await github.rest.issues.updateComment({
79+
owner,
80+
repo,
81+
comment_id: existing.id,
82+
body
83+
})
84+
continue
85+
}
86+
87+
await github.rest.issues.createComment({
88+
owner,
89+
repo,
90+
issue_number: issueNumber,
91+
body
92+
})
93+
}
94+
}
95+
96+
module.exports = {
97+
addBuildDiffComment
98+
}

0 commit comments

Comments
 (0)