Skip to content

Commit a4337c1

Browse files
authored
add resolution option to matrix to test minimum stated deps (opt in) (#35)
* drop py3.9 add resolution to matrix * add conditionals and actionlint test * add back py3.9 * add encoding * use system python version * remove ty
1 parent 8b8a2dd commit a4337c1

7 files changed

Lines changed: 83 additions & 26 deletions

File tree

.github/workflows/autotag.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
tag:
1313
runs-on: ubuntu-latest
1414
steps:
15-
- uses: actions/checkout@v4
15+
- uses: actions/checkout@v5
1616
with:
1717
fetch-depth: 0
1818
- run: |

.github/workflows/test.yml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@ name: Test copier
22

33
on:
44
pull_request:
5-
branches:
6-
- main
5+
branches: [main]
76
push:
8-
branches:
9-
- main
7+
branches: [main]
108
workflow_dispatch:
119

1210
concurrency:
@@ -26,6 +24,6 @@ jobs:
2624
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
2725

2826
steps:
29-
- uses: actions/checkout@v4
27+
- uses: actions/checkout@v5
3028
- uses: astral-sh/setup-uv@v6
3129
- run: uv run pytest tests -v --color=yes

copier.yml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ minimum_python:
8282
when: "{{ mode == 'customize' }}"
8383
help: What is the minimum supported Python version (https://devguide.python.org/versions/)?
8484
type: int
85-
default: 9
85+
default: 10
8686
choices:
8787
🐍 3.9: 9
8888
🐍 3.10: 10
@@ -99,6 +99,15 @@ test_pre_release:
9999
type: bool
100100
default: "{{ mode != 'simple' }}"
101101

102+
test_lowest_pinned_dependencies:
103+
when: "{{ mode == 'customize' }}"
104+
type: bool
105+
default: false
106+
help: |
107+
Would you like to test against both highest and lowest pinned dependencies?
108+
This adds a resolution matrix to test dependency resolution strategies.
109+
(It also requires that you add '>=' to all your deps in pyproject.toml)
110+
102111
use_pre_commit:
103112
when: "{{ mode == 'customize' }}"
104113
type: bool
@@ -115,4 +124,4 @@ use_mypy:
115124
when: "{{ mode == 'customize' and use_pre_commit }}"
116125
type: bool
117126
help: Add mypy for static type checking on type hints?
118-
default: "{{ mode != 'simple' }}"
127+
default: "{{ mode != 'simple' }}"

project/.github/workflows/ci.yml.jinja

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,29 +24,38 @@ jobs:
2424
check-manifest:
2525
runs-on: ubuntu-latest
2626
steps:
27-
- uses: actions/checkout@v4
27+
- uses: actions/checkout@v5
2828
- run: pipx run check-manifest
29-
{% endif %}{% raw %}
30-
test:
31-
name: ${{ matrix.platform }} (${{ matrix.python-version }})
29+
{% endif %}
30+
test:{% raw %}
31+
name: ${{ matrix.platform }} (${{ matrix.python-version }}){% endraw %}{%- if test_lowest_pinned_dependencies %}{% raw %} [${{ matrix.resolution }}]{% endraw %}{% endif %}{% raw %}
3232
runs-on: ${{ matrix.platform }}{% endraw %}
33-
{%- if test_pre_release %}
33+
{%- if test_pre_release or test_lowest_pinned_dependencies %}
3434
env:
35+
{%- if test_pre_release %}
3536
UV_PRERELEASE: {% raw %}${{ github.event_name == 'schedule' && 'allow' || 'if-necessary-or-explicit' }}{% endraw %}
37+
{%- endif %}
38+
{%- if test_lowest_pinned_dependencies %}
39+
UV_RESOLUTION: {% raw %}${{ matrix.resolution }}{% endraw %}
40+
PYTEST_ADDOPTS: {% raw %}${{ matrix.resolution == 'lowest-direct' && '-W ignore' || '' }}{% endraw %}
41+
{%- endif %}
3642
{%- endif %}
3743
strategy:
3844
fail-fast: false
3945
matrix:
4046
python-version: [{%- if minimum_python <= 9 %}"3.9", {% endif -%}
41-
{% if minimum_python <= 10 %}"3.10", {% endif -%}
42-
{% if minimum_python <= 11 %}"3.11", {% endif -%}
43-
{% if minimum_python <= 12 %}"3.12", {% endif -%}
44-
{% if minimum_python <= 12 %}"3.13", {% endif -%}
45-
{% if minimum_python <= 13 %}"3.14"{% endif %}{% raw %}]
46-
platform: [ubuntu-latest, macos-latest, windows-latest]
47-
47+
{%- if minimum_python <= 10 %}"3.10", {% endif -%}
48+
{%- if minimum_python <= 11 %}"3.11", {% endif -%}
49+
{%- if minimum_python <= 12 %}"3.12", {% endif -%}
50+
{%- if minimum_python <= 12 %}"3.13", {% endif -%}
51+
{%- if minimum_python <= 13 %}"3.14"{% endif %}{% raw %}]
52+
platform: [ubuntu-latest, macos-latest, windows-latest]{% endraw %}
53+
{%- if test_lowest_pinned_dependencies %}{% raw %}
54+
resolution: ["highest", "lowest-direct"]{% endraw %}
55+
{%- endif %}
56+
{% raw %}
4857
steps:
49-
- uses: actions/checkout@v4
58+
- uses: actions/checkout@v5
5059

5160
- name: 🐍 Set up Python ${{ matrix.python-version }}
5261
uses: astral-sh/setup-uv@v6
@@ -86,7 +95,7 @@ jobs:
8695
needs: test
8796
runs-on: ubuntu-latest
8897
steps:
89-
- uses: actions/checkout@v4
98+
- uses: actions/checkout@v5
9099
with:
91100
fetch-depth: 0
92101
- uses: hynek/build-and-inspect-python-package@v2
@@ -105,7 +114,7 @@ jobs:
105114

106115
steps:
107116
- name: Download built artifact to dist/
108-
uses: actions/download-artifact@v4
117+
uses: actions/download-artifact@v5
109118
with:
110119
name: Packages
111120
path: dist

project/pyproject.toml.jinja

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ classifiers = [
4141
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
4242
{%- endif %}
4343
"Programming Language :: Python :: 3",
44-
{%- if minimum_python <= 9 %}
44+
{%- if minimum_python <= 9 %}
4545
"Programming Language :: Python :: 3.9",
4646
{%- endif %}
4747
{%- if minimum_python <= 10 %}

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
name = "pyrepo-copier"
33
version = "0.1.0"
44
readme = "README.md"
5+
requires-python = ">=3.9"
56
license = { text = "BSD-3-Clause" }
67

78
[tool.mypy]
@@ -20,6 +21,7 @@ norecursedirs = "{{project_name}}"
2021

2122
[dependency-groups]
2223
dev = [
24+
"actionlint-py>=1.7.7.23",
2325
"check-manifest>=0.50",
2426
"copier>=9.7.1",
2527
"pre-commit>=4.2.0",

tests/test_generate.py

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import os
22
import shutil
3+
import sys
34
import tempfile
45
from contextlib import contextmanager
56
from pathlib import Path
@@ -76,13 +77,18 @@ def test_copier(template: Path, run_copier: Callable[..., Path]):
7677

7778
def test_bake_and_test(template: Path, run_copier: Callable[..., Path]):
7879
NAME = "some-project"
79-
output = run_copier(template, project_name=NAME, git_init=True)
80+
output = run_copier(
81+
template,
82+
project_name=NAME,
83+
git_init=True,
84+
minimum_python=sys.version_info.minor, # use current minor version for CI
85+
)
8086
with inside_dir(str(output)):
8187
run(["uv", "run", "pytest"], check=True)
8288

8389

8490
def test_bake_and_build(template, run_copier: Callable[..., Path]):
85-
output = run_copier(template, git_init=True)
91+
output = run_copier(template, git_init=True, minimum_python=sys.version_info.minor)
8692

8793
with inside_dir(str(output)):
8894
run(["uv", "run", "check-manifest"], check=True)
@@ -100,3 +106,36 @@ def test_bake_and_pre_commit(template, run_copier: Callable[..., Path]):
100106
run(["pre-commit", "install"], check=True)
101107
run(["git", "add", "."], check=True)
102108
run(["pre-commit", "run", "--all-files"], check=True)
109+
110+
111+
@pytest.mark.parametrize(
112+
"kwargs",
113+
[
114+
{"mode": "simple"},
115+
{"mode": "tooling"},
116+
{
117+
"mode": "customize",
118+
"minimum_python": 10,
119+
"test_lowest_pinned_dependencies": True,
120+
"test_pre_release": True,
121+
},
122+
],
123+
ids=lambda d: d["mode"],
124+
)
125+
def test_actionlint_on_rendered_workflow(
126+
template: Path, run_copier: Callable[..., Path], kwargs: dict[str, Any]
127+
):
128+
"""Test that the rendered CI workflow passes actionlint validation."""
129+
# Test with default settings (should not have resolution matrix)
130+
output = run_copier(template, **kwargs)
131+
ci_file = output / ".github" / "workflows" / "ci.yml"
132+
assert ci_file.exists()
133+
134+
# Run actionlint on default configuration
135+
run(["actionlint", str(ci_file)], check=True)
136+
137+
# Verify no resolution matrix in default output
138+
ci_content = ci_file.read_text(encoding="utf-8")
139+
is_custom = kwargs["mode"] == "customize"
140+
assert ("resolution:" in ci_content) is is_custom
141+
assert ("[${{ matrix.resolution }}]" in ci_content) is is_custom

0 commit comments

Comments
 (0)