Skip to content

Commit d62d16e

Browse files
committed
Actions, Hynek-style.
1 parent ec0702e commit d62d16e

File tree

3 files changed

+197
-32
lines changed

3 files changed

+197
-32
lines changed

.github/workflows/ci.yml

+167-24
Original file line numberDiff line numberDiff line change
@@ -3,50 +3,193 @@ name: CI
33

44
on:
55
push:
6-
branches: [trunk]
7-
tags: ["*"]
6+
branches: [main]
87
pull_request:
9-
branches: [trunk]
108
workflow_dispatch:
119

1210
env:
1311
FORCE_COLOR: "1"
14-
PIP_DISABLE_VERSION_CHECK: "1"
12+
PIP_DISABLE_PIP_VERSION_CHECK: "1"
13+
PIP_NO_PYTHON_VERSION_WARNING: "1"
1514

16-
permissions:
17-
contents: read
15+
permissions: {}
1816

1917
jobs:
20-
tests:
21-
name: nox on ${{ matrix.python-version }}
18+
build-package:
19+
name: Build and verify package
2220
runs-on: ubuntu-latest
2321

22+
steps:
23+
- uses: actions/checkout@v4
24+
with:
25+
fetch-depth: 0
26+
27+
- uses: hynek/build-and-inspect-python-package@v2
28+
id: baipp
29+
30+
outputs:
31+
python-versions: ${{ steps.baipp.outputs.supported_python_classifiers_json_array }}
32+
33+
tests:
34+
name: Tests on ${{ matrix.python-version }}
35+
needs: build-package
36+
runs-on: ubuntu-latest
2437
strategy:
2538
fail-fast: false
2639
matrix:
27-
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
40+
python-version: ${{ fromJson(needs.build-package.outputs.python-versions) }}
2841

2942
steps:
30-
- name: Harden Runner
31-
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
32-
with:
33-
disable-sudo: true
34-
egress-policy: block
35-
allowed-endpoints: >
36-
docs.python.org:443
37-
files.pythonhosted.org:443
38-
github.com:443
39-
pypi.org:443
40-
- uses: actions/checkout@v4
43+
- name: Download pre-built packages
44+
uses: actions/download-artifact@v4
45+
with:
46+
name: Packages
47+
path: dist
48+
- run: tar xf dist/*.tar.gz --strip-components=1
4149
- uses: actions/setup-python@v5
4250
with:
4351
python-version: ${{ matrix.python-version }}
44-
- name: "Install dependencies"
52+
allow-prereleases: true
53+
- name: Install test runner
4554
run: |
4655
python -VV
4756
python -Im site
48-
python -Im pip install --upgrade pip setuptools wheel
4957
python -Im pip install --upgrade nox
5058
python -Im nox --version
51-
- name: "Run CI suite with nox"
52-
run: "python -Im nox --non-interactive --error-on-external-run --python ${{ matrix.python-version }}"
59+
- name: Run tests
60+
run: "python -Im nox --non-interactive --error-on-external-run --tag tests --python ${{ matrix.python-version }}"
61+
- name: Upload coverage data
62+
uses: actions/upload-artifact@v4
63+
with:
64+
name: coverage-data-${{ matrix.python-version }}
65+
path: .coverage.*
66+
include-hidden-files: true
67+
if-no-files-found: ignore
68+
69+
70+
coverage:
71+
name: Combine and check coverage
72+
needs: tests
73+
runs-on: ubuntu-latest
74+
75+
steps:
76+
- uses: actions/checkout@v4
77+
- uses: actions/setup-python@v5
78+
with:
79+
python-version: "3.12"
80+
- uses: hynek/setup-cached-uv@v2
81+
- run: python -Im pip install --system --upgrade coverage[toml]
82+
- uses: actions/download-artifact@v4
83+
with:
84+
pattern: coverage-data-*
85+
merge-multiple: true
86+
87+
- name: Combine coverage and fail under 100%
88+
run: |
89+
python -Im pip install --upgrade "coverage[toml]"
90+
coverage combine
91+
coverage html --skip-covered --skip-empty
92+
# Report and write to summary.
93+
coverage report --format=markdown >> $GITHUB_STEP_SUMMARY
94+
# Report again and fail if under 100%.
95+
coverage report --fail-under=100
96+
97+
- name: Upload HTML report if check failed.
98+
uses: actions/upload-artifact@v4
99+
with:
100+
name: html-report
101+
path: htmlcov
102+
if: ${{ failure() }}
103+
104+
105+
docs:
106+
name: Check documentation
107+
needs: build-package
108+
runs-on: ubuntu-latest
109+
steps:
110+
- name: Download pre-built packages
111+
uses: actions/download-artifact@v4
112+
with:
113+
name: Packages
114+
path: dist
115+
- run: tar xf dist/*.tar.gz --strip-components=1
116+
- uses: actions/setup-python@v5
117+
with:
118+
python-version: "3.12"
119+
- name: Set up test runner
120+
- run: |
121+
python -VV
122+
python -Im site
123+
python -Im pip install --upgrade nox
124+
python -Im nox --version
125+
- name: Run documentation checks
126+
run: "python -Im nox --non-interactive --error-on-external-run --tag docs"
127+
128+
129+
lint-format:
130+
name: Lint code and check formatting
131+
needs: build-package
132+
runs-on: ubuntu-latest
133+
steps:
134+
- name: Download pre-built packages
135+
uses: actions/download-artifact@v4
136+
with:
137+
name: Packages
138+
path: dist
139+
- run: tar xf dist/*.tar.gz --strip-components=1
140+
- uses: actions/setup-python@v5
141+
with:
142+
python-version: "3.12"
143+
- name: Set up test runner
144+
- run: |
145+
python -VV
146+
python -Im site
147+
python -Im pip install --upgrade nox
148+
python -Im nox --version
149+
- name: Check code formatting
150+
run: "python -Im nox --non-interactive --error-on-external-run --tag formatters --python 3.12"
151+
- name: Lint code
152+
run: "python -Im nox --non-interactive --error-on-external-run --tag linters --python 3.12"
153+
154+
155+
check-package:
156+
name: Additional package checks
157+
needs: build-package
158+
runs-on: ubuntu-latest
159+
steps:
160+
- name: Download pre-built packages
161+
uses: actions/download-artifact@v4
162+
with:
163+
name: Packages
164+
path: dist
165+
- run: tar xf dist/*.tar.gz --strip-components=1
166+
- uses: actions/setup-python@v5
167+
with:
168+
python-version: "3.12"
169+
- name: Set up test runner
170+
- run: |
171+
python -VV
172+
python -Im site
173+
python -Im pip install --upgrade nox
174+
python -Im nox --version
175+
- name: Check package
176+
run: "python -Im nox --non-interactive --error-on-external-run --tag packaging --python 3.12"
177+
178+
179+
required-checks-pass:
180+
name: Ensure required checks pass for branch protection
181+
if: always()
182+
183+
needs:
184+
- check-package
185+
- coverage
186+
- docs
187+
- lint-format
188+
189+
runs-on: ubuntu-latest
190+
191+
steps:
192+
- name: Decide whether the jobs succeeded or failed
193+
uses: re-actors/alls-green@release/v1
194+
with:
195+
jobs: ${{ toJSON(needs) }}

noxfile.py

+27-8
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
PACKAGE_NAME = "webcolors"
2626

27+
IS_CI = bool(os.getenv("CI", False))
28+
2729
NOXFILE_PATH = pathlib.Path(__file__).parents[0]
2830
ARTIFACT_PATHS = (
2931
NOXFILE_PATH / "src" / f"{PACKAGE_NAME}.egg-info",
@@ -41,6 +43,10 @@ def clean(paths: typing.Iterable[os.PathLike] = ARTIFACT_PATHS) -> None:
4143
Clean up after a test run.
4244
4345
"""
46+
# This cleanup is only useful for the working directory of a local checkout; in CI
47+
# we don't need it because CI environments are ephemeral anyway.
48+
if IS_CI:
49+
return
4450
[
4551
shutil.rmtree(path) if path.is_dir() else path.unlink()
4652
for path in paths
@@ -55,7 +61,7 @@ def clean(paths: typing.Iterable[os.PathLike] = ARTIFACT_PATHS) -> None:
5561
@nox.session(python=["3.8", "3.9", "3.10", "3.11", "3.12"], tags=["tests"])
5662
def tests_with_coverage(session: nox.Session) -> None:
5763
"""
58-
Run the package's unit tests, with coverage report.
64+
Run the package's unit tests, with coverage instrumentation.
5965
6066
"""
6167
session.install(".[tests]")
@@ -71,14 +77,27 @@ def tests_with_coverage(session: nox.Session) -> None:
7177
"unittest",
7278
"discover",
7379
)
80+
clean()
81+
82+
83+
@nox.session(python=["3.12"], tags=["tests"])
84+
def coverage_report(session: nox.Session) -> None:
85+
"""
86+
Combine coverage from the various test runs and output the report.
87+
88+
"""
89+
# In CI this job does not run because we substitute one that integrates with the CI
90+
# system.
91+
if IS_CI:
92+
session.skip(
93+
"Running in CI -- skipping nox coverage job in favor of CI coverage job"
94+
)
95+
session.install("coverage[toml]")
96+
session.run(f"python{session.python}", "-Im", "coverage", "combine")
7497
session.run(
75-
f"python{session.python}",
76-
"-Im",
77-
"coverage",
78-
"report",
79-
"--show-missing",
98+
f"python{session.python}", "-Im", "coverage", "report", "--show-missing"
8099
)
81-
clean()
100+
session.run(f"python{session.python}", "-Im", "coverage", "erase")
82101

83102

84103
@nox.session(python=["3.12"], tags=["tests", "release"])
@@ -185,7 +204,7 @@ def docs_spellcheck(session: nox.Session) -> None:
185204
clean()
186205

187206

188-
@nox.session(python=["3.11"], tags=["docs", "tests"])
207+
@nox.session(python=["3.12"], tags=["docs", "tests"])
189208
def docs_test(session: nox.Session) -> None:
190209
"""
191210
Run the code samples in the documentation with doctest, to ensure they are

pyproject.toml

+3
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ target-version = ["py38", "py39", "py310", "py311", "py312"]
5555
[tool.coverage.report]
5656
fail_under = 100
5757

58+
[tool.coverage.run]
59+
parallel = true
60+
5861
[tool.interrogate]
5962
ignore-init-module = true
6063

0 commit comments

Comments
 (0)