diff --git a/.github/workflows/python-uv-ci-with-db.yml b/.github/workflows/python-uv-ci-with-db.yml new file mode 100644 index 0000000..8dc9357 --- /dev/null +++ b/.github/workflows/python-uv-ci-with-db.yml @@ -0,0 +1,138 @@ +name: Python UV Quality (with DB) + +on: + workflow_call: + inputs: + python_version: + type: string + default: "3.13" + uv_version: + type: string + default: "latest" + working_directory: + type: string + default: "." + cache_dependency_path: + type: string + default: "uv.lock" + env: + description: > + Multiline env vars, one per line: KEY=VALUE + required: false + type: string + default: "" + uv_sync_args: + type: string + default: "--frozen --dev" + format_command: + type: string + default: "uv run ruff format --check ." + lint_command: + type: string + default: "uv run ruff check ." + typecheck_command: + type: string + default: "uv run mypy ." + test_command: + type: string + default: "uv run pytest" + secrets: + ssh_private_key: + required: false + ssh_known_hosts: + required: false + +jobs: + quality: + runs-on: ubuntu-latest + services: + postgres: + image: postgres:16 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: test_db + ports: + - 5432:5432 + options: >- + --health-cmd="pg_isready -U postgres" + --health-interval=10s + --health-timeout=5s + --health-retries=5 + permissions: + contents: read + defaults: + run: + working-directory: ${{ inputs.working_directory }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Load env vars + if: ${{ inputs.env != '' }} + run: | + while IFS= read -r line; do + [ -z "$line" ] && continue + case "$line" in \#*) continue;; esac + if [[ "$line" != *=* ]]; then + echo "Invalid env line: $line" >&2 + exit 1 + fi + echo "$line" >> "$GITHUB_ENV" + done <<< "${{ inputs.env }}" + + - name: Start ssh-agent + if: ${{ secrets.ssh_private_key != '' }} + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.ssh_private_key }} + + - name: Add SSH known hosts + if: ${{ secrets.ssh_known_hosts != '' }} + run: | + mkdir -p ~/.ssh + printf '%s\n' "${{ secrets.ssh_known_hosts }}" >> ~/.ssh/known_hosts + chmod 644 ~/.ssh/known_hosts + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ inputs.python_version }} + + - name: Install uv + env: + UV_VERSION: ${{ inputs.uv_version }} + run: | + python -m pip install --upgrade pip + if [ -z "$UV_VERSION" ] || [ "$UV_VERSION" = "latest" ]; then + python -m pip install uv + else + python -m pip install "uv==$UV_VERSION" + fi + + - name: Cache uv downloads + uses: actions/cache@v4 + with: + path: ~/.cache/uv + key: uv-${{ runner.os }}-${{ hashFiles(inputs.cache_dependency_path) }} + restore-keys: | + uv-${{ runner.os }}- + + - name: Sync dependencies + run: uv sync ${{ inputs.uv_sync_args }} + + - name: Run format check + if: ${{ inputs.format_command != '' }} + run: ${{ inputs.format_command }} + + - name: Run lint + if: ${{ inputs.lint_command != '' }} + run: ${{ inputs.lint_command }} + + - name: Run typecheck + if: ${{ inputs.typecheck_command != '' }} + run: ${{ inputs.typecheck_command }} + + - name: Run tests + if: ${{ inputs.test_command != '' }} + run: ${{ inputs.test_command }} diff --git a/.github/workflows/python-uv-ci.yml b/.github/workflows/python-uv-ci.yml index d59609a..030911e 100644 --- a/.github/workflows/python-uv-ci.yml +++ b/.github/workflows/python-uv-ci.yml @@ -36,14 +36,6 @@ on: test_command: type: string default: "uv run pytest" - enable_db: - description: Enable a local database container for tests. - type: boolean - default: false - db_type: - description: Database type to start when enable_db is true. - type: string - default: "postgres" secrets: ssh_private_key: required: false @@ -127,76 +119,6 @@ jobs: if: ${{ inputs.typecheck_command != '' }} run: ${{ inputs.typecheck_command }} - - name: Cleanup existing database containers - if: ${{ inputs.enable_db }} - run: | - existing_ids="$(docker ps -aq --filter "name=^ci-postgres")" - if [ -n "$existing_ids" ]; then - docker rm -f $existing_ids >/dev/null 2>&1 || true - fi - - - name: Start database - if: ${{ inputs.enable_db }} - run: | - set -euo pipefail - db_type="${{ inputs.db_type }}" - case "$db_type" in - postgres|postgresql) - run_id="${GITHUB_RUN_ID:-local}" - attempt="${GITHUB_RUN_ATTEMPT:-0}" - container_name="ci-postgres-${run_id}-${attempt}" - echo "DB_CONTAINER_NAME=$container_name" >> "$GITHUB_ENV" - docker run -d --name "$container_name" \ - -e POSTGRES_USER=postgres \ - -e POSTGRES_PASSWORD=postgres \ - -e POSTGRES_DB=test_db \ - -p 5432:5432 \ - --health-cmd="pg_isready -U postgres" \ - --health-interval=10s \ - --health-timeout=5s \ - --health-retries=5 \ - postgres:16 - for i in {1..30}; do - health="$(docker inspect --format '{{.State.Health.Status}}' "$container_name" 2>/dev/null || true)" - case "$health" in - healthy) - break - ;; - unhealthy) - echo "Postgres reported unhealthy." >&2 - docker logs "$container_name" || true - exit 1 - ;; - "") - echo "Postgres health status unavailable." >&2 - docker logs "$container_name" || true - exit 1 - ;; - esac - sleep 1 - done - if [ "${health:-}" != "healthy" ]; then - echo "Postgres did not become healthy in time." >&2 - docker logs "$container_name" || true - exit 1 - fi - if [ -z "${DATABASE_URL:-}" ]; then - echo "DATABASE_URL=postgresql://postgres:postgres@localhost:5432/test_db" >> "$GITHUB_ENV" - fi - ;; - *) - echo "Unsupported db_type: $db_type" >&2 - exit 1 - ;; - esac - - name: Run tests if: ${{ inputs.test_command != '' }} run: ${{ inputs.test_command }} - - - name: Cleanup database - if: ${{ always() && inputs.enable_db }} - run: | - if [ -n "${DB_CONTAINER_NAME:-}" ]; then - docker rm -f "$DB_CONTAINER_NAME" >/dev/null 2>&1 || true - fi