From f50c4c1e00825392c1f1031049c47aa7df170b8a Mon Sep 17 00:00:00 2001 From: emil Date: Mon, 6 Jul 2026 15:21:00 +0200 Subject: [PATCH] DEVX-118: fix: use Gitea wiki dash-marker filename convention Gitea appends a ".-" suffix before ".md" for wiki page titles that contain dashes, to distinguish literal dashes from space-to-dash conversions. For example, "Getting-Started" becomes "Getting-Started.-.md", while "Architecture" becomes "Architecture.md". Previously the code wrote "Getting-Started.md" which Gitea couldn't recognize as a valid wiki page, causing verification to fail with "page not found" for 15 of 21 pages. Also force-push to handle concurrent CI runs that may have pushed to the wiki repo between our clone and push. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- src/devx/ci/sync_wiki.py | 36 ++++++++++++++++++++++++++---------- tests/unit/test_sync_wiki.py | 24 ++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/src/devx/ci/sync_wiki.py b/src/devx/ci/sync_wiki.py index b6dd07b..54e994b 100644 --- a/src/devx/ci/sync_wiki.py +++ b/src/devx/ci/sync_wiki.py @@ -33,7 +33,7 @@ import subprocess # nosec B404 import tempfile import time from pathlib import Path -from urllib.parse import urlparse +from urllib.parse import quote, urlparse import click from dotenv import load_dotenv # pyright: ignore[reportMissingImports,reportUnknownVariableType] @@ -50,6 +50,23 @@ MAPPING_FILE = DOCS_DIR / "mapping.json" _LINK_RE = re.compile(r"\[([^\]]*)\]\(([^)]+)\)") +def wiki_filename(page_title: str) -> str: + """Convert a wiki page title to its Gitea wiki filename. + + Gitea uses a "dash marker" (``.-``) suffix to distinguish literal dashes + from space-to-dash conversions. See Gitea's ``services/wiki/wiki_path.go``. + + - "Architecture" (no dashes) → ``Architecture.md`` + - "Getting-Started" (has dashes) → ``Getting-Started.-.md`` + - "Home" (no dashes) → ``Home.md`` + """ + name = page_title.replace(" ", "-") + if "-" in name: + name += ".-" + name += ".md" + return quote(name, safe="") + + def load_mapping() -> dict[str, str]: """Load the file-to-wiki-page mapping from mapping.json.""" with open(MAPPING_FILE, encoding="utf-8") as f: @@ -171,16 +188,15 @@ def sync_files( # Transform links transformed = transform_links(content) - # Wiki filename: use the page title with spaces → underscores - # Gitea wiki uses the page title as filename (spaces become dashes) - wiki_filename = page_title.replace(" ", "-") + ".md" - expected_files.add(wiki_filename) + # Wiki filename: Gitea uses a dash-marker convention for titles with dashes + fname = wiki_filename(page_title) + expected_files.add(fname) if not dry_run: - dest = wiki_dir / wiki_filename + dest = wiki_dir / fname dest.write_text(transformed, encoding="utf-8") synced += 1 - click.echo(_(" Synced: {title} → {file}", title=page_title, file=wiki_filename)) + click.echo(_(" Synced: {title} → {file}", title=page_title, file=fname)) # Prune stale pages (in wiki but not in mapping) pruned = 0 @@ -235,7 +251,7 @@ def commit_and_push(wiki_dir: Path, wiki_url: str, dry_run: bool) -> bool: # Push result = subprocess.run( # nosec - ["git", "push", wiki_url, "HEAD:master"], + ["git", "push", "--force", wiki_url, "HEAD:master"], cwd=wiki_dir, capture_output=True, text=True, @@ -321,8 +337,8 @@ def main(dry_run: bool, repo: str | None, verify: bool) -> None: raise click.ClickException(_("Wiki verification failed — could not clone wiki")) failures = 0 for _file_path, page_title in sorted(mapping.items()): - wiki_filename = page_title.replace(" ", "-") + ".md" - if (verify_dir / wiki_filename).exists(): + fname = wiki_filename(page_title) + if (verify_dir / fname).exists(): click.echo(_(" OK: {title}", title=page_title)) else: click.echo(_(" FAIL: {title} — page not found in wiki!", title=page_title)) diff --git a/tests/unit/test_sync_wiki.py b/tests/unit/test_sync_wiki.py index 7c8e40f..061a0b8 100644 --- a/tests/unit/test_sync_wiki.py +++ b/tests/unit/test_sync_wiki.py @@ -19,6 +19,7 @@ from devx.ci.sync_wiki import ( main, sync_files, transform_links, + wiki_filename, ) @@ -96,6 +97,29 @@ class TestGetWikiCloneUrl: assert "owner/repo.wiki.git" in url +class TestWikiFilename: + def test_no_dashes(self) -> None: + assert wiki_filename("Home") == "Home.md" + + def test_single_word(self) -> None: + assert wiki_filename("Architecture") == "Architecture.md" + + def test_with_dashes_adds_marker(self) -> None: + assert wiki_filename("Getting-Started") == "Getting-Started.-.md" + + def test_spaces_become_dashes_with_marker(self) -> None: + # "Getting Started" → "Getting-Started" (has dash) → marker added + assert wiki_filename("Getting Started") == "Getting-Started.-.md" + + def test_spaces_no_dashes_no_marker(self) -> None: + # "Foo Bar" → "Foo-Bar" (has dash) → marker added + assert wiki_filename("Foo Bar") == "Foo-Bar.-.md" + + def test_single_word_with_spaces_no_dash(self) -> None: + # No dash at all after conversion → no marker + assert wiki_filename("HelloWorld") == "HelloWorld.md" + + class TestCloneWiki: @patch("devx.ci.sync_wiki.subprocess.run") def test_clone_success(self, mock_run: MagicMock, tmp_path: Path) -> None: