summaryrefslogtreecommitdiffstats
path: root/sync-cgit-descs.py
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-05-08 11:06:20 +0200
committerDanilo M. <danix@danix.xyz>2026-05-08 11:06:20 +0200
commit1b63c1cf811312e0593adde95ccf8369c9f6ade2 (patch)
tree6232a18b7c141cd65e5a86ee0b0057040ab9b270 /sync-cgit-descs.py
parent1d3c519a5457275cd2e740ddd2e3b0c783759f66 (diff)
downloadcgit-theme-danix-1b63c1cf811312e0593adde95ccf8369c9f6ade2.tar.gz
cgit-theme-danix-1b63c1cf811312e0593adde95ccf8369c9f6ade2.zip
feat: add sync-cgit-descs.py to sync repo descriptions from gitolite
Reads /var/lib/gitolite3/repositories/*/description and updates repo.desc= in /etc/cgitrc for existing repo blocks only. Supports --dry-run, --cgitrc, and --repo-base overrides. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'sync-cgit-descs.py')
-rwxr-xr-xsync-cgit-descs.py100
1 files changed, 100 insertions, 0 deletions
diff --git a/sync-cgit-descs.py b/sync-cgit-descs.py
new file mode 100755
index 0000000..da7ae6d
--- /dev/null
+++ b/sync-cgit-descs.py
@@ -0,0 +1,100 @@
+#!/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)