-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathnoxfile.py
178 lines (146 loc) · 5.76 KB
/
noxfile.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
"""Nox configuration for test automation and development tasks."""
import json
import os
from pathlib import Path
import nox
nox.options.reuse_existing_virtualenvs = True
nox.options.default_venv_backend = "uv"
NOT_SKIP_WITH_ACT = "not skip_with_act"
def _setup_venv(session: nox.Session, all_extras: bool = True) -> None:
"""Install dependencies for the given session using uv."""
args = ["uv", "sync", "--frozen"]
if all_extras:
args.append("--all-extras")
session.run_install(
*args,
env={
"UV_PROJECT_ENVIRONMENT": session.virtualenv.location,
"UV_PYTHON": str(session.python),
},
)
def _is_act_environment() -> bool:
"""Check if running in GitHub ACT environment.
Returns:
bool: True if running in ACT environment, False otherwise.
"""
return os.environ.get("GITHUB_WORKFLOW_RUNTIME") == "ACT"
@nox.session(python=["3.13"])
def lint(session: nox.Session) -> None:
"""Run code formatting checks, linting, and static type checking."""
_setup_venv(session)
session.run("ruff", "check", ".")
session.run(
"ruff",
"format",
"--check",
".",
)
# session.run("mypy", "src") # noqa: ERA001
@nox.session(python=["3.13"])
def docs(session: nox.Session) -> None:
"""Build documentation and concatenate README."""
_setup_venv(session)
# Concatenate README files
header = Path("_readme_header.md").read_text(encoding="utf-8")
main = Path("_readme_main.md").read_text(encoding="utf-8")
footer = Path("_readme_footer.md").read_text(encoding="utf-8")
readme_content = f"{header}\n\n{main}\n\n{footer}"
Path("README.md").write_text(readme_content, encoding="utf-8")
# Build docs
session.run("make", "-C", "docs", "html", external=True)
@nox.session(python=["3.13"])
def audit(session: nox.Session) -> None:
"""Run security audit and license checks."""
_setup_venv(session)
session.run("pip-audit", "-f", "json", "-o", "vulnerabilities.json")
session.run("jq", ".", "vulnerabilities.json", external=True)
session.run("pip-licenses", "--format=json", "--output-file=licenses.json")
session.run("jq", ".", "licenses.json", external=True)
# Read and parse licenses.json
licenses_data = json.loads(Path("licenses.json").read_text(encoding="utf-8"))
licenses_inverted: dict[str, list[dict[str, str]]] = {}
licenses_inverted = {}
for pkg in licenses_data:
license_name = pkg["License"]
package_info = {"Name": pkg["Name"], "Version": pkg["Version"]}
if license_name not in licenses_inverted:
licenses_inverted[license_name] = []
licenses_inverted[license_name].append(package_info)
# Write inverted data
Path("licenses-inverted.json").write_text(
json.dumps(licenses_inverted, indent=2),
encoding="utf-8",
)
session.run("jq", ".", "licenses-inverted.json", external=True)
session.run("cyclonedx-py", "environment", "-o", "sbom.json")
session.run("jq", ".", "sbom.json", external=True)
@nox.session(python=["3.11", "3.12", "3.13"])
def test(session: nox.Session) -> None:
"""Run tests with pytest."""
_setup_venv(session)
session.run("rm", "-rf", ".coverage", external=True)
# Build pytest arguments with skip_with_act filter if needed
pytest_args = ["pytest", "--disable-warnings", "--junitxml=junit.xml", "-n", "auto", "--dist", "loadgroup"]
if _is_act_environment():
pytest_args.extend(["-k", NOT_SKIP_WITH_ACT])
pytest_args.extend(["-m", "not sequential"])
session.run(*pytest_args)
# Sequential tests
sequential_args = [
"pytest",
"--cov-append",
"--disable-warnings",
"--junitxml=junit.xml",
"-n",
"auto",
"--dist",
"loadgroup",
]
if _is_act_environment():
sequential_args.extend(["-k", NOT_SKIP_WITH_ACT])
sequential_args.extend(["-m", "sequential"])
session.run(*sequential_args)
session.run(
"bash",
"-c",
(
"docker compose ls --format json | jq -r '.[].Name' | "
"grep ^pytest | xargs -I {} docker compose -p {} down --remove-orphans"
),
external=True,
)
@nox.session(python=["3.11", "3.12", "3.13"])
def test_no_extras(session: nox.Session) -> None:
"""Run test sessions without extra dependencies."""
_setup_venv(session, all_extras=False)
no_extras_args = ["pytest", "--cov-append", "--disable-warnings", "--junitxml=junit.xml", "-n", "1"]
if _is_act_environment():
no_extras_args.extend(["-k", NOT_SKIP_WITH_ACT])
no_extras_args.extend(["-m", "no_extras"])
session.run(*no_extras_args)
session.run(
"bash",
"-c",
(
"docker compose ls --format json | jq -r '.[].Name' | "
"grep ^pytest | xargs -I {} docker compose -p {} down --remove-orphans"
),
external=True,
)
@nox.session(python=["3.13"], default=False)
def setup_dev(session: nox.Session) -> None:
"""Setup dev environment post project creation."""
_setup_venv(session)
session.run("ruff", "format", ".", external=True)
git_dir = Path(".git")
if git_dir.is_dir():
session.run("echo", "found .git directory, running pre-commit install and hooks", external=True)
session.run("pre-commit", "install", external=True)
with Path(".secrets.baseline").open("w", encoding="utf-8") as out:
session.run("detect-secrets", "scan", stdout=out, external=True)
session.run("git", "add", ".", external=True)
try:
session.run("pre-commit", external=True)
except Exception: # noqa: BLE001
session.log("pre-commit run failed, continuing anyway")
session.run("git", "add", ".", external=True)