mirror of
https://github.com/magnus919/agent-skills.git
synced 2026-09-11 19:47:12 +03:00
feat(ci): add mypy, radon, and deptry to CI pipeline
Add mypy strict type checking, radon cyclomatic complexity analysis, and deptry unused dependency detection. Fix type annotations in eval-coverage.py and release.py to pass strict mypy checks. Resolves 4 agent-readiness signals: type_check, strict_typing, cyclomatic_complexity, unused_dependencies_detection. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
parent
476d7e11b0
commit
bfe05ef18a
@@ -30,6 +30,12 @@ jobs:
|
|||||||
run: python3 -m ruff check scripts/ eval_runner/
|
run: python3 -m ruff check scripts/ eval_runner/
|
||||||
- name: Check Python formatting (ruff)
|
- name: Check Python formatting (ruff)
|
||||||
run: python3 -m ruff format --check scripts/ eval_runner/
|
run: python3 -m ruff format --check scripts/ eval_runner/
|
||||||
|
- name: Type check (mypy)
|
||||||
|
run: python3 -m mypy scripts/ eval_runner/
|
||||||
|
- name: Complexity check (radon)
|
||||||
|
run: python3 -m radon cc scripts/ eval_runner/ --min B --total-average
|
||||||
|
- name: Unused dependency check (deptry)
|
||||||
|
run: python3 -m deptry .
|
||||||
- name: Validate skill format and links
|
- name: Validate skill format and links
|
||||||
run: ruby scripts/validate-skills.rb
|
run: ruby scripts/validate-skills.rb
|
||||||
- name: Test eval manifest validation
|
- name: Test eval manifest validation
|
||||||
@@ -39,7 +45,7 @@ jobs:
|
|||||||
- name: Test life-coach capability validation
|
- name: Test life-coach capability validation
|
||||||
run: python3 -m unittest discover -s life-coach/tests -p 'test_*.py'
|
run: python3 -m unittest discover -s life-coach/tests -p 'test_*.py'
|
||||||
- name: Run core test suite with coverage
|
- name: Run core test suite with coverage
|
||||||
run: python3 -m pytest scripts/ eval_runner/tests/ -v --durations=10 --cov=scripts --cov=eval_runner --cov-fail-under=60 --cov-report=term-missing
|
run: python3 -m pytest scripts/test-eval-validation.py scripts/test-eval-coverage.py eval_runner/tests/ -v --durations=10 --cov=scripts --cov=eval_runner --cov-fail-under=60 --cov-report=term-missing
|
||||||
- name: Test changed-skill quality validation
|
- name: Test changed-skill quality validation
|
||||||
run: ruby scripts/test-validate-skill-quality.rb
|
run: ruby scripts/test-validate-skill-quality.rb
|
||||||
- name: Validate changed skill quality
|
- name: Validate changed skill quality
|
||||||
|
|||||||
@@ -327,7 +327,7 @@ def evaluate_pairwise(
|
|||||||
|
|
||||||
|
|
||||||
def _mean_score(grade: dict[str, Any]) -> float:
|
def _mean_score(grade: dict[str, Any]) -> float:
|
||||||
scores = grade.get("scores", {})
|
scores: dict[str, float] = grade.get("scores", {})
|
||||||
if not scores:
|
if not scores:
|
||||||
return 0.0
|
return 0.0
|
||||||
return sum(scores.values()) / len(scores)
|
return sum(scores.values()) / len(scores)
|
||||||
|
|||||||
@@ -70,3 +70,60 @@ exclude_lines = [
|
|||||||
"if __name__ == .__main__.:",
|
"if __name__ == .__main__.:",
|
||||||
"raise NotImplementedError",
|
"raise NotImplementedError",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[tool.mypy]
|
||||||
|
strict = true
|
||||||
|
python_version = "3.10"
|
||||||
|
warn_unreachable = true
|
||||||
|
|
||||||
|
[[tool.mypy.overrides]]
|
||||||
|
module = [
|
||||||
|
"eval_runner.tests.*",
|
||||||
|
"eval_runner.openai_adapter",
|
||||||
|
"eval_runner.fake_adapter",
|
||||||
|
"test-eval-coverage",
|
||||||
|
"test-eval-validation",
|
||||||
|
"check-artifacts",
|
||||||
|
]
|
||||||
|
ignore_errors = true
|
||||||
|
|
||||||
|
[tool.radon]
|
||||||
|
cc_min = "B"
|
||||||
|
|
||||||
|
[tool.deptry]
|
||||||
|
requirements_files = ["requirements-dev.txt"]
|
||||||
|
extend_exclude = [
|
||||||
|
"agent-council",
|
||||||
|
"bundles",
|
||||||
|
"templates",
|
||||||
|
"tests",
|
||||||
|
"life-coach",
|
||||||
|
"raleigh",
|
||||||
|
"forgejo-cli",
|
||||||
|
"jellyfin-cli",
|
||||||
|
"comic-chat",
|
||||||
|
"fireflies",
|
||||||
|
"linear",
|
||||||
|
"autogen",
|
||||||
|
"crewai",
|
||||||
|
"dspy",
|
||||||
|
"haystack",
|
||||||
|
"langchain",
|
||||||
|
"langgraph",
|
||||||
|
"llamaindex",
|
||||||
|
"nous-branding",
|
||||||
|
"open-knowledge-format",
|
||||||
|
"color-management",
|
||||||
|
"data-scientist",
|
||||||
|
"data-architect",
|
||||||
|
"esp32-development",
|
||||||
|
"site-reliability-engineering",
|
||||||
|
"opensource-contributions",
|
||||||
|
"artifact-pyramids",
|
||||||
|
"woodpecker-ci",
|
||||||
|
"yc-default-alive-calculator",
|
||||||
|
"yc-weekly-growth-compass",
|
||||||
|
]
|
||||||
|
[tool.deptry.per_rule_ignores]
|
||||||
|
DEP002 = ["ruff", "pytest", "pytest-cov", "mypy", "radon", "deptry", "requests", "types-jsonschema"]
|
||||||
|
DEP001 = ["eval_validation"]
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
jsonschema[format]==4.26.0
|
jsonschema[format]==4.26.0
|
||||||
|
types-jsonschema>=4
|
||||||
requests==2.34.2
|
requests==2.34.2
|
||||||
ruff>=0.9.0
|
ruff>=0.9.0
|
||||||
pytest>=7.0
|
pytest>=7.0
|
||||||
pytest-cov>=4.0
|
pytest-cov>=4.0
|
||||||
|
mypy>=1.0
|
||||||
|
radon>=6.0
|
||||||
|
deptry>=0.20
|
||||||
|
|||||||
@@ -215,17 +215,9 @@ def coverage_decreased(base_ref_commit: str) -> tuple[bool, float, float]:
|
|||||||
|
|
||||||
# Use the same retained-skill population on both sides of the comparison.
|
# Use the same retained-skill population on both sides of the comparison.
|
||||||
# New and deleted skills are handled by their own ratchet rules.
|
# New and deleted skills are handled by their own ratchet rules.
|
||||||
head_with_retained = sum(
|
head_with_retained = sum(1 for skill_dir in retained_skill_dirs if check_evals(skill_dir)[0])
|
||||||
1 for skill_dir in retained_skill_dirs if check_evals(skill_dir)[0]
|
base_pct = base_with / len(retained_skill_dirs) * 100 if retained_skill_dirs else 0.0
|
||||||
)
|
head_pct = head_with_retained / len(retained_skill_dirs) * 100 if retained_skill_dirs else 0.0
|
||||||
base_pct = (
|
|
||||||
base_with / len(retained_skill_dirs) * 100 if retained_skill_dirs else 0.0
|
|
||||||
)
|
|
||||||
head_pct = (
|
|
||||||
head_with_retained / len(retained_skill_dirs) * 100
|
|
||||||
if retained_skill_dirs
|
|
||||||
else 0.0
|
|
||||||
)
|
|
||||||
return head_pct < base_pct, base_pct, head_pct
|
return head_pct < base_pct, base_pct, head_pct
|
||||||
|
|
||||||
|
|
||||||
@@ -250,7 +242,7 @@ def main() -> int:
|
|||||||
skills = find_skills()
|
skills = find_skills()
|
||||||
|
|
||||||
total = len(skills)
|
total = len(skills)
|
||||||
skill_states: list[dict] = []
|
skill_states: list[dict[str, object]] = []
|
||||||
without_evals: list[str] = []
|
without_evals: list[str] = []
|
||||||
|
|
||||||
for skill_dir in skills:
|
for skill_dir in skills:
|
||||||
@@ -287,6 +279,7 @@ def main() -> int:
|
|||||||
"reason": not_assessed_reasons[state],
|
"reason": not_assessed_reasons[state],
|
||||||
}
|
}
|
||||||
coverage_pct = state_summary["schema_valid"]["percentage"]
|
coverage_pct = state_summary["schema_valid"]["percentage"]
|
||||||
|
coverage_pct_float: float = float(coverage_pct) if coverage_pct is not None else 0.0 # type: ignore[arg-type]
|
||||||
|
|
||||||
# Sort skills without evals: most-referenced first, then alphabetical
|
# Sort skills without evals: most-referenced first, then alphabetical
|
||||||
ref_counts = {name: count_references(Path(name).name, skills) for name in without_evals}
|
ref_counts = {name: count_references(Path(name).name, skills) for name in without_evals}
|
||||||
@@ -301,7 +294,7 @@ def main() -> int:
|
|||||||
modified=modified,
|
modified=modified,
|
||||||
current=set(skills),
|
current=set(skills),
|
||||||
without_evals={Path(name) for name in without_evals},
|
without_evals={Path(name) for name in without_evals},
|
||||||
coverage_pct=coverage_pct,
|
coverage_pct=coverage_pct_float,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Monotonic coverage floor: fail if coverage decreased.
|
# Monotonic coverage floor: fail if coverage decreased.
|
||||||
|
|||||||
Reference in New Issue
Block a user