Bump actions/checkout from 6 to 7 (#555) #1380
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 | |
| on: | |
| push: | |
| branches: [main] | |
| paths-ignore: | |
| - "docs-cms/**" | |
| - "docusaurus/**" | |
| - "tooling/build_docs.py" | |
| - "tooling/validate_docs.py" | |
| - "**/*.md" | |
| - ".github/workflows/docs.yml" | |
| pull_request: | |
| branches: [main] | |
| paths-ignore: | |
| - "docs-cms/**" | |
| - "docusaurus/**" | |
| - "tooling/build_docs.py" | |
| - "tooling/validate_docs.py" | |
| - "**/*.md" | |
| - ".github/workflows/docs.yml" | |
| merge_group: | |
| types: [checks_requested] | |
| # Cancel in-progress runs for the same PR/branch/merge group | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.event.merge_group.head_sha || github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # Job 0: Generate CI matrix (selective execution - RFC-045) | |
| generate-matrix: | |
| name: Generate CI Matrix | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| outputs: | |
| matrix: ${{ steps.matrix.outputs.matrix }} | |
| has_test: ${{ steps.matrix.outputs.has_test }} | |
| run_full: ${{ steps.check-label.outputs.run_full }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 # Need full history for git diff | |
| - name: Check for ci:full label | |
| id: check-label | |
| run: | | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| LABELS=$(gh pr view ${{ github.event.pull_request.number }} --json labels --jq '.labels[].name') | |
| if echo "$LABELS" | grep -q "ci:full"; then | |
| echo "run_full=true" >> "$GITHUB_OUTPUT" | |
| echo "✓ ci:full label detected - running full CI" | |
| else | |
| echo "run_full=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| else | |
| echo "run_full=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v7 | |
| with: | |
| version: "latest" | |
| enable-cache: true | |
| - name: Setup Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.11" | |
| - name: Generate selective CI matrix | |
| id: matrix | |
| run: | | |
| if [ "${{ steps.check-label.outputs.run_full }}" = "true" ]; then | |
| # Full CI requested - set outputs to indicate all jobs should run | |
| { | |
| echo "matrix={}" | |
| echo "has_test=true" | |
| echo "has_lint=true" | |
| echo "has_build=true" | |
| echo "has_docs=true" | |
| } >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| BASE_REF="${{ github.event.pull_request.base.sha }}" | |
| HEAD_REF="${{ github.event.pull_request.head.sha }}" | |
| elif [ "${{ github.event_name }}" = "push" ]; then | |
| # For push events, compare current commit against previous commit | |
| BASE_REF="${{ github.event.before }}" | |
| HEAD_REF="${{ github.event.after }}" | |
| else | |
| # For other events (merge_group, etc.), compare against main | |
| BASE_REF="origin/main" | |
| HEAD_REF="HEAD" | |
| fi | |
| echo "📊 Generating selective CI matrix..." | |
| echo "Event: ${{ github.event_name }}" | |
| echo "Base: $BASE_REF" | |
| echo "Head: $HEAD_REF" | |
| uv run tooling/ci_matrix.py \ | |
| --mode=github-actions \ | |
| --base="$BASE_REF" \ | |
| --head="$HEAD_REF" | |
| - name: Display CI plan | |
| if: steps.check-label.outputs.run_full != 'true' | |
| run: | | |
| { | |
| echo "## 📋 Selective CI Execution Plan" | |
| echo "" | |
| echo "### Matrix Outputs" | |
| echo "" | |
| echo "| Output | Value |" | |
| echo "|--------|-------|" | |
| echo "| \`run_full\` | \`${{ steps.check-label.outputs.run_full }}\` |" | |
| echo "| \`has_test\` | \`${{ steps.matrix.outputs.has_test }}\` |" | |
| echo "| \`matrix\` | \`${{ steps.matrix.outputs.matrix }}\` |" | |
| echo "" | |
| echo "### Execution Summary" | |
| echo "" | |
| if [ "${{ steps.matrix.outputs.has_test }}" = "true" ]; then | |
| echo "✅ **Tests**: Will run affected test suites" | |
| echo "" | |
| echo "> Matrix jobs will execute with proper names (e.g., 'Test MemStore Driver')" | |
| else | |
| echo "⏭️ **Tests**: Skipped (no test-related changes)" | |
| echo "" | |
| echo "> Matrix jobs will run but skip actual test execution" | |
| fi | |
| echo "" | |
| echo "💡 Add \`ci:full\` label to run complete CI pipeline" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| - name: Display full CI notice | |
| if: steps.check-label.outputs.run_full == 'true' | |
| run: | | |
| { | |
| echo "## 🔄 Full CI Execution" | |
| echo "" | |
| echo "### Matrix Outputs" | |
| echo "" | |
| echo "| Output | Value |" | |
| echo "|--------|-------|" | |
| echo "| \`run_full\` | \`true\` |" | |
| echo "| \`has_test\` | \`${{ steps.matrix.outputs.has_test }}\` |" | |
| echo "" | |
| echo "✅ Running complete CI pipeline (ci:full label detected)" | |
| echo "" | |
| echo "> All matrix jobs will execute with full test coverage" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| # Job 1: Generate protobuf code once (new) | |
| generate-proto: | |
| name: Generate Protobuf Code | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version: "1.25.4" | |
| cache: false # We'll use custom cache for more control | |
| - name: Cache Go dependencies and build cache | |
| uses: actions/cache@v5 | |
| with: | |
| path: | | |
| ~/go/pkg/mod | |
| ~/.cache/go-build | |
| key: ${{ runner.os }}-go-proto-${{ hashFiles('**/go.sum', '**/go.mod') }} | |
| restore-keys: | | |
| ${{ runner.os }}-go-proto- | |
| ${{ runner.os }}-go- | |
| # Note: No protoc needed - buf uses remote plugins (buf.build/protocolbuffers/go) | |
| - name: Install buf | |
| uses: bufbuild/buf-setup-action@v1 | |
| with: | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| buf_api_token: ${{ secrets.BUF_BSR_KEY }} | |
| buf_user: jrepp | |
| - name: Install Task | |
| run: | | |
| sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin | |
| - name: Generate protobuf code | |
| run: task proto-go | |
| - name: Upload generated proto code | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: proto-generated | |
| path: pkg/plugin/gen/ | |
| retention-days: 1 | |
| # Job 1: Lint Rust code | |
| lint-rust: | |
| name: Lint Rust | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: rustfmt, clippy | |
| - name: Install protoc | |
| run: sudo apt-get update && sudo apt-get install -y protobuf-compiler | |
| - name: Cache Rust dependencies | |
| uses: actions/cache@v5 | |
| with: | |
| path: | | |
| ~/.cargo/registry/index/ | |
| ~/.cargo/registry/cache/ | |
| ~/.cargo/git/db/ | |
| prism-proxy/target/ | |
| key: ${{ runner.os }}-cargo-${{ hashFiles('prism-proxy/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo- | |
| - name: Format Rust code | |
| run: cd prism-proxy && cargo fmt --all -- --check | |
| - name: Lint Rust with Clippy | |
| run: cd prism-proxy && cargo clippy --all-targets --all-features -- -D warnings | |
| # Job 2: Lint Python code | |
| lint-python: | |
| name: Lint Python | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v7 | |
| with: | |
| version: "latest" | |
| enable-cache: true | |
| - name: Setup Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.11" | |
| - name: Lint Python with ruff | |
| run: | | |
| uv run ruff check tooling/ | |
| uv run ruff format --check tooling/ | |
| # Job 2b: Lint Protobuf files | |
| lint-proto: | |
| name: Lint Protobuf | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 # Need full history for breaking change detection | |
| - name: Install buf | |
| uses: bufbuild/buf-setup-action@v1 | |
| with: | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| buf_api_token: ${{ secrets.BUF_BSR_KEY }} | |
| buf_user: jrepp | |
| - name: Lint protobuf files | |
| run: cd proto && buf lint | |
| - name: Check protobuf formatting | |
| run: cd proto && buf format --diff --exit-code | |
| - name: Check for breaking-change label | |
| id: check-breaking-label | |
| if: github.event_name == 'pull_request' | |
| run: | | |
| LABELS=$(gh pr view ${{ github.event.pull_request.number }} --json labels --jq '.labels[].name') | |
| if echo "$LABELS" | grep -q "breaking-change"; then | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| echo "⚠️ breaking-change label detected - skipping breaking changes check" | |
| else | |
| echo "skip=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Check for breaking changes | |
| if: github.event_name == 'pull_request' && steps.check-breaking-label.outputs.skip != 'true' | |
| run: | | |
| cd proto | |
| buf breaking --against "../.git#branch=origin/main,subdir=proto" | |
| - name: Skip breaking changes check notice | |
| if: github.event_name == 'pull_request' && steps.check-breaking-label.outputs.skip == 'true' | |
| run: | | |
| echo "::notice::Breaking changes check skipped due to breaking-change label" | |
| # Job 2c: Lint GitHub Actions workflows | |
| lint-github-actions: | |
| name: Lint GitHub Actions | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Run actionlint | |
| uses: reviewdog/action-actionlint@v1 | |
| with: | |
| reporter: github-check | |
| level: error | |
| fail_level: error | |
| # Job 3: Lint Go code (parallel by category) | |
| lint-go: | |
| name: Lint Go (${{ matrix.category }}) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| needs: generate-proto | |
| strategy: | |
| fail-fast: true # Cancel all on first failure for faster feedback | |
| matrix: | |
| category: [critical, security, style, quality] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version: "1.25.4" | |
| cache: false # We'll use custom cache for more control | |
| - name: Cache Go dependencies and build cache | |
| uses: actions/cache@v5 | |
| with: | |
| path: | | |
| ~/go/pkg/mod | |
| ~/.cache/go-build | |
| key: ${{ runner.os }}-go-lint-${{ hashFiles('**/go.sum', '**/go.mod') }} | |
| restore-keys: | | |
| ${{ runner.os }}-go-lint- | |
| ${{ runner.os }}-go- | |
| - name: Download generated proto code | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: proto-generated | |
| path: pkg/plugin/gen/ | |
| - name: Cache golangci-lint binary | |
| id: cache-golangci-lint | |
| uses: actions/cache@v5 | |
| with: | |
| path: ~/go/bin/golangci-lint | |
| key: ${{ runner.os }}-golangci-lint-binary-v2.5.0 | |
| restore-keys: | | |
| ${{ runner.os }}-golangci-lint-binary- | |
| - name: Install golangci-lint | |
| if: steps.cache-golangci-lint.outputs.cache-hit != 'true' | |
| run: | | |
| curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh \ | |
| | sh -s -- -b "$(go env GOPATH)/bin" v2.5.0 | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v7 | |
| with: | |
| version: "latest" | |
| enable-cache: true | |
| - name: Setup Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.11" | |
| - name: Cache golangci-lint | |
| uses: actions/cache@v5 | |
| with: | |
| path: ~/.cache/golangci-lint | |
| key: ${{ runner.os }}-golangci-lint-${{ matrix.category }}-${{ hashFiles('.golangci.yml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-golangci-lint-${{ matrix.category }}- | |
| - name: Lint Go - ${{ matrix.category }} | |
| run: | | |
| uv run tooling/parallel_lint.py \ | |
| --categories ${{ matrix.category }} \ | |
| --fail-fast | |
| # Build Jobs (run early in parallel after linting passes) | |
| build-rust: | |
| name: Build Rust Components | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| needs: lint-rust | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Install protoc | |
| run: sudo apt-get update && sudo apt-get install -y protobuf-compiler | |
| - name: Cache Rust dependencies | |
| uses: actions/cache@v5 | |
| with: | |
| path: | | |
| ~/.cargo/registry/index/ | |
| ~/.cargo/registry/cache/ | |
| ~/.cargo/git/db/ | |
| prism-proxy/target/ | |
| key: ${{ runner.os }}-cargo-build-${{ hashFiles('prism-proxy/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-build- | |
| ${{ runner.os }}-cargo- | |
| - name: Install Task | |
| run: | | |
| sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin | |
| - name: Build Rust proxy | |
| run: task proxy | |
| - name: Upload Rust binaries | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: rust-binaries | |
| path: prism-proxy/target/release/prism-proxy | |
| retention-days: 7 | |
| build-go: | |
| name: Build Go Components | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| needs: [generate-proto, lint-go] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version: "1.25.4" | |
| cache: false | |
| - name: Cache Go dependencies and build cache | |
| uses: actions/cache@v5 | |
| with: | |
| path: | | |
| ~/go/pkg/mod | |
| ~/.cache/go-build | |
| key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum', '**/go.mod') }} | |
| restore-keys: | | |
| ${{ runner.os }}-go-build- | |
| ${{ runner.os }}-go- | |
| - name: Download generated proto code | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: proto-generated | |
| path: pkg/plugin/gen/ | |
| - name: Install Task | |
| run: | | |
| sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin | |
| - name: Build Go components | |
| run: | | |
| task prismctl | |
| task prism-admin | |
| task prism-loadtest | |
| task prism-launcher | |
| task plugin-watcher | |
| task patterns | |
| - name: Upload Go binaries | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: go-binaries | |
| path: | | |
| build/prismctl | |
| build/prism-admin | |
| build/prism-loadtest | |
| build/prism-launcher | |
| build/plugin-watcher | |
| build/*-runner | |
| retention-days: 7 | |
| # Job 2: Test Rust proxy (unit and integration tests) | |
| # Note: Job always runs to provide visibility. Steps skip when Rust tests aren't needed. | |
| test-proxy: | |
| name: Test Rust Proxy | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| needs: [generate-matrix] | |
| # No job-level 'if' - provides consistent visibility in CI UI | |
| steps: | |
| - name: Check if Rust tests should run | |
| id: should-run | |
| run: | | |
| # Debug: Show execution decision | |
| { | |
| echo "## Rust Proxy Tests Configuration" | |
| echo "" | |
| echo "| Input | Value |" | |
| echo "|-------|-------|" | |
| echo "| run_full | \`${{ needs.generate-matrix.outputs.run_full }}\` |" | |
| echo "| has_test | \`${{ needs.generate-matrix.outputs.has_test }}\` |" | |
| echo "" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| # Rust tests run when full CI or any tests are triggered | |
| # (prism-proxy changes will set has_test=true via test:unit-proxy) | |
| if [[ "${{ needs.generate-matrix.outputs.run_full }}" == "true" ]] || \ | |
| [[ "${{ needs.generate-matrix.outputs.has_test }}" == "true" ]]; then | |
| echo "run=true" >> "$GITHUB_OUTPUT" | |
| echo "**Decision:** Running Rust proxy tests" >> "$GITHUB_STEP_SUMMARY" | |
| else | |
| echo "run=false" >> "$GITHUB_OUTPUT" | |
| echo "**Decision:** Skipping Rust tests (no test-related changes detected)" >> "$GITHUB_STEP_SUMMARY" | |
| echo "::notice::Skipping Rust proxy tests - no test-related changes detected" | |
| fi | |
| - name: Checkout | |
| if: steps.should-run.outputs.run == 'true' | |
| uses: actions/checkout@v7 | |
| - name: Setup Rust | |
| if: steps.should-run.outputs.run == 'true' | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Install protoc | |
| if: steps.should-run.outputs.run == 'true' | |
| run: sudo apt-get update && sudo apt-get install -y protobuf-compiler | |
| - name: Cache Rust dependencies | |
| if: steps.should-run.outputs.run == 'true' | |
| uses: actions/cache@v5 | |
| with: | |
| path: | | |
| ~/.cargo/registry/index/ | |
| ~/.cargo/registry/cache/ | |
| ~/.cargo/git/db/ | |
| prism-proxy/target/ | |
| key: ${{ runner.os }}-cargo-test-${{ hashFiles('prism-proxy/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-test- | |
| ${{ runner.os }}-cargo- | |
| - name: Cache cargo-tarpaulin | |
| if: steps.should-run.outputs.run == 'true' | |
| id: cache-tarpaulin | |
| uses: actions/cache@v5 | |
| with: | |
| path: ~/.cargo/bin/cargo-tarpaulin | |
| key: ${{ runner.os }}-cargo-tarpaulin-0.27.0 | |
| - name: Install cargo-tarpaulin | |
| if: steps.should-run.outputs.run == 'true' && steps.cache-tarpaulin.outputs.cache-hit != 'true' | |
| run: cargo install cargo-tarpaulin --version 0.27.0 | |
| - name: Run Rust unit tests with coverage | |
| if: steps.should-run.outputs.run == 'true' | |
| run: | | |
| mkdir -p build/coverage-reports/coverage-rust | |
| cd prism-proxy && cargo tarpaulin --lib --verbose --out xml --output-dir ../build/coverage-reports/coverage-rust | |
| - name: Run Rust integration tests | |
| if: steps.should-run.outputs.run == 'true' | |
| run: | | |
| echo "Running Rust integration tests..." | |
| cd prism-proxy && cargo test --test integration_test -- --ignored --nocapture || echo "::warning::Rust integration tests failed or were skipped" | |
| echo "Rust integration tests complete" | |
| - name: Upload Rust coverage | |
| if: steps.should-run.outputs.run == 'true' | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: coverage-rust | |
| path: build/coverage-reports/coverage-rust/cobertura.xml | |
| retention-days: 7 | |
| if-no-files-found: error | |
| # Job 3: Test Go drivers and patterns (including acceptance tests) | |
| # Note: Job always runs to ensure matrix.name is properly displayed in UI. | |
| # Steps use conditions to skip actual work when tests aren't needed. | |
| test-patterns: | |
| name: Test ${{ matrix.name }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| needs: [generate-matrix, generate-proto] | |
| # No job-level 'if' - this ensures matrix.name is properly expanded in job names | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # Unit tests for drivers | |
| - name: "MemStore Driver" | |
| path: "pkg/drivers/memstore" | |
| artifact: "memstore" | |
| type: "unit" | |
| - name: "Redis Driver" | |
| path: "pkg/drivers/redis" | |
| artifact: "redis" | |
| type: "unit" | |
| - name: "NATS Driver" | |
| path: "pkg/drivers/nats" | |
| artifact: "nats" | |
| type: "unit" | |
| # Unit tests for patterns | |
| - name: "Consumer Pattern" | |
| path: "patterns/consumer" | |
| artifact: "consumer" | |
| type: "unit" | |
| - name: "Producer Pattern" | |
| path: "patterns/producer" | |
| artifact: "producer" | |
| type: "unit" | |
| - name: "Multicast Registry Pattern" | |
| path: "patterns/multicast_registry" | |
| artifact: "multicast-registry" | |
| type: "unit" | |
| - name: "KeyValue Pattern" | |
| path: "patterns/keyvalue" | |
| artifact: "keyvalue" | |
| type: "unit" | |
| - name: "Mailbox Pattern" | |
| path: "patterns/mailbox" | |
| artifact: "mailbox" | |
| type: "unit" | |
| - name: "Inference Pattern" | |
| path: "patterns/inference/cmd/inference-runner" | |
| artifact: "inference" | |
| type: "unit" | |
| # Acceptance tests for patterns | |
| - name: "KeyValue Acceptance" | |
| path: "tests/acceptance/patterns/keyvalue" | |
| artifact: "acceptance-keyvalue" | |
| type: "acceptance" | |
| pattern_dir: "patterns/keyvalue" | |
| - name: "Consumer Acceptance" | |
| path: "tests/acceptance/patterns/consumer" | |
| artifact: "acceptance-consumer" | |
| type: "acceptance" | |
| pattern_dir: "patterns/consumer" | |
| - name: "Producer Acceptance" | |
| path: "tests/acceptance/patterns/producer" | |
| artifact: "acceptance-producer" | |
| type: "acceptance" | |
| pattern_dir: "patterns/producer" | |
| - name: "ClaimCheck Acceptance" | |
| path: "tests/acceptance/patterns/claimcheck" | |
| artifact: "acceptance-claimcheck" | |
| type: "acceptance" | |
| pattern_dir: "patterns/claimcheck" | |
| - name: "Unified Acceptance" | |
| path: "tests/acceptance/patterns/unified" | |
| artifact: "acceptance-unified" | |
| type: "acceptance" | |
| pattern_dir: "patterns/unified" | |
| - name: "Mailbox Acceptance" | |
| path: "tests/acceptance/patterns/mailbox" | |
| artifact: "acceptance-mailbox" | |
| type: "acceptance" | |
| pattern_dir: "patterns/mailbox" | |
| steps: | |
| - name: Check if tests should run | |
| id: should-run | |
| run: | | |
| # Debug: Show matrix expansion worked correctly | |
| { | |
| echo "## Matrix Configuration" | |
| echo "" | |
| echo "| Property | Value |" | |
| echo "|----------|-------|" | |
| echo "| **Name** | ${{ matrix.name }} |" | |
| echo "| **Path** | \`${{ matrix.path }}\` |" | |
| echo "| **Type** | ${{ matrix.type }} |" | |
| echo "| **Artifact** | ${{ matrix.artifact }} |" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| if [[ -n "${{ matrix.pattern_dir }}" ]]; then | |
| echo "| **Pattern Dir** | \`${{ matrix.pattern_dir }}\` |" >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| echo "" >> "$GITHUB_STEP_SUMMARY" | |
| # Determine if tests should run | |
| { | |
| echo "## Execution Decision" | |
| echo "" | |
| echo "| Input | Value |" | |
| echo "|-------|-------|" | |
| echo "| run_full | \`${{ needs.generate-matrix.outputs.run_full }}\` |" | |
| echo "| has_test | \`${{ needs.generate-matrix.outputs.has_test }}\` |" | |
| echo "" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| if [[ "${{ needs.generate-matrix.outputs.run_full }}" == "true" ]] || \ | |
| [[ "${{ needs.generate-matrix.outputs.has_test }}" == "true" ]]; then | |
| echo "run=true" >> "$GITHUB_OUTPUT" | |
| echo "**Decision:** Running tests" >> "$GITHUB_STEP_SUMMARY" | |
| else | |
| echo "run=false" >> "$GITHUB_OUTPUT" | |
| echo "**Decision:** Skipping tests (no test-related changes detected)" >> "$GITHUB_STEP_SUMMARY" | |
| echo "::notice::Skipping tests for ${{ matrix.name }} - no test-related changes detected" | |
| fi | |
| - name: Checkout | |
| if: steps.should-run.outputs.run == 'true' | |
| uses: actions/checkout@v7 | |
| - name: Setup Go | |
| if: steps.should-run.outputs.run == 'true' | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version: "1.25.4" | |
| cache: false # We'll use custom cache for more control | |
| - name: Download generated proto code | |
| if: steps.should-run.outputs.run == 'true' | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: proto-generated | |
| path: pkg/plugin/gen/ | |
| - name: Cache Go dependencies and build cache | |
| if: steps.should-run.outputs.run == 'true' | |
| uses: actions/cache@v5 | |
| with: | |
| path: | | |
| ~/go/pkg/mod | |
| ~/.cache/go-build | |
| key: ${{ runner.os }}-go-test-${{ hashFiles('**/go.sum', '**/go.mod') }} | |
| restore-keys: | | |
| ${{ runner.os }}-go-test- | |
| ${{ runner.os }}-go- | |
| - name: Download dependencies for acceptance tests | |
| if: steps.should-run.outputs.run == 'true' && matrix.type == 'acceptance' && matrix.pattern_dir != '' | |
| run: | | |
| if [ -d "${{ matrix.pattern_dir }}" ]; then | |
| cd ${{ matrix.pattern_dir }} | |
| go mod download | |
| fi | |
| - name: Build pattern module for acceptance tests | |
| if: steps.should-run.outputs.run == 'true' && matrix.type == 'acceptance' && matrix.pattern_dir != '' | |
| run: | | |
| if [ -d "${{ matrix.pattern_dir }}" ]; then | |
| cd ${{ matrix.pattern_dir }} | |
| go build ./... | |
| fi | |
| - name: Run tests with coverage for ${{ matrix.name }} | |
| if: steps.should-run.outputs.run == 'true' | |
| run: | | |
| cd ${{ matrix.path }} | |
| go test -v -race -coverprofile=coverage.out -covermode=atomic -timeout 15m ./... | |
| go tool cover -func=coverage.out | grep total | awk '{print "Coverage: " $3}' | |
| env: | |
| PRISM_TEST_QUIET: "1" | |
| # Skip testcontainers backends with known driver bugs | |
| # TODO: Remove once NATS metadata preservation and SQLite GetEvent are fixed | |
| PRISM_SKIP_CONTAINERS: "1" | |
| - name: Upload coverage | |
| if: steps.should-run.outputs.run == 'true' | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: coverage-${{ matrix.artifact }} | |
| path: ${{ matrix.path }}/coverage.out | |
| retention-days: 7 | |
| # Job 4: Integration tests | |
| # Note: Job always runs to provide visibility. Steps skip when tests aren't needed. | |
| test-integration: | |
| name: Integration Tests | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| needs: [generate-matrix, generate-proto] | |
| # No job-level 'if' - provides consistent visibility in CI UI | |
| steps: | |
| - name: Check if integration tests should run | |
| id: should-run | |
| run: | | |
| # Debug: Show execution decision | |
| { | |
| echo "## Integration Tests Configuration" | |
| echo "" | |
| echo "| Input | Value |" | |
| echo "|-------|-------|" | |
| echo "| run_full | \`${{ needs.generate-matrix.outputs.run_full }}\` |" | |
| echo "| has_test | \`${{ needs.generate-matrix.outputs.has_test }}\` |" | |
| echo "" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| if [[ "${{ needs.generate-matrix.outputs.run_full }}" == "true" ]] || \ | |
| [[ "${{ needs.generate-matrix.outputs.has_test }}" == "true" ]]; then | |
| echo "run=true" >> "$GITHUB_OUTPUT" | |
| echo "**Decision:** Running integration tests" >> "$GITHUB_STEP_SUMMARY" | |
| else | |
| echo "run=false" >> "$GITHUB_OUTPUT" | |
| echo "**Decision:** Skipping integration tests (no test-related changes detected)" >> "$GITHUB_STEP_SUMMARY" | |
| echo "::notice::Skipping integration tests - no test-related changes detected" | |
| fi | |
| - name: Checkout | |
| if: steps.should-run.outputs.run == 'true' | |
| uses: actions/checkout@v7 | |
| - name: Setup Go | |
| if: steps.should-run.outputs.run == 'true' | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version: "1.25.4" | |
| cache: false # We'll use custom cache for more control | |
| - name: Cache Go dependencies and build cache | |
| if: steps.should-run.outputs.run == 'true' | |
| uses: actions/cache@v5 | |
| with: | |
| path: | | |
| ~/go/pkg/mod | |
| ~/.cache/go-build | |
| key: ${{ runner.os }}-go-integration-${{ hashFiles('**/go.sum', '**/go.mod') }} | |
| restore-keys: | | |
| ${{ runner.os }}-go-integration- | |
| ${{ runner.os }}-go- | |
| - name: Download generated proto code | |
| if: steps.should-run.outputs.run == 'true' | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: proto-generated | |
| path: pkg/plugin/gen/ | |
| - name: Run integration tests with coverage | |
| if: steps.should-run.outputs.run == 'true' | |
| run: | | |
| cd tests/integration | |
| go test -v -race -coverprofile=coverage.out -covermode=atomic -timeout 5m ./... | |
| go tool cover -func=coverage.out | grep total | awk '{print "Integration Coverage: " $3}' | |
| - name: Upload coverage | |
| if: steps.should-run.outputs.run == 'true' | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: coverage-integration | |
| path: tests/integration/coverage.out | |
| retention-days: 7 | |
| # Job 6: Validate documentation | |
| validate-docs: | |
| name: Validate Documentation | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: "20" | |
| cache: "npm" | |
| cache-dependency-path: docusaurus/package-lock.json | |
| - name: Install Node dependencies | |
| run: cd docusaurus && npm ci | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v7 | |
| with: | |
| version: "latest" | |
| enable-cache: true | |
| - name: Setup Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.11" | |
| - name: Validate documentation | |
| run: uv run tooling/validate_docs.py | |
| # Job 7: Build all components | |
| # Unified build status (skipped, parallel builds above replace this) | |
| build: | |
| name: Build All Components | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| needs: [build-rust, build-go, test-proxy, test-patterns, test-integration] | |
| if: always() | |
| steps: | |
| - name: Check build status | |
| run: | | |
| if [ "${{ needs.build-rust.result }}" != "success" ]; then | |
| echo "❌ Rust build failed" | |
| exit 1 | |
| fi | |
| if [ "${{ needs.build-go.result }}" != "success" ]; then | |
| echo "❌ Go build failed" | |
| exit 1 | |
| fi | |
| echo "✅ All builds passed" | |
| # Job 8: Coverage summary | |
| coverage-summary: | |
| name: Coverage Summary | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| needs: [test-proxy, test-patterns, test-integration] | |
| if: always() | |
| steps: | |
| - name: Download all coverage reports | |
| uses: actions/download-artifact@v8 | |
| with: | |
| pattern: coverage-* | |
| path: build/coverage-reports | |
| - name: Display coverage summary | |
| run: | | |
| { | |
| echo "## Coverage Summary" | |
| echo "" | |
| echo "| Component | Coverage Files |" | |
| echo "|-----------|----------------|" | |
| for dir in build/coverage-reports/*/; do | |
| component=$(basename "$dir") | |
| if [ -f "$dir/coverage.out" ]; then | |
| echo "| $component | ✅ Present |" | |
| else | |
| echo "| $component | ❌ Missing |" | |
| fi | |
| done | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| # Job 9: Upload to Codecov | |
| codecov-upload: | |
| name: Upload Coverage to Codecov | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| needs: [test-proxy, test-patterns, test-integration] | |
| if: always() | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Download all coverage reports | |
| uses: actions/download-artifact@v8 | |
| with: | |
| pattern: coverage-* | |
| path: build/coverage-reports | |
| - name: Upload coverage reports to Codecov | |
| uses: codecov/codecov-action@v7 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| files: >- | |
| build/coverage-reports/coverage-rust/cobertura.xml, | |
| build/coverage-reports/coverage-memstore/coverage.out, | |
| build/coverage-reports/coverage-redis/coverage.out, | |
| build/coverage-reports/coverage-nats/coverage.out, | |
| build/coverage-reports/coverage-consumer/coverage.out, | |
| build/coverage-reports/coverage-producer/coverage.out, | |
| build/coverage-reports/coverage-multicast-registry/coverage.out, | |
| build/coverage-reports/coverage-keyvalue/coverage.out, | |
| build/coverage-reports/coverage-mailbox/coverage.out, | |
| build/coverage-reports/coverage-inference/coverage.out, | |
| build/coverage-reports/coverage-integration/coverage.out, | |
| build/coverage-reports/coverage-acceptance-keyvalue/coverage.out, | |
| build/coverage-reports/coverage-acceptance-consumer/coverage.out, | |
| build/coverage-reports/coverage-acceptance-producer/coverage.out, | |
| build/coverage-reports/coverage-acceptance-claimcheck/coverage.out, | |
| build/coverage-reports/coverage-acceptance-unified/coverage.out, | |
| build/coverage-reports/coverage-acceptance-mailbox/coverage.out | |
| flags: unittests,integration,acceptance | |
| name: prism-coverage | |
| fail_ci_if_error: false | |
| verbose: true | |
| # Job 10: CI status check (required for merge) | |
| ci-status: | |
| name: CI Status Check | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| needs: | |
| [ | |
| lint-rust, | |
| lint-python, | |
| lint-proto, | |
| lint-github-actions, | |
| lint-go, | |
| test-proxy, | |
| test-patterns, | |
| test-integration, | |
| validate-docs, | |
| build, | |
| ] | |
| if: always() | |
| steps: | |
| - name: Check all jobs status | |
| id: ci_check | |
| run: | | |
| # Accept both "success" and "skipped" as passing states | |
| check_job() { | |
| local result="$1" | |
| [[ "$result" == "success" ]] || [[ "$result" == "skipped" ]] | |
| } | |
| if ! check_job "${{ needs.lint-rust.result }}" || \ | |
| ! check_job "${{ needs.lint-python.result }}" || \ | |
| ! check_job "${{ needs.lint-proto.result }}" || \ | |
| ! check_job "${{ needs.lint-github-actions.result }}" || \ | |
| ! check_job "${{ needs.lint-go.result }}" || \ | |
| ! check_job "${{ needs.test-proxy.result }}" || \ | |
| ! check_job "${{ needs.test-patterns.result }}" || \ | |
| ! check_job "${{ needs.test-integration.result }}" || \ | |
| ! check_job "${{ needs.validate-docs.result }}" || \ | |
| ! check_job "${{ needs.build.result }}"; then | |
| echo "❌ CI pipeline failed" | |
| echo "status=failure" >> "$GITHUB_OUTPUT" | |
| exit 1 | |
| else | |
| echo "✅ CI pipeline passed" | |
| echo "status=success" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: send-ntfy-notification | |
| if: always() && vars.NTFY_TOPIC != '' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PR_TITLE: ${{ github.event.pull_request.title }} | |
| PR_BODY: ${{ github.event.pull_request.body }} | |
| run: | | |
| STATUS_EMOJI="${{ steps.ci_check.outputs.status == 'success' && '✅' || '❌' }}" | |
| STATUS_TEXT="${{ steps.ci_check.outputs.status == 'success' && 'Passed' || 'Failed' }}" | |
| PRIORITY="${{ steps.ci_check.outputs.status == 'success' && 'default' || 'high' }}" | |
| TAG="${{ steps.ci_check.outputs.status == 'success' && 'white_check_mark' || 'x' }}" | |
| # Get PR information if this is a PR | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| PR_NUMBER="${{ github.event.pull_request.number }}" | |
| # Extract first 2-3 lines of PR description (safely handle special chars) | |
| PR_DESC_SHORT=$(printf '%s\n' "$PR_BODY" | head -3 | sed 's/^/ /') | |
| # Get list of changed files for summary | |
| CHANGED_FILES=$(gh pr view "$PR_NUMBER" --repo "${{ github.repository }}" --json files --jq '.files[].path' | head -5) | |
| FILE_COUNT=$(gh pr view "$PR_NUMBER" --repo "${{ github.repository }}" --json files --jq '.files | length') | |
| # Build changed files summary | |
| if [ "$FILE_COUNT" -gt 0 ]; then | |
| CHANGES_SUMMARY="Changed Files ($FILE_COUNT total):" | |
| CHANGES_SUMMARY="$CHANGES_SUMMARY | |
| $(echo "$CHANGED_FILES" | head -5 | sed 's/^/ • /')" | |
| if [ "$FILE_COUNT" -gt 5 ]; then | |
| CHANGES_SUMMARY="$CHANGES_SUMMARY | |
| ... and $((FILE_COUNT - 5)) more" | |
| fi | |
| else | |
| CHANGES_SUMMARY="No files changed" | |
| fi | |
| PR_INFO="PR #${PR_NUMBER}: ${PR_TITLE} | |
| Description: | |
| ${PR_DESC_SHORT} | |
| ${CHANGES_SUMMARY} | |
| " | |
| else | |
| PR_INFO="Push to ${{ github.ref_name }}" | |
| fi | |
| # Build job results summary | |
| JOBS="Lint Rust: ${{ needs.lint-rust.result == 'success' && '✅' || '❌' }} | |
| Lint Python: ${{ needs.lint-python.result == 'success' && '✅' || '❌' }} | |
| Lint Protobuf: ${{ needs.lint-proto.result == 'success' && '✅' || '❌' }} | |
| Lint GitHub Actions: ${{ needs.lint-github-actions.result == 'success' && '✅' || '❌' }} | |
| Lint Go: ${{ needs.lint-go.result == 'success' && '✅' || '❌' }} | |
| Test Proxy: ${{ needs.test-proxy.result == 'success' && '✅' || '❌' }} | |
| Test Patterns: ${{ needs.test-patterns.result == 'success' && '✅' || '❌' }} | |
| Test Integration: ${{ needs.test-integration.result == 'success' && '✅' || '❌' }} | |
| Validate Docs: ${{ needs.validate-docs.result == 'success' && '✅' || '❌' }} | |
| Build: ${{ needs.build.result == 'success' && '✅' || '❌' }}" | |
| curl -H "Title: ${STATUS_EMOJI} CI Pipeline ${STATUS_TEXT}" \ | |
| -H "Priority: ${PRIORITY}" \ | |
| -H "Tags: ${TAG},ci" \ | |
| -H "Click: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \ | |
| -d "${PR_INFO} | |
| Repository: ${{ github.repository }} | |
| Branch: ${{ github.ref_name }} | |
| Commit: ${GITHUB_SHA:0:7} | |
| Author: ${{ github.actor }} | |
| Job Results: | |
| ${JOBS}" \ | |
| https://ntfy.sh/${{ vars.NTFY_TOPIC }} |