Files
mihailkudravcev 02a05d34d0 Support any 1C config dump and auto wiki URL from its git remote.
Add info command, -c/--config, derived repo.wiki.git target, and README for submodule/root workflows.
2026-07-23 15:24:59 +03:00

138 lines
3.8 KiB
Python

"""Resolve configuration paths and Gitea/GitLab wiki clone URLs."""
from __future__ import annotations
import re
import subprocess
from pathlib import Path
class ResolveError(ValueError):
pass
def resolve_src_root(config_or_src: Path) -> Path:
"""
Accept configuration root (…/crm3-26) or its src/ dump.
Returns absolute path to the directory that contains Catalogs/, Documents/, …
"""
path = Path(config_or_src).expanduser().resolve()
if not path.exists():
raise ResolveError(f"path does not exist: {path}")
if path.is_file():
raise ResolveError(f"expected directory, got file: {path}")
if _looks_like_src(path):
return path
src = path / "src"
if src.is_dir() and _looks_like_src(src):
return src.resolve()
raise ResolveError(
f"not a 1C configuration dump: {path} "
"(need …/src with Catalogs|Documents|Configuration.xml, or the src itself)"
)
def resolve_config_root(config_or_src: Path) -> Path:
"""Configuration root (parent of src when applicable)."""
src = resolve_src_root(config_or_src)
if src.name == "src":
return src.parent
return src
def _looks_like_src(path: Path) -> bool:
if (path / "Configuration.xml").is_file():
return True
markers = ("Catalogs", "Documents", "DataProcessors", "Reports", "CommonModules")
return any((path / name).is_dir() for name in markers)
def git_toplevel(path: Path) -> Path | None:
"""Nearest git work tree containing path (submodule or root project)."""
path = Path(path).resolve()
try:
proc = subprocess.run(
["git", "-C", str(path), "rev-parse", "--show-toplevel"],
capture_output=True,
text=True,
check=False,
)
except OSError:
return None
if proc.returncode != 0:
return None
top = proc.stdout.strip()
return Path(top) if top else None
def git_remote_url(path: Path, *, remote: str = "origin") -> str | None:
"""Clone URL of `remote` for the git repo that owns path."""
top = git_toplevel(path)
if top is None:
return None
try:
proc = subprocess.run(
["git", "-C", str(top), "remote", "get-url", remote],
capture_output=True,
text=True,
check=False,
)
except OSError:
return None
if proc.returncode != 0:
return None
url = proc.stdout.strip()
return url or None
def to_wiki_clone_url(repo_url: str) -> str:
"""
Map configuration repository URL → wiki git URL.
Gitea / GitLab: https://host/org/repo.git → https://host/org/repo.wiki.git
git@host:org/repo.git → git@host:org/repo.wiki.git
"""
url = repo_url.strip().rstrip("/")
if not url:
raise ResolveError("empty repository URL")
if url.endswith(".wiki.git"):
return url
if url.endswith(".git"):
return url[: -len(".git")] + ".wiki.git"
return url + ".wiki.git"
def resolve_wiki_url(
config_or_src: Path,
*,
wiki_url: str | None = None,
remote: str = "origin",
) -> str:
"""Explicit --wiki-url, or derive from git remote of the configuration repo."""
if wiki_url:
return to_wiki_clone_url(wiki_url)
root = resolve_config_root(config_or_src)
repo = git_remote_url(root, remote=remote)
if not repo:
raise ResolveError(
f"cannot detect git remote '{remote}' for {root}; "
"pass --wiki-url explicitly (…/repo.wiki.git)"
)
return to_wiki_clone_url(repo)
def slug_for_path(path: Path) -> str:
"""Short filesystem-safe slug from config root name."""
name = resolve_config_root(path).name
slug = re.sub(r"[^\w.\-]+", "-", name, flags=re.UNICODE).strip("-")
return slug or "config"