Skip to content

Commit f18915e

Browse files
committed
Add CI workflow to compute diff between files dist files
1 parent 320e635 commit f18915e

File tree

2 files changed

+190
-0
lines changed

2 files changed

+190
-0
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/**
2+
* Generate a markdown table with the difference in size of the dist files between the base and the PR.
3+
*/
4+
5+
/*
6+
Usage:
7+
```shell
8+
BASE_DIST_FILES='{"src/Autocomplete/assets/dist/controller.js":{"size":15382,"size_gz":3716,"size_brotli":3125},"src/Chartjs/assets/dist/controller.js":{"size":2281,"size_gz":771,"size_brotli":642},"src/Cropperjs/assets/dist/controller.js":{"size":1044,"size_gz":475,"size_brotli":371}}' \
9+
PR_DIST_FILES='{"src/Chartjs/assets/dist/controller.js":{"size":2281,"size_gz":771,"size_brotli":642},"src/Cropperjs/assets/dist/controller.js":{"size":1044,"size_gz":475,"size_brotli":371},"src/Cropperjs/assets/dist/style.min.css":{"size":32,"size_gz":66,"size_brotli":34},"src/Dropzone/assets/dist/controller.js":{"size":3199,"size_gz":816,"size_brotli":634}}' \
10+
node .github/generate-dist-files-size-diff.mjs
11+
```
12+
*/
13+
14+
if (!process.env.BASE_DIST_FILES) {
15+
throw new Error('Missing or invalid "BASE_DIST_FILES" env variable.');
16+
}
17+
18+
if (!process.env.PR_DIST_FILES) {
19+
throw new Error('Missing or invalid "PR_DIST_FILES" env variable.');
20+
}
21+
22+
/**
23+
* Adapted from https://gist.github.com/zentala/1e6f72438796d74531803cc3833c039c?permalink_comment_id=4455218#gistcomment-4455218
24+
* @param {number} bytes
25+
* @param {number} digits
26+
* @returns {string}
27+
*/
28+
function formatBytes(bytes, digits = 2) {
29+
if (bytes === 0) {
30+
return '0 B';
31+
}
32+
const sizes = [`B`, 'kB', 'MB'];
33+
const i = Math.floor(Math.log(bytes) / Math.log(1024));
34+
35+
return parseFloat((bytes / Math.pow(1024, i)).toFixed(digits)) + ' ' + sizes[i];
36+
}
37+
38+
/**
39+
* @param {number} from
40+
* @param {number} to
41+
* @returns {number}
42+
*/
43+
function computeDiffPercent(from, to) {
44+
return from === to ? 0 : Number(((from - to) / to * 100).toFixed(2));
45+
}
46+
47+
/**
48+
* @param {number} percent
49+
* @returns {string}
50+
*/
51+
function formatDiffPercent(percent) {
52+
return percent > 0 ? `+${percent}% 📈` : percent < 0 ? `${percent}% 📉` : `${percent}%`;
53+
}
54+
55+
export function main() {
56+
let pr = JSON.parse(process.env.PR_DIST_FILES);
57+
let base = JSON.parse(process.env.BASE_DIST_FILES);
58+
let output = '<h1>📊 Dist files size difference</h1>\n\n';
59+
60+
const files = [...new Set([...Object.keys(pr), ...Object.keys(base)])].sort().reduce((acc, file) => {
61+
const diff_percent_size = base[file] && !pr[file] ? -100 : (!base[file] && pr[file] ? 100 : (computeDiffPercent(base[file].size, pr[file].size)));
62+
const diff_percent_size_gz = base[file] && !pr[file] ? -100 : (!base[file] && pr[file] ? 100 : (computeDiffPercent(base[file].size_gz, pr[file].size_gz)));
63+
const diff_percent_size_brotli = base[file] && !pr[file] ? -100 : (!base[file] && pr[file] ? 100 : (computeDiffPercent(base[file].size_brotli, pr[file].size_brotli)));
64+
65+
if (diff_percent_size !== 0 && diff_percent_size_gz !== 0 && diff_percent_size_brotli !== 0) {
66+
acc.set(file, {
67+
diff_percent_size,
68+
diff_percent_size_gz,
69+
diff_percent_size_brotli
70+
});
71+
}
72+
73+
return acc;
74+
}, new Map);
75+
76+
if (files.size === 0) {
77+
output += 'ℹ️ No difference in dist files.\n';
78+
return output;
79+
}
80+
81+
output += `<table>
82+
<thead><tr><th>File</th><th>Diff (B)</th><th>Diff (%)</th></tr></thead>
83+
<tbody>`;
84+
for (const [file, details] of files.entries()) {
85+
output += `<tr>
86+
<td><code>${file}</code></td>
87+
<td>
88+
Size: <code>${formatBytes(base[file]?.size || 0)}</code> vs <code>${formatBytes(pr[file]?.size || 0)}</code><br>
89+
Gzip: <code>${formatBytes(base[file]?.size_gz || 0)}</code> vs <code>${formatBytes(pr[file]?.size_gz || 0)}</code><br>
90+
Brotli: <code>${formatBytes(base[file]?.size_brotli || 0)}</code> vs <code>${formatBytes(pr[file]?.size_brotli || 0)}</code>
91+
</td>
92+
<td align="right">
93+
Size: <b>${formatDiffPercent(details.diff_percent_size)}</b><br>
94+
Gzip: <b>${formatDiffPercent(details.diff_percent_size_gz)}</b><br>
95+
Brotli: <b>${formatDiffPercent(details.diff_percent_size_brotli)}</b>
96+
</td>
97+
</tr>
98+
`;
99+
}
100+
output += `</tbody>
101+
</table>
102+
`;
103+
104+
return output;
105+
}
106+
107+
if (!process.env.CI) {
108+
console.log(main());
109+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
name: Dist Files Size Diff
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- 'src/*/**'
7+
- '!src/*/doc/**'
8+
- '.github/**'
9+
10+
jobs:
11+
dist-files-size-diff:
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- uses: marocchino/sticky-pull-request-comment@v2
16+
with:
17+
message: |
18+
⏳ The dist files size difference is being calculated...
19+
20+
- uses: actions/checkout@v4
21+
22+
- uses: actions/setup-node@v4
23+
with:
24+
node-version: '22'
25+
26+
- name: Configure git
27+
run: |
28+
git config --global user.email ""
29+
git config --global user.name "github-action[bot]"
30+
31+
- name: Get dist files size (from pull request)
32+
id: pr-dist-files
33+
run: |
34+
set -e
35+
36+
FILES=$(find src -mindepth 2 -path '*/assets/dist/*' \( -name "*.js" -o -name "*.css" \) -not \( -path '*/tests/*' -o -path '*/public/*' -o -path '*/vendor/*' \) | sort | while read -r file; do
37+
echo "{\"$file\": {\"size\": $(wc -c < "$file"), \"size_gz\": $(gzip -c "$file" | wc -c), \"size_brotli\": $(brotli -c "$file" | wc -c)}}"
38+
done | jq -s 'add' -c)
39+
40+
echo "files=$FILES" >> $GITHUB_OUTPUT
41+
42+
- name: Get dist files size (from base branch)
43+
id: base-dist-files
44+
run: |
45+
set -e
46+
47+
git fetch origin ${{ github.base_ref }}:${{ github.base_ref }}
48+
49+
FILES=$(find src -mindepth 2 -path '*/assets/dist/*' \( -name "*.js" -o -name "*.css" \) -not \( -path '*/tests/*' -o -path '*/public/*' -o -path '*/vendor/*' \) | sort | while read -r file; do
50+
echo "{\"$file\": {\"size\": $(wc -c < "$file"), \"size_gz\": $(gzip -c "$file" | wc -c), \"size_brotli\": $(brotli -c "$file" | wc -c)}}"
51+
done | jq -s 'add' -c)
52+
53+
echo "files=$FILES" >> $GITHUB_OUTPUT
54+
55+
- name: Generate the diff
56+
id: diff
57+
uses: actions/github-script@v7
58+
env:
59+
PR_DIST_FILES: ${{ steps.pr-dist-files.outputs.files }}
60+
BASE_DIST_FILES: ${{ steps.base-dist-files.outputs.files }}
61+
with:
62+
result-encoding: string
63+
script: |
64+
const { main } = await import('${{ github.workspace }}/.github/generate-dist-files-size-diff.mjs')
65+
66+
return await(main())
67+
68+
- name: Comment on the pull request (if any failure)
69+
if: ${{ failure() }}
70+
uses: marocchino/sticky-pull-request-comment@v2
71+
with:
72+
message: |
73+
❌ The dist files size difference could not be calculated. Please check the logs for more details.
74+
75+
- name: Comment on the pull request (if success)
76+
if: ${{ always() && steps.diff.conclusion == 'success' }}
77+
uses: marocchino/sticky-pull-request-comment@v2
78+
with:
79+
message: |
80+
📊 Dist files size difference
81+
${{ steps.diff.outputs.result }}

0 commit comments

Comments
 (0)