Skip to content

Commit db6104f

Browse files
ci(audience): add CDN bundle size gate (SDK-115)
- Adds bundlebudget.json: 24 KB max / 20 KB warn gzipped for dist/cdn/imtbl-audience.global.js. Current bundle sits at 17.9 KB gz. - Adds .github/workflows/audience-bundle-size.yaml ported from pixel-bundle-size.yaml; runs on PRs touching packages/audience/sdk/** or packages/audience/core/**, posts a sticky comment with raw + gzipped delta vs main, fails if over budget. Refs SDK-115
1 parent 42565f8 commit db6104f

File tree

2 files changed

+157
-0
lines changed

2 files changed

+157
-0
lines changed
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
name: Audience Bundle Size
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- "**"
7+
paths:
8+
- "packages/audience/sdk/**"
9+
- "packages/audience/core/**"
10+
11+
permissions:
12+
pull-requests: write
13+
contents: read
14+
15+
env:
16+
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.TS_IMMUTABLE_SDK_NX_TOKEN }}
17+
18+
jobs:
19+
bundle-size:
20+
name: Audience Bundle Size Check
21+
runs-on: ubuntu-latest-4-cores
22+
steps:
23+
- name: Checkout code
24+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.1
25+
with:
26+
ref: ${{ github.event.pull_request.head.sha }}
27+
fetch-depth: 0
28+
29+
- name: Setup
30+
uses: ./.github/actions/setup
31+
32+
- name: Read budget config
33+
id: budget
34+
run: |
35+
BUDGET_FILE="packages/audience/sdk/bundlebudget.json"
36+
MAX_GZIP=$(jq '.budgets[0].maxSizeGzip' "$BUDGET_FILE")
37+
WARN_GZIP=$(jq '.budgets[0].warnSizeGzip' "$BUDGET_FILE")
38+
echo "max_gzip=$MAX_GZIP" >> "$GITHUB_OUTPUT"
39+
echo "warn_gzip=$WARN_GZIP" >> "$GITHUB_OUTPUT"
40+
41+
- name: Build audience SDK (PR)
42+
run: pnpm --filter @imtbl/audience... build
43+
44+
- name: Measure PR bundle size
45+
id: pr_size
46+
run: |
47+
BUNDLE="packages/audience/sdk/dist/cdn/imtbl-audience.global.js"
48+
RAW_SIZE=$(stat --format=%s "$BUNDLE")
49+
GZIP_SIZE=$(gzip -c "$BUNDLE" | wc -c)
50+
echo "raw=$RAW_SIZE" >> "$GITHUB_OUTPUT"
51+
echo "gzip=$GZIP_SIZE" >> "$GITHUB_OUTPUT"
52+
echo "PR bundle: raw=${RAW_SIZE} bytes, gzip=${GZIP_SIZE} bytes"
53+
54+
- name: Build audience SDK (base) and measure
55+
id: base_size
56+
run: |
57+
BASE_SHA="${{ github.event.pull_request.base.sha }}"
58+
59+
git checkout "$BASE_SHA"
60+
pnpm install --frozen-lockfile
61+
pnpm --filter @imtbl/audience... build 2>/dev/null || true
62+
63+
BUNDLE="packages/audience/sdk/dist/cdn/imtbl-audience.global.js"
64+
if [ -f "$BUNDLE" ]; then
65+
RAW_SIZE=$(stat --format=%s "$BUNDLE")
66+
GZIP_SIZE=$(gzip -c "$BUNDLE" | wc -c)
67+
else
68+
RAW_SIZE=0
69+
GZIP_SIZE=0
70+
fi
71+
echo "raw=$RAW_SIZE" >> "$GITHUB_OUTPUT"
72+
echo "gzip=$GZIP_SIZE" >> "$GITHUB_OUTPUT"
73+
echo "Base bundle: raw=${RAW_SIZE} bytes, gzip=${GZIP_SIZE} bytes"
74+
75+
git checkout "${{ github.event.pull_request.head.sha }}"
76+
pnpm install --frozen-lockfile
77+
78+
- name: Evaluate bundle size
79+
id: evaluate
80+
run: |
81+
PR_GZIP=${{ steps.pr_size.outputs.gzip }}
82+
PR_RAW=${{ steps.pr_size.outputs.raw }}
83+
BASE_GZIP=${{ steps.base_size.outputs.gzip }}
84+
BASE_RAW=${{ steps.base_size.outputs.raw }}
85+
MAX_GZIP=${{ steps.budget.outputs.max_gzip }}
86+
WARN_GZIP=${{ steps.budget.outputs.warn_gzip }}
87+
88+
DELTA_GZIP=$((PR_GZIP - BASE_GZIP))
89+
DELTA_RAW=$((PR_RAW - BASE_RAW))
90+
91+
if [ $DELTA_GZIP -gt 0 ]; then DELTA_GZIP_FMT="+${DELTA_GZIP}"; else DELTA_GZIP_FMT="${DELTA_GZIP}"; fi
92+
if [ $DELTA_RAW -gt 0 ]; then DELTA_RAW_FMT="+${DELTA_RAW}"; else DELTA_RAW_FMT="${DELTA_RAW}"; fi
93+
94+
STATUS="pass"
95+
STATUS_ICON="white_check_mark"
96+
if [ $PR_GZIP -gt $MAX_GZIP ]; then
97+
STATUS="fail"
98+
STATUS_ICON="x"
99+
elif [ $PR_GZIP -gt $WARN_GZIP ]; then
100+
STATUS="warn"
101+
STATUS_ICON="warning"
102+
fi
103+
104+
PR_GZIP_KB=$(echo "scale=2; $PR_GZIP / 1024" | bc)
105+
MAX_GZIP_KB=$(echo "scale=2; $MAX_GZIP / 1024" | bc)
106+
WARN_GZIP_KB=$(echo "scale=2; $WARN_GZIP / 1024" | bc)
107+
108+
{
109+
echo "## :${STATUS_ICON}: Audience Bundle Size — @imtbl/audience"
110+
echo ""
111+
echo "| Metric | Size | Delta vs main |"
112+
echo "|--------|------|---------------|"
113+
echo "| **Gzipped** | ${PR_GZIP} bytes (${PR_GZIP_KB} KB) | ${DELTA_GZIP_FMT} bytes |"
114+
echo "| Raw (minified) | ${PR_RAW} bytes | ${DELTA_RAW_FMT} bytes |"
115+
echo ""
116+
echo "**Budget:** ${MAX_GZIP_KB} KB gzipped (warn at ${WARN_GZIP_KB} KB)"
117+
} > /tmp/comment-body.md
118+
119+
if [ "$STATUS" = "warn" ]; then
120+
echo "" >> /tmp/comment-body.md
121+
echo "> :warning: **Approaching budget** — gzipped size exceeds ${WARN_GZIP_KB} KB warning threshold." >> /tmp/comment-body.md
122+
fi
123+
124+
if [ "$STATUS" = "fail" ]; then
125+
echo "" >> /tmp/comment-body.md
126+
echo "> :x: **Over budget** — gzipped size exceeds ${MAX_GZIP_KB} KB limit. Reduce bundle size before merging." >> /tmp/comment-body.md
127+
fi
128+
129+
echo "status=$STATUS" >> "$GITHUB_OUTPUT"
130+
131+
EOF_MARKER=$(head -c 20 /dev/urandom | base64 | tr -d '/+=' | head -c 20)
132+
{
133+
echo "comment<<${EOF_MARKER}"
134+
cat /tmp/comment-body.md
135+
echo "${EOF_MARKER}"
136+
} >> "$GITHUB_OUTPUT"
137+
138+
- name: Post PR comment
139+
uses: marocchino/sticky-pull-request-comment@67d0dec7b07ed060a405f9b2a64b8ab319fdd7db # pin@v2.9.2
140+
with:
141+
header: audience-bundle-size
142+
message: ${{ steps.evaluate.outputs.comment }}
143+
144+
- name: Fail if over budget
145+
if: steps.evaluate.outputs.status == 'fail'
146+
run: |
147+
echo "::error::Audience bundle gzipped size (${{ steps.pr_size.outputs.gzip }} bytes) exceeds budget (${{ steps.budget.outputs.max_gzip }} bytes)"
148+
exit 1
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"budgets": [
3+
{
4+
"file": "dist/cdn/imtbl-audience.global.js",
5+
"maxSizeGzip": 24576,
6+
"warnSizeGzip": 20480
7+
}
8+
]
9+
}

0 commit comments

Comments
 (0)