|
4 | 4 | import os |
5 | 5 | import re |
6 | 6 | import shutil |
| 7 | +import tempfile |
7 | 8 | from pathlib import Path |
8 | 9 |
|
9 | 10 | import nox |
| 11 | +from nox.command import CommandFailed |
10 | 12 |
|
11 | 13 | nox.options.default_venv_backend = "uv|virtualenv" |
12 | 14 | nox.options.reuse_existing_virtualenvs = True |
|
16 | 18 | PY311 = "3.11" |
17 | 19 | PY312 = "3.12" |
18 | 20 | PY313 = "3.13" |
19 | | -PY_VERSIONS = [PY39, PY310, PY311, PY312, PY313] |
| 21 | +PY314 = "3.14" |
| 22 | +PY_VERSIONS = [PY39, PY310, PY311, PY312, PY313, PY314] |
20 | 23 | PY_DEFAULT = PY_VERSIONS[0] |
21 | 24 | PY_LATEST = PY_VERSIONS[-1] |
22 | 25 |
|
@@ -114,21 +117,51 @@ def tests(session, django): |
114 | 117 |
|
115 | 118 | @nox.session |
116 | 119 | def lint(session): |
117 | | - session.run( |
118 | | - "uv", |
119 | | - "run", |
120 | | - "--no-project", |
121 | | - "--with", |
122 | | - "pre-commit-uv", |
123 | | - "--python", |
124 | | - PY_LATEST, |
125 | | - "pre-commit", |
126 | | - "run", |
127 | | - "--all-files", |
128 | | - "--show-diff-on-failure", |
129 | | - "--color", |
130 | | - "always", |
131 | | - ) |
| 120 | + for python_version in reversed(PY_VERSIONS): |
| 121 | + with tempfile.TemporaryFile(mode="w+") as output_file: |
| 122 | + try: |
| 123 | + session.run( |
| 124 | + "uv", |
| 125 | + "run", |
| 126 | + "--no-project", |
| 127 | + "--with", |
| 128 | + "pre-commit-uv", |
| 129 | + "--python", |
| 130 | + python_version, |
| 131 | + "pre-commit", |
| 132 | + "run", |
| 133 | + "--all-files", |
| 134 | + "--show-diff-on-failure", |
| 135 | + "--color", |
| 136 | + "always", |
| 137 | + stdout=output_file, |
| 138 | + stderr=output_file, |
| 139 | + ) |
| 140 | + output_file.seek(0) |
| 141 | + output = output_file.read().rstrip("\n") |
| 142 | + if output: |
| 143 | + print(output) |
| 144 | + break |
| 145 | + except CommandFailed as e: |
| 146 | + # Parse exit code from exception reason: "Returned code X" |
| 147 | + match = re.search(r"Returned code (\d+)", e.reason or "") |
| 148 | + exit_code = int(match.group(1)) if match else None |
| 149 | + |
| 150 | + # Only retry on exit code 3 (infrastructure error) |
| 151 | + if exit_code == 3: |
| 152 | + session.log( |
| 153 | + f"Linting with Python {python_version} failed due to pre-commit infrastructure issue (exit code 3), trying next version" |
| 154 | + ) |
| 155 | + continue |
| 156 | + else: |
| 157 | + # Real lint failure (exit code 1) or unknown error - re-raise |
| 158 | + output_file.seek(0) |
| 159 | + error_output = output_file.read().rstrip("\n") |
| 160 | + if error_output: |
| 161 | + print(error_output) |
| 162 | + raise |
| 163 | + else: |
| 164 | + session.error("Linting failed with all Python versions") |
132 | 165 |
|
133 | 166 |
|
134 | 167 | @nox.session |
@@ -221,19 +254,6 @@ def cog(session): |
221 | 254 | "-r", |
222 | 255 | *COG_FILES, |
223 | 256 | ) |
224 | | - git_status = session.run("git", "status", "--porcelain", external=True, silent=True) |
225 | | - if not any(cog_file in git_status for cog_file in COG_FILES): |
226 | | - session.log("No changes to documentation files, skipping commit") |
227 | | - return |
228 | | - session.run("git", "add", *COG_FILES, external=True) |
229 | | - session.run( |
230 | | - "git", |
231 | | - "commit", |
232 | | - "-m", |
233 | | - "auto-regenerate docs using cog", |
234 | | - external=True, |
235 | | - silent=True, |
236 | | - ) |
237 | 257 |
|
238 | 258 |
|
239 | 259 | @nox.session |
|
0 commit comments