|
| 1 | +#!/usr/bin/env python |
| 2 | +""" |
| 3 | +github_irc_hooks.py |
| 4 | +=================== |
| 5 | +
|
| 6 | +Python script to setup IRC notification hooks on GitHub repositories. |
| 7 | +
|
| 8 | +Your GitHub API token should either be in your global (user) git config |
| 9 | +as github.token, or in a GITHUB_TOKEN environment variable. |
| 10 | +
|
| 11 | +This script assumes a server without Nickserv. |
| 12 | +
|
| 13 | +Requirements |
| 14 | +------------- |
| 15 | +
|
| 16 | +github3.py (`pip install github3.py`) |
| 17 | +
|
| 18 | +License |
| 19 | +-------- |
| 20 | +
|
| 21 | +Copyright 2014 Jason Antman <[email protected]> <http://www.jasonantman.com> |
| 22 | +Free for any use provided that patches are submitted back to me. |
| 23 | +
|
| 24 | +The latest version of this script can be found at: |
| 25 | +<https://github.com/jantman/misc-scripts/blob/master/github_irc_hooks.py> |
| 26 | +
|
| 27 | +CHANGELOG |
| 28 | +---------- |
| 29 | +
|
| 30 | +2015-07-08 Jason Antman <[email protected]>: |
| 31 | + - initial version of script |
| 32 | +""" |
| 33 | + |
| 34 | +import sys |
| 35 | +import argparse |
| 36 | +import logging |
| 37 | +import subprocess |
| 38 | +import os |
| 39 | +from github3 import login, GitHub |
| 40 | + |
| 41 | +FORMAT = "[%(levelname)s %(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s" |
| 42 | +logging.basicConfig(level=logging.ERROR, format=FORMAT) |
| 43 | +logger = logging.getLogger(__name__) |
| 44 | + |
| 45 | + |
| 46 | +class GitHubIRCHooker: |
| 47 | + """ might as well use a class. It'll make things easier later. """ |
| 48 | + |
| 49 | + def __init__(self, apitoken, server, port, nick, password): |
| 50 | + """ init method, run at class creation """ |
| 51 | + logger.debug("Connecting to GitHub") |
| 52 | + self.gh = login(token=apitoken) |
| 53 | + logger.info("Connected to GitHub API") |
| 54 | + self.server = server |
| 55 | + self.port = port |
| 56 | + self.nick = nick |
| 57 | + self.password = password |
| 58 | + |
| 59 | + def get_config(self, channel, branches): |
| 60 | + config = { |
| 61 | + 'notice': '0', |
| 62 | + 'branches': branches, |
| 63 | + 'room': channel, |
| 64 | + 'ssl': '1', |
| 65 | + 'no_colors': '0', |
| 66 | + 'server': self.server, |
| 67 | + 'nick': self.nick, |
| 68 | + 'nickserv_password': '', |
| 69 | + 'message_without_join': '1', |
| 70 | + 'long_url': '0', |
| 71 | + 'password': self.password, |
| 72 | + 'port': self.port, |
| 73 | + } |
| 74 | + return config |
| 75 | + |
| 76 | + def add_hook(self, repo, channel, branches): |
| 77 | + config = self.get_config(channel, branches) |
| 78 | + logger.info("Adding IRC hook to repo {r}; config: {c}".format( |
| 79 | + c=config, |
| 80 | + r=repo.name |
| 81 | + )) |
| 82 | + hook = repo.create_hook( |
| 83 | + 'irc', |
| 84 | + config, |
| 85 | + events=['push', 'pull_request'], |
| 86 | + active=True, |
| 87 | + ) |
| 88 | + if hook is None: |
| 89 | + logger.error("Error creating hook.") |
| 90 | + raise SystemExit(1) |
| 91 | + logger.info("Added hook to repository.") |
| 92 | + |
| 93 | + def run(self, orgname, reponame, channel, branches): |
| 94 | + """ do stuff here """ |
| 95 | + repo = self.gh.repository(orgname, reponame) |
| 96 | + num_hooks = 0 |
| 97 | + for hook in repo.iter_hooks(): |
| 98 | + num_hooks += 1 |
| 99 | + if hook.name == 'irc': |
| 100 | + logger.error("ERROR: repository already has an IRC hook") |
| 101 | + raise SystemExit(1) |
| 102 | + logger.debug("Repository has %d hooks, no IRC hooks yet.", num_hooks) |
| 103 | + self.add_hook(repo, channel, branches) |
| 104 | + |
| 105 | +def parse_args(argv): |
| 106 | + """ |
| 107 | + parse arguments/options |
| 108 | +
|
| 109 | + this uses the new argparse module instead of optparse |
| 110 | + see: <https://docs.python.org/2/library/argparse.html> |
| 111 | + """ |
| 112 | + p = argparse.ArgumentParser(description='Add IRC notifications to a GitHub repo') |
| 113 | + p.add_argument('-v', '--verbose', dest='verbose', action='count', default=0, |
| 114 | + help='verbose output. specify twice for debug-level output.') |
| 115 | + BRANCHES_DEFAULT = '' |
| 116 | + p.add_argument('-b', '--branches', dest='branches', action='store', |
| 117 | + default=BRANCHES_DEFAULT, |
| 118 | + help='comma-separated list of branch names to notify for' |
| 119 | + ' (default: %s)' % BRANCHES_DEFAULT) |
| 120 | + p.add_argument('-o', '--orgname', dest='orgname', action='store', |
| 121 | + required=True, help='repository owner name') |
| 122 | + p.add_argument('-s', '--server', action='store', required=True, |
| 123 | + help='IRC server hostname/IP') |
| 124 | + p.add_argument('-p', '--port', action='store', required=True, |
| 125 | + help='IRC server port') |
| 126 | + p.add_argument('-n', '--nick', action='store', required=True, |
| 127 | + help='IRC nick') |
| 128 | + p.add_argument('-P', '--password', action='store', required=True, |
| 129 | + default='', |
| 130 | + help='password for IRC nick (server password)') |
| 131 | + p.add_argument('reponame', action='store', help='repository name') |
| 132 | + p.add_argument('channel', action='store', help='channel name') |
| 133 | + args = p.parse_args(argv) |
| 134 | + |
| 135 | + return args |
| 136 | + |
| 137 | +def get_api_token(): |
| 138 | + """ get GH api token """ |
| 139 | + apikey = subprocess.check_output(['git', 'config', '--global', |
| 140 | + 'github.token']).strip() |
| 141 | + if len(apikey) != 40: |
| 142 | + raise SystemExit("ERROR: invalid github api token from `git config " |
| 143 | + "--global github.token`: '%s'" % apikey) |
| 144 | + return apikey |
| 145 | + |
| 146 | +if __name__ == "__main__": |
| 147 | + args = parse_args(sys.argv[1:]) |
| 148 | + if args.verbose > 1: |
| 149 | + logger.setLevel(logging.DEBUG) |
| 150 | + elif args.verbose > 0: |
| 151 | + logger.setLevel(logging.INFO) |
| 152 | + try: |
| 153 | + token = os.environ['GITHUB_TOKEN'] |
| 154 | + logger.debug("Using API token from GITHUB_TOKEN environment variable") |
| 155 | + except KeyError: |
| 156 | + token = get_api_token() |
| 157 | + logger.debug("Using API token from git config 'github.token'") |
| 158 | + script = GitHubIRCHooker( |
| 159 | + token, |
| 160 | + args.server, |
| 161 | + args.port, |
| 162 | + args.nick, |
| 163 | + args.password, |
| 164 | + ) |
| 165 | + script.run(args.orgname, args.reponame, args.channel, args.branches) |
0 commit comments