feat: unify /ai/v1 auth onto NativeAuth + Android session-expiry hand… #191
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: server-ci | |
| on: | |
| push: | |
| paths: | |
| - "server/**" | |
| - ".github/workflows/server-ci.yaml" | |
| - "core/identity/src/test/resources/identity/**" | |
| pull_request: | |
| paths: | |
| - "server/**" | |
| - ".github/workflows/server-ci.yaml" | |
| - "core/identity/src/test/resources/identity/**" | |
| # Cancel superseded runs on the same branch / PR. | |
| concurrency: | |
| group: server-ci-${{ github.ref }} | |
| cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} | |
| jobs: | |
| test: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| mode: [full, sync_only, ai_only] | |
| defaults: | |
| run: | |
| working-directory: server | |
| env: | |
| # Deploy mode matrix: each cell exports the env vars that gate the app. | |
| # Tests that depend on a specific mode (lazy-imports, modes, readyz) | |
| # parameterize their own expectations; mode-agnostic tests run in every | |
| # cell to surface any regression that only shows up under a non-default | |
| # configuration. | |
| QUIRE_SERVER_PROGRESS_ENABLED: ${{ matrix.mode != 'ai_only' && 'true' || 'false' }} | |
| QUIRE_SERVER_AI_ENABLED: ${{ matrix.mode != 'sync_only' && 'true' || 'false' }} | |
| steps: | |
| - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 | |
| - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 | |
| - name: Set up Python | |
| run: uv python install 3.12 | |
| - name: Install | |
| run: | | |
| uv venv | |
| uv pip install -e ".[dev]" | |
| - name: Lint | |
| if: matrix.mode == 'full' | |
| run: uv run ruff check . && uv run ruff format --check . | |
| - name: Install ripgrep (rename guard) | |
| if: matrix.mode == 'full' | |
| run: | | |
| if ! command -v rg >/dev/null; then | |
| sudo apt-get update && sudo apt-get install -y ripgrep | |
| fi | |
| - name: Rename guard self-test | |
| if: matrix.mode == 'full' | |
| working-directory: ${{ github.workspace }} | |
| run: | | |
| set -e | |
| # Proves the rename-guard regex catches the previously-missed | |
| # OPDS_SYNC_FOO pattern (the v1 plan's regex relied on \b after | |
| # `_`, which is not a word boundary before letters). | |
| tmp=$(mktemp /tmp/rename-guard-self-test-XXXX.txt) | |
| printf 'OPDS_SYNC_AI_ENABLED: true\n' > "$tmp" | |
| if ! rg -P '\bopds[-_]sync\b|(?<![A-Z0-9_])OPDS_SYNC_[A-Z0-9_]*' "$tmp" >/dev/null; then | |
| echo "::error::Rename guard regex failed self-test (did NOT match OPDS_SYNC_AI_ENABLED)." | |
| rm -f "$tmp" | |
| exit 1 | |
| fi | |
| rm -f "$tmp" | |
| - name: Rename guard | |
| if: matrix.mode == 'full' | |
| working-directory: ${{ github.workspace }} | |
| run: | | |
| set -o pipefail | |
| # Lock #20 allowlist: | |
| # .claude/, CHANGELOG.md, server/migrations/versions/, | |
| # root README.md, server/docker-compose*.yml, | |
| # server/alembic.ini, server/.env.example (DB-name / deprecation refs). | |
| # Defensive: tests/conftest.py + the import-graph regression test | |
| # reference the legacy names by design. | |
| if rg -n -P '\bopds[-_]sync\b|(?<![A-Z0-9_])OPDS_SYNC_[A-Z0-9_]*' \ | |
| -g '!.claude/**' \ | |
| -g '!CHANGELOG.md' \ | |
| -g '!server/migrations/versions/*.py' \ | |
| -g '!server/quire_server/config.py' \ | |
| -g '!server/docker-compose.yml' \ | |
| -g '!server/docker-compose.full.yml' \ | |
| -g '!server/alembic.ini' \ | |
| -g '!server/migrations/env.py' \ | |
| -g '!server/tests/conftest.py' \ | |
| -g '!server/tests/unit/test_no_stale_opds_sync_imports.py' \ | |
| . ; then | |
| echo "::error::Rename guard found un-allowlisted OPDS_SYNC / opds-sync / opds_sync references." | |
| exit 1 | |
| fi | |
| - name: Test | |
| run: uv run pytest -v | |
| image: | |
| needs: test | |
| runs-on: ubuntu-latest | |
| if: github.ref == 'refs/heads/main' | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 | |
| - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 | |
| - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7 | |
| with: | |
| context: server | |
| push: true | |
| sbom: true | |
| provenance: mode=max | |
| tags: | | |
| ghcr.io/${{ github.repository_owner }}/quire-server:${{ github.sha }} | |
| ghcr.io/${{ github.repository_owner }}/quire-server:latest | |
| scan: | |
| # SARIF upload requires Code Scanning enabled, which is free on | |
| # public repos and gated behind GitHub Advanced Security on private | |
| # ones. Skip until the repo is public. | |
| needs: image | |
| runs-on: ubuntu-latest | |
| if: github.ref == 'refs/heads/main' && github.event.repository.visibility == 'public' | |
| permissions: | |
| contents: read | |
| actions: read | |
| packages: read | |
| security-events: write | |
| steps: | |
| - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - uses: aquasecurity/trivy-action@a9c7b0f06e461e9d4b4d1711f154ee024b8d7ab8 # v0.36.0 | |
| with: | |
| image-ref: ghcr.io/${{ github.repository_owner }}/quire-server:${{ github.sha }} | |
| format: sarif | |
| output: trivy.sarif | |
| severity: HIGH,CRITICAL | |
| ignore-unfixed: true | |
| - uses: github/codeql-action/upload-sarif@7c1e4cf0b20d7c1872b26569c00ba908797a59bf # v4 | |
| with: | |
| sarif_file: trivy.sarif |