ci: populate baselines and gate benchmarks at +20% (#91) #200
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: | |
| - master | |
| pull_request: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| prod-install-smoke: | |
| name: Verify prod requirements.txt is sufficient | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| persist-credentials: false | |
| - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 | |
| with: | |
| python-version: "3.12" | |
| cache: pip | |
| cache-dependency-path: requirements.txt | |
| - name: Install production dependencies only | |
| run: pip install -r requirements.txt | |
| # Boots the app under test_client without pytest. Catches the case | |
| # where someone adds a real third-party import (e.g. werkzeug.X) | |
| # to the source without bumping requirements.txt — that would | |
| # surface here as ImportError before merge. | |
| - name: Import + boot app | |
| run: | | |
| python - <<'PY' | |
| from app import create_app | |
| app = create_app() | |
| client = app.test_client() | |
| assert client.get("/").status_code == 200 | |
| PY | |
| lint-and-audit: | |
| name: ruff + pip-audit (${{ matrix.os }}) | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, windows-latest, macos-latest] | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| persist-credentials: false | |
| - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 | |
| with: | |
| python-version: "3.12" | |
| cache: pip | |
| cache-dependency-path: | | |
| requirements.txt | |
| requirements-dev.txt | |
| - name: Install dev dependencies | |
| run: pip install -r requirements-dev.txt | |
| - name: Ruff | |
| run: ruff check . | |
| - name: Ruff format check | |
| run: ruff format --check . | |
| - name: pip-audit | |
| run: pip-audit -r requirements.txt | |
| pytest: | |
| name: pytest (${{ matrix.os }}) | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, windows-latest, macos-latest] | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| persist-credentials: false | |
| - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 | |
| with: | |
| python-version: "3.12" | |
| cache: pip | |
| cache-dependency-path: | | |
| requirements.txt | |
| requirements-dev.txt | |
| - name: Install dev dependencies (Flask + pytest) | |
| run: pip install -r requirements-dev.txt | |
| # Full suite gate (pyproject addopts include --cov + 60% fail-under). | |
| - name: Run tests | |
| run: pytest --tb=short -q | |
| mypy: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| persist-credentials: false | |
| - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 | |
| with: | |
| python-version: "3.12" | |
| cache: pip | |
| cache-dependency-path: | | |
| requirements.txt | |
| requirements-dev.txt | |
| - name: Install dev dependencies | |
| run: pip install -r requirements-dev.txt | |
| - name: Run mypy (strict, production packages) | |
| run: mypy -p api -p utils -p models | |
| - name: Run mypy on tests (non-strict smoke) | |
| run: mypy tests --config-file mypy-tests.ini --follow-imports skip | |
| integration-tests: | |
| name: API integration tests + coverage (${{ matrix.os }}) | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, windows-latest, macos-latest] | |
| permissions: | |
| contents: read | |
| actions: write | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| persist-credentials: false | |
| - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 | |
| with: | |
| python-version: "3.12" | |
| cache: pip | |
| cache-dependency-path: | | |
| requirements.txt | |
| requirements-dev.txt | |
| - name: Install dev dependencies | |
| run: pip install -r requirements-dev.txt | |
| # Intentional overlap with pytest job: re-runs API/search subset with --cov | |
| # and uploads coverage.xml per OS (pytest job is the full-suite gate). | |
| - name: Run integration tests with coverage | |
| run: pytest tests/test_api_integration.py tests/test_search.py -v --cov=api --cov=utils --cov-report=xml --cov-fail-under=0 | |
| - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: coverage-report-${{ matrix.os }} | |
| path: coverage.xml | |
| js-tests: | |
| name: Frontend unit tests (vitest) (${{ matrix.os }}) | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, windows-latest, macos-latest] | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| persist-credentials: false | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: "20" | |
| cache: npm | |
| - run: npm ci | |
| # npm optional-deps bug: platform rollup binaries are sometimes skipped after npm ci | |
| # (https://github.com/npm/cli/issues/4828). Install the native package explicitly. | |
| - name: Ensure Rollup native binary | |
| shell: bash | |
| run: | | |
| ROLLUP_VERSION=$(node -p "require('./node_modules/rollup/package.json').version") | |
| case "$(node -p "process.platform + '-' + process.arch")" in | |
| darwin-arm64) PKG="@rollup/rollup-darwin-arm64" ;; | |
| darwin-x64) PKG="@rollup/rollup-darwin-x64" ;; | |
| linux-x64) PKG="@rollup/rollup-linux-x64-gnu" ;; | |
| win32-x64) PKG="@rollup/rollup-win32-x64-msvc" ;; | |
| win32-arm64) PKG="@rollup/rollup-win32-arm64-msvc" ;; | |
| *) echo "Unsupported Node platform: $(node -p "process.platform + '-' + process.arch")"; exit 1 ;; | |
| esac | |
| npm install --no-save "${PKG}@${ROLLUP_VERSION}" | |
| # Same vitest suite as npm test; --coverage enforces thresholds in vitest.config.js | |
| - run: npm run test:coverage | |
| benchmarks: | |
| name: Performance benchmarks (gated) | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| persist-credentials: false | |
| - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 | |
| with: | |
| python-version: "3.12" | |
| cache: pip | |
| cache-dependency-path: | | |
| requirements.txt | |
| requirements-dev.txt | |
| - name: Install dev dependencies | |
| run: pip install -r requirements-dev.txt | |
| - name: Run benchmarks | |
| run: > | |
| pytest tests/benchmarks/ | |
| --benchmark-only | |
| --benchmark-json=benchmark-results.json | |
| --benchmark-columns=min,max,mean,stddev,rounds | |
| -o addopts= | |
| - name: Regression gate | |
| run: python scripts/check_benchmark_regression.py benchmark-results.json benchmarks/baselines.json | |
| - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| if: always() | |
| with: | |
| name: benchmark-results | |
| path: benchmark-results.json |