-
-
Notifications
You must be signed in to change notification settings - Fork 35.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
CI: Report tree-shaking size in PR (#25615)
* Add report-size.yml action * Format * Report normal bundle size as well * Wording * Remove pretty-bytes dependency * Add clarification about tree-shaking in message
- Loading branch information
1 parent
c8b99b2
commit 3987bf5
Showing
6 changed files
with
219 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,50 @@ | ||
name: Read size | ||
|
||
on: | ||
pull_request: | ||
paths: | ||
- 'src/**' | ||
- 'package.json' | ||
|
||
# This workflow runs in a read-only environment. We can safely checkout | ||
# the PR code here. | ||
# Reference: | ||
# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ | ||
permissions: | ||
contents: read | ||
|
||
jobs: | ||
read-size: | ||
name: Tree-shaking | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Git checkout | ||
uses: actions/checkout@v3 | ||
- name: Install Node | ||
uses: actions/setup-node@v3 | ||
with: | ||
node-version: 18 | ||
cache: 'npm' | ||
- name: Install dependencies | ||
run: npm ci | ||
- name: Build | ||
run: npm run build-module | ||
- name: === Test tree-shaking === | ||
run: npm run test-treeshake | ||
- name: Read bundle sizes | ||
id: read-size | ||
run: | | ||
FILESIZE=$(stat --format=%s test/treeshake/three.module.min.js) | ||
gzip -k test/treeshake/three.module.min.js | ||
FILESIZE_GZIP=$(stat --format=%s test/treeshake/three.module.min.js.gz) | ||
TREESHAKEN=$(stat --format=%s test/treeshake/index.bundle.min.js) | ||
gzip -k test/treeshake/index.bundle.min.js | ||
TREESHAKEN_GZIP=$(stat --format=%s test/treeshake/index.bundle.min.js.gz) | ||
# write the output in a json file to upload it as artifact | ||
node -pe "JSON.stringify({ filesize: $FILESIZE, gzip: $FILESIZE_GZIP, treeshaken: $TREESHAKEN, treeshakenGzip: $TREESHAKEN_GZIP })" > sizes.json | ||
- name: Upload artifact | ||
uses: actions/upload-artifact@v3 | ||
with: | ||
name: sizes | ||
path: sizes.json |
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,125 @@ | ||
name: Report size | ||
|
||
on: | ||
workflow_run: | ||
workflows: ["Read size"] | ||
types: | ||
- completed | ||
|
||
# This workflow needs to be run with "pull-requests: write" permissions to | ||
# be able to comment on the pull request. We can't checkout the PR code | ||
# in this workflow. | ||
# Reference: | ||
# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ | ||
permissions: | ||
pull-requests: write | ||
|
||
jobs: | ||
report-size: | ||
name: Comment on PR | ||
runs-on: ubuntu-latest | ||
if: github.event.workflow_run.event == 'pull_request' && | ||
github.event.workflow_run.conclusion == 'success' | ||
steps: | ||
# Using actions/download-artifact doesn't work here | ||
# https://github.com/actions/download-artifact/issues/60 | ||
- name: Download artifact | ||
uses: actions/github-script@v6 | ||
id: download-artifact | ||
with: | ||
result-encoding: string | ||
script: | | ||
const fs = require('fs/promises'); | ||
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
run_id: context.payload.workflow_run.id, | ||
}); | ||
const matchArtifact = artifacts.data.artifacts.find((artifact) => artifact.name === 'sizes'); | ||
const download = await github.rest.actions.downloadArtifact({ | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
artifact_id: matchArtifact.id, | ||
archive_format: 'zip', | ||
}); | ||
await fs.writeFile('sizes.zip', Buffer.from(download.data)); | ||
await exec.exec('unzip sizes.zip'); | ||
const json = await fs.readFile('sizes.json', 'utf8'); | ||
return json; | ||
# This runs on the base branch of the PR, meaning "dev" | ||
- name: Git checkout | ||
uses: actions/checkout@v3 | ||
- name: Install Node | ||
uses: actions/setup-node@v3 | ||
with: | ||
node-version: 18 | ||
cache: 'npm' | ||
- name: Install dependencies | ||
run: npm ci | ||
- name: Build | ||
run: npm run build-module | ||
- name: === Test tree-shaking === | ||
run: npm run test-treeshake | ||
- name: Read sizes | ||
id: read-size | ||
run: | | ||
FILESIZE_BASE=$(stat --format=%s test/treeshake/three.module.min.js) | ||
TREESHAKEN_BASE=$(stat --format=%s test/treeshake/index.bundle.min.js) | ||
echo "FILESIZE_BASE=$FILESIZE_BASE" >> $GITHUB_OUTPUT | ||
echo "TREESHAKEN_BASE=$TREESHAKEN_BASE" >> $GITHUB_OUTPUT | ||
- name: Format sizes | ||
id: format | ||
# It's important these are passed as env variables. | ||
# https://securitylab.github.com/research/github-actions-untrusted-input/ | ||
env: | ||
FILESIZE: ${{ fromJSON(steps.download-artifact.outputs.result).filesize }} | ||
FILESIZE_GZIP: ${{ fromJSON(steps.download-artifact.outputs.result).gzip }} | ||
FILESIZE_BASE: ${{ steps.read-size.outputs.FILESIZE_BASE }} | ||
TREESHAKEN: ${{ fromJSON(steps.download-artifact.outputs.result).treeshaken }} | ||
TREESHAKEN_GZIP: ${{ fromJSON(steps.download-artifact.outputs.result).treeshakenGzip }} | ||
TREESHAKEN_BASE: ${{ steps.read-size.outputs.TREESHAKEN_BASE }} | ||
run: | | ||
FILESIZE_FORM=$(node ./test/treeshake/utils/format-size.js "$FILESIZE") | ||
FILESIZE_GZIP_FORM=$(node ./test/treeshake/utils/format-size.js "$FILESIZE_GZIP") | ||
FILESIZE_DIFF=$(node ./test/treeshake/utils/format-diff.js "$FILESIZE" "$FILESIZE_BASE") | ||
TREESHAKEN_FORM=$(node ./test/treeshake/utils/format-size.js "$TREESHAKEN") | ||
TREESHAKEN_GZIP_FORM=$(node ./test/treeshake/utils/format-size.js "$TREESHAKEN_GZIP") | ||
TREESHAKEN_DIFF=$(node ./test/treeshake/utils/format-diff.js "$TREESHAKEN" "$TREESHAKEN_BASE") | ||
echo "FILESIZE=$FILESIZE_FORM" >> $GITHUB_OUTPUT | ||
echo "FILESIZE_GZIP=$FILESIZE_GZIP_FORM" >> $GITHUB_OUTPUT | ||
echo "FILESIZE_DIFF=$FILESIZE_DIFF" >> $GITHUB_OUTPUT | ||
echo "TREESHAKEN=$TREESHAKEN_FORM" >> $GITHUB_OUTPUT | ||
echo "TREESHAKEN_GZIP=$TREESHAKEN_GZIP_FORM" >> $GITHUB_OUTPUT | ||
echo "TREESHAKEN_DIFF=$TREESHAKEN_DIFF" >> $GITHUB_OUTPUT | ||
- name: Find existing comment | ||
uses: peter-evans/find-comment@v2 | ||
id: find-comment | ||
with: | ||
issue-number: ${{ github.event.workflow_run.pull_requests[0].number }} | ||
comment-author: 'github-actions[bot]' | ||
body-includes: Bundle size | ||
- name: Comment on PR | ||
uses: peter-evans/create-or-update-comment@v2 | ||
with: | ||
issue-number: ${{ github.event.workflow_run.pull_requests[0].number }} | ||
comment-id: ${{ steps.find-comment.outputs.comment-id }} | ||
edit-mode: replace | ||
body: | | ||
### 📦 Bundle size | ||
| Filesize | Gzipped | Diff from `${{ github.ref_name }}` | | ||
|----------|---------|------| | ||
| ${{ steps.format.outputs.FILESIZE }} | ${{ steps.format.outputs.FILESIZE_GZIP }} | ${{ steps.format.outputs.FILESIZE_DIFF }} | | ||
### 🌳 Bundle size after tree-shaking | ||
_Includes a renderer, camera, and empty scene._ | ||
| Filesize | Gzipped | Diff from `${{ github.ref_name }}` | | ||
|----------|---------|------| | ||
| ${{ steps.format.outputs.TREESHAKEN }} | ${{ steps.format.outputs.TREESHAKEN_GZIP }} | ${{ steps.format.outputs.TREESHAKEN_DIFF }} | |
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
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
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,10 @@ | ||
// used in report-size.yml | ||
|
||
const filesize = Number( process.argv[ 2 ] ); | ||
const filesizeBase = Number( process.argv[ 3 ] ); | ||
|
||
const diff = ( filesize - filesizeBase ) * 100 / filesizeBase; | ||
const diffString = diff.toFixed( 2 ).slice( - 1 ) === '0' ? diff.toFixed( 1 ) : diff.toFixed( 2 ); | ||
const formatted = `${diff >= 0 ? '+' : ''}${diffString}%`; | ||
|
||
console.log( formatted ); |
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,20 @@ | ||
// used in report-size.yml | ||
|
||
export function formatBytes( bytes, decimals = 1 ) { | ||
|
||
if ( bytes === 0 ) return '0 B'; | ||
|
||
const k = 1000; | ||
const dm = decimals < 0 ? 0 : decimals; | ||
const sizes = [ 'B', 'kB', 'MB', 'GB' ]; | ||
|
||
const i = Math.floor( Math.log( bytes ) / Math.log( k ) ); | ||
|
||
return parseFloat( ( bytes / Math.pow( k, i ) ).toFixed( dm ) ) + ' ' + sizes[ i ]; | ||
|
||
} | ||
|
||
const n = Number( process.argv[ 2 ] ); | ||
const formatted = formatBytes( n ); | ||
|
||
console.log( formatted ); |