Skip to main content

Seamless integration of tox into GitHub Actions.

Project description

tox-gh

PyPI version PyPI Supported Python Versions check Downloads

Seamless integration of tox into GitHub Actions.

tox-gh automatically selects which tox environments to run on each Python version in your GitHub Actions matrix, allowing parallel test execution across multiple workers.


Tutorial: Getting Started

This guide walks you through setting up tox-gh for the first time.

Prerequisites

You'll need a Python project already using tox 4 with a tox.toml, tox.ini, or pyproject.toml configuration file.

Step 1: Add tox-gh configuration

Open your tox configuration file and add a [gh] section that maps Python versions to tox environments.

For tox.toml:

[gh.python]
"3.13" = ["py313"]
"3.12" = ["py312"]

This tells tox-gh: "When running on Python 3.13, run the py313 environment. When running on Python 3.12, run the py312 environment."

Step 2: Create a GitHub Actions workflow

Create .github/workflows/test.yaml:

name: test
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ['3.13', '3.12']
    steps:
      - uses: actions/checkout@v5
      - name: Install uv
        uses: astral-sh/setup-uv@v7
      - name: Install tox
        run: |
          uv tool install --python ${{ matrix.python-version }} tox --with tox-gh
      - name: Run tests
        run: tox

Step 3: Push and verify

Commit your changes and push to GitHub. The workflow will run automatically. You'll see:

  • Two parallel jobs (one for Python 3.13, one for 3.12)
  • Each job runs only its corresponding tox environment
  • GitHub Actions log groups organize output by environment

You've successfully integrated tox-gh. The plugin now handles environment selection automatically based on which Python version GitHub Actions provides.


How-to Guides

How to run multiple environments on a single Python version

Add a list of environments for a Python version:

[gh.python]
"3.13" = ["py313", "type", "lint"]

Now the Python 3.13 job will run three tox environments sequentially: py313, type, and lint.

How to test freethreaded Python builds

Freethreaded Python (3.13t, 3.14t) is automatically detected. Map it like any other version:

[gh.python]
"3.14t" = ["py314t"]
"3.14" = ["py314", "type"]

The plugin checks for the freethreaded attribute and generates the correct version key with the t suffix.

How to explicitly set the Python version

If automatic detection doesn't work for your setup, set TOX_GH_MAJOR_MINOR:

  - name: Run tests
    run: tox
    env:
      TOX_GH_MAJOR_MINOR: ${{ matrix.python-version }}

This forces tox-gh to use the matrix value instead of detecting the Python version.

How to bypass tox-gh environment selection

Run tox with an explicit environment list:

tox -e py313,lint

Or set the TOXENV environment variable:

env:
  TOXENV: py313,lint

When you specify environments explicitly, tox-gh respects your choice and skips its own selection logic.

How to use with uv

Install both tox and tox-gh as uv tools:

  - name: Install tox
    run: uv tool install tox --with tox-uv --with tox-gh

The --with flag ensures tox-gh is available when tox runs.


Reference

Configuration

[gh.python]

Maps Python version strings to lists of tox environments.

Type: dict[str, list[str]]

Location: The configuration can be placed in tox.ini under [gh] python = ..., in tox.toml under [gh.python], or in pyproject.toml under [tool.tox.gh.python].

Format:

[gh.python]
"<version>" = ["<env>", ...]

Version keys (in priority order): Freethreaded Python versions use "3.14t" or "3.13t". Standard CPython versions use "3.14", "3.13", "3.12", "3.11", or "3.10". Major-only fallbacks use "3". PyPy versions use "pypy-3.13", "pypy-3", or "pypy3". Pyston versions use "piston-3.13" or "pyston-3".

Examples:

# Run one environment per version
[gh.python]
"3.13" = ["py313"]

# Run multiple environments on one version
[gh.python]
"3.13" = ["py313", "type", "lint"]

# Freethreaded Python
[gh.python]
"3.14t" = ["py314t"]
"3.14" = ["py314"]

# Fallback to major version
[gh.python]
"3" = ["py3"]

Environment Variables

GITHUB_ACTIONS

Required: Must be "true" for the plugin to activate.

Automatically set by GitHub Actions. The plugin does nothing when running locally.

TOX_GH_MAJOR_MINOR

Optional: Override automatic Python version detection.

Values: Any string matching a key in [gh.python] (e.g., "3.13", "3.14t")

Use cases: This variable is useful when using uv python install instead of setup-python, for freethreaded builds that need explicit version specification, or when automatic detection fails.

Example:

env:
  TOX_GH_MAJOR_MINOR: ${{ matrix.python-version }}

TOXENV

Optional: Explicitly specify which tox environments to run.

When set, tox-gh is completely bypassed and tox runs the specified environments.

Example:

env:
  TOXENV: py313,lint

Python Version Detection

The plugin detects the Python version by first checking the TOX_GH_MAJOR_MINOR environment variable (highest priority). If not set, it uses virtualenv.discovery.py_info.PythonInfo.from_exe() introspection to check the free_threaded attribute (appending a t suffix if true), check the implementation (handling PyPy, Pyston, or CPython), and extract major and minor version numbers.

The detection returns a prioritized list of version keys to try. For freethreaded CPython 3.13, it returns ["3.13t", "3.13", "3"]. For standard CPython 3.13, it returns ["3.13", "3"]. For PyPy 3.13, it returns ["pypy-3.13", "pypy-3", "pypy3"].

The plugin uses the first matching key found in your [gh.python] configuration.

GitHub Actions Integration

Log Grouping

The plugin creates collapsible log groups using GitHub Actions commands. The ::group::tox:install group contains the package installation phase. Each environment's test run gets its own ::group::tox:{env-name} group. The ::endgroup:: command closes the current group. This organization makes long CI logs more readable by sectioning output.

Step Summary

When running multiple environments (2 or more), the plugin writes to $GITHUB_STEP_SUMMARY. Environment passes are marked with ✓ :white_check_mark:: env-name and failures with ✗ :negative_squared_cross_mark:: env-name. Single-environment runs don't produce summary output.

Behavior

Activation Conditions

The plugin activates when GITHUB_ACTIONS=true, no explicit -e flag was passed to tox, and the TOXENV environment variable is not set. Otherwise it remains inactive.

Environment Selection

When active, the plugin first detects the Python version and returns a prioritized key list. It looks up the first matching key in the [gh.python] mapping, inserts the matching environments into tox's env_list via MemoryLoader, and sets the TOX_GH_MAJOR_MINOR environment variable (if not already set) for subprocess inheritance.

Subprocess Provisioning

When tox provisions itself (via requires in tox config), the matched version key is propagated to the subprocess via TOX_GH_MAJOR_MINOR. This ensures provisioned tox processes apply the same environment filtering.


Explanation

Why tox-gh exists

GitHub Actions workflows typically define a matrix strategy with multiple Python versions:

strategy:
  matrix:
    python-version: ['3.13', '3.12', '3.11']

Each matrix cell spawns a separate worker. Without tox-gh, you can either run all tox environments on every worker (which is wasteful and redundant) or manually specify which environments to run via TOXENV (which is verbose and error-prone). tox-gh eliminates both problems by automatically mapping Python versions to appropriate tox environments based on simple configuration.

How environment selection works

The plugin hooks into tox's configuration system via tox_add_core_config. When tox starts, it checks if running in GitHub Actions (GITHUB_ACTIONS=true), verifies the user hasn't specified explicit environments (respecting -e and TOXENV), detects the current Python version via virtualenv's introspection, matches the version against the [gh.python] mapping, and overrides tox's env_list with the matched environments. This happens before tox creates any environments, so tox never sees the unfiltered list.

Design decisions

Why not use tox factors?

Tox 4's native platform factors (linux, darwin, win32) work well for OS-based selection. However, Python version factors still require explicitly setting which environments to run. tox-gh provides automatic version-based selection without workflow boilerplate.

Why detect Python version instead of reading workflow matrix?

The workflow matrix is not accessible from inside the tox process. GitHub Actions only exposes it via environment variables you explicitly pass. Auto-detection means zero required environment variables in simple cases.

Why propagate version key to subprocesses?

When tox provisions itself (installs dependencies into an isolated environment and re-invokes), the MemoryLoader override exists only in the parent process. Without propagation, the child tox process would run all environments. Setting TOX_GH_MAJOR_MINOR in os.environ makes it inherit automatically.

Comparison to tox-gh-actions

tox-gh-actions offers more configuration flexibility ([gh-actions:env] for arbitrary matrix variables) but requires tox 3 or 4. tox-gh is simpler (Python version mapping only) and requires tox 4+. For most projects, Python version mapping is sufficient since tox 4's factor conditions handle other use cases natively.

Limitations

The plugin only supports Python version-based environment selection and requires tox 4.31 or higher. It does not support arbitrary workflow matrix variables beyond Python versions. Version detection may fail with unusual Python installations, in which case you should use the TOX_GH_MAJOR_MINOR override.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tox_gh-1.7.1.tar.gz (11.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tox_gh-1.7.1-py3-none-any.whl (9.9 kB view details)

Uploaded Python 3

File details

Details for the file tox_gh-1.7.1.tar.gz.

File metadata

  • Download URL: tox_gh-1.7.1.tar.gz
  • Upload date:
  • Size: 11.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for tox_gh-1.7.1.tar.gz
Algorithm Hash digest
SHA256 4f4871ee4091b41464f2df9ab1fcedb14a48accdecbb60d53297301a9d8984c6
MD5 b5f6bde94a0ef4bf2c531682c245ba69
BLAKE2b-256 66c0abaf6c1a524547b0b871129e1dad73e51bfa66e682ce8be353c8c3b41ac2

See more details on using hashes here.

Provenance

The following attestation bundles were made for tox_gh-1.7.1.tar.gz:

Publisher: release.yaml on tox-dev/tox-gh

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tox_gh-1.7.1-py3-none-any.whl.

File metadata

  • Download URL: tox_gh-1.7.1-py3-none-any.whl
  • Upload date:
  • Size: 9.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for tox_gh-1.7.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a82285b16a597516f9a24f20326b124ddc7924a8b70cb05a9192e1c53d7f701d
MD5 04ae4cfca76b38615cb11975e3368609
BLAKE2b-256 ad62b5affbf88da680c5c063f3a0f42aaa028bffd66bc8f15c4436e543ef6d94

See more details on using hashes here.

Provenance

The following attestation bundles were made for tox_gh-1.7.1-py3-none-any.whl:

Publisher: release.yaml on tox-dev/tox-gh

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page