Files
export_1c_help/export1c_help/wiki.py
T
mihailkudravcev 046a9a51e3 Preserve user wiki pages on help export push.
Only manifest-managed help pages are updated or removed; add --wipe-unmanaged for a full clean.
2026-07-23 16:30:51 +03:00

144 lines
4.0 KiB
Python

"""Push generated wiki tree into a Gitea wiki git repository."""
from __future__ import annotations
import json
import shutil
import subprocess
from pathlib import Path
# Always owned by export_1c_help (not user articles).
_MANAGED_FIXED = frozenset({"Home.md", "History.md", "_Sidebar.md", "manifest.json"})
_IMAGE_SUFFIXES = frozenset({".png", ".jpg", ".jpeg", ".gif", ".bmp", ".webp"})
class WikiPushError(RuntimeError):
pass
def _run(cmd: list[str], *, cwd: Path) -> str:
proc = subprocess.run(
cmd,
cwd=cwd,
capture_output=True,
text=True,
check=False,
)
if proc.returncode != 0:
raise WikiPushError(
f"$ {' '.join(cmd)}\n{proc.stdout}\n{proc.stderr}".strip()
)
return proc.stdout
def prepare_wiki_clone(
wiki_url: str,
work_dir: Path,
*,
branch: str = "main",
) -> Path:
"""Clone or reset wiki working tree."""
work_dir = work_dir.resolve()
if work_dir.exists() and (work_dir / ".git").exists():
_run(["git", "fetch", "origin"], cwd=work_dir)
_run(["git", "checkout", branch], cwd=work_dir)
_run(["git", "reset", "--hard", f"origin/{branch}"], cwd=work_dir)
else:
if work_dir.exists():
shutil.rmtree(work_dir)
work_dir.parent.mkdir(parents=True, exist_ok=True)
_run(
[
"git",
"clone",
"--branch",
branch,
"--single-branch",
wiki_url,
str(work_dir),
],
cwd=work_dir.parent,
)
return work_dir
def managed_filenames_from_manifest(manifest_path: Path) -> set[str]:
"""Filenames previously written by export_1c_help."""
names = set(_MANAGED_FIXED)
if not manifest_path.is_file():
return names
try:
data = json.loads(manifest_path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError):
return names
for key in ("pages", "deleted_pages"):
for item in data.get(key) or []:
if isinstance(item, dict) and item.get("filename"):
names.add(str(item["filename"]))
return names
def push_wiki(
content_dir: Path,
wiki_url: str,
*,
work_dir: Path,
message: str,
branch: str = "main",
wipe_unmanaged: bool = False,
already_prepared: bool = False,
) -> None:
"""
Clone/fetch wiki, update only export-managed pages, commit, push.
User-written wiki pages (not listed in previous manifest.json) are kept
unless wipe_unmanaged=True.
"""
content_dir = content_dir.resolve()
work_dir = work_dir.resolve()
if not already_prepared:
prepare_wiki_clone(wiki_url, work_dir, branch=branch)
new_files = {
p.name
for p in content_dir.iterdir()
if p.is_file() and p.name != ".git"
}
prev_managed = managed_filenames_from_manifest(work_dir / "manifest.json")
if wipe_unmanaged:
for path in work_dir.iterdir():
if path.name == ".git":
continue
if not path.is_file():
continue
suf = path.suffix.lower()
if suf in {".md", ".json"} or suf in _IMAGE_SUFFIXES:
path.unlink()
else:
# Drop help pages that disappeared from the configuration export.
for name in sorted(prev_managed - new_files):
path = work_dir / name
if path.is_file():
path.unlink()
for src in content_dir.iterdir():
if src.name == ".git":
continue
dst = work_dir / src.name
if src.is_dir():
if dst.exists():
shutil.rmtree(dst)
shutil.copytree(src, dst)
else:
shutil.copy2(src, dst)
_run(["git", "add", "-A"], cwd=work_dir)
status = _run(["git", "status", "--porcelain"], cwd=work_dir)
if not status.strip():
return
_run(["git", "commit", "-m", message], cwd=work_dir)
_run(["git", "push", "origin", f"HEAD:{branch}"], cwd=work_dir)