Seamless integration of tox into GitHub Actions.
Project description
tox-gh
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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4f4871ee4091b41464f2df9ab1fcedb14a48accdecbb60d53297301a9d8984c6
|
|
| MD5 |
b5f6bde94a0ef4bf2c531682c245ba69
|
|
| BLAKE2b-256 |
66c0abaf6c1a524547b0b871129e1dad73e51bfa66e682ce8be353c8c3b41ac2
|
Provenance
The following attestation bundles were made for tox_gh-1.7.1.tar.gz:
Publisher:
release.yaml on tox-dev/tox-gh
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tox_gh-1.7.1.tar.gz -
Subject digest:
4f4871ee4091b41464f2df9ab1fcedb14a48accdecbb60d53297301a9d8984c6 - Sigstore transparency entry: 983764765
- Sigstore integration time:
-
Permalink:
tox-dev/tox-gh@27ae3fff4aaab3f20df0dc660bc8ab6e95826d2a -
Branch / Tag:
refs/tags/1.7.1 - Owner: https://github.com/tox-dev
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@27ae3fff4aaab3f20df0dc660bc8ab6e95826d2a -
Trigger Event:
push
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a82285b16a597516f9a24f20326b124ddc7924a8b70cb05a9192e1c53d7f701d
|
|
| MD5 |
04ae4cfca76b38615cb11975e3368609
|
|
| BLAKE2b-256 |
ad62b5affbf88da680c5c063f3a0f42aaa028bffd66bc8f15c4436e543ef6d94
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tox_gh-1.7.1-py3-none-any.whl -
Subject digest:
a82285b16a597516f9a24f20326b124ddc7924a8b70cb05a9192e1c53d7f701d - Sigstore transparency entry: 983764766
- Sigstore integration time:
-
Permalink:
tox-dev/tox-gh@27ae3fff4aaab3f20df0dc660bc8ab6e95826d2a -
Branch / Tag:
refs/tags/1.7.1 - Owner: https://github.com/tox-dev
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@27ae3fff4aaab3f20df0dc660bc8ab6e95826d2a -
Trigger Event:
push
-
Statement type: