-
Notifications
You must be signed in to change notification settings - Fork 2
188 lines (156 loc) · 6.38 KB
/
performance-budget.yml
File metadata and controls
188 lines (156 loc) · 6.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
name: Bundle Analysis
on:
push:
branches: [main, develop]
paths:
- 'packages/**'
- 'apps/console/**'
- 'pnpm-lock.yaml'
pull_request:
branches: [main, develop]
paths:
- 'packages/**'
- 'apps/console/**'
- 'pnpm-lock.yaml'
concurrency:
group: bundle-analysis-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
pull-requests: write
jobs:
bundle-analysis:
name: Bundle Analysis
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
submodules: true
- name: Enable Corepack
run: corepack enable
- name: Verify pnpm version
run: pnpm --version
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '20.x'
cache: 'pnpm'
- name: Turbo Cache
uses: actions/cache@v5
with:
path: node_modules/.cache/turbo
key: turbo-${{ runner.os }}-${{ github.sha }}
restore-keys: |
turbo-${{ runner.os }}-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build packages
run: pnpm turbo run build --filter='./packages/*'
- name: Build Console
run: pnpm --filter @object-ui/console build
- name: Check console performance budget
id: budget
run: |
# Performance budget: main entry must be < 350 KB gzip
# This is a realistic threshold for a full-featured enterprise app
# with React, routing, UI components, and core business logic.
MAX_ENTRY_GZIP_KB=350
DIST_DIR="apps/console/dist/assets"
if [ ! -d "$DIST_DIR" ]; then
echo "❌ Build output not found at $DIST_DIR"
exit 1
fi
# Find the main entry chunk (index-*.js)
ENTRY_FILE=$(find "$DIST_DIR" -name 'index-*.js' -not -name '*.gz' -not -name '*.br' | head -1)
if [ -z "$ENTRY_FILE" ]; then
echo "⚠️ Could not find main entry chunk, checking all JS files..."
ENTRY_FILE=$(find "$DIST_DIR" -name '*.js' -not -name '*.gz' -not -name '*.br' | sort | head -1)
fi
if [ -z "$ENTRY_FILE" ]; then
echo "❌ No JS files found in $DIST_DIR"
exit 1
fi
echo "📦 Main entry file: $(basename $ENTRY_FILE)"
# Calculate gzip size
GZIP_BYTES=$(gzip -c "$ENTRY_FILE" | wc -c)
GZIP_KB=$(awk "BEGIN {printf \"%.1f\", $GZIP_BYTES / 1024}")
echo " Raw size: $(awk "BEGIN {printf \"%.1f\", $(wc -c < "$ENTRY_FILE") / 1024}") KB"
echo " Gzip size: ${GZIP_KB} KB"
echo " Budget: ${MAX_ENTRY_GZIP_KB} KB"
echo "gzip_kb=$GZIP_KB" >> "$GITHUB_OUTPUT"
echo "budget_kb=$MAX_ENTRY_GZIP_KB" >> "$GITHUB_OUTPUT"
echo "entry_file=$(basename $ENTRY_FILE)" >> "$GITHUB_OUTPUT"
# Check budget
OVER=$(awk "BEGIN {print ($GZIP_KB > $MAX_ENTRY_GZIP_KB) ? 1 : 0}")
if [ "$OVER" -eq 1 ]; then
echo ""
echo "❌ BUDGET EXCEEDED: Main entry is ${GZIP_KB} KB gzip (limit: ${MAX_ENTRY_GZIP_KB} KB)"
echo "budget_status=fail" >> "$GITHUB_OUTPUT"
exit 1
else
echo ""
echo "✅ Budget OK: Main entry is ${GZIP_KB} KB gzip (limit: ${MAX_ENTRY_GZIP_KB} KB)"
echo "budget_status=pass" >> "$GITHUB_OUTPUT"
fi
- name: Generate package size report
id: size-report
if: always()
run: |
echo "## 📦 Bundle Size Report" > size-report.md
echo "" >> size-report.md
echo "| Package | Size | Gzipped |" >> size-report.md
echo "|---------|------|---------|" >> size-report.md
for pkg in packages/*/dist; do
if [ -d "$pkg" ]; then
pkg_name=$(basename $(dirname $pkg))
# Calculate sizes for main bundle files
for file in "$pkg"/*.js; do
if [ -f "$file" ] && [ ! -f "${file}.map" ]; then
# Use portable method to get file size
size=$(wc -c < "$file")
size_kb=$(awk "BEGIN {printf \"%.2f\", $size/1024}")
# Estimate gzipped size
gzip_size=$(gzip -c "$file" | wc -c)
gzip_kb=$(awk "BEGIN {printf \"%.2f\", $gzip_size/1024}")
echo "| $pkg_name ($(basename $file)) | ${size_kb}KB | ${gzip_kb}KB |" >> size-report.md
fi
done
fi
done
echo "" >> size-report.md
echo "### Size Limits" >> size-report.md
echo "- ✅ Core packages should be < 50KB gzipped" >> size-report.md
echo "- ✅ Component packages should be < 100KB gzipped" >> size-report.md
echo "- ⚠️ Plugin packages should be < 150KB gzipped" >> size-report.md
- name: Comment PR with results
if: github.event_name == 'pull_request' && always()
uses: actions/github-script@v9
with:
script: |
const fs = require('fs');
const status = '${{ steps.budget.outputs.budget_status }}';
const gzipKb = '${{ steps.budget.outputs.gzip_kb }}';
const budgetKb = '${{ steps.budget.outputs.budget_kb }}';
const entryFile = '${{ steps.budget.outputs.entry_file }}';
const icon = status === 'pass' ? '✅' : '❌';
let body = `## ${icon} Console Performance Budget
| Metric | Value | Budget |
|--------|-------|--------|
| Main entry (gzip) | **${gzipKb} KB** | ${budgetKb} KB |
| Entry file | \`${entryFile}\` | — |
| Status | **${status === 'pass' ? 'PASS' : 'FAIL'}** | — |
`;
// Append package size report if available
try {
const sizeReport = fs.readFileSync('size-report.md', 'utf8');
body += '\n---\n\n' + sizeReport;
} catch (e) {
// Size report not generated
}
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body
});