Skip to content

feat: add first-class Docker and Podman runtimes #622

feat: add first-class Docker and Podman runtimes

feat: add first-class Docker and Podman runtimes #622

Workflow file for this run

name: Tests And Linting
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled]
schedule:
- cron: "0 3 * * *"
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
PYTHONUNBUFFERED: "1"
FORCE_COLOR: "1"
jobs:
select:
name: Select provider tests
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.select.outputs.matrix }}
mode: ${{ steps.select.outputs.mode }}
providers: ${{ steps.select.outputs.providers }}
run_compatibility: ${{ steps.select.outputs.run_compatibility }}
run_docs: ${{ steps.select.outputs.run_docs }}
run_provider_tests: ${{ steps.select.outputs.run_provider_tests }}
run_quality: ${{ steps.select.outputs.run_quality }}
run_runtime_conformance: ${{ steps.select.outputs.run_runtime_conformance }}
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v7
with:
python-version: "3.12"
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Select provider scope
id: select
env:
BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }}
HEAD_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
LABEL_FULL: ${{ contains(github.event.pull_request.labels.*.name, 'ci:full') }}
run: |
args=(--github-output "$GITHUB_OUTPUT" --summary "$GITHUB_STEP_SUMMARY")
full_reason=""
if [[ "$GITHUB_EVENT_NAME" == "schedule" ]]; then
full_reason="nightly schedule"
elif [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then
full_reason="manual workflow dispatch"
elif [[ "$LABEL_FULL" == "true" ]]; then
full_reason="ci:full label"
elif [[ -z "$BASE_SHA" || "$BASE_SHA" =~ ^0+$ ]]; then
full_reason="unavailable push base"
fi
if [[ -n "$full_reason" ]]; then
args+=(--full --full-reason "$full_reason")
else
args+=(--base-ref "$BASE_SHA" --head-ref "$HEAD_SHA")
fi
uv run --frozen python -m scripts.ci.select_provider_tests "${args[@]}"
compatibility:
name: Clientless compatibility / Python ${{ matrix.python-version }}
if: needs.select.outputs.run_compatibility == 'true'
needs: select
runs-on: ubuntu-latest
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v7
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python-version }}
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Install clientless test dependencies
run: uv sync --frozen --no-dev --group test
- name: Run clientless compatibility tests
run: uv run python -m scripts.ci.run_tests --compatibility
quality:
name: Quality
if: needs.select.outputs.run_quality == 'true'
needs: select
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v7
with:
python-version: "3.12"
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Install dependencies
run: uv sync --frozen --all-extras
- name: Run lint and type checks
run: make lint
docs:
name: Documentation
if: needs.select.outputs.run_docs == 'true'
needs: select
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v7
with:
python-version: "3.12"
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Install dependencies
run: uv sync --frozen --all-extras
- name: Build documentation
run: make docs
provider:
name: Provider / ${{ matrix.provider }} / Python ${{ matrix.python-version }}
if: needs.select.outputs.run_provider_tests == 'true'
needs: select
runs-on: ubuntu-latest
timeout-minutes: 45
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.select.outputs.matrix) }}
steps:
- uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python-version }}
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Install dependencies
run: uv sync --frozen --all-extras
- name: Pull provider images
run: uv run python -m scripts.ci.pull_images --provider "${{ matrix.provider }}"
- name: Run provider tests with coverage
if: matrix.python-version == '3.12'
run: uv run python -m scripts.ci.run_tests --provider "${{ matrix.provider }}" --coverage
- name: Run provider tests
if: matrix.python-version != '3.12'
run: uv run python -m scripts.ci.run_tests --provider "${{ matrix.provider }}"
- name: Name raw coverage data
if: matrix.python-version == '3.12'
run: mv .coverage ".coverage.${{ matrix.provider }}"
- name: Upload raw coverage data
if: matrix.python-version == '3.12'
uses: actions/upload-artifact@v7
with:
name: coverage-data-${{ matrix.provider }}
path: .coverage.${{ matrix.provider }}
include-hidden-files: true
coverage:
name: Coverage
if: needs.select.outputs.run_provider_tests == 'true' && needs.provider.result == 'success'
needs: [select, provider]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v7
with:
python-version: "3.12"
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Install coverage dependencies
run: uv sync --frozen --no-dev --group test
- name: Download raw coverage data
uses: actions/download-artifact@v8
with:
pattern: coverage-data-*
path: coverage-data
merge-multiple: true
- name: Combine coverage data
run: |
uv run coverage combine coverage-data
uv run coverage xml
- name: Upload coverage report to Codecov
uses: codecov/codecov-action@v7.0.0
with:
token: ${{ secrets.CODECOV_TOKEN }}
slug: litestar-org/pytest-databases
files: coverage.xml
fail_ci_if_error: false
runtime_conformance:
name: Runtime conformance / ${{ matrix.runtime }}
if: needs.select.outputs.run_runtime_conformance == 'true'
needs: select
runs-on: ubuntu-latest
timeout-minutes: 25
strategy:
fail-fast: false
matrix:
runtime: [docker, podman]
env:
PYTEST_DATABASES_CONFORMANCE: "1"
PYTEST_DATABASES_CONTAINER_RUNTIME: ${{ matrix.runtime }}
steps:
- uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v7
with:
python-version: "3.12"
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Install test dependencies
run: uv sync --frozen --no-dev --group test --group extra
- name: Start rootless Podman API
if: matrix.runtime == 'podman'
run: |
sudo apt-get update
sudo apt-get install --yes podman
podman version
podman info --debug
socket="$RUNNER_TEMP/podman.sock"
podman system service --time=0 "unix://$socket" >"$RUNNER_TEMP/podman-service.log" 2>&1 &
echo "PODMAN_SERVICE_PID=$!" >> "$GITHUB_ENV"
for attempt in {1..30}; do
if curl --fail --silent --unix-socket "$socket" http://localhost/_ping >/dev/null; then
echo "CONTAINER_HOST=unix://$socket" >> "$GITHUB_ENV"
exit 0
fi
sleep 1
done
ls -la "$socket" || true
cat "$RUNNER_TEMP/podman-service.log"
exit 1
- name: Run runtime and representative provider conformance
run: >-
uv run pytest
tests/test_runtime_conformance.py
tests/test_service_host_port.py
"tests/test_postgres.py::test_service_fixture[postgres_service]"
-q
- name: Report and stop Podman API
if: always() && matrix.runtime == 'podman'
run: |
cat "$RUNNER_TEMP/podman-service.log" || true
if [[ -n "${PODMAN_SERVICE_PID:-}" ]]; then
kill "$PODMAN_SERVICE_PID" || true
fi
ci-required:
name: CI required
if: always()
needs:
[
select,
quality,
compatibility,
docs,
provider,
coverage,
runtime_conformance,
]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v7
with:
python-version: "3.12"
- name: Validate required job results
run: >-
python -m scripts.ci.check_required_jobs
--select "${{ needs.select.result }}"
--quality "${{ needs.quality.result }}"
--compatibility "${{ needs.compatibility.result }}"
--docs "${{ needs.docs.result }}"
--provider "${{ needs.provider.result }}"
--coverage "${{ needs.coverage.result }}"
--runtime-conformance "${{ needs.runtime_conformance.result }}"
--expect-quality "${{ needs.select.outputs.run_quality }}"
--expect-compatibility "${{ needs.select.outputs.run_compatibility }}"
--expect-docs "${{ needs.select.outputs.run_docs }}"
--expect-provider "${{ needs.select.outputs.run_provider_tests }}"
--expect-coverage "${{ needs.select.outputs.run_provider_tests }}"
--expect-runtime-conformance "${{ needs.select.outputs.run_runtime_conformance }}"
# Temporary ruleset compatibility shim. The repository currently requires
# this legacy context; remove it after the ruleset requires CI required.
test_elasticsearch:
name: test_elasticsearch
if: always()
needs: ci-required
runs-on: ubuntu-latest
steps:
- name: Mirror stable gate result
env:
REQUIRED_RESULT: ${{ needs.ci-required.result }}
run: test "$REQUIRED_RESULT" = "success"