#!/usr/bin/env python3 """Install Gitea issue workflow files into one or more repositories.""" from __future__ import annotations import argparse import pathlib import sys from gitea_common import GiteaError, repo_root SOURCE_ROOT = pathlib.Path(__file__).resolve().parents[1] WORKFLOW_FILES = ( ".gitea/PULL_REQUEST_TEMPLATE.md", ".gitea/ISSUE_TEMPLATE/bug_report.md", ".gitea/ISSUE_TEMPLATE/config.yaml", ".gitea/ISSUE_TEMPLATE/docs_workflow.md", ".gitea/ISSUE_TEMPLATE/feature_request.md", ".gitea/ISSUE_TEMPLATE/task.md", ".gitea/ISSUE_TEMPLATE/tech_debt.md", ) MODULE_LABEL_BY_REPO = { "govoplan-core": "module/core", "govoplan-access": "module/access", "govoplan-admin": "module/admin", "govoplan-tenancy": "module/tenancy", "govoplan-policy": "module/policy", "govoplan-audit": "module/audit", "govoplan-mail": "module/mail", "govoplan-files": "module/files", "govoplan-campaign": "module/campaign", "govoplan-web": "module/web", } def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("targets", nargs="*", type=pathlib.Path, help="target repository roots or child paths") parser.add_argument("--module-label", help="module label to write into issue templates; only valid for one target") parser.add_argument("--include-labels-file", action="store_true", help="also copy docs/gitea-labels.json") parser.add_argument("--apply", action="store_true", help="write files instead of previewing changes") args = parser.parse_args() try: target_roots = [repo_root(path) for path in (args.targets or [pathlib.Path.cwd()])] unique_roots = list(dict.fromkeys(target_roots)) if args.module_label and len(unique_roots) != 1: raise GiteaError("--module-label can only be used with a single target") rel_paths = list(WORKFLOW_FILES) if args.include_labels_file: rel_paths.append("docs/gitea-labels.json") for target_root in unique_roots: module_label = args.module_label or infer_module_label(target_root) install_files(target_root, rel_paths, module_label, apply=args.apply) if not args.apply: print("Dry run only. Re-run with --apply to write files.") return 0 except GiteaError as exc: print(f"error: {exc}", file=sys.stderr) return 1 def infer_module_label(target_root: pathlib.Path) -> str: repo_name = target_root.name try: repo_name = target_root.resolve().name except OSError: pass label = MODULE_LABEL_BY_REPO.get(repo_name) if label: return label if repo_name.startswith("govoplan-"): suffix = repo_name.removeprefix("govoplan-") if suffix: return f"module/{suffix}" raise GiteaError(f"cannot infer module label for {target_root}. Use --module-label module/.") def install_files(target_root: pathlib.Path, rel_paths: list[str], module_label: str, *, apply: bool) -> None: print(f"Target: {target_root} ({module_label})") for rel_path in rel_paths: source = SOURCE_ROOT / rel_path target = target_root / rel_path if not source.exists(): raise GiteaError(f"missing workflow source file: {source}") content = source.read_text(encoding="utf-8") content = transform_content(rel_path, content, module_label) existing = target.read_text(encoding="utf-8") if target.exists() else None if existing == content: print(f"unchanged {rel_path}") continue action = "create" if existing is None else "update" print(f"{action if apply else 'would ' + action} {rel_path}") if apply: target.parent.mkdir(parents=True, exist_ok=True) target.write_text(content, encoding="utf-8") def transform_content(rel_path: str, content: str, module_label: str) -> str: if rel_path.startswith(".gitea/ISSUE_TEMPLATE/") and rel_path.endswith(".md"): return content.replace(" - module/core", f" - {module_label}") return content if __name__ == "__main__": raise SystemExit(main())