name: CI on: pull_request: push: branches: - main jobs: repo-hygiene: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Enforce no generated artifacts run: ./scripts/check_no_generated_artifacts.sh - name: Enforce env contract parity run: python3 scripts/check_env_contract.py lint: runs-on: ubuntu-latest needs: [repo-hygiene] steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" - uses: astral-sh/setup-uv@v4 - run: uv sync --extra dev - name: Ruff check run: uv run ruff check . - name: Ruff format check run: uv run ruff format --check . - name: Mypy run: uv run mypy app/ --ignore-missing-imports test: runs-on: ubuntu-latest needs: - repo-hygiene - lint services: postgres: image: postgres:15 env: POSTGRES_DB: scholar POSTGRES_USER: scholar POSTGRES_PASSWORD: scholar ports: - 5432:5432 options: >- --health-cmd "pg_isready -U scholar -d scholar" --health-interval 5s --health-timeout 5s --health-retries 20 env: DATABASE_URL: postgresql+asyncpg://scholar:scholar@localhost:5432/scholar SCHOLAR_IMAGE_UPLOAD_DIR: /tmp/scholarr_uploads/scholar_images steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v5 with: python-version: "3.12" - name: Setup uv uses: astral-sh/setup-uv@v4 - name: Install dependencies run: uv sync --extra dev - name: Migration smoke run: uv run alembic upgrade head - name: Unit tests run: uv run pytest tests/unit - name: Integration tests run: uv run pytest -m integration frontend-quality: runs-on: ubuntu-latest needs: - repo-hygiene steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v5 with: python-version: "3.12" - name: API contract drift check run: python3 scripts/check_frontend_api_contract.py - name: Setup Node uses: actions/setup-node@v6 with: node-version: "20" - name: Install frontend dependencies working-directory: frontend run: npm ci - name: Frontend theme token policy working-directory: frontend run: npm run check:theme-tokens - name: Frontend typecheck working-directory: frontend run: npm run typecheck - name: Frontend unit tests working-directory: frontend run: npm run test:run - name: Frontend build working-directory: frontend run: npm run build docs-quality: runs-on: ubuntu-latest needs: - repo-hygiene steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Node uses: actions/setup-node@v6 with: node-version: "20" - name: Install docs dependencies run: npm ci --prefix docs/website - name: Docs build run: npm --prefix docs/website run build docker-publish: runs-on: ubuntu-latest needs: - repo-hygiene - test - frontend-quality - docs-quality if: github.event_name == 'push' && github.ref == 'refs/heads/main' permissions: contents: read steps: - name: Checkout uses: actions/checkout@v4 - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_PASSWORD }} - name: Derive image metadata id: meta uses: docker/metadata-action@v5 with: images: justinzeus/scholarr tags: | type=raw,value=latest type=sha,format=short,prefix=sha- - name: Build and push multi-arch image uses: docker/build-push-action@v6 with: context: . file: ./Dockerfile target: prod push: true platforms: linux/amd64,linux/arm64 tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max