feat: add interactive document checker (#164)

* feat: add interactive document checker

This utility can be used to audit the documentation of the projects on the shuttle, marking them to be removed from the datasheet.

Usage:
`python3 tt/configure.py --doc-check`, inspect using `nano` controls, then answer prompt upon exiting `nano`.
This commit is contained in:
Kristaps Jurkans
2026-03-30 15:15:24 +02:00
committed by GitHub
parent 635d1b4285
commit 5779f397bc
2 changed files with 111 additions and 1 deletions
+10 -1
View File
@@ -14,7 +14,7 @@ from typing import Dict, List, TypedDict
import yaml import yaml
from config import Config from config import Config
from documentation import Docs from documentation import Docs, interactive_doc_checker
from logo import LogoGenerator from logo import LogoGenerator
from project import Project from project import Project
from rom import ROMFile from rom import ROMFile
@@ -276,6 +276,12 @@ if __name__ == "__main__":
default="1.0.0", default="1.0.0",
nargs="?", nargs="?",
) )
parser.add_argument(
"--doc-check",
help="interactive project document checker",
action="store_const",
const=True,
)
args = parser.parse_args() args = parser.parse_args()
@@ -335,3 +341,6 @@ if __name__ == "__main__":
if args.build_datasheet: if args.build_datasheet:
shuttle.configure_mux() shuttle.configure_mux()
docs.build_datasheet(args.template_version, args.doc_tapeout_index) docs.build_datasheet(args.template_version, args.doc_tapeout_index)
if args.doc_check:
interactive_doc_checker()
+101
View File
@@ -2,6 +2,8 @@ import json
import logging import logging
import math import math
import os import os
import subprocess
from pathlib import Path
from typing import List from typing import List
import chevron import chevron
@@ -271,3 +273,102 @@ class Docs:
f.writelines(datasheet_manifest) f.writelines(datasheet_manifest)
DocsHelper.compile() DocsHelper.compile()
def interactive_doc_checker():
"""
Launch the interactive document checker.
This will go through every project and display the project's documentation (`info.md`) in `nano`, prompting you to
either leave or remove the project datasheet from the shuttle datasheet after closing the `nano` editor.
There are some commands which you can use:
- "y": remove the project from the datasheet
- "n" (default): leave the project in the datasheet
- "e"/"end": save progress to return to later
The results are saved into a separate file (`doc_check.yaml`) - copy the `datasheet_config.disabled` array over to
`config.yaml`. This file does not need to be commited to the repo and can be deleted after.
If `doc_check.yaml` exists, and a `_skip_to_project` key is present, the function will attempt to skip over all
the projects until it encounters the one specified by `_skip_to_project` - this allows for easy resuming and does
not require you to audit every project immediately.
"""
docs = []
skip_projects = False
skip_to = None
# check if we have saved progress
if Path("doc_check.yaml").is_file():
with open("doc_check.yaml", "r") as f:
tmp = yaml.safe_load(f)
if "_skip_to_project" in tmp:
skip_to = tmp["_skip_to_project"]
skip_projects = True
# get in-progress list of disabled projects
docs += tmp["datasheet_config"]["disabled"]
with open("config.yaml") as f:
shuttle_config = yaml.safe_load(f)
if "datasheet_config" in shuttle_config:
if "disabled" in shuttle_config["datasheet_config"]:
docs += shuttle_config["datasheet_config"]["disabled"]
for project in Path("projects").glob("*/docs/info.md"):
name = project.parts[1]
if name in docs:
logging.info(f"skipping {name} (already disabled)")
continue
if skip_projects:
if name != skip_to:
continue
else:
skip_projects = False
# inspect docs with text editor - configure with $PAGER, defaults to `less`
pager = os.environ.get("PAGER", "less")
subprocess.call(f"{pager} '{project}'", shell=True)
while True:
decision = str(input(f"\nremove {name} (y/N)? ")).lower()
if decision == "y":
logging.info(f"removing {name}")
docs.append(name)
break
elif (decision == "n") or (decision == ""):
logging.info(f"not removing {name}")
break
elif (decision == "e") or (decision == "end"):
break
# break out of both loops and save progress
if decision == "end":
skip_to = name
break
if len(docs) == 0:
logging.info("no projects disabled, nothing written")
return
# mock yaml structure (see comment below re intermediate file)
tmp_yaml: dict[str, dict[str, list] | str] = {
"datasheet_config": {"disabled": docs}
}
# save progress for next time
if decision == "end":
tmp_yaml["_skip_to_project"] = name
# NOTE: have to use an intermediate file since writing back to config.yaml would wreck the existing formatting
# a potential upgrade: https://pypi.org/project/ruamel.yaml/
with open("doc_check.yaml", "w+") as f:
logging.info(
"writing to doc_check.yaml -- please copy list of disabled projects to config.yaml"
)
yaml.safe_dump(tmp_yaml, f, default_flow_style=False)