Merge branch 'main' into feature/issue-382-docker-multistage #1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Contract CI | ||
| # Runs on every PR against main and every push to main. | ||
| # Fuzz job is gated to the nightly schedule only. | ||
| on: | ||
| push: | ||
| branches: [main] | ||
| pull_request: | ||
| branches: [main] | ||
| schedule: | ||
| # Nightly fuzz run at 02:00 UTC | ||
| - cron: '0 2 * * *' | ||
| # Cancel in-flight runs for the same ref on new push | ||
| concurrency: | ||
| group: contract-ci-${{ github.ref }} | ||
| cancel-in-progress: true | ||
| env: | ||
| CARGO_TERM_COLOR: always | ||
| RUSTFLAGS: "-D warnings" | ||
| jobs: | ||
| # ── 1. Lint ───────────────────────────────────────────────────────────── | ||
| lint: | ||
| name: Lint (fmt + clippy) | ||
| runs-on: ubuntu-latest | ||
| if: github.event_name != 'schedule' | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - name: Install Rust stable with fmt + clippy | ||
| uses: dtolnay/rust-toolchain@stable | ||
| with: | ||
| components: rustfmt, clippy | ||
| - name: Restore cargo cache | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: | | ||
| ~/.cargo/registry | ||
| ~/.cargo/git | ||
| target | ||
| key: ${{ runner.os }}-cargo-lint-${{ hashFiles('**/Cargo.lock') }} | ||
| restore-keys: ${{ runner.os }}-cargo-lint- | ||
| - name: cargo fmt --check | ||
| run: cargo fmt --all -- --check | ||
| - name: cargo clippy | ||
| run: cargo clippy --features testutils -- -D warnings | ||
| # ── 2. Test + Coverage ────────────────────────────────────────────────── | ||
| test: | ||
| name: Test & Coverage | ||
| runs-on: ubuntu-latest | ||
| if: github.event_name != 'schedule' | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - name: Install Rust stable with llvm-tools | ||
| uses: dtolnay/rust-toolchain@stable | ||
| with: | ||
| components: llvm-tools-preview | ||
| - name: Restore cargo cache | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: | | ||
| ~/.cargo/registry | ||
| ~/.cargo/git | ||
| target | ||
| key: ${{ runner.os }}-cargo-test-${{ hashFiles('**/Cargo.lock') }} | ||
| restore-keys: ${{ runner.os }}-cargo-test- | ||
| - name: Install cargo-llvm-cov | ||
| uses: taiki-e/install-action@cargo-llvm-cov | ||
| - name: Run tests with coverage | ||
| run: | | ||
| cargo llvm-cov --features testutils \ | ||
| --lcov --output-path lcov.info \ | ||
| --summary-only 2>&1 | tee coverage-summary.txt | ||
| - name: Generate HTML mutation report | ||
| if: always() | ||
| run: node scripts/mutation-report.js mutants.out mutants.out/mutation-report.html || true | ||
| - name: Upload mutation report | ||
| if: always() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: workload-governor-wasm-unoptimized | ||
| path: target/wasm32v1-none/release/workload_governor.wasm | ||
| retention-days: 7 | ||
| # ── 4. Optimize + Upload Artifact (every main merge) ──────────────────── | ||
| optimize: | ||
| name: Optimize WASM | ||
| runs-on: ubuntu-latest | ||
| needs: build-wasm | ||
| # Run on every push to main AND on every PR (so the artifact is available | ||
| # for review). Acceptance criterion: artifact uploaded on every main merge. | ||
| if: github.event_name != 'schedule' | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - name: Install Rust stable with wasm32v1-none target | ||
| uses: dtolnay/rust-toolchain@stable | ||
| with: | ||
| targets: wasm32v1-none | ||
| - name: Restore cargo cache | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: | | ||
| mutants-report.txt | ||
| mutants.out/mutation-report.html | ||
| mutants.out/outcomes.json | ||
| mutants.out/caught.txt | ||
| mutants.out/missed.txt | ||
| - name: Optimize WASM | ||
| run: | | ||
| stellar contract optimize \ | ||
| --wasm target/wasm32v1-none/release/workload_governor.wasm \ | ||
| --wasm-out target/wasm32v1-none/release/workload_governor.optimized.wasm | ||
| - name: Report WASM size | ||
| run: | | ||
| SIZE=$(wc -c < target/wasm32v1-none/release/workload_governor.optimized.wasm) | ||
| echo "Optimized WASM size: ${SIZE} bytes (limit 65536)" | ||
| echo "### 📦 WASM Size" >> "$GITHUB_STEP_SUMMARY" | ||
| echo "| Build | Size |" >> "$GITHUB_STEP_SUMMARY" | ||
| echo "| --- | --- |" >> "$GITHUB_STEP_SUMMARY" | ||
| echo "| Optimized | ${SIZE} bytes |" >> "$GITHUB_STEP_SUMMARY" | ||
| [ "$SIZE" -le 65536 ] || (echo "❌ WASM exceeds 64 KB limit" && exit 1) | ||
| - name: Upload optimized WASM artifact | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: workload-governor-wasm-optimized | ||
| path: target/wasm32v1-none/release/workload_governor.optimized.wasm | ||
| # Permanent retention on main merges; 7 days on PRs | ||
| retention-days: ${{ github.ref == 'refs/heads/main' && 90 || 7 }} | ||
| # ── 5. Fuzz (nightly schedule only, 10-minute run) ─────────────────────── | ||
| fuzz: | ||
| name: Fuzz (10 min) | ||
| runs-on: ubuntu-latest | ||
| if: github.event_name == 'schedule' | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - name: Install Rust nightly | ||
| uses: dtolnay/rust-toolchain@nightly | ||
| - name: Restore cargo cache | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: | | ||
| ~/.cargo/registry | ||
| ~/.cargo/git | ||
| target | ||
| key: ${{ runner.os }}-cargo-fuzz-${{ hashFiles('**/Cargo.lock') }} | ||
| restore-keys: ${{ runner.os }}-cargo-fuzz- | ||
| - name: Install cargo-fuzz | ||
| run: cargo install cargo-fuzz --locked | ||
| - name: Run fuzz targets (10 minutes each) | ||
| run: | | ||
| for target in $(cargo fuzz list 2>/dev/null || true); do | ||
| echo "▶ Fuzzing target: $target" | ||
| cargo fuzz run "$target" -- -max_total_time=600 || true | ||
| done | ||
| continue-on-error: true | ||
| - name: Upload fuzz artifacts | ||
| if: always() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: fuzz-artifacts-${{ github.run_id }} | ||
| path: fuzz/artifacts/ | ||
| if-no-files-found: ignore | ||
| retention-days: 30 | ||