From 6967f1d351eefdd0796e3f88cfd6c547cff984fa Mon Sep 17 00:00:00 2001 From: "Rian.be" Date: Wed, 1 Jul 2026 15:53:22 +0200 Subject: [PATCH 1/3] ci: add dependency health checks for #54 --- .github/workflows/pr-check.yml | 18 ++++ .gitignore | 4 +- README.md | 10 ++ scripts/dependencies/__init__.py | 1 + scripts/dependencies/check_package_health.py | 105 +++++++++++++++++++ 5 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 scripts/dependencies/__init__.py create mode 100644 scripts/dependencies/check_package_health.py diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 469015a..69c2550 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -29,6 +29,24 @@ jobs: - name: Build ${{ matrix.configuration }} run: dotnet build ModularityKit.Mutator.slnx -c ${{ matrix.configuration }} --no-restore + dependency-health: + name: dependency health + runs-on: ubuntu-latest + needs: build + + steps: + - uses: actions/checkout@v5 + + - uses: actions/setup-dotnet@v5 + with: + dotnet-version: 10.0.x + + - name: Restore + run: dotnet restore ModularityKit.Mutator.slnx + + - name: Check dependency health + run: python3 -m scripts.dependencies.check_package_health --solution ModularityKit.Mutator.slnx + tests: name: ${{ matrix.name }} runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index d8715dc..48a9ed2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .idea/ bin/ -obj/ \ No newline at end of file +obj/ +__pycache__/ +*.pyc diff --git a/README.md b/README.md index eda9f01..0a27256 100644 --- a/README.md +++ b/README.md @@ -26,3 +26,13 @@ ```bash dotnet build ModularityKit.Mutator.slnx -c Release ``` + +## Dependency checks + +Run these after `dotnet restore ModularityKit.Mutator.slnx`: + +```bash +python3 -m scripts.dependencies.check_package_health --solution ModularityKit.Mutator.slnx +``` + +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. diff --git a/scripts/dependencies/__init__.py b/scripts/dependencies/__init__.py new file mode 100644 index 0000000..91e6317 --- /dev/null +++ b/scripts/dependencies/__init__.py @@ -0,0 +1 @@ +"""Dependency check helpers.""" diff --git a/scripts/dependencies/check_package_health.py b/scripts/dependencies/check_package_health.py new file mode 100644 index 0000000..fe39d91 --- /dev/null +++ b/scripts/dependencies/check_package_health.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 + +from __future__ import annotations + +import argparse +import os +import pathlib +import subprocess +import sys +from typing import Sequence + + +VULNERABLE_HEADING = "has the following vulnerable packages" +OUTDATED_HEADING = "has the following updates to its packages" + + +def repository_root() -> pathlib.Path: + return pathlib.Path(__file__).resolve().parents[2] + + +def run_dotnet_list(solution: str, mode: str) -> subprocess.CompletedProcess[str]: + env = os.environ.copy() + env.setdefault("DOTNET_CLI_UI_LANGUAGE", "en-US") + + return subprocess.run( + [ + "dotnet", + "list", + solution, + "package", + f"--{mode}", + "--include-transitive", + "--format", + "console", + "--no-restore", + ], + cwd=repository_root(), + env=env, + text=True, + capture_output=True, + ) + + +def emit_output(result: subprocess.CompletedProcess[str]) -> None: + if result.stdout: + sys.stdout.write(result.stdout) + if not result.stdout.endswith("\n"): + sys.stdout.write("\n") + + if result.stderr: + sys.stderr.write(result.stderr) + if not result.stderr.endswith("\n"): + sys.stderr.write("\n") + + +def check_mode(solution: str, mode: str, heading: str) -> tuple[bool, int]: + result = run_dotnet_list(solution, mode) + emit_output(result) + + found = heading in result.stdout + if result.returncode != 0 and not found: + return False, result.returncode + + return found, 0 + + +def parse_args(argv: Sequence[str]) -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Check package health for a solution.") + parser.add_argument( + "--solution", + default="ModularityKit.Mutator.slnx", + help="Path to the solution file to inspect.", + ) + return parser.parse_args(argv) + + +def main(argv: Sequence[str]) -> int: + args = parse_args(argv) + + vulnerable_found, vulnerable_error = check_mode(args.solution, "vulnerable", VULNERABLE_HEADING) + if vulnerable_error: + return vulnerable_error + + outdated_found, outdated_error = check_mode(args.solution, "outdated", OUTDATED_HEADING) + if outdated_error: + return outdated_error + + if vulnerable_found: + print( + "Vulnerable packages were reported above. Update the affected package reference and rerun the check.", + file=sys.stderr, + ) + return 1 + + if outdated_found: + print( + "Outdated packages were reported above. Update the affected package references when you are ready.", + file=sys.stderr, + ) + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main(sys.argv[1:])) From ed86ee22cad24060c07c240ba4a37b6149ddd0bd Mon Sep 17 00:00:00 2001 From: "Rian.be" Date: Wed, 1 Jul 2026 16:03:18 +0200 Subject: [PATCH 2/3] ci: let dependency checks restore as needed --- scripts/dependencies/check_package_health.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/dependencies/check_package_health.py b/scripts/dependencies/check_package_health.py index fe39d91..efec737 100644 --- a/scripts/dependencies/check_package_health.py +++ b/scripts/dependencies/check_package_health.py @@ -32,7 +32,6 @@ def run_dotnet_list(solution: str, mode: str) -> subprocess.CompletedProcess[str "--include-transitive", "--format", "console", - "--no-restore", ], cwd=repository_root(), env=env, From 6a34e825d9c2f58880f1c7b1fa166ac8e6b8fe24 Mon Sep 17 00:00:00 2001 From: "Rian.be" Date: Wed, 1 Jul 2026 16:16:30 +0200 Subject: [PATCH 3/3] ci: isolate intermediate output per project --- .github/workflows/pr-check.yml | 3 +++ Directory.Build.props | 5 +++++ 2 files changed, 8 insertions(+) create mode 100644 Directory.Build.props diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 69c2550..c27a3dc 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -176,6 +176,7 @@ jobs: runs-on: ubuntu-latest needs: - build + - dependency-health - tests - examples-core - examples-governance @@ -186,12 +187,14 @@ jobs: - name: Report job results run: | echo "build: ${{ needs.build.result }}" + echo "dependency-health: ${{ needs.dependency-health.result }}" echo "tests: ${{ needs.tests.result }}" echo "examples-core: ${{ needs.examples-core.result }}" echo "examples-governance: ${{ needs.examples-governance.result }}" echo "examples-governance-redis: ${{ needs.examples-governance-redis.result }}" if [ "${{ needs.build.result }}" != "success" ] || \ + [ "${{ needs.dependency-health.result }}" != "success" ] || \ [ "${{ needs.tests.result }}" != "success" ] || \ [ "${{ needs.examples-core.result }}" != "success" ] || \ [ "${{ needs.examples-governance.result }}" != "success" ] || \ diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..eefcd3d --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,5 @@ + + + $(MSBuildProjectDirectory)/obj/$(MSBuildProjectName)/ + +