From ff74b34acb6f60155b582081dc1efaf74adc4582 Mon Sep 17 00:00:00 2001 From: Daniel Prange Date: Fri, 5 Jun 2026 12:44:09 +0300 Subject: [PATCH 1/2] feat: add postgres option for ci-python-library Refs: RATY-348 --- .github/workflows/ci-python-library-README.md | 32 +++++++++++ .github/workflows/ci-python-library.yml | 57 +++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/.github/workflows/ci-python-library-README.md b/.github/workflows/ci-python-library-README.md index 9771e38..ecf817f 100644 --- a/.github/workflows/ci-python-library-README.md +++ b/.github/workflows/ci-python-library-README.md @@ -7,6 +7,7 @@ This reusable workflow is part of the City of Helsinki's GitHub Actions setup, s - **Commit Linting**: Enforces commit message standards using [commitlint](https://commitlint.js.org/). - **Code Style Checks**: Verifies code style and formatting using [pre-commit](https://pre-commit.com/). - **Automated Testing**: Runs project tests across multiple Python versions using [hatch](https://hatch.pypa.io/). +- **Database Testing**: Optionally starts a PostgreSQL (with or without PostGIS) service and sets `DATABASE_URL` for tests that require a database. - **Code Quality Analysis**: Performs a [SonarQube Cloud](https://sonarcloud.io/) scan. ## Requirements for Projects Using the Workflow @@ -28,6 +29,8 @@ To use this reusable workflow, create a project-specific workflow file in your ` - **`python-version`** (string): Python version to use for pre-commit checks. Defaults to `"3.x"`. - **`enable-sonar`** (boolean): Whether to run the SonarQube Cloud Scan job after tests. Defaults to `true`. +- **`postgres-major-version`** (string): PostgreSQL major version to use for testing. Supported versions: `13`, `14`, `17`. Optional - omit if no database is needed. +- **`use-postgis`** (boolean): Set to `true` to use the PostGIS extension. Requires `postgres-major-version` to be set. Defaults to `false`. ### Secrets @@ -59,3 +62,32 @@ jobs: with: python-matrix: "['3.10', '3.11', '3.12', '3.13', '3.14']" ``` + +### Example usage with PostgreSQL (`/.github/workflows/ci.yml`) + +```yaml +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + common: + uses: City-of-Helsinki/.github/.github/workflows/ci-python-library.yml@main + secrets: + sonar-token: ${{ secrets.SONAR_TOKEN }} + github-token: ${{ secrets.GITHUB_TOKEN }} + with: + python-matrix: "['3.12', '3.13']" + postgres-major-version: "17" +``` + +When `postgres-major-version` is set, the workflow starts a PostgreSQL service and exposes a `DATABASE_URL` environment variable (`postgres://test_user:test_password@localhost/test_db`) to the test runner. diff --git a/.github/workflows/ci-python-library.yml b/.github/workflows/ci-python-library.yml index adb8a8c..02b47e6 100644 --- a/.github/workflows/ci-python-library.yml +++ b/.github/workflows/ci-python-library.yml @@ -17,6 +17,16 @@ on: required: false type: boolean default: true + postgres-major-version: + description: "PostgreSQL major version to use for testing (supported: 13, 14, 17). Optional - omit if no database is needed." + required: false + type: string + default: '' + use-postgis: + description: "Set to true to use the PostGIS extension. Requires postgres-major-version to be set." + required: false + type: boolean + default: false secrets: sonar-token: description: "Token for SonarQube Cloud Scan. Required if enable-sonar is set to true." @@ -59,7 +69,38 @@ jobs: matrix: python_version: ${{ fromJSON(inputs.python-matrix) }} + services: + postgres: + # The workflow supports only the PostgreSQL and PostGIS versions used in Platta + image: ${{ inputs.postgres-major-version != '' && + (inputs.use-postgis && + (inputs.postgres-major-version == '17' && 'postgis/postgis:17-3.5-alpine' || + inputs.postgres-major-version == '14' && 'postgis/postgis:14-3.2-alpine' || + inputs.postgres-major-version == '13' && 'postgis/postgis:13-3.2-alpine') || + (inputs.postgres-major-version == '17' && 'postgres:17-alpine' || + inputs.postgres-major-version == '14' && 'postgres:14-alpine' || + inputs.postgres-major-version == '13' && 'postgres:13-alpine')) || + 'postgres:alpine' }} # This fallback is used only so that we don't error out before the validation step + env: + POSTGRES_USER: test_user + POSTGRES_PASSWORD: test_password + POSTGRES_DB: test_db + ports: + - 5432:5432 + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 + steps: + - name: Validate provided PostgreSQL major version + if: ${{ inputs.postgres-major-version != '' }} + env: + POSTGRES_MAJOR_VERSION: ${{ inputs.postgres-major-version }} + run: | + supported_versions=("13" "14" "17") + if [[ ! " ${supported_versions[*]} " == *" ${POSTGRES_MAJOR_VERSION} "* ]]; then + echo "Error: Unsupported PostgreSQL major version provided. Supported versions are 13, 14 and 17." + exit 1 + fi + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Python ${{ matrix.python_version }} @@ -79,6 +120,22 @@ jobs: ${{ runner.os }}-build- ${{ runner.os }}- + - name: Install system packages + if: ${{ inputs.postgres-major-version != '' }} + env: + USE_POSTGIS: ${{ inputs.use-postgis }} + run: | + packages=(libpq-dev) + if [[ "${USE_POSTGIS}" == "true" ]]; then + packages+=(gdal-bin) + fi + sudo apt-get update + sudo apt-get install -y "${packages[@]}" + + - name: Set database URL + if: ${{ inputs.postgres-major-version != '' }} + run: echo "DATABASE_URL=${{ inputs.use-postgis == true && 'postgis' || 'postgres' }}://test_user:test_password@localhost/test_db" >> $GITHUB_ENV + - name: Install hatch run: python -m pip install hatch From a6f2474c5168280fc8381dd0405289c911617419 Mon Sep 17 00:00:00 2001 From: Daniel Prange Date: Fri, 5 Jun 2026 14:38:15 +0300 Subject: [PATCH 2/2] feat: run postgres only if set --- .github/workflows/ci-python-library.yml | 55 ++++++++++++++++--------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci-python-library.yml b/.github/workflows/ci-python-library.yml index 02b47e6..6836bf1 100644 --- a/.github/workflows/ci-python-library.yml +++ b/.github/workflows/ci-python-library.yml @@ -69,26 +69,6 @@ jobs: matrix: python_version: ${{ fromJSON(inputs.python-matrix) }} - services: - postgres: - # The workflow supports only the PostgreSQL and PostGIS versions used in Platta - image: ${{ inputs.postgres-major-version != '' && - (inputs.use-postgis && - (inputs.postgres-major-version == '17' && 'postgis/postgis:17-3.5-alpine' || - inputs.postgres-major-version == '14' && 'postgis/postgis:14-3.2-alpine' || - inputs.postgres-major-version == '13' && 'postgis/postgis:13-3.2-alpine') || - (inputs.postgres-major-version == '17' && 'postgres:17-alpine' || - inputs.postgres-major-version == '14' && 'postgres:14-alpine' || - inputs.postgres-major-version == '13' && 'postgres:13-alpine')) || - 'postgres:alpine' }} # This fallback is used only so that we don't error out before the validation step - env: - POSTGRES_USER: test_user - POSTGRES_PASSWORD: test_password - POSTGRES_DB: test_db - ports: - - 5432:5432 - options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 - steps: - name: Validate provided PostgreSQL major version if: ${{ inputs.postgres-major-version != '' }} @@ -101,6 +81,41 @@ jobs: exit 1 fi + - name: Start PostgreSQL + if: ${{ inputs.postgres-major-version != '' }} + env: + # The workflow supports only the PostgreSQL and PostGIS versions used in Platta + POSTGRES_IMAGE: ${{ inputs.use-postgis && + (inputs.postgres-major-version == '17' && 'postgis/postgis:17-3.5-alpine' || + inputs.postgres-major-version == '14' && 'postgis/postgis:14-3.2-alpine' || + inputs.postgres-major-version == '13' && 'postgis/postgis:13-3.2-alpine') || + (inputs.postgres-major-version == '17' && 'postgres:17-alpine' || + inputs.postgres-major-version == '14' && 'postgres:14-alpine' || + inputs.postgres-major-version == '13' && 'postgres:13-alpine') }} + run: | + docker run -d --name postgres \ + -e POSTGRES_USER=test_user \ + -e POSTGRES_PASSWORD=test_password \ + -e POSTGRES_DB=test_db \ + -p 5432:5432 \ + --health-cmd pg_isready \ + --health-interval 10s \ + --health-timeout 5s \ + --health-retries 5 \ + "$POSTGRES_IMAGE" + echo "Waiting for PostgreSQL to become healthy..." + for _ in $(seq 1 30); do + status=$(docker inspect --format '{{.State.Health.Status}}' postgres) + if [[ "$status" == "healthy" ]]; then + echo "PostgreSQL is healthy." + exit 0 + fi + sleep 2 + done + echo "Error: PostgreSQL did not become healthy in time." + docker logs postgres + exit 1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Python ${{ matrix.python_version }}