name: CI on: push: branches: [main, master] pull_request: branches: [main, master] concurrency: group: ci-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: lint: name: Lint & typecheck runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup uv uses: astral-sh/setup-uv@v8.3.2 with: enable-cache: true cache-dependency-glob: uv.lock - name: Install Python run: uv python install - name: Sync dev dependencies run: uv sync --group dev --frozen - name: Ruff check run: uv run ruff check src tests - name: Ruff format check run: uv run ruff format --check src tests - name: Ty type check run: uv run ty check src test-unit: name: Unit tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup uv uses: astral-sh/setup-uv@v8.3.2 with: enable-cache: true cache-dependency-glob: uv.lock - name: Install Python run: uv python install - name: Sync dev dependencies run: uv sync --group dev --frozen - name: Run unit tests with coverage run: uv run pytest --cov=src/contract_check --cov-branch --cov-report=term-missing --cov-fail-under=50 -m "not integration" tests/unit # Integration tests. test-integration: name: Integration tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup uv uses: astral-sh/setup-uv@v8.3.2 with: enable-cache: true cache-dependency-glob: uv.lock - name: Install Python run: uv python install - name: Sync dev dependencies run: uv sync --group dev --frozen - name: Start infrastructure (postgres/redis/rabbitmq/minio) run: docker compose up -d --wait - name: Run integration tests run: uv run pytest -m integration - name: Teardown infrastructure if: always() run: docker compose down -v # Build and push service images on pushes to main. Set these repository secrets: # REGISTRY e.g. ghcr.io (or docker.io, your-private-registry.io) # REGISTRY_USER registry username / GitHub username # REGISTRY_PASS registry password / GitHub token (classic or PAT) # DEPLOY_HOST external server hostname or IP # DEPLOY_USER SSH user on the external server # DEPLOY_SSH_KEY private SSH key with pull+restart permissions on the server # DEPLOY_DIR remote directory containing the compose override / .env # # The deploy step logs into the server, pulls the freshly built images and # restarts services. Database migrations are *not* run automatically; run # `docker compose run --rm api python -m alembic -c alembic.ini upgrade head` # on the server before/after deployment, or add a manual step. # deploy: # name: Build & deploy # runs-on: ubuntu-latest # needs: [lint, test-unit] # if: github.ref == 'refs/heads/main' # steps: # - uses: actions/checkout@v4 # - name: Set up Docker Buildx # uses: docker/setup-buildx-action@v3 # - name: Log in to container registry # uses: docker/login-action@v3 # with: # registry: ${{ secrets.REGISTRY }} # username: ${{ secrets.REGISTRY_USER }} # password: ${{ secrets.REGISTRY_PASS }} # - name: Build and push images # run: | # for svc in api worker-extract worker-analyze worker-prescreen worker-notify bot; do # tag="${{ secrets.REGISTRY }}/${{ github.repository_owner }}/contract-check-${svc}:${{ github.sha }}" # docker build -f "srv/${svc}/Dockerfile" -t "$tag" . # docker push "$tag" # done # - name: Deploy to external server # uses: appleboy/ssh-action@v1.2.2 # with: # host: ${{ secrets.DEPLOY_HOST }} # username: ${{ secrets.DEPLOY_USER }} # key: ${{ secrets.DEPLOY_SSH_KEY }} # script: | # cd ${{ secrets.DEPLOY_DIR }} # # Pull the exact images referenced in the remote compose override. # docker compose pull # # Rolling restart: infra stays up, services restart with new images. # docker compose up -d --profile services --remove-orphans # # Optional quick health check on the API. # sleep 5 # curl -fsS http://localhost:8000/healthz || exit 1