Files
reliquary/plugins/gitea/skills/gitea/scripts/config.py
T
g4borgandClaude Opus 4.7 68c112d2c8 ✨ Add gitea plugin
Ports dirigent's gitea scripts into a self-contained reliquary plugin.
Scripts run via uv with PEP 723 inline deps — no project venv needed.
Gitea connection inferred from `git remote get-url origin` with env-var
overrides; only GITEA_TOKEN is mandatory. Workpad path and tickets
directory are parameterized so the plugin works in any project. Label
set externalized to labels.default.json with project-level override via
.gitea-labels.json.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 19:52:02 +02:00

118 lines
3.7 KiB
Python

"""Configuration for Gitea API access.
Resolution order:
1. Parse `git remote get-url origin` for base_url/owner/repo.
2. Env vars (GITEA_URL, GITEA_OWNER, GITEA_REPO) override inferred values.
3. GITEA_TOKEN is always required from env.
`.env` in CWD (or any parent) is loaded via python-dotenv.
"""
from __future__ import annotations
import os
import re
import subprocess
import sys
from dataclasses import dataclass
from typing import Optional, Tuple
from dotenv import find_dotenv, load_dotenv
@dataclass(frozen=True)
class GiteaConfig:
base_url: str
token: str
owner: str
repo: str
@property
def api_url(self) -> str:
return f"{self.base_url.rstrip('/')}/api/v1"
_HTTPS_RE = re.compile(r"^(https?)://(?:[^@/]+@)?([^/:]+)(?::\d+)?/([^/]+)/(.+?)(?:\.git)?/?$")
_SSH_URL_RE = re.compile(r"^ssh://(?:[\w.-]+@)?([^:/]+)(?::\d+)?/([^/]+)/(.+?)(?:\.git)?/?$")
_SCP_RE = re.compile(r"^(?:[\w.-]+@)?([^:/]+):([^/]+)/(.+?)(?:\.git)?/?$")
def parse_remote_url(url: str) -> Optional[Tuple[str, str, str]]:
"""Parse a git remote URL into (base_url, owner, repo).
Handles:
git@host:owner/repo.git (SCP-like)
ssh://git@host[:port]/owner/repo.git (SSH URL)
https://[user@]host[:port]/owner/repo(.git)
"""
url = url.strip()
if not url:
return None
m = _HTTPS_RE.match(url)
if m:
scheme, host, owner, repo = m.group(1), m.group(2), m.group(3), m.group(4)
return f"{scheme}://{host}", owner, repo
m = _SSH_URL_RE.match(url)
if m:
host, owner, repo = m.group(1), m.group(2), m.group(3)
return f"https://{host}", owner, repo
m = _SCP_RE.match(url)
if m:
host, owner, repo = m.group(1), m.group(2), m.group(3)
return f"https://{host}", owner, repo
return None
def git_remote_origin() -> Optional[Tuple[str, str, str]]:
"""Return (base_url, owner, repo) parsed from `git remote get-url origin`, or None."""
try:
result = subprocess.run(
["git", "remote", "get-url", "origin"],
capture_output=True,
text=True,
timeout=5,
)
except (FileNotFoundError, subprocess.TimeoutExpired):
return None
if result.returncode != 0:
return None
return parse_remote_url(result.stdout.strip())
def load_config() -> GiteaConfig:
"""Load configuration: git remote inference + env-var overrides.
GITEA_TOKEN is always required. The other fields can come from either
the git remote of the current working directory, or be overridden via
GITEA_URL, GITEA_OWNER, GITEA_REPO in env or .env file.
"""
load_dotenv(find_dotenv(usecwd=True))
inferred = git_remote_origin()
base_url = os.environ.get("GITEA_URL") or (inferred[0] if inferred else None)
owner = os.environ.get("GITEA_OWNER") or (inferred[1] if inferred else None)
repo = os.environ.get("GITEA_REPO") or (inferred[2] if inferred else None)
token = os.environ.get("GITEA_TOKEN")
missing = []
if not token:
missing.append("GITEA_TOKEN")
if not base_url:
missing.append("GITEA_URL (or a git origin remote)")
if not owner:
missing.append("GITEA_OWNER (or a git origin remote)")
if not repo:
missing.append("GITEA_REPO (or a git origin remote)")
if missing:
print(f"Error: missing required configuration: {', '.join(missing)}", file=sys.stderr)
print("Set GITEA_TOKEN in your environment or .env file.", file=sys.stderr)
print("URL/owner/repo are inferred from `git remote get-url origin` when available.", file=sys.stderr)
sys.exit(1)
return GiteaConfig(base_url=base_url, token=token, owner=owner, repo=repo)