Skip to content

Commit 5264502

Browse files
authored
Merge pull request #1572 from joelpeace48-cell/feat/wasm-size-regression-check
feat: add contract WASM size regression check to CI
2 parents 347b6da + baf670b commit 5264502

2 files changed

Lines changed: 212 additions & 0 deletions

File tree

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
name: Contract WASM Size Check
2+
3+
# Runs on every PR that touches contracts/ or this workflow file.
4+
# Also runs on pushes to main so the baseline stays auditable.
5+
6+
on:
7+
pull_request:
8+
paths:
9+
- "contracts/**"
10+
- ".github/workflows/contract-wasm-size.yml"
11+
push:
12+
branches:
13+
- main
14+
paths:
15+
- "contracts/**"
16+
- ".github/workflows/contract-wasm-size.yml"
17+
18+
# ── Tunable knobs ─────────────────────────────────────────────────────────────
19+
env:
20+
# Build artefact path (relative to contracts/)
21+
WASM_PATH: target/wasm32-unknown-unknown/release/portfolio_rebalancer.wasm
22+
# Baseline file path (relative to contracts/)
23+
BASELINE_FILE: wasm-size-baseline.txt
24+
# Percentage growth that triggers a failure (integer, e.g. 10 = 10 %)
25+
WASM_SIZE_THRESHOLD_PCT: "10"
26+
27+
jobs:
28+
wasm-size:
29+
name: WASM size regression check
30+
runs-on: ubuntu-latest
31+
permissions:
32+
pull-requests: write # needed to post a PR comment with the size delta
33+
contents: read
34+
35+
steps:
36+
- uses: actions/checkout@v4
37+
38+
- name: Setup Rust (wasm32 target)
39+
uses: dtolnay/rust-toolchain@stable
40+
with:
41+
targets: wasm32-unknown-unknown
42+
43+
- name: Cache Cargo registry + build artefacts
44+
uses: actions/cache@v4
45+
with:
46+
path: |
47+
~/.cargo/registry
48+
~/.cargo/git
49+
contracts/target
50+
key: wasm-size-${{ runner.os }}-${{ hashFiles('contracts/Cargo.lock', 'contracts/Cargo.toml') }}
51+
restore-keys: |
52+
wasm-size-${{ runner.os }}-
53+
54+
- name: Build contract (release WASM)
55+
working-directory: contracts
56+
run: cargo build --target wasm32-unknown-unknown --release
57+
58+
- name: Measure WASM size and compare against baseline
59+
id: size_check
60+
working-directory: contracts
61+
run: |
62+
set -euo pipefail
63+
64+
# ── Read baseline ──────────────────────────────────────────────────
65+
if [ ! -f "$BASELINE_FILE" ]; then
66+
echo "::error::Baseline file '$BASELINE_FILE' not found in contracts/."
67+
echo "::error::Run 'wc -c < $WASM_PATH' after a release build and add the"
68+
echo "::error::result to contracts/$BASELINE_FILE (see file for format)."
69+
exit 1
70+
fi
71+
72+
# Extract the numeric value from BASELINE= line, ignoring comment lines.
73+
BASELINE=$(grep -E '^BASELINE=[0-9]+' "$BASELINE_FILE" | head -1 | cut -d= -f2)
74+
if [ -z "$BASELINE" ] || [ "$BASELINE" -le 0 ] 2>/dev/null; then
75+
echo "::error::Could not parse a positive integer from BASELINE= in $BASELINE_FILE"
76+
exit 1
77+
fi
78+
79+
# ── Measure actual size ────────────────────────────────────────────
80+
if [ ! -f "$WASM_PATH" ]; then
81+
echo "::error::WASM artefact not found at contracts/$WASM_PATH"
82+
exit 1
83+
fi
84+
ACTUAL=$(wc -c < "$WASM_PATH")
85+
86+
# ── Compute delta ──────────────────────────────────────────────────
87+
# Use awk for floating-point arithmetic; bash only does integer math.
88+
DELTA=$((ACTUAL - BASELINE))
89+
PCT=$(awk "BEGIN { printf \"%.2f\", ($ACTUAL - $BASELINE) / $BASELINE * 100 }")
90+
ABS_PCT=$(awk "BEGIN { printf \"%.2f\", ($ACTUAL - $BASELINE < 0 ? $BASELINE - $ACTUAL : $ACTUAL - $BASELINE) / $BASELINE * 100 }")
91+
92+
# ── Emit step summary ──────────────────────────────────────────────
93+
{
94+
echo "## Contract WASM Size Report"
95+
echo ""
96+
echo "| Metric | Value |"
97+
echo "|--------|-------|"
98+
echo "| Baseline | $(numfmt --grouping "$BASELINE") bytes |"
99+
echo "| Actual | $(numfmt --grouping "$ACTUAL") bytes |"
100+
echo "| Delta | $([ $DELTA -ge 0 ] && echo "+")${DELTA} bytes (${PCT}%) |"
101+
echo "| Threshold | ±${WASM_SIZE_THRESHOLD_PCT}% |"
102+
} >> "$GITHUB_STEP_SUMMARY"
103+
104+
# ── Expose outputs for downstream use (e.g. PR comments) ──────────
105+
echo "baseline=$BASELINE" >> "$GITHUB_OUTPUT"
106+
echo "actual=$ACTUAL" >> "$GITHUB_OUTPUT"
107+
echo "delta=$DELTA" >> "$GITHUB_OUTPUT"
108+
echo "pct=$PCT" >> "$GITHUB_OUTPUT"
109+
110+
# ── Enforce threshold (growth only; shrinkage is always fine) ──────
111+
OVER_THRESHOLD=$(awk "BEGIN { print ($ACTUAL > $BASELINE && ($ACTUAL - $BASELINE) / $BASELINE * 100 > $WASM_SIZE_THRESHOLD_PCT) ? 1 : 0 }")
112+
if [ "$OVER_THRESHOLD" = "1" ]; then
113+
echo "failed=true" >> "$GITHUB_OUTPUT"
114+
echo "::error::WASM size regression: ${ACTUAL} bytes is ${PCT}% larger than baseline ${BASELINE} bytes (threshold: +${WASM_SIZE_THRESHOLD_PCT}%)."
115+
echo "::error::To accept this growth, update BASELINE in contracts/${BASELINE_FILE} and commit the change alongside your feature."
116+
exit 1
117+
fi
118+
119+
echo "failed=false" >> "$GITHUB_OUTPUT"
120+
121+
if [ "$DELTA" -lt 0 ]; then
122+
echo "::notice::WASM shrank by ${ABS_PCT}% (${ACTUAL} vs baseline ${BASELINE} bytes). Consider updating the baseline."
123+
else
124+
echo "::notice::WASM size OK: ${ACTUAL} bytes (+${PCT}% within ${WASM_SIZE_THRESHOLD_PCT}% threshold)."
125+
fi
126+
127+
- name: Post PR comment with size delta
128+
if: github.event_name == 'pull_request'
129+
uses: actions/github-script@v7
130+
with:
131+
script: |
132+
const baseline = '${{ steps.size_check.outputs.baseline }}';
133+
const actual = '${{ steps.size_check.outputs.actual }}';
134+
const delta = '${{ steps.size_check.outputs.delta }}';
135+
const pct = '${{ steps.size_check.outputs.pct }}';
136+
const failed = '${{ steps.size_check.outputs.failed }}' === 'true';
137+
const threshold = process.env.WASM_SIZE_THRESHOLD_PCT;
138+
139+
const sign = delta >= 0 ? '+' : '';
140+
const icon = failed ? '❌' : (delta < 0 ? '✅' : '✅');
141+
const status = failed
142+
? `**Regression detected** — size grew by ${sign}${delta} bytes (${sign}${pct}%), exceeding the ±${threshold}% threshold.`
143+
: `Size change is within the ±${threshold}% threshold.`;
144+
145+
const body = [
146+
`### ${icon} Contract WASM Size Check`,
147+
'',
148+
`| | Bytes |`,
149+
`|---|---|`,
150+
`| Baseline | ${Number(baseline).toLocaleString()} |`,
151+
`| This PR | ${Number(actual).toLocaleString()} |`,
152+
`| Delta | ${sign}${Number(delta).toLocaleString()} (${sign}${pct}%) |`,
153+
'',
154+
status,
155+
'',
156+
failed
157+
? `To accept this size increase, update \`BASELINE\` in \`contracts/wasm-size-baseline.txt\` to \`${actual}\` in this PR.`
158+
: '',
159+
].join('\n');
160+
161+
// Upsert: replace an existing comment if re-running.
162+
const { data: comments } = await github.rest.issues.listComments({
163+
owner: context.repo.owner,
164+
repo: context.repo.repo,
165+
issue_number: context.issue.number,
166+
});
167+
const existing = comments.find(c =>
168+
c.user.type === 'Bot' && c.body.includes('Contract WASM Size Check')
169+
);
170+
if (existing) {
171+
await github.rest.issues.updateComment({
172+
owner: context.repo.owner,
173+
repo: context.repo.repo,
174+
comment_id: existing.id,
175+
body,
176+
});
177+
} else {
178+
await github.rest.issues.createComment({
179+
owner: context.repo.owner,
180+
repo: context.repo.repo,
181+
issue_number: context.issue.number,
182+
body,
183+
});
184+
}

contracts/wasm-size-baseline.txt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Contract WASM size baseline (bytes)
2+
#
3+
# This file is the single source of truth for the WASM size regression check.
4+
# The CI workflow (.github/workflows/contract-wasm-size.yml) builds the
5+
# contract, measures the release WASM in bytes, and fails the build if the
6+
# new size exceeds this baseline by more than WASM_SIZE_THRESHOLD_PCT (default 10%).
7+
#
8+
# How to update this baseline
9+
# ──────────────────────────
10+
# When a legitimate feature warrants a larger WASM binary:
11+
#
12+
# 1. Build locally:
13+
# cd contracts
14+
# cargo build --target wasm32-unknown-unknown --release
15+
#
16+
# 2. Measure the new size:
17+
# wc -c < target/wasm32-unknown-unknown/release/portfolio_rebalancer.wasm
18+
#
19+
# 3. Replace the number on the BASELINE= line below with the new byte count.
20+
#
21+
# 4. Commit the updated baseline alongside the feature change so reviewers
22+
# can see the explicit size delta in the PR diff.
23+
#
24+
# The baseline below was recorded from the release build at the time this
25+
# check was introduced. If CI reports that the baseline is wrong on the first
26+
# run, update it with the value from step 2 above.
27+
#
28+
BASELINE=57344

0 commit comments

Comments
 (0)