Skip to content

feat(api): add by-job-id PII approve/deny endpoints #258

feat(api): add by-job-id PII approve/deny endpoints

feat(api): add by-job-id PII approve/deny endpoints #258

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
# Cancel in-progress runs for the same branch/PR
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
PYTHON_VERSION: "3.11"
jobs:
# ============================================================================
# Stage 1: Fast feedback (< 2 min)
# ============================================================================
unit-tests:
name: Unit Tests
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Free disk space
run: |
sudo rm -rf /opt/hostedtoolcache/CodeQL /opt/hostedtoolcache/go \
/opt/hostedtoolcache/Ruby /usr/share/dotnet /usr/local/lib/android \
/usr/local/share/boost /usr/local/.ghcup /opt/ghc /usr/share/swift &
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Cache uv
uses: actions/cache@v4
with:
path: ~/.cache/uv
key: uv-${{ runner.os }}-${{ hashFiles('pyproject.toml') }}
restore-keys: uv-${{ runner.os }}-
- name: Install uv and dependencies
run: |
pip install uv
uv sync --all-extras
- name: Run unit tests
env:
ANTHROPIC_API_KEY: test-key-for-ci
AWS_ACCESS_KEY_ID: test
AWS_SECRET_ACCESS_KEY: test
AWS_DEFAULT_REGION: us-east-1
run: |
uv run pytest tests/unit \
-v \
--tb=short \
-m "unit" \
--cov=src \
--cov-report=term-missing \
--cov-report=xml \
--maxfail=5 \
--durations=10
- name: Upload coverage
uses: actions/upload-artifact@v4
if: always()
with:
name: unit-coverage
path: coverage.xml
retention-days: 7
# ============================================================================
# Stage 2: Integration tests with real services (< 5 min)
# ============================================================================
integration-tests:
name: Integration Tests
runs-on: ubuntu-latest
timeout-minutes: 10
needs: unit-tests
services:
redis:
image: redis:7-alpine
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 6379:6379
floci:
image: hectorvent/floci:1.5.3
env:
FLOCI_DEFAULT_REGION: us-east-1
options: >-
--health-cmd "curl -sf http://localhost:4566/ || exit 1"
--health-interval 10s
--health-timeout 5s
--health-retries 10
ports:
- 4566:4566
steps:
- name: Free disk space
run: |
sudo rm -rf /opt/hostedtoolcache/CodeQL /opt/hostedtoolcache/go \
/opt/hostedtoolcache/Ruby /usr/share/dotnet /usr/local/lib/android \
/usr/local/share/boost /usr/local/.ghcup /opt/ghc /usr/share/swift &
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Cache uv
uses: actions/cache@v4
with:
path: ~/.cache/uv
key: uv-${{ runner.os }}-${{ hashFiles('pyproject.toml') }}
restore-keys: uv-${{ runner.os }}-
- name: Install uv and dependencies
run: |
pip install uv
uv sync --all-extras
- name: Initialize S3 buckets on Floci
# GitHub Actions service containers can't run init sidecars, so the
# same init-aws.sh that docker-compose.dev.yml uses is invoked here
# against the floci service container. AWS_ENDPOINT_URL routes every
# `aws` command to Floci; see infrastructure/floci/init-aws.sh.
env:
AWS_ACCESS_KEY_ID: test
AWS_SECRET_ACCESS_KEY: test
AWS_DEFAULT_REGION: us-east-1
AWS_ENDPOINT_URL: http://localhost:4566
run: |
pip install awscli
bash infrastructure/floci/init-aws.sh
- name: Run integration tests
env:
ANTHROPIC_API_KEY: test-key-for-ci
AWS_ACCESS_KEY_ID: test
AWS_SECRET_ACCESS_KEY: test
AWS_DEFAULT_REGION: us-east-1
AWS_ENDPOINT_URL: http://localhost:4566
REDIS_URL: redis://localhost:6379
SKIP_BEDROCK_TESTS: "1"
run: |
uv run pytest tests/integration \
-v \
--tb=short \
-m "integration" \
--cov=src \
--cov-report=term-missing \
--cov-report=xml \
--maxfail=5 \
--durations=10
- name: Upload coverage
uses: actions/upload-artifact@v4
if: always()
with:
name: integration-coverage
path: coverage.xml
retention-days: 7
# ============================================================================
# Stage 3: E2E tests - full Docker stack (< 10 min)
# Only runs on PRs to main and pushes to main
# ============================================================================
e2e-tests:
name: E2E Tests (Docker)
runs-on: ubuntu-latest
timeout-minutes: 25
needs: integration-tests
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Free disk space
run: |
sudo rm -rf /opt/hostedtoolcache /usr/share/dotnet /usr/local/lib/android \
/usr/local/share/boost /usr/local/.ghcup /opt/ghc /usr/share/swift \
/usr/local/share/chromium /usr/local/share/powershell /usr/local/graalvm \
/usr/local/julia* /usr/local/sqlpackage /opt/pipx
sudo apt-get clean || true
docker system prune -af --volumes || true
df -h /
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Cache Docker layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: docker-${{ runner.os }}-${{ hashFiles('Dockerfile', 'pyproject.toml', 'uv.lock') }}
restore-keys: docker-${{ runner.os }}-
- name: Create .env file
run: cp .env.example .env
- name: Build Docker image with cache
run: |
docker buildx build \
--file Dockerfile \
--target development \
--cache-from type=local,src=/tmp/.buildx-cache \
--cache-to type=local,dest=/tmp/.buildx-cache-new,mode=max \
--load \
-t equalify-reflow:ci \
.
- name: Rotate buildx cache
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
- name: Start services
run: |
docker compose -f docker-compose.yml -f docker-compose.ci.yml up -d --no-build
- name: Wait for services
run: |
echo "Waiting for API Gateway..."
timeout 90 bash -c 'until docker compose -f docker-compose.yml -f docker-compose.ci.yml exec -T api-gateway echo "ready" 2>/dev/null; do sleep 3; done'
- name: Run E2E tests
run: |
docker compose -f docker-compose.yml -f docker-compose.ci.yml exec -T \
-e SKIP_BEDROCK_TESTS=1 \
api-gateway \
uv run pytest tests/e2e \
-v \
--tb=short \
-m "slow" \
--maxfail=3 \
--durations=20
- name: Collect logs on failure
if: failure()
run: |
docker compose -f docker-compose.yml -f docker-compose.ci.yml logs > docker-logs.txt 2>&1
- name: Upload logs on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: docker-logs
path: docker-logs.txt
retention-days: 7
- name: Stop services
if: always()
run: docker compose -f docker-compose.yml -f docker-compose.ci.yml down -v
# ============================================================================
# Final status check for branch protection
# ============================================================================
ci-success:
name: CI Success
runs-on: ubuntu-latest
needs: [unit-tests, integration-tests, e2e-tests]
if: always()
steps:
- name: Check all jobs passed
run: |
if [[ "${{ needs.unit-tests.result }}" != "success" ]] || \
[[ "${{ needs.integration-tests.result }}" != "success" ]] || \
[[ "${{ needs.e2e-tests.result }}" != "success" ]]; then
echo "::error::One or more CI jobs failed"
exit 1
fi
echo "All CI jobs passed successfully"