Skip to content

Commit bbee9ee

Browse files
authored
set up Python runner in CI workflow and add chapter reference checker script to validate chapter references
Cherry picked out of the profiles PR.
1 parent a85550b commit bbee9ee

File tree

2 files changed

+344
-0
lines changed

2 files changed

+344
-0
lines changed
Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
#!/usr/bin/env python3
2+
# Copyright 2025 Holochip, Inc.
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
"""
6+
Script to ensure that all chapter files are properly referenced in the Guide
7+
This script scans the chapters directory to identify all chapter files and checks if they are properly
8+
referenced in the three files. If a chapter is missing from any of the files, the script can add it
9+
to the appropriate section.
10+
"""
11+
12+
import os
13+
import re
14+
import sys
15+
from pathlib import Path
16+
17+
# Paths to the files that need to be checked
18+
README_PATH = "README.adoc"
19+
GUIDE_PATH = "guide.adoc"
20+
NAV_PATH = "antora/modules/ROOT/nav.adoc"
21+
22+
# Directories to scan for chapter files
23+
CHAPTERS_DIR = "chapters"
24+
EXTENSIONS_DIR = os.path.join(CHAPTERS_DIR, "extensions")
25+
26+
def get_all_chapter_files():
27+
"""
28+
Scan the chapters directory and its subdirectories to find all .adoc files.
29+
Returns a dictionary with relative paths as keys and file info as values.
30+
"""
31+
chapter_files = {}
32+
33+
# Scan main chapters directory
34+
for file in os.listdir(CHAPTERS_DIR):
35+
if file.endswith(".adoc") and os.path.isfile(os.path.join(CHAPTERS_DIR, file)):
36+
rel_path = os.path.join(CHAPTERS_DIR, file)
37+
chapter_files[rel_path] = {
38+
"name": file,
39+
"path": rel_path,
40+
"is_extension": False
41+
}
42+
43+
# Scan extensions directory
44+
if os.path.exists(EXTENSIONS_DIR):
45+
for file in os.listdir(EXTENSIONS_DIR):
46+
if file.endswith(".adoc") and os.path.isfile(os.path.join(EXTENSIONS_DIR, file)):
47+
rel_path = os.path.join(EXTENSIONS_DIR, file)
48+
chapter_files[rel_path] = {
49+
"name": file,
50+
"path": rel_path,
51+
"is_extension": True
52+
}
53+
54+
return chapter_files
55+
56+
def extract_title_from_chapter(file_path):
57+
"""
58+
Extract the title from a chapter file.
59+
Returns the title or the filename if no title is found.
60+
"""
61+
try:
62+
with open(file_path, 'r', encoding='utf-8') as f:
63+
content = f.read()
64+
65+
# Look for a title pattern like "= Title" or "== Title"
66+
title_match = re.search(r'^=+\s+(.+?)$', content, re.MULTILINE)
67+
if title_match:
68+
return title_match.group(1).strip()
69+
70+
# If no title found, use the filename without extension
71+
return os.path.splitext(os.path.basename(file_path))[0]
72+
except Exception as e:
73+
print(f"Error reading {file_path}: {e}")
74+
return os.path.splitext(os.path.basename(file_path))[0]
75+
76+
def check_readme_references(chapter_files):
77+
"""
78+
Check if all chapter files are referenced in README.adoc.
79+
Returns a list of files that are not referenced.
80+
"""
81+
try:
82+
with open(README_PATH, 'r', encoding='utf-8') as f:
83+
readme_content = f.read()
84+
85+
missing_files = []
86+
for file_path, info in chapter_files.items():
87+
# Convert path to the format used in README.adoc
88+
rel_path = file_path.replace(CHAPTERS_DIR + "/", "")
89+
90+
# Check if the file is referenced in README.adoc
91+
if not re.search(rf'xref:\{{chapters\}}{re.escape(rel_path)}', readme_content):
92+
missing_files.append(info)
93+
94+
return missing_files
95+
except Exception as e:
96+
print(f"Error checking README.adoc: {e}")
97+
return list(chapter_files.values())
98+
99+
def check_guide_references(chapter_files):
100+
"""
101+
Check if all chapter files are referenced in guide.adoc.
102+
Returns a list of files that are not referenced.
103+
"""
104+
try:
105+
with open(GUIDE_PATH, 'r', encoding='utf-8') as f:
106+
guide_content = f.read()
107+
108+
missing_files = []
109+
for file_path, info in chapter_files.items():
110+
# Convert path to the format used in guide.adoc
111+
rel_path = file_path.replace(CHAPTERS_DIR + "/", "")
112+
113+
# Check if the file is referenced in guide.adoc
114+
# The pattern needs to match both include:{chapters}file.adoc[] and include::{chapters}file.adoc[]
115+
if not (re.search(rf'include:\{{chapters\}}{re.escape(rel_path)}', guide_content) or
116+
re.search(rf'include::\{{chapters\}}{re.escape(rel_path)}', guide_content)):
117+
missing_files.append(info)
118+
119+
return missing_files
120+
except Exception as e:
121+
print(f"Error checking guide.adoc: {e}")
122+
return list(chapter_files.values())
123+
124+
def check_nav_references(chapter_files):
125+
"""
126+
Check if all chapter files are referenced in nav.adoc.
127+
Returns a list of files that are not referenced.
128+
"""
129+
try:
130+
with open(NAV_PATH, 'r', encoding='utf-8') as f:
131+
nav_content = f.read()
132+
133+
missing_files = []
134+
for file_path, info in chapter_files.items():
135+
# Convert path to the format used in nav.adoc
136+
rel_path = file_path.replace(CHAPTERS_DIR + "/", "")
137+
138+
# Check if the file is referenced in nav.adoc
139+
if not re.search(rf'xref:\{{chapters\}}{re.escape(rel_path)}', nav_content):
140+
missing_files.append(info)
141+
142+
return missing_files
143+
except Exception as e:
144+
print(f"Error checking nav.adoc: {e}")
145+
return list(chapter_files.values())
146+
147+
def update_readme(missing_files):
148+
"""
149+
Update README.adoc to include missing chapter references.
150+
Returns True if the file was updated, False otherwise.
151+
"""
152+
if not missing_files:
153+
return False
154+
155+
try:
156+
with open(README_PATH, 'r', encoding='utf-8') as f:
157+
content = f.readlines()
158+
159+
# Find appropriate sections to add the missing files
160+
extensions_section_idx = None
161+
main_section_idx = None
162+
163+
for i, line in enumerate(content):
164+
if "= When and Why to use Extensions" in line:
165+
extensions_section_idx = i
166+
elif "= Using Vulkan" in line:
167+
main_section_idx = i
168+
169+
if extensions_section_idx is None or main_section_idx is None:
170+
print("Could not find appropriate sections in README.adoc")
171+
return False
172+
173+
# Add missing files to appropriate sections
174+
for file_info in missing_files:
175+
title = extract_title_from_chapter(file_info["path"])
176+
rel_path = file_info["path"].replace(CHAPTERS_DIR + "/", "")
177+
178+
if file_info["is_extension"]:
179+
# Add to extensions section
180+
content.insert(extensions_section_idx + 2, f"== xref:{{chapters}}{rel_path}[{title}]\n\n")
181+
extensions_section_idx += 2 # Adjust index for next insertion
182+
else:
183+
# Add to main section
184+
content.insert(main_section_idx + 2, f"== xref:{{chapters}}{rel_path}[{title}]\n\n")
185+
main_section_idx += 2 # Adjust index for next insertion
186+
187+
# Write updated content back to file
188+
with open(README_PATH, 'w', encoding='utf-8') as f:
189+
f.writelines(content)
190+
191+
return True
192+
except Exception as e:
193+
print(f"Error updating README.adoc: {e}")
194+
return False
195+
196+
def update_guide(missing_files):
197+
"""
198+
Update guide.adoc to include missing chapter references.
199+
Returns True if the file was updated, False otherwise.
200+
"""
201+
if not missing_files:
202+
return False
203+
204+
try:
205+
with open(GUIDE_PATH, 'r', encoding='utf-8') as f:
206+
content = f.readlines()
207+
208+
# Find appropriate sections to add the missing files
209+
extensions_section_idx = None
210+
main_section_idx = None
211+
212+
for i, line in enumerate(content):
213+
if "= When and Why to use Extensions" in line:
214+
extensions_section_idx = i
215+
elif "= Using Vulkan" in line:
216+
main_section_idx = i
217+
218+
if extensions_section_idx is None or main_section_idx is None:
219+
print("Could not find appropriate sections in guide.adoc")
220+
return False
221+
222+
# Add missing files to appropriate sections
223+
for file_info in missing_files:
224+
rel_path = file_info["path"].replace(CHAPTERS_DIR + "/", "")
225+
226+
if file_info["is_extension"]:
227+
# Add to extensions section
228+
content.insert(extensions_section_idx + 2, f"include::{{chapters}}{rel_path}[]\n\n")
229+
extensions_section_idx += 2 # Adjust index for next insertion
230+
else:
231+
# Add to main section
232+
content.insert(main_section_idx + 2, f"include::{{chapters}}{rel_path}[]\n\n")
233+
main_section_idx += 2 # Adjust index for next insertion
234+
235+
# Write updated content back to file
236+
with open(GUIDE_PATH, 'w', encoding='utf-8') as f:
237+
f.writelines(content)
238+
239+
return True
240+
except Exception as e:
241+
print(f"Error updating guide.adoc: {e}")
242+
return False
243+
244+
def update_nav(missing_files):
245+
"""
246+
Update nav.adoc to include missing chapter references.
247+
Returns True if the file was updated, False otherwise.
248+
"""
249+
if not missing_files:
250+
return False
251+
252+
try:
253+
with open(NAV_PATH, 'r', encoding='utf-8') as f:
254+
content = f.readlines()
255+
256+
# Find appropriate sections to add the missing files
257+
extensions_section_idx = None
258+
main_section_idx = None
259+
260+
for i, line in enumerate(content):
261+
if "* When and Why to use Extensions" in line:
262+
extensions_section_idx = i
263+
elif "* Using Vulkan" in line:
264+
main_section_idx = i
265+
266+
if extensions_section_idx is None or main_section_idx is None:
267+
print("Could not find appropriate sections in nav.adoc")
268+
return False
269+
270+
# Add missing files to appropriate sections
271+
for file_info in missing_files:
272+
rel_path = file_info["path"].replace(CHAPTERS_DIR + "/", "")
273+
274+
if file_info["is_extension"]:
275+
# Add to extensions section
276+
content.insert(extensions_section_idx + 1, f"** xref:{{chapters}}{rel_path}[]\n")
277+
extensions_section_idx += 1 # Adjust index for next insertion
278+
else:
279+
# Add to main section
280+
content.insert(main_section_idx + 1, f"** xref:{{chapters}}{rel_path}[]\n")
281+
main_section_idx += 1 # Adjust index for next insertion
282+
283+
# Write updated content back to file
284+
with open(NAV_PATH, 'w', encoding='utf-8') as f:
285+
f.writelines(content)
286+
287+
return True
288+
except Exception as e:
289+
print(f"Error updating nav.adoc: {e}")
290+
return False
291+
292+
def main():
293+
"""
294+
Main function to check and update chapter references.
295+
"""
296+
print("Checking chapter references...")
297+
298+
# Get all chapter files
299+
chapter_files = get_all_chapter_files()
300+
print(f"Found {len(chapter_files)} chapter files")
301+
302+
# Check if all chapter files are referenced in the three files
303+
readme_missing = check_readme_references(chapter_files)
304+
guide_missing = check_guide_references(chapter_files)
305+
nav_missing = check_nav_references(chapter_files)
306+
307+
print(f"Missing from README.adoc: {len(readme_missing)}")
308+
print(f"Missing from guide.adoc: {len(guide_missing)}")
309+
print(f"Missing from nav.adoc: {len(nav_missing)}")
310+
311+
# Update files if needed
312+
readme_updated = update_readme(readme_missing)
313+
guide_updated = update_guide(guide_missing)
314+
nav_updated = update_nav(nav_missing)
315+
316+
if readme_updated:
317+
print("Updated README.adoc")
318+
if guide_updated:
319+
print("Updated guide.adoc")
320+
if nav_updated:
321+
print("Updated nav.adoc")
322+
323+
# Return non-zero exit code if any files were missing references
324+
if readme_missing or guide_missing or nav_missing:
325+
print("Some chapter files were missing references and have been added.")
326+
return 1
327+
else:
328+
print("All chapter files are properly referenced.")
329+
return 0
330+
331+
if __name__ == "__main__":
332+
sys.exit(main())

.github/workflows/CI.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,18 @@ jobs:
2424
with:
2525
ruby-version: '3.0'
2626

27+
# Set up Python for chapter reference checker
28+
- name: Set up Python
29+
uses: actions/setup-python@v4
30+
with:
31+
python-version: '3.10'
32+
33+
# Run chapter reference checker
34+
- name: Check chapter references
35+
run: |
36+
chmod +x .github/scripts/check_chapter_references.py
37+
python .github/scripts/check_chapter_references.py
38+
2739
# Run awesome_bot checker
2840
# Apparently this has trouble parsing one wiki link with () in it, so added to whitelist
2941
# need request-delay or will get 429 errors for sites such as GitHub

0 commit comments

Comments
 (0)