Merge pull request #833 from uchechithelmaonye-cpu/security/enhance-s… #621
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: Comprehensive Test Suite | |
| on: | |
| push: | |
| branches: [main, develop] | |
| pull_request: | |
| branches: [main, develop] | |
| env: | |
| CARGO_TERM_COLOR: always | |
| RUST_BACKTRACE: 1 | |
| # Enforced by cargo llvm-cov --fail-under-lines (line %, package-scoped runs). | |
| COVERAGE_FAIL_LINES_PREDICT_IQ: "80" | |
| COVERAGE_FAIL_LINES_PREDICTIQ_API: "17" | |
| # Soroban contract WASM size limit (64 KB internal budget target) | |
| # Soroban's actual limit is larger, but we enforce this stricter threshold | |
| # to ensure contract remains performant and deployable across all networks. | |
| WASM_SIZE_LIMIT_BYTES: "65536" | |
| jobs: | |
| prometheus-rules-test: | |
| name: Prometheus SLO Rules Testing | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Prometheus tools | |
| run: | | |
| wget https://github.com/prometheus/prometheus/releases/download/v2.45.0/prometheus-2.45.0.linux-amd64.tar.gz | |
| tar xzf prometheus-2.45.0.linux-amd64.tar.gz | |
| sudo mv prometheus-2.45.0.linux-amd64/promtool /usr/local/bin/ | |
| - name: Check Prometheus rules syntax | |
| run: promtool check rules performance/config/prometheus-slo-rules.yml | |
| - name: Check alert rules syntax | |
| run: promtool check rules performance/config/alerts.yaml | |
| - name: Run Prometheus rules unit tests | |
| run: promtool test rules performance/config/prometheus-slo-rules.test.yml | |
| unit-tests: | |
| name: Unit Tests | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: actions-rs/toolchain@v1 | |
| with: | |
| profile: minimal | |
| toolchain: stable | |
| override: true | |
| - name: Cache cargo registry | |
| uses: actions/cache@v5 | |
| with: | |
| path: ~/.cargo/registry | |
| key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} | |
| - name: Cache cargo index | |
| uses: actions/cache@v5 | |
| with: | |
| path: ~/.cargo/git | |
| key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} | |
| - name: Cache cargo build | |
| uses: actions/cache@v5 | |
| with: | |
| path: target | |
| key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} | |
| - name: Install Soroban CLI | |
| run: | | |
| cargo install --locked soroban-cli --features opt | |
| - name: Run unit tests | |
| run: cargo test --lib --workspace | |
| working-directory: contracts/predict-iq | |
| - name: Run module tests | |
| run: cargo test --lib --features testutils | |
| working-directory: contracts/predict-iq | |
| integration-tests: | |
| name: Integration Tests | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: actions-rs/toolchain@v1 | |
| with: | |
| profile: minimal | |
| toolchain: stable | |
| override: true | |
| - name: Cache dependencies | |
| uses: actions/cache@v5 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} | |
| - name: Install Soroban CLI | |
| run: cargo install --locked soroban-cli --features opt | |
| - name: Run integration tests | |
| run: cargo test --test '*' --workspace | |
| working-directory: contracts/predict-iq | |
| api-rate-limit-tests: | |
| name: API Rate Limiting Integration Tests | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: actions-rs/toolchain@v1 | |
| with: | |
| profile: minimal | |
| toolchain: stable | |
| override: true | |
| - name: Cache dependencies | |
| uses: actions/cache@v5 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: ${{ runner.os }}-cargo-api-${{ hashFiles('services/api/Cargo.lock') }} | |
| - name: Run rate limiting integration tests | |
| run: cargo test --test rate_limiting_tests | |
| working-directory: services/api | |
| gas-benchmarks: | |
| name: Gas Benchmarks | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: actions-rs/toolchain@v1 | |
| with: | |
| profile: minimal | |
| toolchain: stable | |
| override: true | |
| - name: Cache dependencies | |
| uses: actions/cache@v5 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} | |
| - name: Install Soroban CLI | |
| run: cargo install --locked soroban-cli --features opt | |
| - name: Run gas benchmarks | |
| run: cargo test --benches --features testutils 2>&1 | tee benchmark-output.txt | |
| working-directory: contracts/predict-iq | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: "18" | |
| - name: Compare gas benchmarks against baseline | |
| working-directory: contracts/predict-iq | |
| run: | | |
| node scripts/compare-gas-benchmarks.js --branch main --threshold 10 | |
| - name: Upload benchmark report | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: gas-benchmark-report | |
| path: contracts/predict-iq/target/gas-benchmark-report.md | |
| retention-days: 30 | |
| - name: Comment PR with benchmark results | |
| if: github.event_name == 'pull_request' && always() | |
| uses: actions/github-script@v9 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const path = 'contracts/predict-iq/target/gas-benchmark-report.md'; | |
| if (fs.existsSync(path)) { | |
| const report = fs.readFileSync(path, 'utf8'); | |
| github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: report | |
| }); | |
| } | |
| - name: Fail if gas regression detected | |
| if: failure() && github.event_name == 'pull_request' | |
| run: | | |
| echo "❌ Gas regression detected. Check the PR comment for details." | |
| exit 1 | |
| save-gas-baseline: | |
| name: Save Gas Benchmark Baseline | |
| runs-on: ubuntu-latest | |
| needs: [gas-benchmarks] | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: actions-rs/toolchain@v1 | |
| with: | |
| profile: minimal | |
| toolchain: stable | |
| override: true | |
| - name: Cache dependencies | |
| uses: actions/cache@v5 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} | |
| - name: Install Soroban CLI | |
| run: cargo install --locked soroban-cli --features opt | |
| - name: Run gas benchmarks | |
| run: cargo test --benches --features testutils | |
| working-directory: contracts/predict-iq | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: "18" | |
| - name: Save baseline for main branch | |
| working-directory: contracts/predict-iq | |
| run: | | |
| node scripts/compare-gas-benchmarks.js --save-baseline --branch main | |
| - name: Create Pull Request with baseline updates | |
| uses: peter-evans/create-pull-request@v6 | |
| with: | |
| commit-message: "chore: update gas benchmark baseline for main branch" | |
| title: "chore: update gas benchmark baseline" | |
| body: | | |
| ## Automated Gas Benchmark Baseline Update | |
| This PR updates the gas benchmark baseline after changes to main branch. | |
| **Changes:** | |
| - Updated `.gas-benchmarks/` files with latest benchmark results | |
| **Note:** This is an automated PR. Please review the baseline changes before merging. | |
| branch: chore/gas-baseline-${{ github.run_id }} | |
| delete-branch: true | |
| labels: | | |
| ci/cd | |
| automated | |
| backend-coverage: | |
| name: Backend coverage (contracts + API) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: actions-rs/toolchain@v1 | |
| with: | |
| profile: minimal | |
| toolchain: stable | |
| override: true | |
| components: llvm-tools-preview | |
| - name: Install cargo-llvm-cov | |
| run: cargo install cargo-llvm-cov --locked | |
| - name: Install Soroban CLI | |
| run: cargo install --locked soroban-cli --features opt | |
| - name: Prepare coverage output directory | |
| run: mkdir -p coverage-reports | |
| - name: Coverage gate — predict-iq (Soroban contract) | |
| env: | |
| CARGO_LLVM_COV_TARGET_DIR: ${{ github.workspace }}/llvm-cov-target/predict-iq | |
| run: > | |
| cargo llvm-cov -p predict-iq --all-features | |
| --fail-under-lines ${COVERAGE_FAIL_LINES_PREDICT_IQ} | |
| --lcov --output-path coverage-reports/predict-iq.lcov | |
| --html --output-dir coverage-reports/predict-iq-html | |
| - name: Coverage gate — predictiq-api (Rust API service) | |
| env: | |
| CARGO_LLVM_COV_TARGET_DIR: ${{ github.workspace }}/llvm-cov-target/predictiq-api | |
| working-directory: services/api | |
| run: > | |
| cargo llvm-cov | |
| --fail-under-lines ${COVERAGE_FAIL_LINES_PREDICTIQ_API} | |
| --lcov --output-path ../../coverage-reports/predictiq-api.lcov | |
| --html --output-dir ../../coverage-reports/predictiq-api-html | |
| - name: Upload HTML + LCOV artifacts | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: backend-coverage | |
| path: | | |
| coverage-reports/*.lcov | |
| coverage-reports/predict-iq-html | |
| coverage-reports/predictiq-api-html | |
| if-no-files-found: error | |
| - name: Upload predict-iq LCOV to Codecov | |
| uses: codecov/codecov-action@v4 | |
| with: | |
| files: coverage-reports/predict-iq.lcov | |
| flags: predict-iq | |
| fail_ci_if_error: true | |
| verbose: true | |
| - name: Upload predictiq-api LCOV to Codecov | |
| uses: codecov/codecov-action@v4 | |
| with: | |
| files: coverage-reports/predictiq-api.lcov | |
| flags: predictiq-api | |
| fail_ci_if_error: true | |
| verbose: true | |
| security-audit: | |
| name: Security Audit | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: actions-rs/toolchain@v1 | |
| with: | |
| profile: minimal | |
| toolchain: stable | |
| override: true | |
| - name: Install cargo-audit | |
| run: cargo install cargo-audit | |
| - name: Run security audit | |
| run: cargo audit | |
| working-directory: contracts/predict-iq | |
| sast-scanning: | |
| name: SAST Security Scanning | |
| runs-on: ubuntu-latest | |
| permissions: | |
| security-events: write | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Run Semgrep SAST | |
| uses: returntocorp/semgrep-action@v1 | |
| with: | |
| config: >- | |
| p/security-audit | |
| p/rust | |
| p/typescript | |
| p/javascript | |
| generateSarif: true | |
| - name: Upload Semgrep SARIF | |
| uses: github/codeql-action/upload-sarif@v4 | |
| if: always() | |
| with: | |
| sarif_file: semgrep.sarif | |
| - name: Fail on critical findings | |
| run: | | |
| if [ -f semgrep.sarif ]; then | |
| critical_count=$(jq '[.runs[].results[] | select(.level == "error")] | length' semgrep.sarif) | |
| if [ "$critical_count" -gt 0 ]; then | |
| echo "Found $critical_count critical security findings" | |
| exit 1 | |
| fi | |
| fi | |
| secrets-scanning: | |
| name: Secrets Scanning | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Run Gitleaks | |
| uses: gitleaks/gitleaks-action@v2 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }} | |
| - name: Run TruffleHog | |
| uses: trufflesecurity/trufflehog@main | |
| with: | |
| path: ./ | |
| base: ${{ github.event.repository.default_branch }} | |
| head: HEAD | |
| extra_args: --only-verified | |
| dependency-scanning: | |
| name: Dependency Vulnerability Scanning | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Run Trivy vulnerability scanner | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| scan-type: "fs" | |
| scan-ref: "." | |
| format: "sarif" | |
| output: "trivy-results.sarif" | |
| severity: "CRITICAL,HIGH" | |
| exit-code: "1" | |
| - name: Upload Trivy SARIF | |
| uses: github/codeql-action/upload-sarif@v4 | |
| if: always() | |
| with: | |
| sarif_file: trivy-results.sarif | |
| - name: Scan Rust dependencies | |
| run: | | |
| cargo install cargo-audit | |
| cargo audit --deny warnings | |
| working-directory: contracts/predict-iq | |
| - name: Scan Node.js dependencies | |
| run: | | |
| npm audit --audit-level=high | |
| working-directory: frontend | |
| continue-on-error: true | |
| container-scanning: | |
| name: Container Image Scanning | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Build API container image | |
| run: | | |
| docker build -t predictiq-api:${{ github.sha }} -f services/api/Dockerfile . | |
| continue-on-error: true | |
| - name: Scan API container with Trivy | |
| uses: aquasecurity/trivy-action@master | |
| if: success() | |
| with: | |
| image-ref: "predictiq-api:${{ github.sha }}" | |
| format: "sarif" | |
| output: "trivy-container.sarif" | |
| severity: "CRITICAL,HIGH" | |
| exit-code: "1" | |
| - name: Upload container scan SARIF | |
| uses: github/codeql-action/upload-sarif@v4 | |
| if: always() | |
| with: | |
| sarif_file: trivy-container.sarif | |
| codeql-analysis: | |
| name: CodeQL Analysis | |
| runs-on: ubuntu-latest | |
| permissions: | |
| security-events: write | |
| contents: read | |
| strategy: | |
| matrix: | |
| language: ["javascript", "typescript", "rust"] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust (for Rust analysis) | |
| if: matrix.language == 'rust' | |
| uses: actions-rs/toolchain@v1 | |
| with: | |
| profile: minimal | |
| toolchain: stable | |
| override: true | |
| - name: Initialize CodeQL | |
| uses: github/codeql-action/init@v4 | |
| with: | |
| languages: ${{ matrix.language }} | |
| queries: security-extended | |
| - name: Autobuild | |
| uses: github/codeql-action/autobuild@v4 | |
| - name: Perform CodeQL Analysis | |
| uses: github/codeql-action/analyze@v4 | |
| with: | |
| category: "/language:${{ matrix.language }}" | |
| clippy: | |
| name: Clippy Lints | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: actions-rs/toolchain@v1 | |
| with: | |
| profile: minimal | |
| toolchain: stable | |
| override: true | |
| components: clippy | |
| - name: Run Clippy | |
| run: cargo clippy --all-targets --all-features -- -D warnings | |
| working-directory: contracts/predict-iq | |
| oracle-quality-gate: | |
| name: Oracle Module — Clippy + Warnings-as-Errors | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: actions-rs/toolchain@v1 | |
| with: | |
| profile: minimal | |
| toolchain: stable | |
| override: true | |
| components: clippy | |
| - name: Clippy (deny warnings) — oracle module | |
| env: | |
| RUSTFLAGS: -D warnings | |
| run: > | |
| cargo clippy | |
| --package predict-iq | |
| --all-features | |
| -- -D warnings | |
| working-directory: contracts/predict-iq | |
| - name: Build (deny warnings) — contract crate | |
| env: | |
| RUSTFLAGS: -D warnings | |
| run: cargo build --package predict-iq --all-features | |
| working-directory: contracts/predict-iq | |
| - name: Run oracle tests | |
| run: cargo test --package predict-iq --lib oracles | |
| working-directory: contracts/predict-iq | |
| format: | |
| name: Code Formatting | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: actions-rs/toolchain@v1 | |
| with: | |
| profile: minimal | |
| toolchain: stable | |
| override: true | |
| components: rustfmt | |
| - name: Check formatting | |
| run: cargo fmt --all -- --check | |
| working-directory: contracts/predict-iq | |
| build-optimized: | |
| name: Build Optimized Contract | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: actions-rs/toolchain@v1 | |
| with: | |
| profile: minimal | |
| toolchain: stable | |
| override: true | |
| target: wasm32-unknown-unknown | |
| - name: Install Soroban CLI | |
| run: cargo install --locked soroban-cli --features opt | |
| - name: Build optimized WASM | |
| run: cargo build --target wasm32-unknown-unknown --release | |
| working-directory: contracts/predict-iq | |
| - name: Optimize WASM | |
| run: | | |
| soroban contract optimize \ | |
| --wasm target/wasm32-unknown-unknown/release/predict_iq.wasm \ | |
| --wasm-out target/wasm32-unknown-unknown/release/predict_iq_optimized.wasm | |
| working-directory: contracts/predict-iq | |
| - name: Check WASM size | |
| run: | | |
| size=$(stat -f%z target/wasm32-unknown-unknown/release/predict_iq_optimized.wasm 2>/dev/null || stat -c%s target/wasm32-unknown-unknown/release/predict_iq_optimized.wasm) | |
| echo "Optimized WASM size: $size bytes" | |
| if [ $size -gt ${{ env.WASM_SIZE_LIMIT_BYTES }} ]; then | |
| echo "WASM size $size exceeds limit of ${{ env.WASM_SIZE_LIMIT_BYTES }} bytes" | |
| exit 1 | |
| fi | |
| working-directory: contracts/predict-iq | |
| api-cache-tests: | |
| name: API Cache Tests (Redis) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: actions-rs/toolchain@v1 | |
| with: | |
| profile: minimal | |
| toolchain: stable | |
| override: true | |
| - name: Cache cargo dependencies | |
| uses: actions/cache@v5 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: ${{ runner.os }}-cargo-api-${{ hashFiles('services/api/Cargo.lock') }} | |
| - name: Run cache tests | |
| run: cargo test --lib cache | |
| working-directory: services/api | |
| e2e-market-creation: | |
| name: E2E — Market Creation Flow | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: frontend | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: "18" | |
| cache: "npm" | |
| cache-dependency-path: frontend/package-lock.json | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Install Playwright browsers | |
| run: npx playwright install --with-deps chromium | |
| - name: Run market-creation E2E tests | |
| run: npx playwright test e2e/market-creation.spec.ts --project=chromium | |
| env: | |
| CI: "true" | |
| - name: Run flaky test detection | |
| run: npx playwright test e2e/market-creation.spec.ts --project=chromium | |
| env: | |
| CI: "true" | |
| FLAKY_DETECTION: "true" | |
| - name: Upload Playwright report | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: playwright-market-creation-report | |
| path: frontend/playwright-report/ | |
| retention-days: 14 | |
| # ── #740: validate every migration has a rollback script ──────────────────── | |
| validate-migration-rollbacks: | |
| name: Validate Migration Rollback Scripts | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Check every migration has a rollback script | |
| run: | | |
| MISSING=0 | |
| MIGRATIONS_DIR="services/api/database/migrations" | |
| ROLLBACKS_DIR="services/api/database/migrations/rollbacks" | |
| for f in "$MIGRATIONS_DIR"/*.sql; do | |
| base=$(basename "$f" .sql) | |
| down="${ROLLBACKS_DIR}/${base}_down.sql" | |
| if [ ! -f "$down" ]; then | |
| echo "❌ Missing rollback: $down (for $f)" | |
| MISSING=1 | |
| else | |
| echo "✅ $down" | |
| fi | |
| done | |
| if [ "$MISSING" -eq 1 ]; then | |
| echo "" | |
| echo "One or more migrations are missing rollback scripts." | |
| echo "Add the corresponding *_down.sql file in $ROLLBACKS_DIR." | |
| exit 1 | |
| fi | |
| echo "" | |
| echo "All migration files have corresponding rollback scripts." | |
| - name: Validate rollback SQL syntax (dry-run) | |
| run: | | |
| for f in services/api/database/migrations/rollbacks/*_down.sql; do | |
| # psql --dry-run is not universally available; use a syntax check via | |
| # pg_format or simply assert the file is non-empty and contains SQL keywords. | |
| if [ ! -s "$f" ]; then | |
| echo "❌ Empty rollback script: $f" | |
| exit 1 | |
| fi | |
| if ! grep -qiE "^(DROP|ALTER|DELETE|TRUNCATE)" "$f"; then | |
| echo "❌ Rollback script appears to contain no DDL/DML: $f" | |
| exit 1 | |
| fi | |
| echo "✅ $f" | |
| done | |
| # ── #741: API Criterion benchmarks ─────────────────────────────────────────── | |
| api-criterion-benchmarks: | |
| name: API Criterion Benchmarks | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: actions-rs/toolchain@v1 | |
| with: | |
| profile: minimal | |
| toolchain: stable | |
| override: true | |
| - name: Cache cargo dependencies | |
| uses: actions/cache@v3 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: ${{ runner.os }}-cargo-api-bench-${{ hashFiles('services/api/Cargo.lock') }} | |
| - name: Run API benchmarks | |
| run: cargo bench 2>&1 | tee ../../bench-output.txt | |
| working-directory: services/api | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v3 | |
| with: | |
| node-version: "18" | |
| - name: Parse benchmark output | |
| run: | | |
| node services/api/scripts/parse-bench-output.js \ | |
| bench-output.txt bench-results.json | |
| - name: Compare against baseline | |
| if: github.event_name == 'pull_request' | |
| run: | | |
| node services/api/scripts/compare-api-benchmarks.js \ | |
| bench-results.json \ | |
| services/api/benches/.benchmarks/baseline.json \ | |
| --threshold 10 | |
| - name: Upload benchmark results | |
| if: always() | |
| uses: actions/upload-artifact@v3 | |
| with: | |
| name: api-benchmark-results | |
| path: | | |
| bench-output.txt | |
| bench-results.json | |
| retention-days: 30 | |
| save-api-bench-baseline: | |
| name: Save API Benchmark Baseline | |
| runs-on: ubuntu-latest | |
| needs: [api-criterion-benchmarks] | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: actions-rs/toolchain@v1 | |
| with: | |
| profile: minimal | |
| toolchain: stable | |
| override: true | |
| - name: Cache cargo dependencies | |
| uses: actions/cache@v3 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: ${{ runner.os }}-cargo-api-bench-${{ hashFiles('services/api/Cargo.lock') }} | |
| - name: Run API benchmarks | |
| run: cargo bench 2>&1 | tee ../../bench-output.txt | |
| working-directory: services/api | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v3 | |
| with: | |
| node-version: "18" | |
| - name: Parse and save new baseline | |
| run: | | |
| node services/api/scripts/parse-bench-output.js \ | |
| bench-output.txt \ | |
| services/api/benches/.benchmarks/baseline.json | |
| - name: Create Pull Request with baseline updates | |
| uses: peter-evans/create-pull-request@v6 | |
| with: | |
| commit-message: "chore: update API benchmark baseline for main branch" | |
| title: "chore: update API benchmark baseline" | |
| body: | | |
| ## Automated API Benchmark Baseline Update | |
| This PR updates the API benchmark baseline after changes to main branch. | |
| **Changes:** | |
| - Updated `services/api/benches/.benchmarks/baseline.json` with latest benchmark results | |
| **Note:** This is an automated PR. Please review the baseline changes before merging. | |
| branch: chore/api-baseline-${{ github.run_id }} | |
| delete-branch: true | |
| labels: | | |
| ci/cd | |
| automated | |
| # ── #743: frontend unit-test coverage gate ─────────────────────────────────── | |
| frontend-unit-coverage: | |
| name: Frontend Unit Test Coverage | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: frontend | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v3 | |
| with: | |
| node-version: "18" | |
| cache: "npm" | |
| cache-dependency-path: frontend/package-lock.json | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Run Jest with coverage | |
| run: npm run test:ci | |
| env: | |
| CI: "true" | |
| - name: Upload coverage report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: frontend-coverage-report | |
| path: frontend/coverage/ | |
| retention-days: 14 | |
| all-tests-passed: | |
| name: All Tests Passed | |
| needs: | |
| - prometheus-rules-test | |
| - unit-tests | |
| - integration-tests | |
| - api-rate-limit-tests | |
| - gas-benchmarks | |
| - backend-coverage | |
| - security-audit | |
| - sast-scanning | |
| - secrets-scanning | |
| - dependency-scanning | |
| - codeql-analysis | |
| - clippy | |
| - oracle-quality-gate | |
| - format | |
| - build-optimized | |
| - api-cache-tests | |
| - e2e-market-creation | |
| - documentation-sync | |
| - validate-migration-rollbacks | |
| - api-criterion-benchmarks | |
| - frontend-unit-coverage | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Success | |
| run: echo "All tests passed successfully!" | |
| documentation-sync: | |
| name: Documentation Sync Check | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v3 | |
| with: | |
| node-version: "18" | |
| - name: Check API_SPEC.md is in sync with openapi.yaml | |
| run: node scripts/generate-api-spec.js --check | |
| - name: Fail if out of sync | |
| if: failure() | |
| run: | | |
| echo "❌ API_SPEC.md is out of sync with openapi.yaml" | |
| echo "" | |
| echo "To fix this, run:" | |
| echo " node scripts/generate-api-spec.js" | |
| echo " git add API_SPEC.md" | |
| echo " git commit -m 'chore: regenerate API_SPEC.md'" | |
| exit 1 |