Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calculate package size diff on PRs #5235

Merged
merged 16 commits into from
Mar 6, 2024
47 changes: 45 additions & 2 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ on:

permissions:
contents: read
pull-requests: write

env:
# For PRs and MergeQueues, the target commit is used, and for push events, github.event.previous is used.
Expand Down Expand Up @@ -48,11 +49,26 @@ jobs:
with:
ref: ${{ env.targetHash }}

- name: Install dependencies
if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
uses: cypress-io/github-action@v6
with:
# just perform install
runTests: false

- name: Build
if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' && github.event_name == 'pull_request' }}
run: |
pnpm run build:viz
mkdir -p cypress/snapshots/stats/base
mv stats cypress/snapshots/stats/base

- name: Cypress run
uses: cypress-io/github-action@v4
uses: cypress-io/github-action@v6
id: cypress-snapshot-gen
if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
with:
install: false
start: pnpm run dev
wait-on: 'http://localhost:9000'
browser: chrome
Expand Down Expand Up @@ -86,15 +102,42 @@ jobs:
path: ./cypress/snapshots
key: ${{ runner.os }}-snapshots-${{ env.targetHash }}

- name: Install dependencies
uses: cypress-io/github-action@v6
with:
runTests: false

- name: Build
id: size
if: ${{ github.event_name == 'pull_request' && matrix.containers == 1 }}
run: |
pnpm run build:viz
mv stats cypress/snapshots/stats/head
{
echo 'size_diff<<EOF'
npx tsx scripts/size.ts
echo EOF
} >> "$GITHUB_OUTPUT"

# Size diff only needs to be posted from one job, on PRs.
- name: Comment PR size difference
if: ${{ github.event_name == 'pull_request' && matrix.containers == 1 }}
uses: thollander/actions-comment-pull-request@v2
with:
message: |
${{ steps.size.outputs.size_diff }}
comment_tag: size-diff

# Install NPM dependencies, cache them correctly
# and run all Cypress tests
- name: Cypress run
uses: cypress-io/github-action@v4
uses: cypress-io/github-action@v6
id: cypress
# If CYPRESS_RECORD_KEY is set, run in parallel on all containers
# Otherwise (e.g. if running from fork), we run on a single container only
if: ${{ ( env.CYPRESS_RECORD_KEY != '' ) || ( matrix.containers == 1 ) }}
with:
install: false
start: pnpm run dev:coverage
wait-on: 'http://localhost:9000'
browser: chrome
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
"jsdom": "^22.0.0",
"langium-cli": "3.0.1",
"lint-staged": "^13.2.1",
"markdown-table": "^3.0.3",
"nyc": "^15.1.0",
"path-browserify": "^1.0.1",
"pnpm": "^8.6.8",
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

82 changes: 82 additions & 0 deletions scripts/size.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/* eslint-disable no-console */
import type { Metafile } from 'esbuild';
import { readFile } from 'fs/promises';
import { globby } from 'globby';
import { markdownTable } from 'markdown-table';
export const getSizes = (metafile: Metafile) => {
const { outputs } = metafile;
const sizes = Object.keys(outputs)
.filter((key) => key.endsWith('js') && !key.includes('chunk'))
.map((key) => {
const { bytes } = outputs[key];
return [key.replace('dist/', ''), bytes];
});
return sizes;
};

const readStats = async (path: string): Promise<Record<string, number>> => {
const files = await globby(path);
const contents = await Promise.all(files.map((file) => readFile(file, 'utf-8')));
const sizes = contents.flatMap((content) => getSizes(JSON.parse(content)));
return Object.fromEntries(sizes);
};

const formatBytes = (bytes: number): string => {
if (bytes == 0) {
return '0 Bytes';
}
const base = 1024;
const decimals = 2;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(base));
return parseFloat((bytes / Math.pow(base, i)).toFixed(decimals)) + ' ' + sizes[i];
};

const formatSize = (bytes: number): string => {
const formatted = formatBytes(bytes);
if (formatted.includes('Bytes')) {
return formatted;
}
return `${formatBytes(bytes)} (${bytes} Bytes)`;
};

const percentageDifference = (oldValue: number, newValue: number): string => {
const difference = Math.abs(newValue - oldValue);
const avg = (newValue + oldValue) / 2;
const percentage = (difference / avg) * 100;
const roundedPercentage = percentage.toFixed(2); // Round to two decimal places
if (roundedPercentage === '0.00') {
return '0.00%';
}
const sign = newValue > oldValue ? '+' : '-';
return `${sign}${roundedPercentage}%`;
};

const main = async () => {
const oldStats = await readStats('./cypress/snapshots/stats/base/**/*.json');
const newStats = await readStats('./cypress/snapshots/stats/head/**/*.json');
const diff = Object.entries(newStats)
.filter(([, value]) => value > 2048)
.map(([key, value]) => {
const oldValue = oldStats[key];
const delta = value - oldValue;
const output = [
key,
formatSize(oldValue),
formatSize(value),
formatSize(delta),
percentageDifference(oldValue, value),
];
return output;
})
.filter(([, , , delta]) => delta !== '0 Bytes');
if (diff.length === 0) {
console.log('No changes in bundle sizes');
return;
}
console.log(
markdownTable([['File', 'Previous Size', 'New Size', 'Difference', '% Change'], ...diff])
);
};

void main().catch((e) => console.error(e));
Loading