-
-
Notifications
You must be signed in to change notification settings - Fork 1
270 lines (230 loc) · 10.3 KB
/
Copy pathbinary-size.yml
File metadata and controls
270 lines (230 loc) · 10.3 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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
name: Binary Size
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: binary-size-${{ github.head_ref || github.ref }}
cancel-in-progress: true
env:
# Maximum allowed binary size in bytes (16MB = 16777216).
#
# Baseline: the CLI measured ~1.9MB until Aug 2026, when 8556d46 made
# craft's first-party zig-js runtime part of the default build. That is a
# deliberate subsystem, not bloat — see the js-runtime option in
# packages/zig/build.zig (~+6.5MB in ReleaseFast; the ReleaseSafe binary
# we ship and measure here lands at ~13.9MB, already stripped, with no
# unwind tables or error traces). The gate sits ~15% above that baseline
# so it still catches accidental growth; opt-out `-Djs-runtime=false`
# builds remain around 2MB.
MAX_BINARY_SIZE: 16777216
# Warning threshold (14.5MB = 15204352), just above the ~13.9MB baseline
# so creep surfaces as a warning well before the hard limit.
WARN_BINARY_SIZE: 15204352
defaults:
run:
shell: bash
jobs:
track:
runs-on: ubuntu-latest
# The last step posts the size report as a pull request comment, and the
# default token here is read-only, so it failed with "Resource not
# accessible by integration" — after the build and the measuring had all
# succeeded. Requested explicitly rather than left to the repository
# default, which is what made this invisible.
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup Zig
uses: pantry-pm/pantry/packages/action@235036fa0f48bae99b2293df5a3dc35c809b1777 # pinned: last SHA whose bundled typescript resolves on linux-x64
- name: Install Linux dependencies
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev
- name: Cache Zig artifacts
uses: actions/cache@v5
with:
path: |
~/.cache/zig
packages/zig/zig-cache
key: size-${{ runner.os }}-zig-master-${{ hashFiles('packages/zig/build.zig') }}
restore-keys: |
size-${{ runner.os }}-zig-master-
- name: First-party Zig dependencies
uses: ./.github/actions/first-party-zig-deps
- name: Build release binary
working-directory: packages/zig
run: |
zig build -Doptimize=ReleaseSafe
- name: Measure binary size
id: size
working-directory: packages/zig
run: |
BINARY_PATH="zig-out/bin/craft"
if [ -f "$BINARY_PATH" ]; then
SIZE=$(wc -c < "$BINARY_PATH" | tr -d ' ')
SIZE_KB=$((SIZE / 1024))
SIZE_MB=$(echo "scale=2; $SIZE / 1048576" | bc)
echo "size=$SIZE" >> $GITHUB_OUTPUT
echo "size_kb=$SIZE_KB" >> $GITHUB_OUTPUT
echo "size_mb=$SIZE_MB" >> $GITHUB_OUTPUT
echo "Binary size: ${SIZE_KB}KB (${SIZE_MB}MB)"
else
echo "Binary not found at $BINARY_PATH"
exit 1
fi
- name: Check size limits
run: |
SIZE=${{ steps.size.outputs.size }}
SIZE_MB=${{ steps.size.outputs.size_mb }}
if [ "$SIZE" -gt "$MAX_BINARY_SIZE" ]; then
echo "::error::Binary size (${SIZE_MB}MB) exceeds maximum allowed size ($(echo "scale=2; $MAX_BINARY_SIZE / 1048576" | bc)MB)"
exit 1
elif [ "$SIZE" -gt "$WARN_BINARY_SIZE" ]; then
echo "::warning::Binary size (${SIZE_MB}MB) is approaching the limit"
fi
- name: First-party Zig dependencies
uses: ./.github/actions/first-party-zig-deps
- name: Get baseline size (main branch)
if: github.event_name == 'pull_request'
id: baseline
run: |
git fetch origin main
# Anchored at the workspace and `|| true`-guarded on purpose: the trap
# fires at the end of the step, by which point `cd packages/zig` below
# has moved us, so a relative pathspec resolves against the wrong
# directory and fails. Under `set -e` that failing restore became the
# step's exit status, so every pull request got a red Binary Size
# check for a cleanup step rather than for its binary size.
trap 'git -C "$GITHUB_WORKSPACE" restore --staged --worktree --source=HEAD -- packages/zig || true' EXIT
git checkout origin/main -- packages/zig
cd packages/zig
zig build -Doptimize=ReleaseSafe
if [ -f "zig-out/bin/craft" ]; then
BASELINE=$(wc -c < "zig-out/bin/craft" | tr -d ' ')
echo "baseline=$BASELINE" >> $GITHUB_OUTPUT
else
echo "::error::Baseline binary was not generated"
exit 1
fi
- name: Calculate size difference
if: github.event_name == 'pull_request'
id: diff
run: |
CURRENT=${{ steps.size.outputs.size }}
BASELINE=${{ steps.baseline.outputs.baseline }}
if [ "$BASELINE" -gt 0 ]; then
DIFF=$((CURRENT - BASELINE))
DIFF_KB=$((DIFF / 1024))
PERCENT=$(echo "scale=2; ($DIFF * 100) / $BASELINE" | bc)
echo "diff=$DIFF" >> $GITHUB_OUTPUT
echo "diff_kb=$DIFF_KB" >> $GITHUB_OUTPUT
echo "percent=$PERCENT" >> $GITHUB_OUTPUT
if [ "$DIFF" -gt 0 ]; then
echo "direction=increased" >> $GITHUB_OUTPUT
elif [ "$DIFF" -lt 0 ]; then
echo "direction=decreased" >> $GITHUB_OUTPUT
else
echo "direction=unchanged" >> $GITHUB_OUTPUT
fi
fi
- name: Generate size report
run: |
echo "## Binary Size Report" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Metric | Value |" >> $GITHUB_STEP_SUMMARY
echo "|--------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Current Size | ${{ steps.size.outputs.size_kb }}KB (${{ steps.size.outputs.size_mb }}MB) |" >> $GITHUB_STEP_SUMMARY
if [ "${{ github.event_name }}" == "pull_request" ] && [ "${{ steps.baseline.outputs.baseline }}" -gt 0 ]; then
BASELINE_KB=$(( ${{ steps.baseline.outputs.baseline }} / 1024 ))
echo "| Baseline (main) | ${BASELINE_KB}KB |" >> $GITHUB_STEP_SUMMARY
echo "| Difference | ${{ steps.diff.outputs.diff_kb }}KB (${{ steps.diff.outputs.percent }}%) |" >> $GITHUB_STEP_SUMMARY
echo "| Status | ${{ steps.diff.outputs.direction }} |" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Limits" >> $GITHUB_STEP_SUMMARY
echo "- Warning threshold: $(echo "scale=2; $WARN_BINARY_SIZE / 1048576" | bc)MB" >> $GITHUB_STEP_SUMMARY
echo "- Maximum allowed: $(echo "scale=2; $MAX_BINARY_SIZE / 1048576" | bc)MB" >> $GITHUB_STEP_SUMMARY
- name: Comment on PR
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const size = '${{ steps.size.outputs.size_kb }}';
const sizeMB = '${{ steps.size.outputs.size_mb }}';
const diff = '${{ steps.diff.outputs.diff_kb }}';
const percent = '${{ steps.diff.outputs.percent }}';
const direction = '${{ steps.diff.outputs.direction }}';
const warnMB = (Number(process.env.WARN_BINARY_SIZE) / 1048576).toFixed(2);
const maxMB = (Number(process.env.MAX_BINARY_SIZE) / 1048576).toFixed(2);
let emoji = '✅';
if (direction === 'increased' && parseInt(diff) > 10) emoji = '⚠️';
if (direction === 'decreased') emoji = '📉';
const body = `## ${emoji} Binary Size Report
| Metric | Value |
|--------|-------|
| Current Size | ${size}KB (${sizeMB}MB) |
| Change | ${diff}KB (${percent}%) ${direction} |
<details>
<summary>Size limits</summary>
- Warning: ${warnMB}MB
- Maximum: ${maxMB}MB
</details>`;
// Find existing comment
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const existingComment = comments.find(c =>
c.user.login === 'github-actions[bot]' &&
c.body.includes('Binary Size Report')
);
if (existingComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existingComment.id,
body: body
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: body
});
}
- name: Restore size history
if: github.ref == 'refs/heads/main'
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
run: |
# The history file lives only in the uploaded artifact (never in the
# repo), so pull the previous run's copy before appending — without
# this each artifact holds a single entry and no history accumulates.
mkdir -p .github/metrics
PREV_RUN=$(gh run list --workflow "Binary Size" --branch main --status success --limit 1 --json databaseId --jq '.[0].databaseId // empty')
if [ -n "$PREV_RUN" ]; then
gh run download "$PREV_RUN" -n binary-size-metrics -D .github/metrics || true
fi
- name: Save size history
if: github.ref == 'refs/heads/main'
run: |
mkdir -p .github/metrics
DATE=$(date -u +%Y-%m-%d)
COMMIT=$(git rev-parse --short HEAD)
echo "{\"date\": \"$DATE\", \"commit\": \"$COMMIT\", \"size\": ${{ steps.size.outputs.size }}}" >> .github/metrics/binary-size.jsonl
- name: Upload metrics
if: github.ref == 'refs/heads/main'
uses: actions/upload-artifact@v4
with:
name: binary-size-metrics
path: .github/metrics/
retention-days: 90