Skip to content
This repository was archived by the owner on Mar 11, 2026. It is now read-only.

Commit ee345f1

Browse files
committed
refactor: flatten wiki sync link resolution
1 parent 4a37ccf commit ee345f1

2 files changed

Lines changed: 122 additions & 81 deletions

File tree

scripts/sync_docs_to_wiki.py

Lines changed: 84 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,44 @@
1313
"zh/config/providers/start.md": "zh/providers/start.md",
1414
"en/config/providers/start.md": "en/providers/start.md",
1515
}
16+
LANG_CONFIG = {
17+
"zh": {
18+
"index_title": "# AstrBot 中文文档",
19+
"index_intro": "该页面由 `AstrBot-docs` 自动同步到 GitHub Wiki。",
20+
"index_links": [
21+
("关于 AstrBot", "zh-what-is-astrbot"),
22+
("社区", "zh-community"),
23+
("常见问题", "zh-faq"),
24+
],
25+
"home_intro": "该 Wiki 由 `AstrBot-docs` 自动同步生成。",
26+
"home_links": [
27+
("中文文档入口", "zh-index"),
28+
("English Docs", "Home-en"),
29+
],
30+
"sidebar_language_label": "Chinese",
31+
"sidebar_home_label": "首页",
32+
"sidebar_home_target": "Home",
33+
"sidebar_docs_entry_label": "文档入口",
34+
},
35+
"en": {
36+
"index_title": "# AstrBot English Documentation",
37+
"index_intro": "This page is synchronized automatically from `AstrBot-docs` to the GitHub wiki.",
38+
"index_links": [
39+
("What is AstrBot", "en-what-is-astrbot"),
40+
("Community", "en-community"),
41+
("FAQ", "en-faq"),
42+
],
43+
"home_intro": "This wiki is synchronized automatically from `AstrBot-docs`.",
44+
"home_links": [
45+
("English docs entry", "en-index"),
46+
("中文文档入口", "Home"),
47+
],
48+
"sidebar_language_label": "English",
49+
"sidebar_home_label": "Home",
50+
"sidebar_home_target": "Home-en",
51+
"sidebar_docs_entry_label": "Docs Entry",
52+
},
53+
}
1654

1755

1856
@dataclass
@@ -174,26 +212,6 @@ def parse_doc_target(target: str) -> tuple[str, str] | None:
174212
return base_target, anchor
175213

176214

177-
def resolve_absolute_target(base_target: str, source_path: str) -> PurePosixPath:
178-
source_language = language_for_source(source_path)
179-
target = base_target.lstrip("/")
180-
if not target:
181-
return PurePosixPath(source_language) / "index.md"
182-
if target in {"en", "en/"}:
183-
return PurePosixPath("en") / "index.md"
184-
if target in {"zh", "zh/"}:
185-
return PurePosixPath("zh") / "index.md"
186-
if target.startswith(("en/", "zh/")):
187-
return prepare_candidate_path(PurePosixPath(target))
188-
language_root = source_language if source_language == "en" else "zh"
189-
return prepare_candidate_path(PurePosixPath(language_root) / target)
190-
191-
192-
def resolve_relative_target(base_target: str, source_path: str) -> PurePosixPath:
193-
source = PurePosixPath(source_path)
194-
return prepare_candidate_path(source.parent / base_target)
195-
196-
197215
def find_candidates_by_suffix(
198216
language: str, suffix: str, source_pages: tuple[str, ...]
199217
) -> list[str]:
@@ -236,6 +254,34 @@ def find_existing_source_path(
236254
return ResolutionResult(resolved_path=None)
237255

238256

257+
def resolve_link_path(
258+
base_target: str,
259+
source_path: str,
260+
source_root: Path,
261+
source_pages: tuple[str, ...],
262+
) -> ResolutionResult:
263+
source_language = language_for_source(source_path)
264+
265+
if base_target.startswith("/"):
266+
target = base_target.lstrip("/")
267+
if not target:
268+
candidate = PurePosixPath(source_language) / "index.md"
269+
elif target in {"en", "en/"}:
270+
candidate = PurePosixPath("en") / "index.md"
271+
elif target in {"zh", "zh/"}:
272+
candidate = PurePosixPath("zh") / "index.md"
273+
elif target.startswith(("en/", "zh/")):
274+
candidate = PurePosixPath(target)
275+
else:
276+
language_root = source_language if source_language == "en" else "zh"
277+
candidate = PurePosixPath(language_root) / target
278+
else:
279+
candidate = PurePosixPath(source_path).parent / base_target
280+
281+
candidate = prepare_candidate_path(candidate)
282+
return find_existing_source_path(candidate, source_root, source_pages)
283+
284+
239285
class LinkResolver:
240286
def __init__(self, source_root: Path):
241287
self.source_root = Path(source_root)
@@ -247,12 +293,12 @@ def resolve(self, target: str, source_path: str) -> ResolutionResult:
247293
return ResolutionResult(resolved_path=None)
248294

249295
base_target, _ = parsed_target
250-
if base_target.startswith("/"):
251-
candidate = resolve_absolute_target(base_target, source_path)
252-
else:
253-
candidate = resolve_relative_target(base_target, source_path)
254-
255-
return find_existing_source_path(candidate, self.source_root, self.source_pages)
296+
return resolve_link_path(
297+
base_target=base_target,
298+
source_path=source_path,
299+
source_root=self.source_root,
300+
source_pages=self.source_pages,
301+
)
256302

257303
def resolve_path(self, target: str, source_path: str) -> str | None:
258304
return self.resolve(target, source_path).resolved_path
@@ -353,65 +399,22 @@ def extract_title(content: str, source_path: str) -> str:
353399

354400

355401
def build_language_index(language: str, page_names: set[str]) -> str:
356-
if language == "zh":
357-
lines = [
358-
"# AstrBot 中文文档",
359-
"",
360-
"该页面由 `AstrBot-docs` 自动同步到 GitHub Wiki。",
361-
"",
362-
]
363-
links = [
364-
("关于 AstrBot", "zh-what-is-astrbot"),
365-
("社区", "zh-community"),
366-
("常见问题", "zh-faq"),
367-
]
368-
else:
369-
lines = [
370-
"# AstrBot English Documentation",
371-
"",
372-
"This page is synchronized automatically from `AstrBot-docs` to the GitHub wiki.",
373-
"",
374-
]
375-
links = [
376-
("What is AstrBot", "en-what-is-astrbot"),
377-
("Community", "en-community"),
378-
("FAQ", "en-faq"),
379-
]
402+
config = LANG_CONFIG[language]
403+
lines = [config["index_title"], "", config["index_intro"], ""]
380404

381-
for label, page_name in links:
405+
for label, page_name in config["index_links"]:
382406
if page_name in page_names:
383407
lines.append(f"- [{label}]({page_name})")
384408

385409
return normalize_content("\n".join(lines))
386410

387411

388412
def build_home_page(language: str) -> str:
389-
if language == "zh":
390-
return normalize_content(
391-
"\n".join(
392-
[
393-
"# AstrBot Wiki",
394-
"",
395-
"该 Wiki 由 `AstrBot-docs` 自动同步生成。",
396-
"",
397-
"- [中文文档入口](zh-index)",
398-
"- [English Docs](Home-en)",
399-
],
400-
),
401-
)
402-
403-
return normalize_content(
404-
"\n".join(
405-
[
406-
"# AstrBot Wiki",
407-
"",
408-
"This wiki is synchronized automatically from `AstrBot-docs`.",
409-
"",
410-
"- [English docs entry](en-index)",
411-
"- [中文文档入口](Home)",
412-
],
413-
),
414-
)
413+
config = LANG_CONFIG[language]
414+
lines = ["# AstrBot Wiki", "", config["home_intro"], ""]
415+
for label, target in config["home_links"]:
416+
lines.append(f"- [{label}]({target})")
417+
return normalize_content("\n".join(lines))
415418

416419

417420
def sidebar_group_name(group: str) -> str:
@@ -422,23 +425,23 @@ def sidebar_group_name(group: str) -> str:
422425

423426
def build_sidebar(page_infos: list[PageInfo]) -> str:
424427
lines: list[str] = []
425-
language_labels = {"zh": "Chinese", "en": "English"}
426428

427429
for language in ("zh", "en"):
430+
config = LANG_CONFIG[language]
428431
infos = [
429432
info
430433
for info in page_infos
431434
if info.language == language and not info.is_index
432435
]
433436
infos.sort(key=lambda info: info.source_path)
434437

435-
lines.append(f"### {language_labels[language]}")
438+
lines.append(f"### {config['sidebar_language_label']}")
436439
lines.append("")
437440
lines.append(
438-
f"- [{'Home' if language == 'en' else '首页'}]({'Home-en' if language == 'en' else 'Home'})",
441+
f"- [{config['sidebar_home_label']}]({config['sidebar_home_target']})",
439442
)
440443
lines.append(
441-
f"- [{'Docs Entry' if language == 'en' else '文档入口'}]({language}-index)",
444+
f"- [{config['sidebar_docs_entry_label']}]({language}-index)",
442445
)
443446

444447
grouped: dict[str, list[PageInfo]] = {}

tests/test_sync_docs_to_wiki.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ def test_module_exposes_consolidated_helper_names(self):
4747

4848
self.assertTrue(hasattr(module, "prepare_candidate_path"))
4949
self.assertTrue(hasattr(module, "find_candidates_by_suffix"))
50+
self.assertTrue(hasattr(module, "resolve_link_path"))
51+
self.assertTrue(hasattr(module, "LANG_CONFIG"))
5052

5153
def test_parse_doc_target_returns_base_and_anchor(self):
5254
module = load_sync_module()
@@ -181,6 +183,42 @@ def test_link_resolver_resolves_source_paths(self):
181183
"zh/deploy/guide.md",
182184
)
183185

186+
def test_resolve_link_path_resolves_relative_target(self):
187+
module = load_sync_module()
188+
189+
with TemporaryDirectory() as temp_dir:
190+
source_root = Path(temp_dir) / "docs"
191+
(source_root / "zh" / "providers").mkdir(parents=True)
192+
(source_root / "zh" / "agent-runners").mkdir(parents=True)
193+
(source_root / "zh" / "providers" / "dify.md").write_text(
194+
"# Dify\n",
195+
encoding="utf-8",
196+
)
197+
(source_root / "zh" / "agent-runners" / "dify.md").write_text(
198+
"# Agent Runner\n",
199+
encoding="utf-8",
200+
)
201+
202+
self.assertEqual(
203+
module.resolve_link_path(
204+
base_target="../agent-runners/dify.md",
205+
source_path="zh/providers/dify.md",
206+
source_root=source_root,
207+
source_pages=module.discover_source_pages(str(source_root)),
208+
).resolved_path,
209+
"zh/agent-runners/dify.md",
210+
)
211+
212+
def test_build_home_page_uses_language_config(self):
213+
module = load_sync_module()
214+
215+
self.assertIn(
216+
module.LANG_CONFIG["zh"]["home_intro"], module.build_home_page("zh")
217+
)
218+
self.assertIn(
219+
module.LANG_CONFIG["en"]["home_intro"], module.build_home_page("en")
220+
)
221+
184222
def test_prepare_candidate_path_normalizes_suffix_and_alias(self):
185223
module = load_sync_module()
186224

0 commit comments

Comments
 (0)