Skip to content

Commit ab861ab

Browse files
committed
Refactor github CI workflows
- `publish.yaml`: Publish to test.pypi.org and pypi.org in the same job. - Use a single deployment environment (`publish-pypi`) for both test and production publishing endpoints. - `ci-workflow.yaml`: A high level reusable workflow to call other workflows. - Orchestrate all CI jobs from a single workflow. - Use workflow inputs (`jobs`) to control which jobs to run. - `ci.yaml`: Configure the CI workflow to use the new ci-workflow.yaml. - Here is where the triggers and workflow configuration are set. - This file will call `ci-workflow.yaml` with the appropriate inputs. Assumes tests and code checks are run by `tox` and configured in the `pyproject.toml` file.
1 parent 63b3db6 commit ab861ab

File tree

8 files changed

+164
-865
lines changed

8 files changed

+164
-865
lines changed

.github/workflows/ci-release.yaml

Lines changed: 0 additions & 53 deletions
This file was deleted.

.github/workflows/ci-test-build.yaml

Lines changed: 0 additions & 30 deletions
This file was deleted.

.github/workflows/ci-test.yaml

Lines changed: 0 additions & 18 deletions
This file was deleted.

.github/workflows/ci-workflow.yaml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: CI Workflow
2+
3+
# A reusable github workflow to run python CI workflows, including static code
4+
# checks, automated testing (using pytest and tox), package building,
5+
# publishing to test.pypi.org and pypi.org, and creating a GitHub release.
6+
#
7+
# The workflow elements to selected are specified in the `jobs` input. The
8+
# default is to run the `test` and `build` jobs. The CI workflow is
9+
# configured in the tox-gh config (eg. pyproject.toml). See
10+
# https://github.com/tox-dev/tox-gh.
11+
#
12+
# Accepts the following inputs as json strings:
13+
# - `jobs`: the list of jobs to run,
14+
# - `os`: the list of operating systems on which to run tests, and
15+
# - `python-version`: the list of python versions on which to run tests.
16+
#
17+
# Assumes the configurations for all tools (`mypy`, `uv`, `ruff`, `pytest`,
18+
# ...) are fully specified in config files (eg `pyproject.toml`).
19+
#
20+
# These workflows use `tox`, as it can be more easily customised in your
21+
# pyproject.toml so that the same CI process runs on your local computer and
22+
# on github.
23+
#
24+
# All workflows use astral `uv` to execute actions wherever possible.
25+
26+
on:
27+
workflow_call:
28+
inputs:
29+
jobs:
30+
description: >-
31+
A json string containing the list of jobs to run. Available jobs are:
32+
- `check`: Run static code checks (using `mypy`, `ruff`, etc.).
33+
- `test`: Run tests and code checks using `tox`.
34+
- `build`: Build the python package.
35+
- `publish-test`: Publish the package to test.pypi.org.
36+
- `publish`: Publish the package to pypi.org (runs `publish-test`).
37+
- `release`: Create a GitHub release.
38+
The default is `["test", "build"]`.
39+
default: '["test", "build"]'
40+
type: string
41+
required: false
42+
os:
43+
description: >-
44+
A json string containing the list of operating systems on which to
45+
run the test matrix.
46+
default: '["ubuntu-latest", "windows-latest", "macos-latest"]'
47+
type: string
48+
required: false
49+
python-version:
50+
description: >-
51+
A json string containing the list of python versions on
52+
which to run the test matrix.
53+
default: '["3.9", "3.10", "3.11", "3.12", "3.13"]'
54+
type: string
55+
required: false
56+
57+
jobs:
58+
test:
59+
if: ${{ contains(fromJson(inputs.jobs), 'test') }}
60+
name: Tests
61+
uses: glenn20/python-ci/.github/workflows/test-tox.yaml@dev
62+
with:
63+
os: ${{ inputs.os }}
64+
python-version: ${{ inputs.python-version }}
65+
66+
build:
67+
name: Build python package
68+
uses: glenn20/python-ci/.github/workflows/build.yaml@dev

.github/workflows/ci.yaml

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
name: CI workflow for python projects
2+
3+
# Run the CI test workflow of jobs which includes:
4+
# - `check`: Run static code checks (using `mypy`, `ruff`, etc.).
5+
# - `test`: Run tests (and code checks) using `tox`.
6+
# - `build`: Build the python package.
7+
# - `publish-test`: Publish the package to test.pypi.org.
8+
# - `publish`: Publish the package to pypi.org (runs `publish-test`).
9+
# - `release`: Create a GitHub release.
10+
11+
on:
12+
push:
13+
branches: ["**"] # Push commits to any branch
14+
tags: ["v[0-9]*"] # Publish on tags matching "v*", eg. "v1.0.0"
15+
16+
# Configure the workflows here. Each environment variable name should be a
17+
# wildcard matching the
18+
# `on-<github.event_name>-<github.ref_type>-<github.ref_name>` format. For
19+
# example, if the event is a push to a tag `v1.0.0`, the environment variable
20+
# `on-push-tag-v*` will match. The value of the matching variable will be
21+
# written to $GITHUB_OUTPUT to set the jobs, python versions, and operating
22+
# systems to run the workflow on. The first match found is used.
23+
env:
24+
on-push-tag-*: | # Push version tag matching "v*", eg. "v1.0.0"
25+
jobs=["test", "build", "publish", "release"]
26+
python-version=["3.9", "3.10", "3.11", "3.12", "3.13"]
27+
os=["ubuntu-latest"]
28+
on-push-branch-main: | # Push commits to main branch
29+
jobs=["test", "build", "publish-test"]
30+
python-version=["3.9", "3.10", "3.11", "3.12", "3.13"]
31+
os=["ubuntu-latest"]
32+
on-push-branch-*: | # Push commits to other branches
33+
jobs=["test", "build", "publish-test"]
34+
python-version=["3.9", "3.13"]
35+
os=["ubuntu-latest"]
36+
37+
jobs:
38+
config:
39+
# Select the workflow config based on the event trigger.
40+
runs-on: ubuntu-latest
41+
outputs:
42+
jobs: ${{ steps.config.outputs.jobs }}
43+
python-version: ${{ steps.config.outputs.python-version }}
44+
os: ${{ steps.config.outputs.os }}
45+
steps:
46+
- name: Select the workflow configuration
47+
id: config
48+
run: | # Find the matching environment variable based on the event trigger
49+
tag="on-${{ github.event_name }}-${{ github.ref_type }}-${{ github.ref_name }}"
50+
for key in $(echo '${{ toJson(env) }}' | jq -r 'keys_unsorted[]'); do
51+
if [[ "$tag" == $key ]]; then
52+
# Write value of the matching environment variable to $GITHUB_OUTPUT
53+
echo '${{ toJson(env) }}' | jq -r ".[\"$key\"]" >> $GITHUB_OUTPUT
54+
exit 0 # Stop after the first match
55+
fi
56+
done
57+
echo "No matching environment variable found for '$tag'."
58+
59+
ci-workflow:
60+
name: CI workflow
61+
needs: config
62+
uses: ./.github/workflows/ci-workflow.yaml
63+
with:
64+
jobs: ${{ needs.config.outputs.jobs }}
65+
os: ${{ needs.config.outputs.os }}
66+
python-version: ${{ needs.config.outputs.python-version }}
67+
68+
# We can't use trusted publishing from a reusable workflow in another
69+
# repository, so the publish workflows must be run from here, not in the
70+
# ci-workflow.yaml.
71+
publish:
72+
if: ${{ contains(needs.config.outputs.jobs, '"publish') }}
73+
name: Publish to pypi.org
74+
runs-on: ubuntu-latest
75+
needs: [config, ci-workflow]
76+
environment:
77+
name: publish-pypi
78+
url: ${{ steps.publish.outputs.url }}
79+
permissions:
80+
id-token: write # Required for trusted publishing
81+
steps:
82+
- id: publish
83+
uses: glenn20/python-ci/publish@dev
84+
with:
85+
test-only: ${{ contains(needs.config.outputs.jobs, '"publish"') && 'true' || 'false' }}
86+
87+
release:
88+
if: ${{ contains(fromJson(needs.config.outputs.jobs), 'release') }}
89+
name: Create GitHub release
90+
needs: [config, ci-workflow]
91+
permissions:
92+
id-token: write # IMPORTANT: mandatory for github release
93+
contents: write # IMPORTANT: mandatory for github release
94+
uses: glenn20/python-ci/.github/workflows/github-release.yaml@dev

.github/workflows/publish.yaml

Lines changed: 0 additions & 55 deletions
This file was deleted.

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ run.omit = ["_version.py"]
8383
report.skip_covered = false
8484
append = true
8585

86-
[tool.tox.gh.python] # For Github Actions workflow - see
86+
# For Github Actions workflow - see https://github.com/tox-dev/tox-gh
87+
[tool.tox.gh.python]
8788
"3.13" = ["clean", "typing", "lint", "format", "3.13"]
8889
"3.12" = ["3.12"]
8990
"3.11" = ["3.11"]
@@ -111,7 +112,6 @@ dependency_groups = ["test"] # Everything we need to run the tox tests
111112
labels = ["test"]
112113
package = "editable" # "editable" runs a bit faster then "wheel"
113114
runner = "uv-venv-runner" # We love uv
114-
passenv = ["GITHUB_ACTIONS"] # So conftest.py knows we are running in github actions
115115

116116
[tool.tox.env.3.13]
117117
# Run coverage tests only on the latest python version

0 commit comments

Comments
 (0)