feat(proxy): request body cap + per-tenant rate limit plumbing #45
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
| # E2E PR-Gate Workflow (Loop E2E-L9 of #91, issue #99). | |
| # | |
| # Fast must-green subset of the adversarial test framework. Runs on | |
| # every PR that touches the proxy, security analyzers, e2e harness, the | |
| # corpus, or the workflow file itself. Wall-clock target ≤ 5 min; | |
| # 10 min hard cap per L9 acceptance. | |
| # | |
| # Selection: scenarios tagged `pr-gate` (20 of 50 today). Drives the | |
| # `--tag=pr-gate` filter in `tests/e2e/conftest.py::pytest_addoption`. | |
| # | |
| # The full corpus runs nightly (Loop E2E-L10, e2e-nightly.yml). Adding | |
| # new must-green scenarios = tag them `pr-gate`; the workflow picks | |
| # them up on the next PR with no edits here. | |
| name: E2E PR Gate | |
| on: | |
| pull_request: | |
| paths: | |
| - 'crates/llmtrace-proxy/**' | |
| - 'crates/llmtrace-security/**' | |
| - 'crates/llmtrace-core/**' | |
| - 'crates/llmtrace-storage/**' | |
| - 'config*.yaml' | |
| - 'benchmarks/attacks/**' | |
| - 'tests/e2e/**' | |
| - 'scripts/e2e/**' | |
| - 'requirements-e2e.txt' | |
| - 'Cargo.lock' | |
| - 'pytest.ini' | |
| - '.github/workflows/e2e-pr.yml' | |
| workflow_dispatch: | |
| # Cancel in-flight runs when a PR is force-pushed; only the latest | |
| # state matters and the proxy build is the slow step. | |
| concurrency: | |
| group: e2e-pr-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| e2e-pr-gate: | |
| name: E2E PR Gate | |
| runs-on: ubuntu-latest | |
| # 10-minute hard cap per L9 acceptance. If we hit this, the | |
| # `pr-gate` tag set is the knob: drop scenarios from it rather | |
| # than raising the cap. The full corpus is nightly's job. | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 | |
| with: | |
| python-version: "3.12" | |
| - name: Install Python deps | |
| run: python3 -m pip install -r requirements-e2e.txt | |
| # E2E-012 from L2: validate the corpus before we boot anything. | |
| # Cheap (<1 s) and saves 5+ min when a malformed YAML lands. | |
| - name: Validate scenarios | |
| run: python3 scripts/e2e/validate_scenarios.py | |
| - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable | |
| # protoc is required by llmtrace-proto build.rs. | |
| - name: Install protoc | |
| run: sudo apt-get update && sudo apt-get install -y protobuf-compiler | |
| # Cargo cache keyed on Cargo.lock per L9 spec. The release build of | |
| # the proxy is the dominant cost (~3 min cold, <30 s warm). | |
| - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 | |
| with: | |
| path: | | |
| ~/.cargo/bin/ | |
| ~/.cargo/registry/index/ | |
| ~/.cargo/registry/cache/ | |
| ~/.cargo/git/db/ | |
| target/ | |
| key: ${{ runner.os }}-cargo-e2e-pr-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-e2e-pr- | |
| # Release build of the proxy only — the cascade fixture spawns | |
| # this binary. Avoid `cargo build --workspace` which would compile | |
| # bindings (nodejs, python, wasm) we don't need at runtime. | |
| - name: Build llmtrace-proxy (release) | |
| run: cargo build --release --manifest-path crates/llmtrace-proxy/Cargo.toml | |
| # Run the pr-gate subset against the in-process FastAPI mock | |
| # upstream. Mock-only here because real-LLM cost belongs in | |
| # nightly (L10). JUnit XML lets the Actions UI render per-scenario | |
| # rows on a failure. | |
| - name: Run pr-gate scenarios | |
| run: | | |
| python3 -m pytest tests/e2e/test_cascade.py \ | |
| --tag=pr-gate \ | |
| -v \ | |
| --junit-xml=tests/e2e/.logs/junit-pr-gate.xml \ | |
| --color=yes | |
| # Upload artifacts unconditionally so a failed run still gives the | |
| # triager something to download. Logs include proxy stdout/stderr | |
| # captured by the conftest fixture, which is the highest-ROI thing | |
| # to read first when a scenario fails. | |
| - name: Upload junit + proxy logs | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: e2e-pr-gate-artifacts | |
| path: | | |
| tests/e2e/.logs/ | |
| if-no-files-found: warn | |
| # Help the next reader if the job times out. The Actions UI shows | |
| # "the operation was canceled" on timeout-minutes hit; this echoes | |
| # the actionable knob into the same step output. | |
| - name: Timeout helper | |
| if: cancelled() | |
| run: | | |
| echo "::error::PR-gate exceeded 10 min cap." | |
| echo "Knob: edit benchmarks/attacks/**/*.yaml to drop" | |
| echo "scenarios from the 'pr-gate' tag set, or move slow" | |
| echo "scenarios to the nightly job (e2e-nightly.yml, L10)." | |
| exit 1 |