name: CI on: push: branches: ["**"] pull_request: workflow_dispatch: inputs: skip_integration: description: "Skip integration tests" type: boolean default: false permissions: checks: write contents: read env: CARGO_TERM_COLOR: always RUST_BACKTRACE: 1 # ───────────────────────────────────────────────────────────────────────────── # Job 1 – Build matrix # # Builds on Linux x86_64 and Linux aarch64. macOS and Windows are in a # separate ci-compat.yml workflow so their failures don't mark this run red. # ───────────────────────────────────────────────────────────────────────────── jobs: build: name: Build (${{ matrix.os }}) runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: - os: ubuntu-latest - os: ubuntu-24.04-arm steps: - uses: actions/checkout@v4 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable - name: Cache Cargo registry + build uses: actions/cache@v4 with: path: | ~/.cargo/registry ~/.cargo/git target key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} restore-keys: | ${{ runner.os }}-cargo- - name: Build run: cargo build --release # Upload the Linux binary so integration jobs can use it without rebuilding - name: Upload Linux binary if: matrix.os == 'ubuntu-latest' uses: actions/upload-artifact@v4 with: name: fips-linux path: | target/release/fips target/release/fipsctl target/release/fipstop retention-days: 1 # ───────────────────────────────────────────────────────────────────────────── # Job 2 – Unit tests # # Runs `cargo test` on Linux. Gated on the build matrix completing so we # don't waste runner time if compilation is broken. # ───────────────────────────────────────────────────────────────────────────── test: name: Unit tests runs-on: ubuntu-latest needs: [build] steps: - uses: actions/checkout@v4 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable - name: Cache Cargo registry + build uses: actions/cache@v4 with: path: | ~/.cargo/registry ~/.cargo/git target key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} restore-keys: | ${{ runner.os }}-cargo- - name: Install cargo-nextest uses: taiki-e/install-action@nextest - name: Run unit tests run: cargo nextest run --all --profile ci - name: Publish test report (Checks tab) uses: dorny/test-reporter@v2 if: always() with: name: Unit Tests path: target/nextest/ci/junit.xml reporter: java-junit fail-on-error: false - name: Publish test report (run summary) uses: mikepenz/action-junit-report@v4 if: always() with: report_paths: target/nextest/ci/junit.xml check_name: Unit Tests Summary fail_on_failure: false # ───────────────────────────────────────────────────────────────────────────── # Job 3 – Integration tests (static mesh + chaos simulation) # # Runs only when both build and test succeed. Each topology / scenario is a # separate matrix entry so they run in parallel. # # Static topologies → build Docker images, start containers, ping-test # Chaos scenarios → build sim image, run stochastic simulation # ───────────────────────────────────────────────────────────────────────────── integration: name: Integration (${{ matrix.suite }}) runs-on: ubuntu-latest needs: [build, test] if: ${{ !inputs.skip_integration }} strategy: fail-fast: false matrix: include: # ── Static mesh topologies ───────────────────────────────────────── - suite: static-mesh type: static topology: mesh - suite: static-chain type: static topology: chain # ── Chaos / stochastic scenarios ─────────────────────────────────── - suite: chaos-smoke-10 type: chaos scenario: smoke-10 - suite: chaos-10 type: chaos scenario: chaos-10 - suite: ethernet-mesh type: chaos scenario: ethernet-mesh - suite: ethernet-only type: chaos scenario: ethernet-only - suite: bottleneck-parent type: chaos scenario: bottleneck-parent - suite: cost-avoidance type: chaos scenario: cost-avoidance - suite: cost-mixed-7node type: chaos scenario: cost-mixed-7node - suite: cost-reeval type: chaos scenario: cost-reeval - suite: cost-stability type: chaos scenario: cost-stability - suite: depth-vs-cost type: chaos scenario: depth-vs-cost - suite: mixed-technology type: chaos scenario: mixed-technology # ── Sidecar deployment ────────────────────────────────────────── - suite: sidecar type: sidecar steps: - uses: actions/checkout@v4 # Fetch the pre-built Linux binary from job 1 - name: Download Linux binary uses: actions/download-artifact@v4 with: name: fips-linux path: _bin # ── Static topology ──────────────────────────────────────────────────── - name: Install binary (static) if: matrix.type == 'static' run: | chmod +x _bin/fips _bin/fipsctl cp _bin/fips testing/static/fips cp _bin/fipsctl testing/static/fipsctl - name: Generate configs (static) if: matrix.type == 'static' run: bash testing/static/scripts/generate-configs.sh ${{ matrix.topology }} - name: Build Docker images (static) if: matrix.type == 'static' run: | docker compose -f testing/static/docker-compose.yml \ --profile ${{ matrix.topology }} build - name: Start containers (static) if: matrix.type == 'static' run: | docker compose -f testing/static/docker-compose.yml \ --profile ${{ matrix.topology }} up -d - name: Run ping test (static) if: matrix.type == 'static' run: bash testing/static/scripts/ping-test.sh ${{ matrix.topology }} - name: Collect logs on failure (static) if: matrix.type == 'static' && failure() run: | docker compose -f testing/static/docker-compose.yml \ --profile ${{ matrix.topology }} logs --no-color - name: Stop containers (static) if: matrix.type == 'static' && always() run: | docker compose -f testing/static/docker-compose.yml \ --profile ${{ matrix.topology }} down --volumes --remove-orphans # ── Chaos simulation ─────────────────────────────────────────────────── - name: Install Python deps (chaos) if: matrix.type == 'chaos' run: pip3 install --quiet pyyaml jinja2 - name: Install binary (chaos) if: matrix.type == 'chaos' run: | chmod +x _bin/fips _bin/fipsctl cp _bin/fips testing/chaos/fips cp _bin/fipsctl testing/chaos/fipsctl - name: Build chaos Docker image if: matrix.type == 'chaos' run: docker build -t fips-chaos:latest testing/chaos - name: Run chaos scenario if: matrix.type == 'chaos' run: bash testing/chaos/scripts/chaos.sh ${{ matrix.scenario }} - name: Upload sim results on failure (chaos) if: matrix.type == 'chaos' && failure() uses: actions/upload-artifact@v4 with: name: sim-results-${{ matrix.scenario }} path: testing/chaos/sim-results/ retention-days: 7 # ── Sidecar deployment ────────────────────────────────────────────── - name: Install binary (sidecar) if: matrix.type == 'sidecar' run: | chmod +x _bin/fips _bin/fipsctl _bin/fipstop cp _bin/fips testing/sidecar/fips cp _bin/fipsctl testing/sidecar/fipsctl cp _bin/fipstop testing/sidecar/fipstop - name: Run sidecar integration test if: matrix.type == 'sidecar' run: bash testing/sidecar/scripts/test-sidecar.sh - name: Collect logs on failure (sidecar) if: matrix.type == 'sidecar' && failure() run: | for node in a b c; do echo "--- sidecar-${node} logs ---" docker logs "sidecar-${node}-fips-1" 2>&1 || true echo "" done