aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2015-06-08 20:13:27 +0200
committerRalf Jung <post@ralfj.de>2015-06-08 20:13:27 +0200
commit6290489e9a0359df09c7f09650ffb02f86bceb14 (patch)
treef3d7b37bfb153dd6fc9e143d1399af386f1ff014
parent9e8ef5ff99e70c7cc97445acbd91abaf539fd536 (diff)
add a script to setup github repositories appropriately
-rw-r--r--git_mirror.py8
-rwxr-xr-xgithub-add-hooks.py85
2 files changed, 89 insertions, 4 deletions
diff --git a/git_mirror.py b/git_mirror.py
index a3b7fe9..b4b4964 100644
--- a/git_mirror.py
+++ b/git_mirror.py
@@ -27,6 +27,7 @@ import hmac, hashlib
import email.mime.text, email.utils, smtplib
mail_sender = "null@localhost"
+config_file = os.path.join(os.path.dirname(__file__), 'git-mirror.conf')
class GitCommand:
def __getattr__(self, name):
@@ -53,10 +54,10 @@ def git_is_forced_update(oldsha, newsha):
assert code in (0, 1)
return False if code == 0 else True # if oldsha is an ancestor of newsha, then this was a "good" (non-forced) update
-def read_config(fname, defSection = 'DEFAULT'):
+def read_config(defSection = 'DEFAULT'):
'''Reads a config file that may have options outside of any section.'''
config = configparser.ConfigParser()
- with open(fname) as file:
+ with open(config_file) as file:
stream = itertools.chain(("["+defSection+"]\n",), file)
config.read_file(stream)
return config
@@ -189,8 +190,7 @@ def find_repo_by_directory(repos, dir):
def load_repos():
global mail_sender
- conffile = os.path.join(os.path.dirname(__file__), 'git-mirror.conf')
- conf = read_config(conffile)
+ conf = read_config()
mail_sender = conf['DEFAULT']['mail-sender']
repos = {}
diff --git a/github-add-hooks.py b/github-add-hooks.py
new file mode 100755
index 0000000..8e65d86
--- /dev/null
+++ b/github-add-hooks.py
@@ -0,0 +1,85 @@
+#!/usr/bin/python3
+import random, string, argparse, os.path, subprocess, shutil
+import requests, json
+from git_mirror import *
+
+def random_string(length):
+ alphabet = string.digits + string.ascii_letters
+ r = random.SystemRandom()
+ result = ""
+ for i in range(length):
+ result += r.choice(alphabet)
+ return result
+
+def generate_ssh_key(name, bits):
+ subprocess.check_call(["ssh-keygen", "-f", name, "-C", name, "-b", str(bits), "-q", "-N", ""])
+
+def add_deploy_key(key_name, repo_owner, repo_name, access_token):
+ url = "https://api.github.com/repos/{owner}/{repo}/keys?access_token={token}".format(owner=repo_owner, repo=repo_name, token=access_token)
+ data = { 'title': os.path.basename(key_name), 'key': open(key_name+".pub").read() }
+ r = requests.post(url, data=json.dumps(data))
+ if r.status_code >= 300:
+ raise Exception(str(json.loads(r.content.decode('utf-8'))))
+
+def add_web_hook(webhook_url, hmac_secret, repo_owner, repo_name, access_token):
+ url = 'https://api.github.com/repos/{owner}/{repo}/hooks?access_token={token}'.format(owner=repo_owner, repo=repo_name, token=access_token)
+ data = {
+ 'name': "web",
+ 'active': True,
+ 'events': ['push'],
+ 'config': {
+ 'url': webhook_url,
+ 'content_type': "json",
+ 'secret': hmac_secret,
+ }
+ }
+ r = requests.post(url, data=json.dumps(data))
+ if r.status_code >= 300:
+ raise Exception(str(json.loads(r.content.decode('utf-8'))))
+
+# get config and user arguments
+conf = read_config()
+parser = argparse.ArgumentParser(description='Update and build a bunch of stuff')
+parser.add_argument("-o", "--owner",
+ dest="owner",
+ help="The owner of this hook on GitHub")
+parser.add_argument("-e", "--email",
+ dest="email",
+ help="An email address that gets notified in case of trouble with the hook")
+parser.add_argument("-l", "--local",
+ dest="local",
+ help="The local directory of the repository")
+parser.add_argument("-n", "--name",
+ dest="name", default=None,
+ help="The name of the repository on GitHub (defaults to the basename of the local directory)")
+args = parser.parse_args()
+args.local = os.path.abspath(args.local)
+assert os.path.isdir(args.local), "Local repository has to be a directory"
+if args.name is None:
+ args.name = os.path.basename(args.local)
+ if args.name.endswith(".git"):
+ args.name = args.name[:-4]
+hmac_secret = random_string(64)
+ssh_deploy_key = os.path.join(os.path.expanduser('~/.ssh'), args.name+"-github")
+github_token = conf['DEFAULT']['github-token']
+webhook_url = conf['DEFAULT']['webhook-url']
+
+# append to the configuration (after making a backup)
+shutil.copy(config_file, config_file+".bak")
+with open(config_file, 'a') as f:
+ f.write('\n[{}]\n'.format(args.name))
+ f.write('owner={}\n'.format(args.owner))
+ f.write('local={}\n'.format(args.local))
+ f.write('deploy-key={}\n'.format(os.path.basename(ssh_deploy_key)))
+ f.write('hmac-secret={}\n'.format(hmac_secret))
+ f.write('mirror-github=git@github.com:{}/{}.git\n'.format(args.owner, args.name))
+
+try:
+ generate_ssh_key(ssh_deploy_key, 4*1024)
+ add_deploy_key(ssh_deploy_key, args.owner, args.name, github_token)
+ add_web_hook(webhook_url+"?repository="+args.name, hmac_secret, args.owner, args.name, github_token)
+ print("Done! Your GitHub repository is set up.\nRemember to configure the git-mirror hook for the local repository {}, e.g. in your gitolite configuration!", args.local)
+except E:
+ shutil.copy(config_file+".bak", config_file)
+ raise
+