test(contracts): implement comprehensive access control test matrix (#249) #65
Workflow file for this run
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: CI/CD Pipeline (PR Safe) | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened] | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| CARGO_TERM_COLOR: always | |
| NPM_CONFIG_FETCH_RETRIES: 5 | |
| NPM_CONFIG_FETCH_RETRY_MINTIMEOUT: 20000 | |
| NPM_CONFIG_FETCH_RETRY_MAXTIMEOUT: 120000 | |
| jobs: | |
| detect-changes: | |
| name: Detect Changes | |
| runs-on: ubuntu-latest | |
| outputs: | |
| contracts: ${{ steps.changes.outputs.contracts }} | |
| backend: ${{ steps.changes.outputs.backend }} | |
| frontend: ${{ steps.changes.outputs.frontend }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - id: changes | |
| uses: dorny/paths-filter@v3 | |
| with: | |
| filters: | | |
| contracts: | |
| - 'contracts/**' | |
| - '.github/workflows/ci-pr.yml' | |
| backend: | |
| - 'backend/**' | |
| - 'package.json' | |
| - 'package-lock.json' | |
| - '.github/workflows/ci-pr.yml' | |
| frontend: | |
| - 'frontend/**' | |
| - 'package.json' | |
| - 'package-lock.json' | |
| - '.github/workflows/ci-pr.yml' | |
| build-contracts: | |
| name: Build & Lint Contracts | |
| needs: detect-changes | |
| if: ${{ needs.detect-changes.outputs.contracts == 'true' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| toolchain: stable | |
| components: clippy, rustfmt | |
| - name: Cache Cargo dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/bin/ | |
| ~/.cargo/registry/index/ | |
| ~/.cargo/registry/cache/ | |
| ~/.cargo/git/db/ | |
| contracts/target/ | |
| key: ${{ runner.os }}-cargo-contracts-${{ hashFiles('contracts/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-contracts- | |
| - name: Cache cargo-audit binary | |
| id: cache-audit | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cargo/bin/cargo-audit | |
| key: ${{ runner.os }}-cargo-audit-bin-0.22 | |
| - name: Check formatting | |
| run: cd contracts && cargo fmt --all -- --check | |
| - name: Run Clippy | |
| run: cd contracts && cargo clippy -- -D warnings | |
| - name: Build contracts (release) | |
| run: cd contracts && cargo build --release | |
| - name: Build tests (no run) | |
| run: cd contracts && cargo test --no-run --release | |
| continue-on-error: true | |
| test-contracts: | |
| name: Test Contracts (${{ matrix.test-group }}) | |
| needs: build-contracts | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| test-group: | |
| - unit | |
| - integration | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| toolchain: stable | |
| - name: Cache Cargo dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/bin/ | |
| ~/.cargo/registry/index/ | |
| ~/.cargo/registry/cache/ | |
| ~/.cargo/git/db/ | |
| contracts/target/ | |
| key: ${{ runner.os }}-cargo-contracts-${{ hashFiles('contracts/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-contracts- | |
| - name: Run ${{ matrix.test-group }} tests | |
| run: | | |
| if [ "${{ matrix.test-group }}" = "integration" ]; then | |
| cd contracts && cargo test --test '*' --release | |
| else | |
| cd contracts && cargo test --lib --release | |
| fi | |
| build-backend: | |
| name: Build Backend | |
| needs: detect-changes | |
| if: ${{ needs.detect-changes.outputs.backend == 'true' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| cache-dependency-path: | | |
| package-lock.json | |
| backend/package-lock.json | |
| - name: Verify lock file | |
| run: | | |
| if [ ! -f package-lock.json ]; then | |
| echo "Error: root package-lock.json is missing. Run 'npm install --package-lock-only' in the project root" | |
| exit 1 | |
| fi | |
| - name: Install dependencies | |
| run: npm ci -w backend | |
| - name: Run backend linter | |
| run: npm run lint -w backend 2>/dev/null || echo "No linter configured for backend" | |
| - name: Build backend | |
| run: npm run build -w backend | |
| - name: Cache backend build | |
| uses: actions/cache@v4 | |
| with: | |
| path: backend/.next | |
| key: ${{ runner.os }}-backend-${{ hashFiles('backend/**/*.ts', 'backend/**/*.tsx', 'backend/package-lock.json') }} | |
| enable-cross-os-access: true | |
| test-backend: | |
| name: Test Backend | |
| needs: build-backend | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| cache-dependency-path: | | |
| package-lock.json | |
| backend/package-lock.json | |
| - name: Install dependencies | |
| run: npm ci -w backend | |
| - name: Run backend tests | |
| run: npm test -w backend 2>/dev/null || echo "No tests configured for backend" | |
| build-frontend: | |
| name: Build Frontend | |
| needs: detect-changes | |
| if: ${{ needs.detect-changes.outputs.frontend == 'true' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| cache-dependency-path: | | |
| package-lock.json | |
| frontend/package-lock.json | |
| - name: Verify lock file | |
| run: | | |
| if [ ! -f package-lock.json ]; then | |
| echo "Error: root package-lock.json is missing. Run 'npm install --package-lock-only' in the project root" | |
| exit 1 | |
| fi | |
| - name: Install dependencies | |
| run: npm ci -w frontend | |
| - name: Build frontend | |
| run: npm run build -w frontend | |
| env: | |
| NEXT_PUBLIC_STELLAR_RECEIVER_ADDRESS: ${{ secrets.NEXT_PUBLIC_STELLAR_RECEIVER_ADDRESS || 'GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWNA' }} | |
| security-scan: | |
| name: Security Scan | |
| needs: detect-changes | |
| if: ${{ always() }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| security-events: write | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| cache-dependency-path: package-lock.json | |
| - name: Install root dependencies (for audit) | |
| run: npm ci --ignore-scripts | |
| - name: npm audit (report only, non-blocking) | |
| run: npm audit --audit-level=critical --workspaces --include-workspace-root || echo "npm audit found issues (non-blocking)" | |
| - name: Setup Rust for cargo-audit | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| toolchain: stable | |
| - name: Cache cargo-audit binary | |
| id: cache-audit | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cargo/bin/cargo-audit | |
| key: ${{ runner.os }}-cargo-audit-bin-0.22 | |
| - name: Install cargo-audit (if not cached) | |
| if: steps.cache-audit.outputs.cache-hit != 'true' | |
| run: cargo install --locked cargo-audit --version ^0.22 --force | |
| - name: cargo audit (Rust dependencies, non-blocking) | |
| working-directory: contracts | |
| run: cargo audit || echo "cargo audit found issues (non-blocking)" | |
| - name: Run Trivy vulnerability scanner | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| scan-type: 'fs' | |
| scan-ref: '.' | |
| format: 'sarif' | |
| output: 'trivy-results.sarif' | |
| - name: Upload Trivy scan results | |
| uses: github/codeql-action/upload-sarif@v4 | |
| with: | |
| sarif_file: 'trivy-results.sarif' | |
| ci-status: | |
| name: CI Status Check | |
| needs: | |
| - build-contracts | |
| - test-contracts | |
| - build-backend | |
| - test-backend | |
| - build-frontend | |
| - security-scan | |
| if: ${{ always() }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check build-contracts status | |
| run: | | |
| echo "build-contracts: ${{ needs.build-contracts.result }}" | |
| echo "test-contracts: ${{ needs.test-contracts.result }}" | |
| echo "build-backend: ${{ needs.build-backend.result }}" | |
| echo "test-backend: ${{ needs.test-backend.result }}" | |
| echo "build-frontend: ${{ needs.build-frontend.result }}" | |
| echo "security-scan: ${{ needs.security-scan.result }}" | |
| - name: Fail if any critical job failed | |
| if: > | |
| needs.build-contracts.result == 'failure' || | |
| needs.build-backend.result == 'failure' || | |
| needs.build-frontend.result == 'failure' | |
| run: exit 1 |