|
| 1 | +#!/usr/bin/env python |
| 2 | +# vim:ts=4:sts=4:sw=4:et |
| 3 | +# |
| 4 | +# Author: Hari Sekhon |
| 5 | +# Date: 2016-07-21 16:19:19 +0100 (Thu, 21 Jul 2016) |
| 6 | +# |
| 7 | +# https://github.com/harisekhon/pytools |
| 8 | +# |
| 9 | +# License: see accompanying Hari Sekhon LICENSE file |
| 10 | +# |
| 11 | +# If you're using my code you're welcome to connect with me on LinkedIn |
| 12 | +# and optionally send me feedback to help steer this or other code I publish |
| 13 | +# |
| 14 | +# https://www.linkedin.com/in/harisekhon |
| 15 | +# |
| 16 | + |
| 17 | +""" |
| 18 | +
|
| 19 | +Tool to check Git branches have their upstream set consistently |
| 20 | +
|
| 21 | +Mainly written for my https://github.com/harisekhon/Dockerfiles repo |
| 22 | +which has over 100 branches which get merged, pulled and pushed around |
| 23 | +
|
| 24 | +""" |
| 25 | + |
| 26 | +from __future__ import absolute_import |
| 27 | +from __future__ import division |
| 28 | +from __future__ import print_function |
| 29 | +#from __future__ import unicode_literals |
| 30 | + |
| 31 | +import os |
| 32 | +import re |
| 33 | +import sys |
| 34 | +import traceback |
| 35 | +import git |
| 36 | +srcdir = os.path.abspath(os.path.dirname(__file__)) |
| 37 | +libdir = os.path.join(srcdir, 'pylib') |
| 38 | +sys.path.append(libdir) |
| 39 | +try: |
| 40 | + # pylint: disable=wrong-import-position |
| 41 | + from harisekhon.utils import die, ERRORS, log, log_option, uniq_list_ordered, validate_regex |
| 42 | + from harisekhon import CLI |
| 43 | +except ImportError as _: |
| 44 | + print(traceback.format_exc(), end='') |
| 45 | + sys.exit(4) |
| 46 | + |
| 47 | +__author__ = 'Hari Sekhon' |
| 48 | +__version__ = '0.1' |
| 49 | + |
| 50 | + |
| 51 | +class GitCheckBranchesUpstream(CLI): |
| 52 | + |
| 53 | + def __init__(self): |
| 54 | + # Python 2.x |
| 55 | + super(GitCheckBranchesUpstream, self).__init__() |
| 56 | + # Python 3.x |
| 57 | + # super().__init__() |
| 58 | + self.status = "OK" |
| 59 | + self.origin = None |
| 60 | + self.branch_prefix = None |
| 61 | + self.timeout_default = 86400 |
| 62 | + self.verbose_default = 2 |
| 63 | + |
| 64 | + def add_options(self): |
| 65 | + self.add_opt('-b', '--branch-prefix', help='Branch prefix regex to check') |
| 66 | + self.add_opt('-o', '--origin', help='Origin repo (default: origin)', default='origin') |
| 67 | + |
| 68 | + def run(self): |
| 69 | + if not self.args: |
| 70 | + self.usage('no git directory args given') |
| 71 | + self.origin = self.get_opt('origin') |
| 72 | + args = uniq_list_ordered(self.args) |
| 73 | + self.branch_prefix = self.get_opt('branch_prefix') |
| 74 | + if self.branch_prefix is not None: |
| 75 | + validate_regex(self.branch_prefix, 'branch prefix') |
| 76 | + self.branch_prefix = re.compile(self.branch_prefix) |
| 77 | + for arg in args: |
| 78 | + if not os.path.exists(arg): |
| 79 | + print("'%s' not found" % arg) |
| 80 | + sys.exit(ERRORS['WARNING']) |
| 81 | + if os.path.isfile(arg): |
| 82 | + log_option('file', arg) |
| 83 | + elif os.path.isdir(arg): |
| 84 | + log_option('directory', arg) |
| 85 | + else: |
| 86 | + die("path '%s' could not be determined as either a file or directory" % arg) |
| 87 | + for arg in args: |
| 88 | + self.check_git_branches_upstream(arg) |
| 89 | + if self.status == "OK": |
| 90 | + log.info('SUCCESS - All Git branches are tracking the expected upstream origin branches') |
| 91 | + else: |
| 92 | + log.critical('FAILED - Found Git branches not tracking the expected upstream origin branches') |
| 93 | + sys.exit(ERRORS['CRITICAL']) |
| 94 | + |
| 95 | + def check_git_branches_upstream(self, target): |
| 96 | + target = os.path.abspath(target) |
| 97 | + gitroot = self.find_git_root(target) |
| 98 | + if gitroot is None: |
| 99 | + die('Failed to find git root for target {0}'.format(target)) |
| 100 | + log.debug("finding branches for target '{0}'".format(target)) |
| 101 | + repo = git.Repo(gitroot) |
| 102 | + branches = repo.branches |
| 103 | + if self.branch_prefix is not None: |
| 104 | + log.debug('restricting to branches matching branch prefix') |
| 105 | + branches = [x for x in branches if self.branch_prefix.match(str(x))] |
| 106 | + #if log.isEnabledFor(logging.DEBUG): |
| 107 | + #log.debug('\n\nbranches for target %s:\n\n%s\n', target, '\n'.join(list(branches))) |
| 108 | + for branch in branches: |
| 109 | + expected = '{0}/{1}'.format(self.origin, branch) |
| 110 | + tracking_branch = str(branch.tracking_branch()) |
| 111 | + if tracking_branch == expected: |
| 112 | + log.info("OK: branch '{0}' is tracking '{1}'".format(branch, tracking_branch)) |
| 113 | + else: |
| 114 | + self.status = "ERROR" |
| 115 | + log.error("BAD: branch '{0}' is tracking '{1}' (expected '{2}')" |
| 116 | + .format(branch, tracking_branch, expected)) |
| 117 | + |
| 118 | + # move to pylib and add unit tests |
| 119 | + @staticmethod |
| 120 | + def find_git_root(target): |
| 121 | + target = os.path.abspath(target) |
| 122 | + log.debug("finding git root for target '{0}'".format(target)) |
| 123 | + gitroot = target |
| 124 | + while gitroot and gitroot != '/': |
| 125 | + log.debug("trying '{0}'".format(gitroot)) |
| 126 | + # os.path.isdir doesn't work on git submodule Dockerfiles in PyTools repo :-/ |
| 127 | + if os.path.exists(os.path.join(gitroot, '.git')): |
| 128 | + log.debug("found git root for target '{0}': '{1}'".format(target, gitroot)) |
| 129 | + return gitroot |
| 130 | + gitroot = os.path.dirname(gitroot) |
| 131 | + return None |
| 132 | + |
| 133 | + |
| 134 | +if __name__ == '__main__': |
| 135 | + GitCheckBranchesUpstream().main() |
0 commit comments