mirror of
https://github.com/eunomia-bpf/bpf-developer-tutorial.git
synced 2026-11-01 13:15:01 +08:00
Refactor source block validation
This commit is contained in:
@@ -117,7 +117,7 @@ def atomic_write(path: pathlib.Path, content: str) -> None:
|
|||||||
pathlib.Path(temporary_name).unlink(missing_ok=True)
|
pathlib.Path(temporary_name).unlink(missing_ok=True)
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def parse_arguments() -> argparse.Namespace:
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("--repo", required=True, help="absolute repository root")
|
parser.add_argument("--repo", required=True, help="absolute repository root")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@@ -132,77 +132,88 @@ def main() -> int:
|
|||||||
mode = parser.add_mutually_exclusive_group(required=True)
|
mode = parser.add_mutually_exclusive_group(required=True)
|
||||||
mode.add_argument("--write", action="store_true", help="replace marked blocks")
|
mode.add_argument("--write", action="store_true", help="replace marked blocks")
|
||||||
mode.add_argument("--check", action="store_true", help="fail when blocks differ")
|
mode.add_argument("--check", action="store_true", help="fail when blocks differ")
|
||||||
args = parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
root = pathlib.Path(args.repo).resolve()
|
|
||||||
if not (root / ".git").exists() and not (root / ".git").is_file():
|
|
||||||
print(f"not a Git worktree: {root}", file=sys.stderr)
|
|
||||||
return 2
|
|
||||||
|
|
||||||
failed = False
|
def resolve_readmes(
|
||||||
|
root: pathlib.Path, values: list[str]
|
||||||
|
) -> list[tuple[str, pathlib.Path]]:
|
||||||
readmes: list[tuple[str, pathlib.Path]] = []
|
readmes: list[tuple[str, pathlib.Path]] = []
|
||||||
for value in args.readme:
|
seen: set[pathlib.Path] = set()
|
||||||
try:
|
for value in values:
|
||||||
readme = contained_path(root, value, "README path")
|
readme = contained_path(root, value, "README path")
|
||||||
if not readme.is_file():
|
if not readme.is_file():
|
||||||
raise ValueError(f"README does not exist: {value}")
|
raise ValueError(f"README does not exist: {value}")
|
||||||
if readme in {path for _, path in readmes}:
|
if readme in seen:
|
||||||
raise ValueError(f"duplicate README path: {value}")
|
raise ValueError(f"duplicate README path: {value}")
|
||||||
readmes.append((value, readme))
|
readmes.append((value, readme))
|
||||||
except (OSError, ValueError) as error:
|
seen.add(readme)
|
||||||
print(error, file=sys.stderr)
|
return readmes
|
||||||
failed = True
|
|
||||||
|
|
||||||
if failed:
|
|
||||||
return 1
|
|
||||||
|
|
||||||
readme_paths = {path for _, path in readmes}
|
def resolve_expected_sources(
|
||||||
|
root: pathlib.Path,
|
||||||
|
values: list[str],
|
||||||
|
readme_paths: set[pathlib.Path],
|
||||||
|
) -> set[pathlib.Path]:
|
||||||
expected_sources: set[pathlib.Path] = set()
|
expected_sources: set[pathlib.Path] = set()
|
||||||
for value in args.expected_source:
|
for value in values:
|
||||||
try:
|
source = contained_path(root, value, "expected source path")
|
||||||
source = contained_path(root, value, "expected source path")
|
if not source.is_file():
|
||||||
if not source.is_file():
|
raise ValueError(f"expected source file does not exist: {value}")
|
||||||
raise ValueError(f"expected source file does not exist: {value}")
|
if source in readme_paths:
|
||||||
if source in readme_paths:
|
raise ValueError(f"expected source cannot be a tutorial README: {value}")
|
||||||
raise ValueError(f"expected source cannot be a tutorial README: {value}")
|
if source.suffix not in LANGUAGES:
|
||||||
if source.suffix not in LANGUAGES:
|
raise ValueError(f"unsupported expected-source file type: {value}")
|
||||||
raise ValueError(f"unsupported expected-source file type: {value}")
|
if source in expected_sources:
|
||||||
if source in expected_sources:
|
raise ValueError(f"duplicate expected source: {value}")
|
||||||
raise ValueError(f"duplicate expected source: {value}")
|
expected_sources.add(source)
|
||||||
expected_sources.add(source)
|
return expected_sources
|
||||||
except (OSError, ValueError) as error:
|
|
||||||
print(error, file=sys.stderr)
|
|
||||||
failed = True
|
|
||||||
|
|
||||||
if failed:
|
|
||||||
return 1
|
|
||||||
|
|
||||||
|
def inventory_mismatch(
|
||||||
|
value: str,
|
||||||
|
root: pathlib.Path,
|
||||||
|
expected_sources: set[pathlib.Path],
|
||||||
|
sources: set[pathlib.Path],
|
||||||
|
) -> ValueError | None:
|
||||||
|
missing = expected_sources - sources
|
||||||
|
extra = sources - expected_sources
|
||||||
|
if not missing and not extra:
|
||||||
|
return None
|
||||||
|
details = []
|
||||||
|
if missing:
|
||||||
|
details.append(f"missing: {display_paths(missing, root)}")
|
||||||
|
if extra:
|
||||||
|
details.append(f"unexpected: {display_paths(extra, root)}")
|
||||||
|
return ValueError(
|
||||||
|
f"complete-source inventory mismatch in {value} ({'; '.join(details)})"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def plan_updates(
|
||||||
|
root: pathlib.Path,
|
||||||
|
readmes: list[tuple[str, pathlib.Path]],
|
||||||
|
expected_sources: set[pathlib.Path],
|
||||||
|
) -> list[tuple[str, pathlib.Path, str, str, int]]:
|
||||||
|
readme_paths = {path for _, path in readmes}
|
||||||
planned: list[tuple[str, pathlib.Path, str, str, int]] = []
|
planned: list[tuple[str, pathlib.Path, str, str, int]] = []
|
||||||
for value, readme in readmes:
|
for value, readme in readmes:
|
||||||
try:
|
before = readme.read_text(encoding="utf-8")
|
||||||
before = readme.read_text(encoding="utf-8")
|
after, sources = render(readme, root, readme_paths)
|
||||||
after, sources = render(readme, root, readme_paths)
|
mismatch = inventory_mismatch(value, root, expected_sources, sources)
|
||||||
missing = expected_sources - sources
|
if mismatch:
|
||||||
extra = sources - expected_sources
|
raise mismatch
|
||||||
if missing or extra:
|
planned.append((value, readme, before, after, len(sources)))
|
||||||
details = []
|
return planned
|
||||||
if missing:
|
|
||||||
details.append(f"missing: {display_paths(missing, root)}")
|
|
||||||
if extra:
|
|
||||||
details.append(f"unexpected: {display_paths(extra, root)}")
|
|
||||||
raise ValueError(
|
|
||||||
f"complete-source inventory mismatch in {value} ({'; '.join(details)})"
|
|
||||||
)
|
|
||||||
planned.append((value, readme, before, after, len(sources)))
|
|
||||||
except (OSError, ValueError) as error:
|
|
||||||
print(error, file=sys.stderr)
|
|
||||||
failed = True
|
|
||||||
|
|
||||||
if failed:
|
|
||||||
return 1
|
|
||||||
|
|
||||||
|
def execute_plan(
|
||||||
|
planned: list[tuple[str, pathlib.Path, str, str, int]], write: bool
|
||||||
|
) -> int:
|
||||||
|
failed = False
|
||||||
for value, readme, before, after, blocks in planned:
|
for value, readme, before, after, blocks in planned:
|
||||||
if args.write:
|
if write:
|
||||||
if before != after:
|
if before != after:
|
||||||
atomic_write(readme, after)
|
atomic_write(readme, after)
|
||||||
print(f"synced {blocks} source blocks in {value}")
|
print(f"synced {blocks} source blocks in {value}")
|
||||||
@@ -215,5 +226,25 @@ def main() -> int:
|
|||||||
return 1 if failed else 0
|
return 1 if failed else 0
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
args = parse_arguments()
|
||||||
|
root = pathlib.Path(args.repo).resolve()
|
||||||
|
if not (root / ".git").exists():
|
||||||
|
print(f"not a Git worktree: {root}", file=sys.stderr)
|
||||||
|
return 2
|
||||||
|
|
||||||
|
try:
|
||||||
|
readmes = resolve_readmes(root, args.readme)
|
||||||
|
readme_paths = {path for _, path in readmes}
|
||||||
|
expected_sources = resolve_expected_sources(
|
||||||
|
root, args.expected_source, readme_paths
|
||||||
|
)
|
||||||
|
planned = plan_updates(root, readmes, expected_sources)
|
||||||
|
except (OSError, ValueError) as error:
|
||||||
|
print(error, file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
return execute_plan(planned, args.write)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
raise SystemExit(main())
|
raise SystemExit(main())
|
||||||
|
|||||||
Reference in New Issue
Block a user