-
-
Notifications
You must be signed in to change notification settings - Fork 335
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
minor #2269 Add CI workflow to compute diff between files dist files …
…(Kocal) This PR was squashed before being merged into the 2.x branch. Discussion ---------- Add CI workflow to compute diff between files dist files | Q | A | ------------- | --- | Bug fix? | no | New feature? | no <!-- please update src/**/CHANGELOG.md files --> | Issues | Fix #... <!-- prefix each issue number with "Fix #", no need to create an issue if none exist, explain below instead --> | License | MIT ## 🚨 Before merging, we should drop the commit that modify files (in order to generate the diff table) <!-- Replace this notice by a description of your feature/bugfix. This will help reviewers and should be a good start for the documentation. Additionally (see https://symfony.com/releases): - Always add tests and ensure they pass. - For new features, provide some code snippets to help understand usage. - Features and deprecations must be submitted against branch main. - Changelog entry should follow https://symfony.com/doc/current/contributing/code/conventions.html#writing-a-changelog-entry - Never break backward compatibility (see https://symfony.com/bc). --> This PR is purely internal, and aims to display the `assets/dist/*.{js,css}` files diff between `2.x` and a pull-request. Similar to https://github.com/marketplace/actions/pkg-size-action, but fully internal. I wanted a tool that display dist files size diff for each pull-request, because I was a bit afraid of changes done in #2160. When a PR is opened, it check dist files between the base branch (`2.x`) and the pull request, and it create a GitHub comment. The comment is created by https://github.com/marocchino/sticky-pull-request-comment, and is automatically updated depending of the check state. If any diff between dist files, then a table is displayed, with a line per file. It shows the original size and compressed (gzip and brotli) sizes, and also a difference in %. ## States _Currently not working on this repository, but you can see them on https://github.com/Kocal/symfony-ux/pull/1_ ### <img width="931" alt="Capture d’écran 2024-10-13 à 11 45 10" src="https://github.com/user-attachments/assets/288523cc-f8fa-48a7-a1e1-08174403b54d"> When opening a PR ### When an issue happened <img width="914" alt="Capture d’écran 2024-10-13 à 11 45 19" src="https://github.com/user-attachments/assets/ae0dae2a-1998-498c-935f-e5d42f78889e"> ### When there is no difference between base and PR <img width="949" alt="Capture d’écran 2024-10-13 à 11 25 31" src="https://github.com/user-attachments/assets/0a7a56d8-1183-4341-99f6-ca386f552298"> ### When there is difference between base and PR <img width="913" alt="image" src="https://github.com/user-attachments/assets/9a5ccaf0-96cf-4189-9bc3-2e35157db70a"> Commits ------- c6d4db6 Add CI workflow to compute diff between files dist files
- Loading branch information
Showing
3 changed files
with
267 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
/** | ||
* Generate a markdown table with the difference in size of the dist files between the base and the PR. | ||
*/ | ||
|
||
/* | ||
Usage: | ||
```shell | ||
BASE_DIST_FILES='{"src/Autocomplete/assets/dist/controller.js":{"size":15382,"size_gz":3716},"src/Chartjs/assets/dist/controller.js":{"size":2281,"size_gz":771},"src/Cropperjs/assets/dist/controller.js":{"size":1044,"size_gz":475}}' \ | ||
PR_DIST_FILES='{"src/Chartjs/assets/dist/controller.js":{"size":1281,"size_gz":171},"src/Cropperjs/assets/dist/controller.js":{"size":1044,"size_gz":475},"src/Cropperjs/assets/dist/style.min.css":{"size":32,"size_gz":66},"src/Dropzone/assets/dist/controller.js":{"size":3199,"size_gz":816},"src/Map/src/Bridge/Google/assets/dist/foo.js":{"size":3199,"size_gz":816}}' \ | ||
GITHUB_REPOSITORY='symfony/ux' \ | ||
GITHUB_HEAD_REF='my-branch-name' \ | ||
node .github/generate-dist-files-size-diff.mjs | ||
``` | ||
*/ | ||
|
||
if (!process.env.BASE_DIST_FILES) { | ||
throw new Error('Missing or invalid "BASE_DIST_FILES" env variable.'); | ||
} | ||
|
||
if (!process.env.PR_DIST_FILES) { | ||
throw new Error('Missing or invalid "PR_DIST_FILES" env variable.'); | ||
} | ||
|
||
if (!process.env.GITHUB_REPOSITORY) { | ||
throw new Error('Missing or invalid "GITHUB_REPOSITORY" env variable.'); | ||
} | ||
|
||
if (!process.env.GITHUB_HEAD_REF) { | ||
throw new Error('Missing or invalid "GITHUB_HEAD_REF" env variable.'); | ||
} | ||
|
||
/** | ||
* Adapted from https://gist.github.com/zentala/1e6f72438796d74531803cc3833c039c?permalink_comment_id=4455218#gistcomment-4455218 | ||
* @param {number} bytes | ||
* @param {number} digits | ||
* @returns {string} | ||
*/ | ||
function formatBytes(bytes, digits = 2) { | ||
if (bytes === 0) { | ||
return '0 B'; | ||
} | ||
const sizes = [`B`, 'kB', 'MB']; | ||
const i = Math.floor(Math.log(bytes) / Math.log(1024)); | ||
|
||
return parseFloat((bytes / Math.pow(1024, i)).toFixed(digits)) + ' ' + sizes[i]; | ||
} | ||
|
||
/** | ||
* @param {number} from | ||
* @param {number} to | ||
* @returns {number} | ||
*/ | ||
function computeDiffPercent(from, to) { | ||
if (from === to) { | ||
return 0; | ||
} | ||
|
||
return Math.round((from - to) / from * -100); | ||
} | ||
|
||
/** | ||
* @param {number} percent | ||
* @returns {string} | ||
*/ | ||
function formatDiffPercent(percent) { | ||
return percent > 0 ? `+${percent}% 📈` : percent < 0 ? `${percent}% 📉` : `${percent}%`; | ||
} | ||
|
||
export function main() { | ||
const repoUrl = `https://github.com/${process.env.GITHUB_REPOSITORY}`; | ||
/** @type {Record<string, {size: number, size_gz: number}>} */ | ||
const base = JSON.parse(process.env.BASE_DIST_FILES); | ||
/** @type {Record<string, {size: number, size_gz: number}>} */ | ||
const pr = JSON.parse(process.env.PR_DIST_FILES); | ||
let output = '<h1>📊 Dist packagesFiles size difference</h1>\n\n'; | ||
|
||
/** | ||
* @type {Map<string, { | ||
* meta: { | ||
* packageName: string, | ||
* bridgeName: string, | ||
* url: string, | ||
* }, | ||
* files: Set<{ | ||
* state: 'added' | 'removed' | 'changed', | ||
* before: {size: number, sizeGz: number}, | ||
* after: {size: number, sizeGz: number}, | ||
* diffPercent: {size: number, sizeGz: number}, | ||
* meta: {fileNameShort: string, fileNameUrl: string} | ||
* }> | ||
* }>} | ||
*/ | ||
const packagesFiles = [...new Set([...Object.keys(pr), ...Object.keys(base)])] | ||
.sort() | ||
.reduce((acc, file) => { | ||
const beforeSize = base[file]?.size || 0; | ||
const afterSize = pr[file]?.size || 0; | ||
const beforeSizeGz = base[file]?.size_gz || 0; | ||
const afterSizeGz = pr[file]?.size_gz || 0; | ||
|
||
if (beforeSize !== afterSize) { | ||
const isBridge = file.includes('src/Bridge'); // we assume that's enough for now | ||
const packageName = file.split('/')[1]; | ||
const bridgeName = isBridge ? file.split('/')[4] : ''; | ||
const key = isBridge ? `${packageName} (Bridge ${bridgeName})` : packageName; | ||
if (!acc.has(key)) { | ||
acc.set(key, { | ||
meta: { | ||
packageName, | ||
bridgeName, | ||
url: isBridge ? `${repoUrl}/tree/${process.env.GITHUB_HEAD_REF}/src/${packageName}/src/Bridge/${bridgeName}/assets/dist` : `${repoUrl}/tree/${process.env.GITHUB_HEAD_REF}/src/${packageName}/assets/dist`, | ||
}, files: new Set(), | ||
}); | ||
} | ||
|
||
const added = !base[file] && pr[file]; | ||
const removed = base[file] && !pr[file]; | ||
|
||
acc.get(key).files.add({ | ||
state: added ? 'added' : (removed ? 'removed' : 'changed'), | ||
before: { size: beforeSize, sizeGz: beforeSizeGz }, | ||
after: { size: afterSize, sizeGz: afterSizeGz }, | ||
diffPercent: { | ||
size: removed ? -100 : (added ? 100 : computeDiffPercent(beforeSize, afterSize)), | ||
sizeGz: removed ? -100 : (added ? 100 : computeDiffPercent(beforeSizeGz, afterSizeGz)), | ||
}, | ||
meta: { | ||
fileNameShort: file.replace(isBridge ? `src/${file.split('/')[1]}/src/Bridge/${file.split('/')[4]}/assets/dist/` : `src/${file.split('/')[1]}/assets/dist/`, ''), | ||
fileNameUrl: `${repoUrl}/blob/${process.env.GITHUB_HEAD_REF}/${file}`, | ||
}, | ||
}); | ||
} | ||
|
||
return acc; | ||
}, new Map); | ||
|
||
if (packagesFiles.size === 0) { | ||
output += 'ℹ️ No difference in dist packagesFiles.\n'; | ||
return output; | ||
} | ||
|
||
output += 'Thanks for the PR! Here is the difference in size of the dist packagesFiles between the base and the PR.\n'; | ||
output += 'Please review the changes and make sure they are expected.\n\n'; | ||
output += `<table> | ||
<thead><tr><th>File</th><th>Before (Size / Gzip)</th><th>After (Size / Gzip)</th></tr></thead> | ||
<tbody>`; | ||
for (const [pkgKey, pkg] of packagesFiles.entries()) { | ||
output += `<tr><td colspan="3"><a href="${pkg.meta.url}"><b>${pkgKey}</b></a></td></tr>`; | ||
for (const file of pkg.files) { | ||
output += `<tr> | ||
<td><a href="${file.meta.fileNameUrl}"><code>${file.meta.fileNameShort}</code></a></td> | ||
`; | ||
output += file.state === 'added' | ||
? `<td><em>Added</em></td>` | ||
: `<td> | ||
<code>${formatBytes(file.before.size)}</code> | ||
/ <code>${formatBytes(file.before.sizeGz)}</code> | ||
</td>`; | ||
output += file.state === 'removed' | ||
? `<td><em>Removed</em></td>` | ||
: `<td> | ||
<code>${formatBytes(file.after.size)}</code>${file.state === 'changed' ? `<sup>${formatDiffPercent(file.diffPercent.size)}</sup>` : ''} | ||
/ <code>${formatBytes(file.after.sizeGz)}</code>${file.state === 'changed' ? `<sup>${formatDiffPercent(file.diffPercent.sizeGz)}</sup>` : ''} | ||
</td>`; | ||
output += `</tr>`; | ||
} | ||
} | ||
output += `</tbody> | ||
</table> | ||
`; | ||
|
||
return output; | ||
} | ||
|
||
if (!process.env.CI) { | ||
console.log(main()); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
name: Dist Files Size Diff (Comment) | ||
|
||
on: | ||
workflow_run: | ||
workflows: ["Dist Files Size Diff"] | ||
types: | ||
- completed | ||
|
||
jobs: | ||
dist-files-size-diff: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Download artifact | ||
uses: actions/download-artifact@v4 | ||
with: | ||
name: dist-size-${{ github.event.number }} | ||
|
||
- name: Comment on the pull request (if success) | ||
if: ${{ always() && steps.diff.conclusion == 'success' }} | ||
uses: marocchino/sticky-pull-request-comment@v2 | ||
with: | ||
path: ./dist-size.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
name: Dist Files Size Diff | ||
|
||
on: | ||
pull_request: | ||
types: [opened, synchronize] | ||
paths: | ||
- 'src/*/assets/dist/**' | ||
- 'src/*/src/Bridge/*/assets/dist/**' | ||
|
||
jobs: | ||
dist-files-size-diff: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Configure git | ||
run: | | ||
git config --global user.email "" | ||
git config --global user.name "github-action[bot]" | ||
- uses: actions/checkout@v4 | ||
with: | ||
ref: ${{ github.base_ref }} | ||
|
||
- name: Get dist files size (from base branch) | ||
id: base-dist-files | ||
run: | | ||
set -e | ||
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 | ||
echo "{\"$file\": {\"size\": $(wc -c < "$file"), \"size_gz\": $(gzip -c "$file" | wc -c)}}" | ||
done | jq -s 'add' -c) | ||
echo "files=$FILES" >> $GITHUB_OUTPUT | ||
- uses: actions/checkout@v4 | ||
|
||
- name: Get dist files size (from pull request) | ||
id: pr-dist-files | ||
run: | | ||
set -e | ||
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 | ||
echo "{\"$file\": {\"size\": $(wc -c < "$file"), \"size_gz\": $(gzip -c "$file" | wc -c)}}" | ||
done | jq -s 'add' -c) | ||
echo "files=$FILES" >> $GITHUB_OUTPUT | ||
- name: Generate the diff | ||
id: diff | ||
uses: actions/github-script@v7 | ||
env: | ||
BASE_DIST_FILES: ${{ steps.base-dist-files.outputs.files }} | ||
PR_DIST_FILES: ${{ steps.pr-dist-files.outputs.files }} | ||
with: | ||
result-encoding: string | ||
script: | | ||
const fs = require('fs') | ||
const { main } = await import('${{ github.workspace }}/.github/generate-dist-files-size-diff.mjs') | ||
const diff = await main() | ||
console.log(diff); | ||
fs.writeFileSync(process.env.GITHUB_WORKSPACE + '/dist-size.md', diff) | ||
- name: Upload the diff | ||
uses: actions/upload-artifact@v4 | ||
with: | ||
name: dist-size-${{ github.event.number }} | ||
path: ./dist-size.md |