Skip to content

Commit c5adc48

Browse files
add workflow to check that all .md files are linked in SUMMARY.md (#518)
1 parent 9f8b72e commit c5adc48

File tree

5 files changed

+102
-0
lines changed

5 files changed

+102
-0
lines changed

.github/workflows/test-summary.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
name: Test SUMMARY.md
2+
3+
on:
4+
pull_request:
5+
push:
6+
7+
jobs:
8+
test-summary:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- name: Checkout repository
12+
uses: actions/checkout@v4
13+
- name: Run SUMMARY.md link check
14+
run: python tests/test_summary.py

src/SUMMARY.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ Space Station 14
123123
- [Device Network](en/space-station-14/core-tech/device-network.md)
124124
- [NPCs](en/space-station-14/core-tech/npcs.md)
125125
- [Entity Tables](en/space-station-14/core-tech/entity-tables.md)
126+
- [Body](en/space-station-14/core-tech/body.md)
126127
- [Chemistry](en/space-station-14/core-tech/chemistry.md)
127128
- [Metabolism](en/space-station-14/core-tech/chemistry/metabolism.md)
128129
- [Reactions](en/space-station-14/core-tech/chemistry/reactions.md)
@@ -150,6 +151,7 @@ Space Station 14
150151
- [PR Guidelines](en/space-station-14/character-species/guidelines.md)
151152
152153
- [Proposals]()
154+
- [Vulpkanin](en/space-station-14/character-species/proposals/vulpkanin.md)
153155

154156
- [Combat](en/space-station-14/combat.md)
155157
- [PR Guidelines](en/space-station-14/combat/guidelines.md)
@@ -206,13 +208,16 @@ Space Station 14
206208
207209
- [Proposals]()
208210
- [Stat Panels](en/space-station-14/user-interface/proposals/statpanels.md)
211+
- [Playtime Reminders](en/space-station-14/user-interface/proposals/playtimereminders.md)
209212
- [Departments](en/space-station-14/departments.md)
210213
- [Atmos](en/space-station-14/departments/atmos.md)
211214
- [PR Guidelines](en/space-station-14/departments/atmos/guidelines.md)
212215

213216
- [Proposals]()
214217
- [Atmos Rework](en/space-station-14/departments/atmos/proposals/atmos-rework.md)
215218
- [Station Air Recirculation](en/space-station-14/departments/atmos/proposals/station-air-recirculation.md)
219+
- [Inverse Pneumatic Valves](en/space-station-14/departments/atmos/proposals/inverse-pneumatic-valves.md)
220+
216221

217222
- [Cargo](en/space-station-14/departments/cargo.md)
218223
- [PR Guidelines](en/space-station-14/departments/cargo/guidelines.md)
@@ -255,6 +260,7 @@ Space Station 14
255260
- [Proposals]()
256261
- [GenPop Prisoners](en/space-station-14/departments/security/proposals/genpop-prisoners.md)
257262
- [Reduced Metagaming Mechanics](en/space-station-14/departments/security/proposals/reduced-metagaming.md)
263+
- [Secdog Unit Ghost role](en/space-station-14/departments/security/proposals/SecDog.md)
258264
- [Service](en/space-station-14/departments/service.md)
259265
- [PR Guidelines](en/space-station-14/departments/service/guidelines.md)
260266

File renamed without changes.

tests/test_summary.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#!/usr/bin/env python3
2+
3+
import re
4+
import sys
5+
from pathlib import Path
6+
7+
REPO_ROOT = Path.cwd()
8+
9+
# Folders to check
10+
DOCS_DIRS = [
11+
REPO_ROOT / r"src/en",
12+
]
13+
14+
# Folders to ignore
15+
EXEMPT_DIRS = [
16+
REPO_ROOT / r"src/en/assets",
17+
REPO_ROOT / r"src/en/templates",
18+
]
19+
20+
# The file to check the links for
21+
SUMMARY_FILE = REPO_ROOT / r"src/SUMMARY.md"
22+
23+
24+
def get_summary_links():
25+
"""Parse SUMMARY.md and return set of linked md paths (absolute)."""
26+
links = set()
27+
# regex explanation:
28+
# valid links have the form
29+
# [How do I code?](en/general-development/setup/howdoicode.md)
30+
# \[ and \] match literal brackets
31+
# [^]] means "not a closing bracket"
32+
# [^]]+ matches the text inside the brackets, with at least one character
33+
# \( and \) match literal parenthesis
34+
# [^)] means "not a closing parentheses"
35+
# [^)]+ matches the file link, with at least one character
36+
# ([^)]+) will capture the file link inside the parentheses
37+
link_pattern = re.compile(r"\[[^]]+\]\(([^)]+)\)")
38+
39+
for line in SUMMARY_FILE.read_text(encoding="utf-8").splitlines():
40+
match = link_pattern.search(line)
41+
if match:
42+
path = match.group(1)
43+
absolute = (SUMMARY_FILE.parent / path).resolve()
44+
links.add(absolute)
45+
46+
return links
47+
48+
49+
def get_all_docs(folder: Path):
50+
"""Collect all .md files in the given folder, skipping exempt folders."""
51+
exempt_resolved = [ex.resolve() for ex in EXEMPT_DIRS]
52+
53+
docs = set()
54+
for p in folder.rglob("*.md"):
55+
# Skip exempt dirs
56+
if any(ex in p.resolve().parents for ex in exempt_resolved):
57+
continue
58+
docs.add(p.resolve())
59+
60+
return docs
61+
62+
63+
def main() -> int:
64+
summary_links = get_summary_links()
65+
missing_any = False
66+
67+
for docs_dir in DOCS_DIRS:
68+
docs = get_all_docs(docs_dir)
69+
print(f"found {len(docs)} .md files in {docs_dir}")
70+
missing = [d for d in docs if d not in summary_links]
71+
72+
if missing:
73+
print(f"❌ The following docs in {docs_dir} are not linked in SUMMARY.md:")
74+
for m in missing:
75+
print(" -", m.relative_to(Path.cwd()))
76+
missing_any = True
77+
else:
78+
print(f"✅ All docs in {docs_dir} are linked in SUMMARY.md")
79+
80+
return 1 if missing_any else 0
81+
82+
exit(main())

0 commit comments

Comments
 (0)