diff --git a/.github/workflows/load-precheck.yml b/.github/workflows/load-precheck.yml new file mode 100644 index 0000000..c1e80e8 --- /dev/null +++ b/.github/workflows/load-precheck.yml @@ -0,0 +1,175 @@ +name: Load — Precheck baseline + +on: + schedule: + # Weekly, Mondays 07:00 UTC. + - cron: '0 7 * * 1' + workflow_dispatch: + inputs: + commit_baseline: + description: 'Commit the run as a new baseline under load/baselines/' + type: boolean + default: false + precheck_ref: + description: 'governs-ai/precheck git ref to build from' + required: false + default: 'main' + +permissions: + contents: write + pull-requests: write + +jobs: + load-precheck: + name: k6 load test (ramp 10→1000 RPS over 5m) + runs-on: ubuntu-latest + timeout-minutes: 30 + + services: + postgres: + image: pgvector/pgvector:pg15 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: governs_ci + ports: + - 5432:5432 + options: >- + --health-cmd "pg_isready -U postgres" + --health-interval 10s + --health-timeout 5s + --health-retries 10 + redis: + image: redis:7-alpine + ports: + - 6379:6379 + options: >- + --health-cmd "redis-cli ping" + --health-interval 10s + --health-timeout 5s + --health-retries 10 + + env: + PRECHECK_API_KEY: ci-load-test-key + PRECHECK_BASE_URL: http://localhost:8080 + DATABASE_URL: postgresql://postgres:postgres@localhost:5432/governs_ci + REDIS_URL: redis://localhost:6379/0 + + steps: + - name: Checkout tests repo + uses: actions/checkout@v4 + with: + path: tests + + - name: Checkout precheck service + uses: actions/checkout@v4 + with: + repository: governs-ai/precheck + ref: ${{ github.event.inputs.precheck_ref || 'main' }} + path: precheck + token: ${{ secrets.GOVERNSAI_CI_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build precheck image + uses: docker/build-push-action@v6 + with: + context: precheck + load: true + tags: governs-ai/precheck:ci + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Run precheck container + run: | + docker run -d --name precheck \ + --network host \ + -e APP_BIND=0.0.0.0:8080 \ + -e DATABASE_URL="$DATABASE_URL" \ + -e REDIS_URL="$REDIS_URL" \ + -e PRECHECK_API_KEY="$PRECHECK_API_KEY" \ + -e PII_DETECTION_ENABLED=true \ + governs-ai/precheck:ci + + - name: Wait for precheck /api/v1/health + run: | + for i in $(seq 1 60); do + if curl -fsS http://localhost:8080/api/v1/health >/dev/null 2>&1; then + echo "precheck ready after ${i}s" + exit 0 + fi + sleep 2 + done + echo "precheck did not become healthy in time" + docker logs precheck || true + exit 1 + + - name: Install k6 + run: | + sudo gpg -k + sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg \ + --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69 + echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" \ + | sudo tee /etc/apt/sources.list.d/k6.list + sudo apt-get update + sudo apt-get install -y k6 + + - name: Run k6 load test + working-directory: tests + env: + SUMMARY_PATH: load/baselines/precheck-latest.json + run: | + mkdir -p load/baselines + k6 run load/precheck-baseline.js + + - name: Regression check vs previous baseline + id: regression + working-directory: tests + run: node load/compare-baseline.mjs --summary load/baselines/precheck-latest.json + + - name: Archive k6 summary + if: always() + uses: actions/upload-artifact@v4 + with: + name: k6-precheck-summary + path: tests/load/baselines/precheck-latest.json + if-no-files-found: error + + - name: Commit new baseline (manual dispatch only) + if: >- + success() && + github.event_name == 'workflow_dispatch' && + github.event.inputs.commit_baseline == 'true' + working-directory: tests + run: | + DATE=$(date -u +%Y-%m-%d) + DEST="load/baselines/precheck-${DATE}.json" + if [ -e "$DEST" ]; then + echo "Baseline $DEST already exists — baselines are append-only. Re-run after midnight UTC or pick a new label." + exit 1 + fi + cp load/baselines/precheck-latest.json "$DEST" + git config user.name "governsai-ci" + git config user.email "ci@governs.ai" + git checkout -b "chore/baseline-${DATE}-${GITHUB_RUN_ID}" + git add "$DEST" + git commit -m "chore(load): record precheck baseline ${DATE} + +Recorded from scheduled k6 run. Refs GOV-566." + git push --set-upstream origin "chore/baseline-${DATE}-${GITHUB_RUN_ID}" + gh pr create \ + --base dev \ + --title "chore(load): precheck baseline ${DATE}" \ + --body "Automated baseline capture from load-precheck workflow run ${GITHUB_RUN_ID}. Tag Forge for review." + env: + GH_TOKEN: ${{ secrets.GOVERNSAI_CI_TOKEN }} + + - name: Notify on regression + if: failure() && steps.regression.outcome == 'failure' + run: | + echo "::error::Precheck load test regressed beyond the 20% P99 threshold or crossed the 0.1% error-rate SLO. See artifact k6-precheck-summary." + + - name: Collect precheck logs on failure + if: failure() + run: docker logs precheck || true diff --git a/README.md b/README.md index ea6fcee..8de2449 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,27 @@ npx playwright test e2e/smoke.spec.ts ```bash npm run test:load # or directly: -PRECHECK_BASE_URL=http://localhost:8080 k6 run load/precheck-baseline.js +PRECHECK_BASE_URL=http://localhost:3080 k6 run load/precheck-baseline.js ``` -Record a new baseline under `load/baselines/YYYY-MM-DD-