132 lines
4.7 KiB
Python
132 lines
4.7 KiB
Python
"""把 Gitea Wiki 任务归档人工按需导出到 docs/task。"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import re
|
||
from pathlib import Path
|
||
from typing import Any
|
||
|
||
from new_task_archive import safe_title
|
||
from wiki_docs import (
|
||
DEFAULT_CONFIG,
|
||
ROOT,
|
||
WikiClient,
|
||
WikiDocsError,
|
||
dirty_paths,
|
||
load_config,
|
||
parse_mirror,
|
||
write_mirror,
|
||
)
|
||
|
||
|
||
TASK_PAGE_PATTERN = re.compile(r"^Task-(?P<number>\d+)-(?P<title>.+)$")
|
||
|
||
|
||
def task_revision(metadata: dict[str, Any], page_name: str) -> str:
|
||
last_commit = metadata.get("last_commit")
|
||
revision = last_commit.get("sha") if isinstance(last_commit, dict) else None
|
||
if not isinstance(revision, str) or not revision:
|
||
raise WikiDocsError(f"Wiki 页面缺少 revision:{page_name}")
|
||
return revision
|
||
|
||
|
||
def existing_task_mirrors(root: Path = ROOT) -> dict[str, Path]:
|
||
"""按镜像头匹配已有文件,兼容历史自定义文件名。"""
|
||
|
||
mirrors: dict[str, Path] = {}
|
||
task_dir = root / "docs" / "task"
|
||
if not task_dir.is_dir():
|
||
return mirrors
|
||
for path in task_dir.glob("*.md"):
|
||
try:
|
||
metadata, _ = parse_mirror(path.read_text(encoding="utf-8"))
|
||
except (OSError, UnicodeDecodeError, WikiDocsError) as exc:
|
||
raise WikiDocsError(f"已有任务镜像无效 {path.name}:{exc}") from exc
|
||
page_name = metadata.get("wiki_page", "")
|
||
if not TASK_PAGE_PATTERN.fullmatch(page_name):
|
||
raise WikiDocsError(f"已有任务镜像页面名无效 {path.name}:{page_name}")
|
||
if page_name in mirrors:
|
||
raise WikiDocsError(f"任务页面存在重复本地镜像:{page_name}")
|
||
mirrors[page_name] = path
|
||
return mirrors
|
||
|
||
|
||
def task_target(page_name: str, root: Path = ROOT) -> Path:
|
||
match = TASK_PAGE_PATTERN.fullmatch(page_name)
|
||
if match is None:
|
||
raise WikiDocsError(f"不是任务归档页面:{page_name}")
|
||
title = safe_title(match.group("title"))
|
||
if not title:
|
||
raise WikiDocsError(f"任务归档标题无效:{page_name}")
|
||
return root / "docs" / "task" / f"{match.group('number')}-{title}.md"
|
||
|
||
|
||
def export_task_archives(
|
||
client: WikiClient, *, export_all: bool = False, root: Path = ROOT
|
||
) -> list[str]:
|
||
"""增量或全量读取任务归档;绝不删除本地文件。"""
|
||
|
||
dirty = dirty_paths(["docs/task"], root)
|
||
if dirty:
|
||
raise WikiDocsError(
|
||
"本地任务镜像存在未提交改动,已停止以防覆盖:\n" + "\n".join(dirty)
|
||
)
|
||
|
||
existing = existing_task_mirrors(root)
|
||
pages = []
|
||
for metadata in client.list_pages():
|
||
title = metadata.get("title")
|
||
if isinstance(title, str) and TASK_PAGE_PATTERN.fullmatch(title):
|
||
pages.append((int(title.split("-", 2)[1]), title, metadata))
|
||
pages.sort(key=lambda item: (item[0], item[1]))
|
||
|
||
messages: list[str] = []
|
||
targets: set[Path] = set()
|
||
for _, page_name, metadata in pages:
|
||
target = existing.get(page_name, task_target(page_name, root))
|
||
if target in targets:
|
||
raise WikiDocsError(f"多个任务页面映射到同一本地路径:{target.name}")
|
||
targets.add(target)
|
||
revision = task_revision(metadata, page_name)
|
||
if not export_all and target.is_file():
|
||
local_metadata, _ = parse_mirror(target.read_text(encoding="utf-8"))
|
||
if (
|
||
local_metadata.get("wiki_page") == page_name
|
||
and local_metadata.get("wiki_revision") == revision
|
||
):
|
||
messages.append(f"跳过:{target.relative_to(root)} <- {page_name}@{revision[:12]}")
|
||
continue
|
||
page = client.get_page_from_metadata(metadata, page_name)
|
||
changed = write_mirror(target, page)
|
||
action = "已导出" if changed else "无变化"
|
||
messages.append(f"{action}:{target.relative_to(root)} <- {page_name}@{revision[:12]}")
|
||
return messages
|
||
|
||
|
||
def main() -> int:
|
||
parser = argparse.ArgumentParser(description="人工按需导出 Gitea Wiki 任务归档")
|
||
parser.add_argument(
|
||
"--all", action="store_true", help="全量读取全部线上任务归档;默认按 revision 增量"
|
||
)
|
||
parser.add_argument(
|
||
"--config", default=str(DEFAULT_CONFIG), help="核心 Wiki 映射配置"
|
||
)
|
||
args = parser.parse_args()
|
||
try:
|
||
config = load_config(Path(args.config).resolve())
|
||
messages = export_task_archives(
|
||
WikiClient(config), export_all=args.all
|
||
)
|
||
except WikiDocsError as exc:
|
||
print(f"错误:{exc}")
|
||
return 1
|
||
for message in messages:
|
||
print(message)
|
||
print("任务归档全量导出完成" if args.all else "任务归档增量导出完成")
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(main())
|