Skip to content

size analysis workflow #63

size analysis workflow

size analysis workflow #63

name: Bundle Analysis
on:
pull_request:
branches: [main]
jobs:
analyze:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Setup bun
uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- name: Install dependencies
run: bun install
- name: Build PR branch
run: |
echo "Building PR branch..."
bun run build
mkdir -p /tmp/dist-pr
cp -r dist/* /tmp/dist-pr/
- name: Try build main branch
continue-on-error: true
id: main-build
run: |
echo "Attempting to build main branch..."
git stash -u
git checkout main
bun install
if bun run build; then
mkdir -p /tmp/dist-main
cp -r dist/* /tmp/dist-main/
echo "main_build_success=true" >> $GITHUB_OUTPUT
else
echo "main_build_success=false" >> $GITHUB_OUTPUT
fi
# Restore PR dist
rm -rf dist
cp -r /tmp/dist-pr/* dist/
- name: Generate size report
id: size-report
run: |
cat > analyze.js << 'SCRIPT'
const fs = require('fs');
function analyzeDirectory(dir) {
const files = fs.readdirSync(dir);
let result = { total: 0, assets: {} };
// First handle files in root of dist
files.forEach(file => {
const fullPath = `${dir}/${file}`;
if (fs.statSync(fullPath).isFile()) {
if (file.endsWith('.js') || file.endsWith('.css') || file.endsWith('.html')) {
const stats = fs.statSync(fullPath);
result.assets[file] = stats.size;
result.total += stats.size;
}
}
});
// Then handle assets directory if it exists
const assetsPath = `${dir}/assets`;
if (fs.existsSync(assetsPath)) {
const assetFiles = fs.readdirSync(assetsPath);
assetFiles.forEach(file => {
if (file.endsWith('.js') || file.endsWith('.css')) {
const stats = fs.statSync(`${assetsPath}/${file}`);
result.assets[`assets/${file}`] = stats.size;
result.total += stats.size;
}
});
}
return result;
}
function formatSize(bytes) {
if (bytes < 1024) return `${bytes.toFixed(2)} B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`;
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
}
try {
const sizes = analyzeDirectory('./dist');
// Sort assets by size for better reporting
const sortedAssets = Object.entries(sizes.assets)
.sort(([, a], [, b]) => b - a);
fs.appendFileSync(process.env.GITHUB_OUTPUT, `pr_total=${formatSize(sizes.total)}\n`);
// Compare with main if available
const hasMainBuild = process.env.MAIN_BUILD_SUCCESS === 'true' && fs.existsSync('/tmp/dist-main');
let diffMessage = 'No comparison available - first PR with bundle analysis';
if (hasMainBuild) {
const mainSizes = analyzeDirectory('/tmp/dist-main');
const diff = sizes.total - mainSizes.total;
const diffPercent = ((diff / mainSizes.total) * 100).toFixed(2);
const diffFormatted = formatSize(Math.abs(diff));
const trend = diff > 0 ? '📈' : '📉';
diffMessage = `${trend} Diff from main: **${diff > 0 ? '+' : '-'}${diffFormatted}** (${diffPercent}%)\nBase
size (main): ${formatSize(mainSizes.total)}`;
}
fs.appendFileSync(process.env.GITHUB_OUTPUT, `diff_message=${diffMessage}\n`);
// Format assets list with better formatting
const assets = sortedAssets
.map(([name, size]) => `${name.padEnd(40)} ${formatSize(size)}`)
.join('\\n');
fs.appendFileSync(process.env.GITHUB_OUTPUT, `assets=${assets || 'No assets found'}\n`);
// List files larger than 5MB
const largeFiles = sortedAssets
.filter(([_, size]) => size > 5 * 1024 * 1024)
.map(([name, size]) => `${name.padEnd(40)} ${formatSize(size)}`)
.join('\\n');
fs.appendFileSync(process.env.GITHUB_OUTPUT, `breakdown=${largeFiles || 'No files over 5MB'}\n`);
} catch (error) {
console.error('Error processing build output:', error);
process.exit(1);
}
SCRIPT
MAIN_BUILD_SUCCESS=${{ steps.main-build.outputs.main_build_success }} node analyze.js
- name: Add PR Comment
uses: mshick/add-pr-comment@v2
with:
message: |
# 📦 Bundle Size Report
## Total Bundle Size
Current: **${{ steps.size-report.outputs.pr_total }}**
${{ steps.size-report.outputs.diff_message }}
## Large Files (>5MB)
```
${{ steps.size-report.outputs.breakdown }}
```
## All Assets
```
${{ steps.size-report.outputs.assets }}
```
<details>
<summary>ℹ️ About this report</summary>
- Sizes are shown in bytes (B), kilobytes (KB), or megabytes (MB)
- Large files are those over 5MB
- Asset types include .js, .css, and .html files
- Diff shows size change compared to main branch when available
</details>
proxy-url: https://add-pr-comment-proxy-tscircuit.vercel.app/api
repo-token: ${{ secrets.GITHUB_TOKEN }}
allow-repeats: false