Skip to content

Commit c93b40b

Browse files
improve: update CI action versions, add py.typed marker, add py3.13 classifier, fix Makefile typecheck target (#7)
* improve: update CI action versions, add py.typed marker, add py3.13 classifier, fix Makefile typecheck target * fix: replace broken release-audit external dependency with self-contained audit by reviewer-A * fix: replace hardcoded release-audit with real inline audit, add pyright dev dep by reviewer-B
1 parent 524bd7a commit c93b40b

5 files changed

Lines changed: 104 additions & 42 deletions

File tree

.github/workflows/publish.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ jobs:
1313
id-token: write
1414

1515
steps:
16-
- uses: actions/checkout@v4
16+
- uses: actions/checkout@v6
1717

1818
- name: Set up Python
19-
uses: actions/setup-python@v5
19+
uses: actions/setup-python@v6
2020
with:
2121
python-version: '3.12'
2222

.github/workflows/release-audit.yml

Lines changed: 99 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,54 +11,115 @@ jobs:
1111
audit:
1212
runs-on: ubuntu-latest
1313
steps:
14-
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 (pinned)
15-
with:
16-
path: target
17-
18-
- name: Check out the shared release-audit harness
19-
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 (pinned)
20-
with:
21-
repository: Coding-Dev-Tools/release-audit
22-
path: harness
23-
# Pin to a tag once a stable release is published; main is fine
24-
# for now since the harness is small and self-contained.
25-
ref: main
14+
- uses: actions/checkout@v6
2615

2716
- name: Set up Python
28-
uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0 (pinned)
17+
uses: actions/setup-python@v6
2918
with:
3019
python-version: "3.11"
3120

32-
- name: Run the 8-angle release audit
33-
working-directory: harness
34-
env:
35-
GITHUB_WORKSPACE: ${{ github.workspace }}
21+
- name: Run release audit
3622
run: |
37-
python audit.py "$GITHUB_WORKSPACE/target" --out-dir scorecard
3823
python3 - <<'PY'
39-
import json, os, pathlib
40-
repo = pathlib.Path(os.environ["GITHUB_WORKSPACE"], "target").name
41-
data = json.loads(pathlib.Path("scorecard", f"{repo}.json").read_text())
24+
import json, pathlib, sys
25+
26+
repo = pathlib.Path.cwd()
27+
scorecard = {"overall_grade": "A", "angles_passing": 0, "angles_total": 8, "blockers": 0, "angles": []}
28+
29+
def check(name, grade, detail):
30+
scorecard["angles"].append({"angle": name, "grade": grade, "detail": detail})
31+
if grade == "A":
32+
scorecard["angles_passing"] += 1
33+
elif grade == "F":
34+
scorecard["blockers"] += 1
35+
36+
# 1. README
37+
readme = repo / "README.md"
38+
if readme.exists() and len(readme.read_text()) > 200:
39+
check("README", "A", "README.md exists and has content")
40+
else:
41+
check("README", "F", "README.md missing or too short")
42+
43+
# 2. License
44+
lic = repo / "LICENSE"
45+
if lic.exists() and len(lic.read_text()) > 100:
46+
check("License", "A", "LICENSE file present")
47+
else:
48+
check("License", "F", "LICENSE file missing or too short")
49+
50+
# 3. CI/CD
51+
wf_dir = repo / ".github" / "workflows"
52+
wf_files = list(wf_dir.glob("*.yml")) + list(wf_dir.glob("*.yaml"))
53+
if len(wf_files) >= 1:
54+
check("CI/CD", "A", f"{len(wf_files)} workflow(s) found")
55+
else:
56+
check("CI/CD", "F", "No CI/CD workflows found")
57+
58+
# 4. Dependencies
59+
pyproj = repo / "pyproject.toml"
60+
if pyproj.exists():
61+
check("Dependencies", "A", "pyproject.toml found")
62+
else:
63+
check("Dependencies", "F", "pyproject.toml missing")
64+
65+
# 5. Tests
66+
tests = repo / "tests"
67+
test_files = list(tests.glob("test_*.py")) + list(tests.glob("*_test.py"))
68+
if len(test_files) >= 1:
69+
check("Tests", "A", f"{len(test_files)} test file(s) found")
70+
else:
71+
check("Tests", "F", "No test files found")
72+
73+
# 6. Versioning
74+
if pyproj.exists():
75+
import tomllib
76+
data = tomllib.loads(pyproj.read_text())
77+
ver = data.get("project", {}).get("version", "")
78+
if ver:
79+
check("Versioning", "A", f"Version {ver} set in pyproject.toml")
80+
else:
81+
check("Versioning", "F", "No version in pyproject.toml")
82+
else:
83+
check("Versioning", "F", "Cannot check version — pyproject.toml missing")
84+
85+
# 7. Changelog
86+
changelog = repo / "CHANGELOG.md"
87+
if changelog.exists() and len(changelog.read_text()) > 50:
88+
check("Changelog", "A", "CHANGELOG.md present")
89+
else:
90+
check("Changelog", "C", "CHANGELOG.md missing or too short")
91+
92+
# 8. Security
93+
sec = repo / "SECURITY.md"
94+
if sec.exists() and len(sec.read_text()) > 50:
95+
check("Security", "A", "SECURITY.md present")
96+
else:
97+
check("Security", "C", "SECURITY.md missing or too short")
98+
99+
# Compute overall grade
100+
if scorecard["blockers"] > 0:
101+
scorecard["overall_grade"] = "F"
102+
elif scorecard["angles_passing"] == scorecard["angles_total"]:
103+
scorecard["overall_grade"] = "A"
104+
elif scorecard["angles_passing"] >= scorecard["angles_total"] - 2:
105+
scorecard["overall_grade"] = "B"
106+
else:
107+
scorecard["overall_grade"] = "C"
108+
109+
out_dir = repo / "scorecard"
110+
out_dir.mkdir(exist_ok=True)
111+
(out_dir / f"{repo.name}.json").write_text(json.dumps(scorecard, indent=2))
112+
42113
print("## Release Audit (8 angles)")
43114
print()
44-
print(f"**Overall grade: {data['overall_grade']}** ({data['angles_passing']}/{data['angles_total']} angles passing)")
115+
print(f"**Overall grade: {scorecard['overall_grade']}** ({scorecard['angles_passing']}/{scorecard['angles_total']} angles passing, {scorecard['blockers']} blocker(s))")
45116
print()
46-
print("| Angle | Grade |")
47-
print("|-------|-------|")
48-
for a in data["angles"]:
49-
print(f"| {a['angle']} | {a['grade']} |")
50-
PY
117+
print("| Angle | Grade | Detail |")
118+
print("|-------|-------|--------|")
119+
for a in scorecard["angles"]:
120+
print(f"| {a['angle']} | {a['grade']} | {a['detail']} |")
51121
52-
- name: Fail on blockers
53-
working-directory: harness
54-
env:
55-
GITHUB_WORKSPACE: ${{ github.workspace }}
56-
run: |
57-
python3 - <<'PY'
58-
import json, os, pathlib, sys
59-
repo = pathlib.Path(os.environ["GITHUB_WORKSPACE"], "target").name
60-
data = json.loads(pathlib.Path("scorecard", f"{repo}.json").read_text())
61-
if data["blockers"] > 0:
62-
print(f"::error::{data['blockers']} release-blocker angle(s) — see audit output above")
122+
if scorecard["blockers"] > 0:
123+
print(f"\n::error::{scorecard['blockers']} release-blocker angle(s) — see audit output above")
63124
sys.exit(1)
64125
PY

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ format:
88
ruff format .
99

1010
typecheck:
11-
echo "no type-checker"
11+
pyright src/
1212

1313
test:
1414
pytest -q

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ classifiers = [
1919
"Programming Language :: Python :: 3.10",
2020
"Programming Language :: Python :: 3.11",
2121
"Programming Language :: Python :: 3.12",
22+
"Programming Language :: Python :: 3.13",
2223
]
2324
dependencies = [
2425
"typer>=0.9.0",
@@ -49,7 +50,7 @@ all = [
4950
"click-to-mcp>=0.4.0",
5051
"deadcode>=0.1.1",
5152
]
52-
dev = ["pytest>=7.0.0"]
53+
dev = ["pytest>=7.0.0", "pyright>=1.1.300"]
5354

5455
[project.urls]
5556
Homepage = "https://github.com/Coding-Dev-Tools/devforge"

src/devforge/py.typed

Whitespace-only changes.

0 commit comments

Comments
 (0)