From c3861dee8572dbd5746d101b4638157016cd580f Mon Sep 17 00:00:00 2001 From: neumattock <152253273+newmattock@users.noreply.github.com> Date: Sat, 27 Jun 2026 07:06:36 -0700 Subject: [PATCH] ci: enforce contract wasm size budget --- .github/workflows/wasm-size.yml | 42 +++++++++++++++++++++++++++++++++ docs/wasm-budget.md | 41 ++++++++++++++++++++++++++++++++ scripts/check-wasm-size.sh | 25 +++++++++++++------- 3 files changed, 100 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/wasm-size.yml create mode 100644 docs/wasm-budget.md diff --git a/.github/workflows/wasm-size.yml b/.github/workflows/wasm-size.yml new file mode 100644 index 00000000..3693748c --- /dev/null +++ b/.github/workflows/wasm-size.yml @@ -0,0 +1,42 @@ +name: WASM Size Budget + +on: + pull_request: + branches: [main, master, develop] + push: + branches: [main, master, develop, "feature/**", "chore/**", "ci/**"] + workflow_dispatch: + +jobs: + wasm-size: + name: Contract WASM size check + runs-on: ubuntu-latest + env: + WASM_SIZE_LIMIT_BYTES: "102400" + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: wasm32-unknown-unknown + + - name: Cache cargo + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-wasm-size-${{ hashFiles('**/Cargo.lock', '**/Cargo.toml') }} + restore-keys: | + ${{ runner.os }}-cargo-wasm-size- + ${{ runner.os }}-cargo- + + - name: Check per-contract WASM size + shell: bash + run: | + chmod +x scripts/check-wasm-size.sh + ./scripts/check-wasm-size.sh diff --git a/docs/wasm-budget.md b/docs/wasm-budget.md new file mode 100644 index 00000000..0c65dfc2 --- /dev/null +++ b/docs/wasm-budget.md @@ -0,0 +1,41 @@ +# Contract WASM Size Budget + +Callora contract builds are gated by a per-contract WASM budget of 100 KB +(`102400` bytes). Keeping each publishable contract below this threshold limits +deployment cost, leaves room for emergency patches, and prevents accidental +dependency or feature growth from reaching production. + +## Local Check + +Run the same check used by CI from the repository root: + +```bash +./scripts/check-wasm-size.sh +``` + +The script discovers publishable contract crates under `contracts/*/Cargo.toml` +by looking for `crate-type = ["cdylib", ...]`, builds only those crates for +`wasm32-unknown-unknown` in release mode, then checks each generated `.wasm` +artifact independently. + +## Configuration + +The default limit is `102400` bytes. To test a tighter or temporary budget: + +```bash +WASM_SIZE_LIMIT_BYTES=90000 ./scripts/check-wasm-size.sh +``` + +For fixture-based checks where artifacts are already built, set +`SKIP_WASM_BUILD=1` and point `CARGO_TARGET_DIR` at the artifact tree: + +```bash +SKIP_WASM_BUILD=1 CARGO_TARGET_DIR=/tmp/callora-target ./scripts/check-wasm-size.sh +``` + +## CI Gate + +`.github/workflows/wasm-size.yml` runs on pull requests and relevant pushes. +Any missing artifact or artifact over the configured byte limit fails the job. +The main CI workflow also invokes the script during the release build job, so a +size regression blocks both focused and full contract checks. diff --git a/scripts/check-wasm-size.sh b/scripts/check-wasm-size.sh index 0fb6a7af..07fee35c 100755 --- a/scripts/check-wasm-size.sh +++ b/scripts/check-wasm-size.sh @@ -1,15 +1,20 @@ #!/usr/bin/env bash -# Check that all publishable contract WASM binaries stay under the 64 KiB Soroban limit. +# Check that all publishable contract WASM binaries stay under the Callora size budget. set -euo pipefail -MAX_SIZE_BYTES=$((64 * 1024)) +MAX_SIZE_BYTES="${WASM_SIZE_LIMIT_BYTES:-102400}" TARGET_DIR="${CARGO_TARGET_DIR:-target}/wasm32-unknown-unknown/release" contract_manifests=() contract_packages=() failed=0 +if ! [[ "$MAX_SIZE_BYTES" =~ ^[1-9][0-9]*$ ]]; then + echo "ERROR: WASM_SIZE_LIMIT_BYTES must be a positive integer, got: $MAX_SIZE_BYTES" + exit 1 +fi + if command -v cargo >/dev/null 2>&1; then CARGO_BIN=$(command -v cargo) elif command -v cargo.exe >/dev/null 2>&1; then @@ -79,7 +84,7 @@ check_wasm() { size_kib=$((size_bytes / 1024)) if [ "$size_bytes" -gt "$MAX_SIZE_BYTES" ]; then - echo "FAIL $crate: ${size_bytes} bytes (${size_kib} KiB) exceeds 65536-byte limit" + echo "FAIL $crate: ${size_bytes} bytes (${size_kib} KiB) exceeds ${MAX_SIZE_BYTES}-byte limit" failed=1 return fi @@ -96,19 +101,23 @@ cargo_args=(build --target wasm32-unknown-unknown --release) for crate in "${contract_packages[@]}"; do cargo_args+=(-p "$crate") done -"$CARGO_BIN" "${cargo_args[@]}" +if [ "${SKIP_WASM_BUILD:-0}" = "1" ]; then + echo "Skipping cargo build because SKIP_WASM_BUILD=1" +else + "$CARGO_BIN" "${cargo_args[@]}" +fi echo "" -echo "WASM size check (limit: 65536 bytes / 64 KiB)" -echo "---------------------------------------------" +echo "WASM size check (limit: ${MAX_SIZE_BYTES} bytes)" +echo "---------------------------------------" for crate in "${contract_packages[@]}"; do check_wasm "$crate" done echo "" if [ "$failed" -ne 0 ]; then - echo "One or more publishable contract WASM artifacts are missing or exceed the Soroban size limit." + echo "One or more publishable contract WASM artifacts are missing or exceed the configured size budget." exit 1 fi -echo "All publishable contract WASM artifacts are within the Soroban size limit." +echo "All publishable contract WASM artifacts are within the configured size budget."