import os import sys import stat import shutil import subprocess import configparser from xml.sax.saxutils import escape USER = 'git' WWW_DIR = '/var/www/git/' REPO_DIR = os.path.expanduser('~/repos/') CONF_PATH = os.path.expanduser('~/stagit.conf') if os.environ.get('USER') != USER: sys.stderr.write('wrong user\n') sys.exit(1) def create_sh_script(path, lines): if lines: with open(path, 'w') as fh: fh.write('#!/bin/sh\n') for line in lines: fh.write(line + '\n') os.chmod(path, 0o775) elif os.path.exists(path): os.unlink(path) def touch(path): with open(path, 'a'): os.utime(path, None) def get_repo_dir(repo): return os.path.join(REPO_DIR, repo + '.git') class Config: def __init__(self): self.config = configparser.ConfigParser() self.config.read(CONF_PATH) def get(self, section, key): return self.config.get(section, key, fallback='') def getboolean(self, section, key): return self.config.getboolean(section, key, fallback=False) def can_ssh(self, repo, user): allowed = self.get(repo, 'ssh').split() return user in allowed or '@all' in allowed def iter_repos(self): for key in self.config: if key != 'DEFAULT': yield key config = Config() def update_repo(repo): repodir = get_repo_dir(repo) if not os.path.exists(repodir): os.makedirs(repodir, exist_ok=True) subprocess.check_call(['git', 'init', '--bare'], cwd=repodir) export_ok = os.path.join(repodir, 'git-daemon-export-ok') post_update = [] if config.getboolean(repo, 'http'): post_update.append( 'python3 -c \'import os, stagit; ' 'stagit.render_repo(os.environ["STAGIT_REPO"])\'' ) touch(export_ok) else: if os.path.exists(export_ok): os.unlink(export_ok) if config.get(repo, 'post-update'): post_update.append(config.get(repo, 'post-update')) create_sh_script(os.path.join(repodir, 'hooks', 'post-update'), post_update) with open(os.path.join(repodir, 'description'), 'w') as fh: fh.write(config.get(repo, 'desc')) with open(os.path.join(repodir, 'url'), 'w') as fh: fh.write(config.get(repo, 'url')) def render_index(): fh = open(os.path.join(WWW_DIR, 'index.html'), 'w') fh.write('\n') fh.write('\n\n') fh.write('') fh.write('\n') fh.write('Repositories\n') fh.write('\n') fh.write('\n\n') fh.write('

Repositories

') fh.write('
\n') fh.write('
\n') fh.write('\n\n') fh.write('\n') fh.write('\n\n') for repo in config.iter_repos(): if config.getboolean(repo, 'http'): fh.write('\n' % ( escape(repo), escape(repo), escape(config.get(repo, 'desc')) )) fh.write('\n
NameDescription
%s%s
\n') fh.write('
\n\n\n') fh.close() def render_repo(repo): print('Generating HTML for %s…' % repo) target_dir = os.path.join(WWW_DIR, repo) if os.path.isdir(target_dir): shutil.rmtree(target_dir) os.makedirs(target_dir) subprocess.check_call(['stagit', get_repo_dir(repo)], cwd=target_dir) def render_all(): for repo in os.listdir(WWW_DIR): path = os.path.join(WWW_DIR, repo) if os.path.isdir(path): shutil.rmtree(path) for repo in config.iter_repos(): if config.getboolean(repo, 'http'): render_repo(repo) render_index() if __name__ == '__main__': all_repos = set(config.iter_repos()) phy_repos = set(fn[:-4] for fn in os.listdir(REPO_DIR) if fn.endswith('.git')) stale_repos = phy_repos - all_repos print('Updating repos …') for repo in all_repos: update_repo(repo) if stale_repos: print('Warning: stale files for deleted repos:', ', '.join(stale_repos)) render_all()