#!/usr/bin/env python3 """ sync-cgit-descs.py — update repo.desc= in /etc/cgitrc from gitolite description files. Only updates existing repo blocks. Never adds new blocks or removes repos. Skips repos whose description file is missing or contains the gitolite default. """ import argparse import os import re import sys CGITRC = "/etc/cgitrc" REPO_BASE = "/var/lib/gitolite3/repositories" GITOLITE_DEFAULT = "Unnamed repository" def read_description(repo_url): path = os.path.join(REPO_BASE, repo_url + ".git", "description") if not os.path.exists(path): return None desc = open(path).read().strip() if not desc or GITOLITE_DEFAULT in desc: return None return desc def sync(cgitrc_path, dry_run=False): with open(cgitrc_path) as f: lines = f.readlines() out = [] i = 0 changes = 0 while i < len(lines): line = lines[i] m = re.match(r"^repo\.url=(.+)", line.rstrip()) if m: repo_url = m.group(1) desc = read_description(repo_url) out.append(line) i += 1 # consume lines until next repo block or EOF, updating repo.desc= if found desc_written = False block = [] while i < len(lines): next_line = lines[i] if re.match(r"^repo\.url=", next_line): break if re.match(r"^#?repo\.desc=", next_line): if desc: new_line = f"repo.desc={desc}\n" if next_line.rstrip() != new_line.rstrip(): print(f" {repo_url}: '{next_line.rstrip()}' -> '{new_line.rstrip()}'") changes += 1 block.append(new_line) else: block.append(next_line) desc_written = True else: block.append(next_line) i += 1 # insert repo.desc= after repo.url= if not found in block if not desc_written and desc: block.insert(0, f"repo.desc={desc}\n") print(f" {repo_url}: inserted repo.desc={desc}") changes += 1 out.extend(block) else: out.append(line) i += 1 if changes == 0: print("No changes.") return if dry_run: print(f"\n-- dry run: {changes} change(s), not writing --") else: with open(cgitrc_path, "w") as f: f.writelines(out) print(f"\nWrote {changes} change(s) to {cgitrc_path}") if __name__ == "__main__": parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--cgitrc", default=CGITRC, help=f"cgitrc path (default: {CGITRC})") parser.add_argument("--repo-base", default=REPO_BASE, help=f"gitolite repos dir (default: {REPO_BASE})") parser.add_argument("--dry-run", action="store_true", help="print changes without writing") args = parser.parse_args() REPO_BASE = args.repo_base if not os.path.exists(args.cgitrc): print(f"error: {args.cgitrc} not found", file=sys.stderr) sys.exit(1) sync(args.cgitrc, dry_run=args.dry_run)