()
| 59 | |
| 60 | |
| 61 | def main(): |
| 62 | parser = argparse.ArgumentParser(description="Validate fulltext-title consistency") |
| 63 | parser.add_argument("--changed-only", action="store_true", |
| 64 | help="Only check files changed in this PR") |
| 65 | args = parser.parse_args() |
| 66 | |
| 67 | if not INDEX_PATH.exists(): |
| 68 | print("Index not found, skipping validation") |
| 69 | return |
| 70 | |
| 71 | with open(INDEX_PATH, "r", encoding="utf-8") as f: |
| 72 | data = json.load(f) |
| 73 | |
| 74 | entries = data.get("entries", []) |
| 75 | slug_to_entry = {} |
| 76 | for e in entries: |
| 77 | s = e.get("slug", "") |
| 78 | if s: |
| 79 | slug_to_entry[s] = e |
| 80 | |
| 81 | changed_slugs = None |
| 82 | if args.changed_only: |
| 83 | changed_slugs = _get_changed_slugs() |
| 84 | if changed_slugs is not None: |
| 85 | print(f"Checking {len(changed_slugs)} changed fulltext files") |
| 86 | else: |
| 87 | print("Could not determine changed files, checking all") |
| 88 | |
| 89 | mismatches = [] |
| 90 | checked = 0 |
| 91 | |
| 92 | for slug, entry in slug_to_entry.items(): |
| 93 | if changed_slugs is not None and slug not in changed_slugs: |
| 94 | continue |
| 95 | |
| 96 | fp = FULLTEXT_DIR / f"{slug}.md" |
| 97 | if not fp.exists(): |
| 98 | continue |
| 99 | |
| 100 | content = fp.read_text(encoding="utf-8", errors="replace") |
| 101 | body = content |
| 102 | sep = content.find("\n---\n") |
| 103 | if sep >= 0: |
| 104 | body = content[sep + 5:] |
| 105 | |
| 106 | if not body or len(body) < 200: |
| 107 | continue |
| 108 | |
| 109 | checked += 1 |
| 110 | title_zh = entry.get("title", {}).get("zh", "") |
| 111 | |
| 112 | if is_catalog_content(body): |
| 113 | mismatches.append((slug, title_zh, "catalog")) |
| 114 | continue |
| 115 | |
| 116 | if is_draft_content(body): |
| 117 | mismatches.append((slug, title_zh, "draft")) |
| 118 | continue |
no test coverage detected