Skip to content

Commit 6967f1d

Browse files
committed
ci: add dependency health checks for #54
1 parent 2e31b1b commit 6967f1d

5 files changed

Lines changed: 137 additions & 1 deletion

File tree

.github/workflows/pr-check.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,24 @@ jobs:
2929
- name: Build ${{ matrix.configuration }}
3030
run: dotnet build ModularityKit.Mutator.slnx -c ${{ matrix.configuration }} --no-restore
3131

32+
dependency-health:
33+
name: dependency health
34+
runs-on: ubuntu-latest
35+
needs: build
36+
37+
steps:
38+
- uses: actions/checkout@v5
39+
40+
- uses: actions/setup-dotnet@v5
41+
with:
42+
dotnet-version: 10.0.x
43+
44+
- name: Restore
45+
run: dotnet restore ModularityKit.Mutator.slnx
46+
47+
- name: Check dependency health
48+
run: python3 -m scripts.dependencies.check_package_health --solution ModularityKit.Mutator.slnx
49+
3250
tests:
3351
name: ${{ matrix.name }}
3452
runs-on: ubuntu-latest

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
.idea/
22
bin/
3-
obj/
3+
obj/
4+
__pycache__/
5+
*.pyc

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,13 @@
2626
```bash
2727
dotnet build ModularityKit.Mutator.slnx -c Release
2828
```
29+
30+
## Dependency checks
31+
32+
Run these after `dotnet restore ModularityKit.Mutator.slnx`:
33+
34+
```bash
35+
python3 -m scripts.dependencies.check_package_health --solution ModularityKit.Mutator.slnx
36+
```
37+
38+
The check reports vulnerable packages as a failing condition and prints outdated packages for review. When a package needs attention, update the affected `PackageReference` version in the owning project and rerun the check.

scripts/dependencies/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Dependency check helpers."""
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#!/usr/bin/env python3
2+
3+
from __future__ import annotations
4+
5+
import argparse
6+
import os
7+
import pathlib
8+
import subprocess
9+
import sys
10+
from typing import Sequence
11+
12+
13+
VULNERABLE_HEADING = "has the following vulnerable packages"
14+
OUTDATED_HEADING = "has the following updates to its packages"
15+
16+
17+
def repository_root() -> pathlib.Path:
18+
return pathlib.Path(__file__).resolve().parents[2]
19+
20+
21+
def run_dotnet_list(solution: str, mode: str) -> subprocess.CompletedProcess[str]:
22+
env = os.environ.copy()
23+
env.setdefault("DOTNET_CLI_UI_LANGUAGE", "en-US")
24+
25+
return subprocess.run(
26+
[
27+
"dotnet",
28+
"list",
29+
solution,
30+
"package",
31+
f"--{mode}",
32+
"--include-transitive",
33+
"--format",
34+
"console",
35+
"--no-restore",
36+
],
37+
cwd=repository_root(),
38+
env=env,
39+
text=True,
40+
capture_output=True,
41+
)
42+
43+
44+
def emit_output(result: subprocess.CompletedProcess[str]) -> None:
45+
if result.stdout:
46+
sys.stdout.write(result.stdout)
47+
if not result.stdout.endswith("\n"):
48+
sys.stdout.write("\n")
49+
50+
if result.stderr:
51+
sys.stderr.write(result.stderr)
52+
if not result.stderr.endswith("\n"):
53+
sys.stderr.write("\n")
54+
55+
56+
def check_mode(solution: str, mode: str, heading: str) -> tuple[bool, int]:
57+
result = run_dotnet_list(solution, mode)
58+
emit_output(result)
59+
60+
found = heading in result.stdout
61+
if result.returncode != 0 and not found:
62+
return False, result.returncode
63+
64+
return found, 0
65+
66+
67+
def parse_args(argv: Sequence[str]) -> argparse.Namespace:
68+
parser = argparse.ArgumentParser(description="Check package health for a solution.")
69+
parser.add_argument(
70+
"--solution",
71+
default="ModularityKit.Mutator.slnx",
72+
help="Path to the solution file to inspect.",
73+
)
74+
return parser.parse_args(argv)
75+
76+
77+
def main(argv: Sequence[str]) -> int:
78+
args = parse_args(argv)
79+
80+
vulnerable_found, vulnerable_error = check_mode(args.solution, "vulnerable", VULNERABLE_HEADING)
81+
if vulnerable_error:
82+
return vulnerable_error
83+
84+
outdated_found, outdated_error = check_mode(args.solution, "outdated", OUTDATED_HEADING)
85+
if outdated_error:
86+
return outdated_error
87+
88+
if vulnerable_found:
89+
print(
90+
"Vulnerable packages were reported above. Update the affected package reference and rerun the check.",
91+
file=sys.stderr,
92+
)
93+
return 1
94+
95+
if outdated_found:
96+
print(
97+
"Outdated packages were reported above. Update the affected package references when you are ready.",
98+
file=sys.stderr,
99+
)
100+
101+
return 0
102+
103+
104+
if __name__ == "__main__":
105+
raise SystemExit(main(sys.argv[1:]))

0 commit comments

Comments
 (0)