summaryrefslogtreecommitdiffstats
path: root/sync-cgit-descs.py
blob: da7ae6d20fa4ff10eeed15c55bddb265475f8865 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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)