Skip to content

build(deps): bump the npm-packages group across 1 directory with 5 updates #164

build(deps): bump the npm-packages group across 1 directory with 5 updates

build(deps): bump the npm-packages group across 1 directory with 5 updates #164

Workflow file for this run

name: Bundle Size
on:
push:
branches:
- main
pull_request:
paths-ignore:
- "**.md"
permissions:
contents: read
pull-requests: write
actions: read
jobs:
bundle-size:
name: Continuous Integration
runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event.sender.type != 'Bot'
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: "1.3.6"
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Build
run: bunx --bun turbo run build:prod
- name: Remove source maps
run: find apps/*/dist -name '*.map' -delete
# Flag mode-specific bundles that exceed budget. If
# `apps/protocol/dist/assets/index-*.js` exceeds 600 KB raw, it
# almost certainly means a static smoldot or chain-specs import
# crept back into the protocol iframe's cold-start chunk — which
# silently inflates RPC-mode users by ~3 MB. Overages emit a
# workflow warning and surface in the PR comment, but do NOT fail
# the job — the signal belongs in review, not the required-checks
# gate. The thresholds below have headroom above the current
# sizes; tighten once they've soaked.
- name: Check mode-specific bundle budgets
run: |
: > /tmp/budget-warnings.md
check() {
local label=$1 glob=$2 max=$3
local file
file=$(ls $glob 2>/dev/null | head -1)
if [[ -z "$file" ]]; then
echo "::warning::${label}: no file matched $glob"
echo "- \`${label}\`: no file matched \`${glob}\`" >> /tmp/budget-warnings.md
return
fi
local size
size=$(stat --format=%s "$file")
if (( size > max )); then
echo "::warning::${label} exceeded budget: ${size} B > ${max} B (${file})"
local fmt_size fmt_max fmt_over
fmt_size=$(awk "BEGIN {printf \"%.1f KB\", $size/1024}")
fmt_max=$(awk "BEGIN {printf \"%.1f KB\", $max/1024}")
fmt_over=$(awk "BEGIN {printf \"%.1f KB\", ($size-$max)/1024}")
echo "- \`${label}\` exceeded budget: **${fmt_size}** > ${fmt_max} (+${fmt_over}) — \`${file#apps/}\`" >> /tmp/budget-warnings.md
else
echo "${label}: ${size} B (budget ${max} B) — ok"
fi
}
# Protocol iframe main entry (RPC mode cold start) — must NOT
# bundle smoldot / chain specs.
check "protocol-iframe-entry" \
"apps/protocol/dist/assets/index-*.js" \
$((600 * 1024))
# Host shell main entry — must not regress more than ~20% above
# its current ~460 KB raw size.
check "host-entry" \
"apps/host/dist/assets/index-*.js" \
$((600 * 1024))
# ── PR: download baseline from main ────────────────────────
- name: Download baseline from main
if: github.event_name == 'pull_request'
id: baseline
uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 # v21
with:
name: bundle-size-baseline
workflow: bundle-size.yml
branch: main
path: /tmp/baseline
if_no_artifact_found: warn
# ── PR: build comparison report and comment ────────────────
- name: Generate bundle size report
if: github.event_name == 'pull_request'
run: |
SIZE_THRESHOLD=512000 # 500 KB
BASELINE_FILE="/tmp/baseline/bundle-baseline.json"
if [[ -f "$BASELINE_FILE" ]]; then
HAS_BASELINE=true
else
HAS_BASELINE=false
echo "::warning::No baseline found — sizes shown without comparison"
fi
# Strip content hashes: "fetch-CMRV9u5T.js" → "fetch.js"
strip_hash() {
echo "$1" | sed -E 's/-[A-Za-z0-9_-]{8}\./\./'
}
fmt() {
local bytes=$1
if (( bytes >= 1048576 )); then
awk "BEGIN {printf \"%.2f MB\", $bytes/1048576}"
elif (( bytes >= 1024 )); then
awk "BEGIN {printf \"%.1f KB\", $bytes/1024}"
else
echo "${bytes} B"
fi
}
# Format a diff: returns "" if no baseline or unchanged, else " (+1.2 KB)" / " (-3.4 KB)"
fmt_diff() {
local current=$1 base=$2
if [[ -z "$base" ]] || (( base == 0 )); then
echo ""
return
fi
local diff=$((current - base))
if (( diff == 0 )); then
echo ""
return
fi
local abs_diff=$diff
(( abs_diff < 0 )) && abs_diff=$((-diff))
local formatted
formatted=$(fmt $abs_diff)
if (( diff > 0 )); then
echo " (+${formatted})"
else
echo " (-${formatted})"
fi
}
# Read a value from the baseline JSON
baseline_val() {
local name=$1 field=$2
if $HAS_BASELINE; then
python3 -c "
import json, sys
d = json.load(open('$BASELINE_FILE'))
e = d.get('$name', {})
print(e.get('$field', ''))" 2>/dev/null
fi
}
rows=""
large_rows=""
total_raw=0
total_br=0
total_gz=0
base_total_raw=0
base_total_br=0
base_total_gz=0
for app_dist in apps/host/dist apps/sandbox/dist; do
[ -d "$app_dist" ] || continue
app_name=$(basename "$(dirname "$app_dist")")
while IFS= read -r file; do
rel="${app_name}/${file#${app_dist}/}"
name=$(strip_hash "$rel")
raw=$(stat --format=%s "$file")
total_raw=$((total_raw + raw))
if [[ -f "${file}.br" ]]; then
br=$(stat --format=%s "${file}.br")
else
br=$raw
fi
total_br=$((total_br + br))
if [[ -f "${file}.gz" ]]; then
gz=$(stat --format=%s "${file}.gz")
else
gz=$raw
fi
total_gz=$((total_gz + gz))
# Look up baseline
base_raw=$(baseline_val "$name" "raw")
base_br=$(baseline_val "$name" "br")
base_gz=$(baseline_val "$name" "gz")
if [[ -n "$base_raw" ]]; then
base_total_raw=$((base_total_raw + base_raw))
base_total_br=$((base_total_br + base_br))
base_total_gz=$((base_total_gz + base_gz))
fi
raw_diff=$(fmt_diff $raw "$base_raw")
br_diff=$(fmt_diff $br "$base_br")
gz_diff=$(fmt_diff $gz "$base_gz")
raw_fmt="$(fmt $raw)${raw_diff}"
br_fmt="$(fmt $br)${br_diff}"
gz_fmt="$(fmt $gz)${gz_diff}"
row="| \`${name}\` | ${raw_fmt} | ${br_fmt} | ${gz_fmt} |"
rows="${rows}${row}"$'\n'
if (( raw >= SIZE_THRESHOLD )); then
large_rows="${large_rows}${row}"$'\n'
fi
done < <(find "$app_dist" -type f ! -name '*.br' ! -name '*.gz' | sort)
done
# Format totals with diffs
total_raw_diff=""
total_br_diff=""
total_gz_diff=""
if $HAS_BASELINE && (( base_total_raw > 0 )); then
total_raw_diff=$(fmt_diff $total_raw $base_total_raw)
total_br_diff=$(fmt_diff $total_br $base_total_br)
total_gz_diff=$(fmt_diff $total_gz $base_total_gz)
fi
total_raw_fmt="$(fmt $total_raw)${total_raw_diff}"
total_br_fmt="$(fmt $total_br)${total_br_diff}"
total_gz_fmt="$(fmt $total_gz)${total_gz_diff}"
br_pct=$(awk "BEGIN {printf \"%.0f\", (1 - $total_br/$total_raw) * 100}")
{
echo "## Bundle Size Report"
echo ""
if [[ -s /tmp/budget-warnings.md ]]; then
echo "> [!WARNING]"
echo "> **Bundle budget exceeded** — not a blocker, but please confirm the regression is intentional."
echo ">"
while IFS= read -r line; do echo "> ${line}"; done < /tmp/budget-warnings.md
echo ""
fi
if ! $HAS_BASELINE; then
echo "> No baseline found on \`main\`. Merge to \`main\` to establish a baseline."
echo ""
fi
# Show only chunks over 500 KB + total by default
if [[ -n "$large_rows" ]]; then
echo "**Chunks over 500 KB:**"
echo ""
echo "| File | Raw | Brotli | Gzip |"
echo "|------|----:|-------:|-----:|"
echo -n "$large_rows"
echo "| **Total** | **${total_raw_fmt}** | **${total_br_fmt}** (-${br_pct}%) | **${total_gz_fmt}** |"
else
echo "No chunks over 500 KB."
echo ""
echo "**Total:** ${total_raw_fmt} raw / ${total_br_fmt} brotli (-${br_pct}%) / ${total_gz_fmt} gzip"
fi
# Full file list in expandable section
echo ""
echo "<details>"
echo "<summary>All files</summary>"
echo ""
echo "| File | Raw | Brotli | Gzip |"
echo "|------|----:|-------:|-----:|"
echo -n "$rows"
echo "| **Total** | **${total_raw_fmt}** | **${total_br_fmt}** (-${br_pct}%) | **${total_gz_fmt}** |"
echo ""
echo "</details>"
echo ""
SHORT_SHA="${GITHUB_SHA:0:7}"
echo "<sub>Commit: [${SHORT_SHA}](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/commit/${GITHUB_SHA})</sub>"
} > /tmp/comment-body.md
- name: Post or update PR comment
if: github.event_name == 'pull_request'
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const fs = require('fs');
const body = fs.readFileSync('/tmp/comment-body.md', 'utf8');
const marker = '## Bundle Size Report';
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const existing = comments.find(c => c.body?.startsWith(marker));
if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body,
});
}
# ── main: generate and upload baseline sizes ───────────────
- name: Generate baseline sizes
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
run: |
# Strip content hashes from filenames: "fetch-CMRV9u5T.js" → "fetch.js"
strip_hash() {
echo "$1" | sed -E 's/-[A-Za-z0-9_-]{8}\./\./'
}
echo "{" > /tmp/bundle-baseline.json
first=true
for app_dist in apps/host/dist apps/sandbox/dist; do
[ -d "$app_dist" ] || continue
app_name=$(basename "$(dirname "$app_dist")")
while IFS= read -r file; do
rel="${app_name}/${file#${app_dist}/}"
name=$(strip_hash "$rel")
raw=$(stat --format=%s "$file")
br=$raw
[[ -f "${file}.br" ]] && br=$(stat --format=%s "${file}.br")
gz=$raw
[[ -f "${file}.gz" ]] && gz=$(stat --format=%s "${file}.gz")
$first || echo "," >> /tmp/bundle-baseline.json
first=false
printf ' "%s": {"raw": %d, "br": %d, "gz": %d}' "$name" "$raw" "$br" "$gz" >> /tmp/bundle-baseline.json
done < <(find "$app_dist" -type f ! -name '*.br' ! -name '*.gz' | sort)
done
echo "" >> /tmp/bundle-baseline.json
echo "}" >> /tmp/bundle-baseline.json
- name: Upload baseline artifact
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: bundle-size-baseline
path: /tmp/bundle-baseline.json
retention-days: 90
overwrite: true