Compare commits

..
2 Commits
Author SHA1 Message Date
devx-ci-bot 40a94df029 release: v0.35.7 [skip ci] 2026-07-06 13:22:27 +00:00
emilandDevin <158243242+devin-ai-integration[bot]@users.noreply.github.com> f50c4c1e00 DEVX-118: fix: use Gitea wiki dash-marker filename convention
Post-merge / detect-type (push) Successful in 10s
Post-merge / validate-commit-msg (push) Successful in 11s
Post-merge / vikunja (push) Successful in 24s
Post-merge / configure-repo (push) Successful in 16s
Post-merge / sync-wiki (push) Successful in 35s
Post-merge / release (push) Successful in 44s
Post-merge / publish (push) Successful in 22s
Post-merge / badges (push) Failing after 30s
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>
2026-07-06 15:21:22 +02:00
7 changed files with 64 additions and 18 deletions
+6
View File
@@ -2,6 +2,12 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [0.35.7] - 2026-07-06
### Bug Fixes
- Use Gitea wiki dash-marker filename convention
## [0.35.6] - 2026-07-06 ## [0.35.6] - 2026-07-06
### Bug Fixes ### Bug Fixes
+3 -3
View File
@@ -87,7 +87,7 @@ extra index and list devx in your dependencies:
```toml ```toml
[project] [project]
dependencies = [ dependencies = [
"devx>=0.35.6", "devx>=0.35.7",
] ]
[tool.pip] [tool.pip]
@@ -101,8 +101,8 @@ pip install -e .
``` ```
> **Note:** If your project requires a specific devx version, pin it in > **Note:** If your project requires a specific devx version, pin it in
> `dependencies` (for example, `"devx==0.35.6"`) or use a version constraint > `dependencies` (for example, `"devx==0.35.7"`) or use a version constraint
> (for example, `"devx>=0.35.6,<0.36"`). > (for example, `"devx>=0.35.7,<0.36"`).
### Optional extras ### Optional extras
+2 -2
View File
@@ -74,14 +74,14 @@ Add devx to your `pyproject.toml` dependencies and configure the registry:
```toml ```toml
[project] [project]
dependencies = [ dependencies = [
"devx>=0.35.6", "devx>=0.35.7",
] ]
[tool.pip] [tool.pip]
extra-index-url = "https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple" extra-index-url = "https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple"
``` ```
Pin a specific version if needed: `"devx==0.35.6"` or `"devx>=0.35.6,<0.36"`. Pin a specific version if needed: `"devx==0.35.7"` or `"devx>=0.35.7,<0.36"`.
### Optional extras ### Optional extras
+2 -2
View File
@@ -48,12 +48,12 @@ Add devx to your `pyproject.toml`:
```toml ```toml
[project] [project]
dependencies = [ dependencies = [
"devx>=0.35.6", "devx>=0.35.7",
] ]
[project.optional-dependencies] [project.optional-dependencies]
dev = [ dev = [
"devx>=0.35.6", "devx>=0.35.7",
] ]
``` ```
+1 -1
View File
@@ -1,3 +1,3 @@
"""devx — reusable development and CI/CD tools for oblachno-oss projects.""" """devx — reusable development and CI/CD tools for oblachno-oss projects."""
__version__ = "0.35.6" __version__ = "0.35.7"
+26 -10
View File
@@ -33,7 +33,7 @@ import subprocess # nosec B404
import tempfile import tempfile
import time import time
from pathlib import Path from pathlib import Path
from urllib.parse import urlparse from urllib.parse import quote, urlparse
import click import click
from dotenv import load_dotenv # pyright: ignore[reportMissingImports,reportUnknownVariableType] from dotenv import load_dotenv # pyright: ignore[reportMissingImports,reportUnknownVariableType]
@@ -50,6 +50,23 @@ MAPPING_FILE = DOCS_DIR / "mapping.json"
_LINK_RE = re.compile(r"\[([^\]]*)\]\(([^)]+)\)") _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]: def load_mapping() -> dict[str, str]:
"""Load the file-to-wiki-page mapping from mapping.json.""" """Load the file-to-wiki-page mapping from mapping.json."""
with open(MAPPING_FILE, encoding="utf-8") as f: with open(MAPPING_FILE, encoding="utf-8") as f:
@@ -171,16 +188,15 @@ def sync_files(
# Transform links # Transform links
transformed = transform_links(content) transformed = transform_links(content)
# Wiki filename: use the page title with spaces → underscores # Wiki filename: Gitea uses a dash-marker convention for titles with dashes
# Gitea wiki uses the page title as filename (spaces become dashes) fname = wiki_filename(page_title)
wiki_filename = page_title.replace(" ", "-") + ".md" expected_files.add(fname)
expected_files.add(wiki_filename)
if not dry_run: if not dry_run:
dest = wiki_dir / wiki_filename dest = wiki_dir / fname
dest.write_text(transformed, encoding="utf-8") dest.write_text(transformed, encoding="utf-8")
synced += 1 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) # Prune stale pages (in wiki but not in mapping)
pruned = 0 pruned = 0
@@ -235,7 +251,7 @@ def commit_and_push(wiki_dir: Path, wiki_url: str, dry_run: bool) -> bool:
# Push # Push
result = subprocess.run( # nosec result = subprocess.run( # nosec
["git", "push", wiki_url, "HEAD:master"], ["git", "push", "--force", wiki_url, "HEAD:master"],
cwd=wiki_dir, cwd=wiki_dir,
capture_output=True, capture_output=True,
text=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")) raise click.ClickException(_("Wiki verification failed — could not clone wiki"))
failures = 0 failures = 0
for _file_path, page_title in sorted(mapping.items()): for _file_path, page_title in sorted(mapping.items()):
wiki_filename = page_title.replace(" ", "-") + ".md" fname = wiki_filename(page_title)
if (verify_dir / wiki_filename).exists(): if (verify_dir / fname).exists():
click.echo(_(" OK: {title}", title=page_title)) click.echo(_(" OK: {title}", title=page_title))
else: else:
click.echo(_(" FAIL: {title} — page not found in wiki!", title=page_title)) click.echo(_(" FAIL: {title} — page not found in wiki!", title=page_title))
+24
View File
@@ -19,6 +19,7 @@ from devx.ci.sync_wiki import (
main, main,
sync_files, sync_files,
transform_links, transform_links,
wiki_filename,
) )
@@ -96,6 +97,29 @@ class TestGetWikiCloneUrl:
assert "owner/repo.wiki.git" in url 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: class TestCloneWiki:
@patch("devx.ci.sync_wiki.subprocess.run") @patch("devx.ci.sync_wiki.subprocess.run")
def test_clone_success(self, mock_run: MagicMock, tmp_path: Path) -> None: def test_clone_success(self, mock_run: MagicMock, tmp_path: Path) -> None: