We can use uv to make linting and testing on GitHub Actions around 1.5 times as fast.
Linting
When using pre-commit for linting:
name: Lint
on: [push, pull_request, workflow_dispatch]
env:
FORCE_COLOR: 1
permissions:
contents: read
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: "3.x"
cache: pip
- uses: pre-commit/action@v3.0.1
We can replace pre-commit/action with tox-dev/action-pre-commit-uv:
- uses: actions/setup-python@v5
with:
python-version: "3.x"
- cache: pip
- - uses: pre-commit/action@v3.0.1
+ - uses: tox-dev/action-pre-commit-uv@v1
name: Lint
on: [push, pull_request, workflow_dispatch]
env:
FORCE_COLOR: 1
permissions:
contents: read
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: "3.x"
- uses: tox-dev/action-pre-commit-uv@v1
This means uv will create virtual environments and install packages for pre-commit, which is faster for the initial seed operation when there's no cache.
Lint comparison
For example: python/blurb#32
Testing
When testing with tox:
name: Test
on: [push, pull_request, workflow_dispatch]
permissions:
contents: read
env:
FORCE_COLOR: 1
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
cache: pip
- name: Install dependencies
run: |
python --version
python -m pip install -U pip
python -m pip install -U tox
- name: Tox tests
run: |
tox -e py
We can replace tox with tox-uv:
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- cache: pip
- - name: Install dependencies
- run: |
- python --version
- python -m pip install -U pip
- python -m pip install -U tox
+ - name: Install uv
+ uses: hynek/setup-cached-uv@v2
- name: Tox tests
run: |
- tox -e py
+ uvx --with tox-uv tox -e py
name: Test
on: [push, pull_request, workflow_dispatch]
permissions:
contents: read
env:
FORCE_COLOR: 1
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Install uv
uses: hynek/setup-cached-uv@v2
- name: Tox tests
run: |
uvx --with tox-uv tox -e py
tox-uv is tox plugin to replace virtualenv and pip with uv in your tox environments. We only need to install uv, and use uvx
to both install tox-uv and run tox, for faster installs of tox, the virtual environment, and the dependencies within it.
Test comparison
For example: python/blurb#32
No pip with tox-uv
One difference with uv environments compared to regular venv/virtualenv ones is that they do not have pip. This means calls to pip need replacing, for example in tox.ini
:
[testenv]
-commands_pre =
- {envpython} -m pip install -U -r requirements.txt
+deps =
+ -r requirements.txt
pass_env =
FORCE_COLOR
commands =
{envpython} -m pytest {posargs}
If you still need pip (or setuptools or wheel), add uv_seed = True
to your [testenv]
to inject them.
Bonus tip
Run the new tool zizmor to find security issues in GitHub Actions.
Header photo: "Road cycling at the 1952 Helsinki Olympics" by Olympia-Kuva Oy & Helsinki City Museum, Public Domain.