Skip to content
DevOps AI ToolKit
Newsletter
All prompts
AI for GitLab CI/CD Difficulty: Intermediate ClaudeChatGPTCursor

GitLab CI/CD Python Tox Multi-Version Pipeline Prompt

Test a Python package across interpreter versions in GitLab CI with tox — parallel matrix, pip/tox caching, coverage merge, and clean version-to-job mapping.

Target user
Python and DevOps engineers running multi-version test matrices in GitLab CI
Difficulty
Intermediate
Tools
Claude, ChatGPT, Cursor

The prompt

You are a senior Python release engineer who runs multi-interpreter test matrices in GitLab CI using tox. You know how to map tox environments to CI jobs cleanly, cache pip without corrupting per-version virtualenvs, run versions in parallel, and merge coverage into one report — and the traps in each.

I will provide:
- The package (`pyproject.toml`/`setup.cfg`, `tox.ini` if any)
- Supported Python versions (e.g., 3.9–3.13) and any pre-release targets
- Current CI (YAML) or none
- Extra factors (lint, type-check, docs, min vs. latest deps)
- The goal (build the matrix / speed it up / add coverage merge)

Your job:

1. **Map tox envs to CI jobs, one version per job**:
   - Prefer `parallel:matrix:` with a `PYTHON_VERSION` axis, each job on the matching `python:X.Y` image running `tox -e py{XY}`.
   - Or use tox's own factors and a single `TOXENV` per job. Never loop all versions in one job.
2. **Cache correctly**:
   - Cache the **pip download cache** (`~/.cache/pip`) keyed on the lockfile — safe across versions.
   - If caching `.tox/`, key it on `PYTHON_VERSION` + `tox.ini` hash so envs never cross interpreters.
3. **Handle pre-release/optional versions** with `allow_failure: true` scoped ONLY to non-supported versions; supported versions must gate.
4. **Add non-test factors** (lint, mypy, build) as their own jobs so a lint failure doesn't masquerade as a test failure.
5. **Merge coverage**: each job emits `.coverage.$PYTHON_VERSION`; a final job runs `coverage combine` + `coverage xml` and publishes the Cobertura report + `coverage:` regex for the MR badge.
6. **Speed**: use `needs` so coverage-merge starts as soon as the test jobs finish; pin tox and use `--installpkg` where a prebuilt wheel avoids recompiling per version.
7. **Fail fast where useful** but keep the full matrix visible so you see if a failure is one version or all.

Deliverables:
- A `tox.ini` (or confirmation the existing one is CI-ready)
- A `.gitlab-ci.yml` with a clean per-version matrix, correct caching, and coverage merge
- The `coverage:` regex + Cobertura wiring for the MR
- Which versions gate vs. allow-failure, and why

Mark DESTRUCTIVE or RISKY: cross-version `.tox/` cache reuse, allow_failure on supported versions, and coverage combine across incompatible sources.

---

Package config: [pyproject/setup.cfg + tox.ini]
Supported versions: [e.g. 3.9-3.13 + pre-release?]
Current CI (YAML): [PASTE or none]
Extra factors: [lint / mypy / docs / min-deps]
Goal: [build matrix / speed / coverage merge]

Run this prompt with AI

Test it, get an AI-improved version, or compare models — live in the Prompt Workspace. No copy-paste.

Why this prompt works

The common tox-in-CI mistake is a single job that loops every interpreter — slow, and it hides which version broke. Splitting to one job per version makes failures legible and reruns cheap, but then the cache becomes a footgun: a shared .tox/ corrupts across ABIs. This prompt fixes both, and adds the coverage-merge step teams usually skip.

How to use it

  1. One CI job per Python version via parallel:matrix.
  2. Cache pip downloads; never share built .tox/ across versions.
  3. allow_failure only on pre-release interpreters.
  4. Combine per-version coverage into one Cobertura report.

Useful commands

# Run a single env locally, matching one CI job
tox -e py312

# List configured environments
tox -l

# Merge coverage from multiple version runs
coverage combine .coverage.*      # each job wrote .coverage.<ver>
coverage report && coverage xml   # -> coverage.xml (Cobertura)

GitLab CI patterns

Per-version matrix with safe pip cache

stages: [test, coverage]

.tox-test:
  stage: test
  image: python:${PYTHON_VERSION}
  variables:
    PIP_CACHE_DIR: "$CI_PROJECT_DIR/.pip-cache"
  cache:
    key:
      files: [poetry.lock]        # or requirements*.txt / pyproject.toml
    paths:
      - .pip-cache                # pip DOWNLOAD cache — safe across versions
  before_script:
    - pip install tox
  script:
    - tox -e "py${PYTHON_VERSION//./}"      # 3.12 -> py312
    - mv .coverage ".coverage.${PYTHON_VERSION}"
  artifacts:
    paths: [".coverage.${PYTHON_VERSION}"]

test:
  extends: .tox-test
  parallel:
    matrix:
      - PYTHON_VERSION: ["3.9", "3.10", "3.11", "3.12"]

test-prerelease:
  extends: .tox-test
  allow_failure: true             # ONLY for unsupported/pre-release
  parallel:
    matrix:
      - PYTHON_VERSION: ["3.13-rc"]

Non-test factors as separate jobs

lint:
  stage: test
  image: python:3.12
  script:
    - pip install tox
    - tox -e lint

typecheck:
  stage: test
  image: python:3.12
  script:
    - pip install tox
    - tox -e mypy

Coverage merge + MR badge

coverage:
  stage: coverage
  image: python:3.12
  needs:
    - job: test
      artifacts: true
  script:
    - pip install coverage
    - coverage combine .coverage.*
    - coverage report
    - coverage xml
  coverage: '/^TOTAL.+?(\d+\%)$/'
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage.xml

Minimal tox.ini

[tox]
envlist = py39,py310,py311,py312
isolated_build = true

[testenv]
deps = -r requirements-dev.txt
commands = coverage run -m pytest {posargs}

[testenv:lint]
deps = ruff
commands = ruff check .

[testenv:mypy]
deps = mypy
commands = mypy src

Common findings this catches

  • A single job looping all interpreters, hiding which version failed.
  • Shared .tox/ cache restored into the wrong Python image, causing ABI errors.
  • Supported versions marked allow_failure to force a green pipeline.
  • No coverage merge, so the MR badge reflects only one interpreter.
  • Lint/type failures reported as test failures because they share a job.

When to escalate

  • Compiled C-extension packages needing per-version wheels — may need a build matrix and cibuildwheel.
  • Dropping or adding a supported Python version — a release-policy decision.
  • Flaky version-specific failures — investigate the interpreter/dep interaction, don’t blanket allow-failure.

Related prompts

More GitLab CI/CD prompts & error guides

Browse every GitLab CI/CD prompt and troubleshooting guide in one place.

Free download · 368-page PDF

Reading prompts? Get all 500 in one free PDF

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.