chore(ci): add turbo caching and replace size-limit (#524)

This commit is contained in:
rahim
2026-02-13 21:15:34 +11:00
committed by GitHub
parent 1c916efed4
commit 7b41930eed
9 changed files with 929 additions and 341 deletions
+76 -171
View File
@@ -9,7 +9,7 @@ permissions:
pull-requests: write
jobs:
size:
pr-size:
runs-on: ubuntu-latest
steps:
@@ -25,191 +25,96 @@ jobs:
node-version: 22
cache: pnpm
- name: Install and build PR
- name: Cache turbo build setup
uses: actions/cache@v5
with:
path: .turbo
key: ${{ runner.os }}-turbo-${{ github.sha }}
restore-keys: |
${{ runner.os }}-turbo-
- name: Install and build
run: pnpm install && pnpm build:packages
- name: Measure PR bundle size
run: pnpm -w exec size-limit --json > /tmp/pr-size.json
- name: Measure bundle size
run: node .github/scripts/bundle-size.js --json pr-size.json
- name: Measure base bundle size
- name: Upload size data
uses: actions/upload-artifact@v4
with:
name: pr-size
path: pr-size.json
base-size:
runs-on: ubuntu-latest
steps:
- name: Checkout base
uses: actions/checkout@v5
with:
ref: ${{ github.base_ref }}
- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version: 22
cache: pnpm
- name: Cache turbo build setup
uses: actions/cache@v5
with:
path: .turbo
key: ${{ runner.os }}-turbo-${{ github.event.pull_request.base.sha }}
restore-keys: |
${{ runner.os }}-turbo-
- name: Install and build
run: |
git fetch origin ${{ github.base_ref }} --depth=1
git checkout -f FETCH_HEAD
pnpm install
if [ -f .size-limit.json ]; then
if [ -f .github/scripts/bundle-size.js ]; then
pnpm build:packages
pnpm -w exec size-limit --json > /tmp/base-size.json
node .github/scripts/bundle-size.js --json base-size.json
else
echo '[]' > /tmp/base-size.json
echo '[]' > base-size.json
fi
- name: Report
- name: Upload size data
uses: actions/upload-artifact@v4
with:
name: base-size
path: base-size.json
report:
runs-on: ubuntu-latest
needs: [pr-size, base-size]
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Download size data
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Generate report
run: >
node .github/scripts/bundle-size-report.js
--pr artifacts/pr-size/pr-size.json
--base artifacts/base-size/base-size.json
> report.md
- name: Post comment
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const pr = JSON.parse(fs.readFileSync('/tmp/pr-size.json', 'utf8'));
const base = JSON.parse(fs.readFileSync('/tmp/base-size.json', 'utf8'));
const baseMap = Object.fromEntries(base.map(e => [e.name, e.size]));
function formatBytes(bytes) {
if (bytes < 1024) return `${bytes} B`;
return `${(bytes / 1024).toFixed(2)} kB`;
}
function formatDelta(current, previous) {
if (previous === undefined) return { bytes: '—', pct: '' };
const diff = current - previous;
if (diff === 0) return { bytes: '0 B', pct: '0%' };
const sign = diff > 0 ? '+' : '-';
const pct = Math.abs((diff / previous) * 100).toFixed(1);
return { bytes: `${sign}${formatBytes(Math.abs(diff))}`, pct: `${sign}${pct}%` };
}
function statusIcon(current, previous) {
if (previous === undefined) return '🆕';
const diff = current - previous;
if (diff === 0) return '✅';
if (diff < 0) return '🔽';
const pct = (diff / previous) * 100;
return pct > 10 ? '🔴' : '🔺';
}
// Delta bar: visualizes % change relative to the largest change
function deltaBar(current, previous, maxAbsPct) {
const width = 8;
if (previous === undefined || maxAbsPct === 0) return '░'.repeat(width);
const pct = Math.abs(((current - previous) / previous) * 100);
const filled = Math.round((pct / maxAbsPct) * width);
return '█'.repeat(filled) + '░'.repeat(width - filled);
}
// Group entries by package: @videojs/utils/* -> utils, @videojs/store/* -> store, etc.
const groups = new Map();
for (const entry of pr) {
const match = entry.name.match(/^@videojs\/([^/]+)/);
const pkg = match ? match[1] : 'other';
if (!groups.has(pkg)) groups.set(pkg, []);
groups.get(pkg).push(entry);
}
// First pass: compute per-package totals
const pkgData = [];
let grandTotalPr = 0;
let grandTotalBase = 0;
for (const [pkg, entries] of groups) {
const pkgTotalPr = entries.reduce((s, e) => s + e.size, 0);
const pkgTotalBase = entries.reduce((s, e) => s + (baseMap[e.name] ?? 0), 0);
const hasBase = entries.some(e => baseMap[e.name] !== undefined);
grandTotalPr += pkgTotalPr;
grandTotalBase += pkgTotalBase;
const pkgPrev = entries.length > 1 ? pkgTotalBase : baseMap[entries[0].name];
const pkgCur = entries.length > 1 ? pkgTotalPr : entries[0].size;
const changed = hasBase && pkgCur !== pkgPrev;
pkgData.push({ pkg, entries, pkgTotalPr, pkgTotalBase, hasBase, pkgPrev, pkgCur, changed });
}
const maxAbsPct = Math.max(
...pkgData.map(p => {
if (!p.hasBase || p.pkgPrev === 0) return 0;
return Math.abs(((p.pkgCur - p.pkgPrev) / p.pkgPrev) * 100);
})
);
// Overview table
const overview = [];
overview.push('| Package | Size | Diff | | % | |');
overview.push('|---|--:|--:|---|--:|:-:|');
for (const p of pkgData) {
const d = formatDelta(p.pkgCur, p.pkgPrev);
const icon = p.hasBase ? statusIcon(p.pkgCur, p.pkgPrev) : '';
const bar = `\`${deltaBar(p.pkgCur, p.pkgPrev, maxAbsPct)}\``;
overview.push(
`| **@videojs/${p.pkg}** | **${formatBytes(p.pkgCur)}** | ${p.hasBase ? d.bytes : '—'} | ${bar} | ${p.hasBase ? d.pct : ''} | ${icon} |`
);
}
// Detail sections
const details = [];
details.push('#### Subpath Breakdown');
details.push('');
for (const p of pkgData) {
const { pkg, entries, pkgTotalPr, pkgTotalBase, hasBase } = p;
// Subpath display: @videojs/utils/dom -> ./dom, @videojs/store -> .
const displayName = (name) => {
const sub = name.replace(`@videojs/${pkg}`, '');
return sub ? `.${sub}` : '.';
};
details.push(`<details>`);
details.push(`<summary><code>@videojs/${pkg}</code></summary>`);
details.push('');
details.push('| Subpath | Base | PR | Diff | % | |');
details.push('|---|--:|--:|--:|--:|:-:|');
for (const entry of entries) {
const prev = baseMap[entry.name];
const d = formatDelta(entry.size, prev);
details.push(
`| \`${displayName(entry.name)}\` | ${prev !== undefined ? formatBytes(prev) : '—'} | **${formatBytes(entry.size)}** | ${d.bytes} | ${d.pct} | ${statusIcon(entry.size, prev)} |`
);
}
if (entries.length > 1) {
const d = formatDelta(pkgTotalPr, pkgTotalBase);
details.push(
`| **total** | **${hasBase ? formatBytes(pkgTotalBase) : '—'}** | **${formatBytes(pkgTotalPr)}** | **${hasBase ? d.bytes : '—'}** | **${hasBase ? d.pct : ''}** | |`
);
}
details.push('');
details.push('</details>');
details.push('');
}
const grandDelta = formatDelta(grandTotalPr, grandTotalBase);
const body = fs.readFileSync('report.md', 'utf8');
const marker = '<!-- bundle-size-report -->';
const body = [
marker,
'### 📦 Bundle Size Report',
'',
...overview,
'',
`**Total: ${formatBytes(grandTotalPr)}**${grandTotalBase ? ` · ${grandDelta.bytes} · ${grandDelta.pct}` : ''}`,
'',
'---',
'',
...details,
'---',
'',
'<details>',
'<summary>️ How to interpret</summary>',
'',
'Each package shows its own code size with workspace and peer dependencies externalized.',
'Sizes are minified + brotli, measured via [size-limit](https://github.com/ai/size-limit) with esbuild.',
'',
'| Icon | Meaning |',
'|---|---|',
'| ✅ | No change |',
'| 🔺 | Increased ≤ 10% |',
'| 🔴 | Increased > 10% |',
'| 🔽 | Decreased |',
'| 🆕 | New (no baseline) |',
'',
'Run `pnpm size` locally to check current sizes.',
'</details>',
].join('\n');
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
+9
View File
@@ -55,6 +55,15 @@ jobs:
if: ${{ steps.release.outputs.releases_created == 'true' }}
run: pnpm i --frozen-lockfile
- name: Cache turbo build setup
if: ${{ steps.release.outputs.releases_created == 'true' }}
uses: actions/cache@v5
with:
path: .turbo
key: ${{ runner.os }}-turbo-${{ github.sha }}
restore-keys: |
${{ runner.os }}-turbo-
- name: Clean
if: ${{ steps.release.outputs.releases_created == 'true' }}
run: pnpm clean
+8
View File
@@ -28,5 +28,13 @@ jobs:
- name: Install dependencies
run: pnpm install
- name: Cache turbo build setup
uses: actions/cache@v5
with:
path: .turbo
key: ${{ runner.os }}-turbo-${{ github.sha }}
restore-keys: |
${{ runner.os }}-turbo-
- name: Build
run: pnpm build:packages
+8
View File
@@ -30,5 +30,13 @@ jobs:
- name: Install dependencies
run: pnpm install
- name: Cache turbo build setup
uses: actions/cache@v5
with:
path: .turbo
key: ${{ runner.os }}-turbo-${{ github.sha }}
restore-keys: |
${{ runner.os }}-turbo-
- name: Run website tests
run: pnpm --filter site test