Skip to content

Commit 237f7b9

Browse files
AbanoubGhadbanclaude
authored andcommitted
Add bundle size CI check using size-limit (#2149)
## Summary Add automated bundle size monitoring to CI using [size-limit](https://github.com/ai/size-limit). This helps prevent unexpected bundle size regressions by comparing PR bundle sizes against the base branch. ### How It Works 1. CI checks out the base branch and measures bundle sizes 2. CI checks out the PR branch and measures bundle sizes 3. Compares sizes - fails if any package increases by more than 0.5KB 4. Posts a size report comment on the PR ### What Gets Measured - **react-on-rails**: Raw, gzip, and brotli compressed sizes - **react-on-rails-pro**: Raw, gzip, and brotli compressed sizes - **react-on-rails-pro-node-renderer**: Raw, gzip, and brotli compressed sizes - **Webpack bundled imports**: Client-side bundle sizes when importing via webpack ### Local Testing ```sh # Check current bundle sizes pnpm run size # Compare your branch against master bin/compare-bundle-sizes ``` ### Bypassing the Check For intentional size increases (e.g., adding new features): ```sh bin/skip-bundle-size-check git add .bundle-size-skip-branch git commit -m "Skip bundle size check for intentional size increase" git push ``` ## Pull Request checklist - [x] Add/update test to cover these changes - N/A (CI workflow) - [x] Update documentation - Added to CONTRIBUTING.md - [x] Update CHANGELOG file <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Automated bundle size CI monitoring compares PR bundle sizes against the base branch and fails if any package exceeds a 0.5KB growth threshold. * Added bypass mechanism for intentional bundle size increases. * **Documentation** * Bundle size checking and comparison commands documented. * Local tooling available to measure and compare package sizes against a base branch. * **Chores** * Bundle size monitoring workflow set up for CI. <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 31e1e2e commit 237f7b9

File tree

8 files changed

+605
-1
lines changed

8 files changed

+605
-1
lines changed

.bundle-size-skip-branch

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# This file allows skipping the bundle size CI check for a specific branch.
2+
# When a branch name in this file matches the PR branch, the size check is skipped.
3+
#
4+
# Usage: Run `bin/skip-bundle-size-check` to set the current branch, then commit and push.
5+
#
6+
# This is useful when you have an intentional size increase that exceeds the 0.5KB threshold.

.github/workflows/bundle-size.yml

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
name: Bundle Size
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- 'packages/**'
7+
- 'package.json'
8+
- 'pnpm-lock.yaml'
9+
- '.size-limit.json'
10+
- '.github/workflows/bundle-size.yml'
11+
- '.bundle-size-skip-branch'
12+
13+
jobs:
14+
check-skip:
15+
runs-on: ubuntu-22.04
16+
outputs:
17+
skip: ${{ steps.skip-check.outputs.skip }}
18+
steps:
19+
- name: Checkout PR branch
20+
uses: actions/checkout@v4
21+
22+
- name: Check if branch should skip size check
23+
id: skip-check
24+
env:
25+
BRANCH: ${{ github.head_ref }}
26+
run: |
27+
SKIP_FILE=".bundle-size-skip-branch"
28+
SKIP_BRANCH=$(grep -v '^[[:space:]]*#' "$SKIP_FILE" 2>/dev/null | grep -v '^[[:space:]]*$' | tr -d '[:space:]' || echo "")
29+
if [ "$SKIP_BRANCH" = "$BRANCH" ]; then
30+
echo "skip=true" >> $GITHUB_OUTPUT
31+
echo "::notice::Branch '$BRANCH' is set to skip size check"
32+
else
33+
echo "skip=false" >> $GITHUB_OUTPUT
34+
fi
35+
36+
size:
37+
needs: check-skip
38+
if: needs.check-skip.outputs.skip != 'true'
39+
runs-on: ubuntu-22.04
40+
permissions:
41+
contents: read
42+
pull-requests: write
43+
env:
44+
CI_JOB_NUMBER: 1
45+
steps:
46+
- name: Setup Node
47+
uses: actions/setup-node@v4
48+
with:
49+
node-version: '22'
50+
51+
- name: Setup pnpm
52+
uses: pnpm/action-setup@v4
53+
with:
54+
version: 9
55+
56+
# 1. Get PR's size-limit config first (base branch may not have it)
57+
- name: Checkout PR branch for config
58+
uses: actions/checkout@v4
59+
60+
- name: Save size-limit config
61+
run: cp .size-limit.json /tmp/size-limit-config.json
62+
63+
# 2. Get base branch sizes
64+
- name: Checkout base branch
65+
uses: actions/checkout@v4
66+
with:
67+
ref: ${{ github.base_ref }}
68+
69+
- name: Copy size-limit config to base branch
70+
run: cp /tmp/size-limit-config.json .size-limit.json
71+
72+
- name: Install base dependencies
73+
run: pnpm install --frozen-lockfile
74+
75+
- name: Build base branch
76+
run: pnpm run build
77+
78+
- name: Verify build artifacts
79+
run: |
80+
missing=0
81+
for pkg in react-on-rails react-on-rails-pro react-on-rails-pro-node-renderer; do
82+
if ! ls packages/$pkg/lib/*.js >/dev/null 2>&1; then
83+
echo "::error::Missing build artifacts in packages/$pkg/lib/"
84+
missing=1
85+
fi
86+
done
87+
if [ $missing -eq 1 ]; then
88+
exit 1
89+
fi
90+
echo "All build artifacts verified"
91+
92+
- name: Measure base branch sizes
93+
run: |
94+
pnpm exec size-limit --json > /tmp/base-sizes.json
95+
echo "Base branch sizes:"
96+
cat /tmp/base-sizes.json
97+
98+
# 3. Checkout PR and set dynamic limits
99+
- name: Checkout PR branch
100+
uses: actions/checkout@v4
101+
102+
- name: Install PR dependencies
103+
run: pnpm install --frozen-lockfile
104+
105+
- name: Set dynamic limits (base + 0.5KB)
106+
run: node scripts/bundle-size.mjs set-limits --base /tmp/base-sizes.json
107+
108+
# 4. Run the action with dynamic limits
109+
- name: Check bundle size
110+
uses: andresz1/size-limit-action@v1
111+
with:
112+
github_token: ${{ secrets.GITHUB_TOKEN }}
113+
package_manager: pnpm
114+
build_script: build
115+
skip_step: install

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ Changes since the last non-beta release.
2525

2626
#### Added
2727

28+
- **Bundle Size CI Monitoring**: Added automated bundle size tracking to CI using size-limit. Compares PR bundle sizes against the base branch and fails if any package increases by more than 0.5KB. Includes local comparison tool (`bin/compare-bundle-sizes`) and bypass mechanism (`bin/skip-bundle-size-check`) for intentional size increases. [PR 2149](https://github.com/shakacode/react_on_rails/pull/2149) by [AbanoubGhadban](https://github.com/AbanoubGhadban).
29+
2830
- **Service Dependency Checking for bin/dev**: Added optional `.dev-services.yml` configuration to validate required external services (Redis, PostgreSQL, Elasticsearch, etc.) are running before `bin/dev` starts the development server. Provides clear error messages with start commands and install hints when services are missing. Zero impact if not configured - backwards compatible with all existing installations. [PR 2098](https://github.com/shakacode/react_on_rails/pull/2098) by [justin808](https://github.com/justin808).
2931

3032
#### Changed

CONTRIBUTING.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,69 @@ Run only ESLint:
305305
pnpm run lint
306306
```
307307

308+
### Bundle Size Checks
309+
310+
React on Rails monitors bundle sizes in CI to prevent unexpected size increases. The CI compares your PR's bundle sizes against the base branch and fails if any package increases by more than 0.5KB.
311+
312+
#### Running Locally
313+
314+
Check current bundle sizes:
315+
316+
```sh
317+
pnpm run size
318+
```
319+
320+
Get JSON output for programmatic use:
321+
322+
```sh
323+
pnpm run size:json
324+
```
325+
326+
Compare your branch against the base branch:
327+
328+
```sh
329+
bin/compare-bundle-sizes
330+
```
331+
332+
This script automatically:
333+
334+
1. Stashes any uncommitted changes
335+
2. Checks out and builds the base branch (default: `master`)
336+
3. Checks out and builds your current branch
337+
4. Compares the sizes and shows a detailed report
338+
339+
Options:
340+
341+
```sh
342+
bin/compare-bundle-sizes main # Compare against 'main' instead of 'master'
343+
bin/compare-bundle-sizes --hierarchical # Group results by package
344+
```
345+
346+
#### Bypassing the Check
347+
348+
If your PR intentionally increases bundle size (e.g., adding a new feature), you can skip the bundle size check:
349+
350+
```sh
351+
# Run from your PR branch
352+
bin/skip-bundle-size-check
353+
git add .bundle-size-skip-branch
354+
git commit -m "Skip bundle size check for intentional size increase"
355+
git push
356+
```
357+
358+
This sets your branch to skip the size check. The skip only applies to the specific branch name written to `.bundle-size-skip-branch`.
359+
360+
**Important**: Only skip the check when the size increase is justified. Document why the increase is acceptable in your PR description.
361+
362+
#### What Gets Measured
363+
364+
The CI measures sizes for:
365+
366+
- **react-on-rails**: Raw, gzip, and brotli compressed sizes
367+
- **react-on-rails-pro**: Raw, gzip, and brotli compressed sizes
368+
- **react-on-rails-pro-node-renderer**: Raw, gzip, and brotli compressed sizes
369+
- **Webpack bundled imports**: Client-side bundle sizes when importing via webpack
370+
308371
### Starting the Dummy App
309372

310373
To run the dummy app, it's **CRITICAL** to not just run `rails s`. You have to run `foreman start` with one of the Procfiles. If you don't do this, then `webpack` will not generate a new bundle, and you will be seriously confused when you change JavaScript and the app does not change. If you change the Webpack configs, then you need to restart Foreman. If you change the JS code for react-on-rails, you need to run `pnpm run build` in the project root.

bin/compare-bundle-sizes

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Compare bundle sizes between current branch and a base branch
4+
#
5+
# Usage:
6+
# bin/compare-bundle-sizes [base-branch]
7+
#
8+
# Arguments:
9+
# base-branch The branch to compare against (default: master)
10+
#
11+
# Examples:
12+
# bin/compare-bundle-sizes # Compare against master
13+
# bin/compare-bundle-sizes develop # Compare against develop
14+
# bin/compare-bundle-sizes feature/some-branch
15+
16+
set -e
17+
18+
BASE_BRANCH="${1:-master}"
19+
CURRENT_BRANCH=$(git branch --show-current)
20+
STASHED=false
21+
22+
# Colors for output
23+
YELLOW='\033[1;33m'
24+
BLUE='\033[0;34m'
25+
NC='\033[0m' # No Color
26+
27+
cleanup() {
28+
echo -e "\n${BLUE}Cleaning up...${NC}"
29+
git checkout "$CURRENT_BRANCH" --quiet 2>/dev/null || true
30+
if [ "$STASHED" = true ]; then
31+
git stash pop --quiet 2>/dev/null || true
32+
fi
33+
}
34+
35+
trap cleanup EXIT
36+
37+
echo -e "${BLUE}📦 Bundle Size Comparison${NC}"
38+
echo -e " Current branch: ${YELLOW}$CURRENT_BRANCH${NC}"
39+
echo -e " Base branch: ${YELLOW}$BASE_BRANCH${NC}"
40+
echo ""
41+
42+
# Check for uncommitted changes
43+
if ! git diff --quiet || ! git diff --cached --quiet; then
44+
echo -e "${YELLOW}Stashing uncommitted changes...${NC}"
45+
git stash push -m "compare-bundle-sizes temp stash" --quiet
46+
STASHED=true
47+
fi
48+
49+
# Get base branch sizes
50+
echo -e "${BLUE}Building base branch ($BASE_BRANCH)...${NC}"
51+
git fetch origin "$BASE_BRANCH" --quiet 2>/dev/null || true
52+
git checkout "$BASE_BRANCH" --quiet 2>/dev/null || git checkout "origin/$BASE_BRANCH" --quiet
53+
pnpm install --frozen-lockfile 2>&1 | grep -v "^$" | head -5 || true
54+
pnpm run build 2>&1 | grep -v "^$" | tail -3 || true
55+
56+
echo -e "${BLUE}Measuring base branch sizes...${NC}"
57+
pnpm exec size-limit --json > /tmp/base-sizes.json
58+
59+
# Get current branch sizes
60+
echo -e "${BLUE}Building current branch ($CURRENT_BRANCH)...${NC}"
61+
git checkout "$CURRENT_BRANCH" --quiet
62+
pnpm install --frozen-lockfile 2>&1 | grep -v "^$" | head -5 || true
63+
pnpm run build 2>&1 | grep -v "^$" | tail -3 || true
64+
65+
echo -e "${BLUE}Measuring current branch sizes...${NC}"
66+
pnpm exec size-limit --json > /tmp/current-sizes.json
67+
68+
# Compare sizes using the bundle-size script
69+
node scripts/bundle-size.mjs compare --base /tmp/base-sizes.json --current /tmp/current-sizes.json

bin/skip-bundle-size-check

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Skip bundle size check for current branch
4+
#
5+
# Usage:
6+
# bin/skip-bundle-size-check
7+
#
8+
9+
set -e
10+
11+
SKIP_FILE=".bundle-size-skip-branch"
12+
BRANCH=$(git branch --show-current)
13+
14+
if [ -z "$BRANCH" ]; then
15+
echo "Error: Not on a branch (detached HEAD?)"
16+
exit 1
17+
fi
18+
19+
# Write comment header and branch name
20+
cat > "$SKIP_FILE" << EOF
21+
# This file allows skipping the bundle size CI check for a specific branch.
22+
# When a branch name in this file matches the PR branch, the size check is skipped.
23+
#
24+
# Usage: Run \`bin/skip-bundle-size-check\` to set the current branch, then commit and push.
25+
#
26+
# This is useful when you have an intentional size increase that exceeds the 0.5KB threshold.
27+
$BRANCH
28+
EOF
29+
echo "Set '$BRANCH' as the branch to skip bundle size check"
30+
echo ""
31+
echo "Next steps:"
32+
echo " git add $SKIP_FILE"
33+
echo " git commit -m 'Skip bundle size check for $BRANCH'"
34+
echo " git push"

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
"@types/react-dom": "^18.3.5",
3535
"@types/turbolinks": "^5.2.2",
3636
"create-react-class": "^15.7.0",
37-
"globals": "^16.2.0",
3837
"eslint": "^9.24.0",
3938
"eslint-config-prettier": "^10.1.1",
4039
"eslint-config-shakacode": "^19.0.0",
@@ -47,6 +46,7 @@
4746
"eslint-plugin-react": "^7.37.5",
4847
"eslint-plugin-react-hooks": "^5.2.0",
4948
"eslint-plugin-testing-library": "^7.1.1",
49+
"globals": "^16.2.0",
5050
"jest": "^29.7.0",
5151
"jest-environment-jsdom": "^29.7.0",
5252
"jest-fetch-mock": "^3.0.3",

0 commit comments

Comments
 (0)