when: event: [push, pull_request] branch: [dev, main, master] services: - name: postgres image: postgres:17-alpine environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: blog_test steps: - name: deps image: python:3.13 volumes: - /tmp/uv-cache:/root/.cache/uv environment: UV_CACHE_DIR: /root/.cache/uv UV_LINK_MODE: copy UV_PYTHON: "3.13" commands: - pip install uv - uv sync --group lints --group tests --group types --group dev - name: lint image: python:3.13 volumes: - /tmp/uv-cache:/root/.cache/uv environment: UV_CACHE_DIR: /root/.cache/uv UV_LINK_MODE: copy UV_PYTHON: "3.13" depends_on: [deps] commands: - pip install uv - uv run --no-sync ruff check . - uv run --no-sync ruff format --check . - uv run --no-sync isort --check-only . - name: type image: python:3.13 volumes: - /tmp/uv-cache:/root/.cache/uv environment: UV_CACHE_DIR: /root/.cache/uv UV_LINK_MODE: copy UV_PYTHON: "3.13" depends_on: [deps] commands: - pip install uv - uv run --no-sync mypy . - name: test-unit image: python:3.13 volumes: - /tmp/uv-cache:/root/.cache/uv environment: UV_CACHE_DIR: /root/.cache/uv UV_LINK_MODE: copy UV_PYTHON: "3.13" COVERAGE_FILE: .coverage.unit depends_on: [deps] commands: - pip install uv - uv run --no-sync pytest tests/unit/ -o "addopts=--cov=app --cov-report=term-missing --cov-fail-under=0" - name: test-integration image: python:3.13 volumes: - /tmp/uv-cache:/root/.cache/uv environment: UV_CACHE_DIR: /root/.cache/uv UV_LINK_MODE: copy UV_PYTHON: "3.13" DB_URL: postgresql+asyncpg://postgres:postgres@postgres:5432/blog_test SKIP_INIT_DB: "1" COVERAGE_FILE: .coverage.integration depends_on: [deps] commands: - pip install uv - uv run --no-sync pytest tests/integration/ -v -o "addopts=--cov=app --cov-report=term-missing --cov-fail-under=0" - name: test-e2e image: python:3.13 volumes: - /tmp/uv-cache:/root/.cache/uv environment: UV_CACHE_DIR: /root/.cache/uv UV_LINK_MODE: copy UV_PYTHON: "3.13" DB_URL: postgresql+asyncpg://postgres:postgres@postgres:5432/blog_test SKIP_INIT_DB: "1" depends_on: [test-integration] commands: - pip install uv - uv run --no-sync alembic upgrade head - apt-get update && apt-get install -y libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libasound2 - uv run --no-sync playwright install chromium - uv run --no-sync blog & - sleep 5 - uv run --no-sync pytest tests/e2e/ -v --no-cov - name: coverage image: python:3.13 volumes: - /tmp/uv-cache:/root/.cache/uv environment: UV_CACHE_DIR: /root/.cache/uv UV_LINK_MODE: copy UV_PYTHON: "3.13" depends_on: [test-unit, test-integration, test-e2e] commands: - pip install uv - uv run --no-sync coverage combine .coverage.unit .coverage.integration - uv run --no-sync coverage report --fail-under=70 --include=app/* - uv run --no-sync coverage html - name: pr-comment image: python:3.13 volumes: - /tmp/uv-cache:/root/.cache/uv environment: UV_CACHE_DIR: /root/.cache/uv UV_LINK_MODE: copy UV_PYTHON: "3.13" GITEA_API_TOKEN: from_secret: gitea_api_token depends_on: [coverage] when: event: [pull_request] commands: - pip install uv - | SHA7=$(printf '%.7s' "${CI_COMMIT_SHA:-unknown}") SOURCE="${CI_COMMIT_SOURCE:-${CI_COMMIT_SOURCE_BRANCH:-?}}" TARGET="${CI_COMMIT_TARGET:-${CI_COMMIT_TARGET_BRANCH:-?}}" PIPELINE_URL="${CI_PIPELINE_URL:-}" COVER=$(uv run --no-sync coverage report --include='app/*' | tail -1 | awk '{print $NF}') if [ -z "$GITEA_API_TOKEN" ]; then echo "pr-comment: GITEA_API_TOKEN not set, skipping" exit 0 fi FMT='{"body": "## CI Results\n\n| Check | Result |\n|-------|--------|\n| Commit | `%s` |\n| Branch | %s -> %s |\n| Coverage | **%s** |\n| Pipeline | [View](%s) |\n\n---\n*Reported by @cicd*"}' BODY=$(printf "$FMT" "$SHA7" "$SOURCE" "$TARGET" "$COVER" "$PIPELINE_URL") curl -s -X POST "${CI_FORGE_URL}/api/v1/repos/${CI_REPO_OWNER}/${CI_REPO_NAME}/issues/${CI_COMMIT_PULL_REQUEST}/comments" -H "Authorization: token $${GITEA_API_TOKEN}" -H "Content-Type: application/json" --data-binary "$BODY"