gerrit: move the download-gerrit-topic.py script from internal Change-Id: I6441d5250b261f11e873c88c6a73d7aaec188844
diff --git a/download-gerrit-topic.py b/download-gerrit-topic.py new file mode 100755 index 0000000..52596a3 --- /dev/null +++ b/download-gerrit-topic.py
@@ -0,0 +1,122 @@ +#!/usr/bin/env python3 +"""Download from gerrit all the changes of the specified topic. The new +branches will be called TOPIC. If a project has multiple changes, they must +all form a parent-child chain, and only the last child will be downloaded. +""" + +import argparse +import http +import logging +import subprocess +import sys +import tempfile + +from louhi.common import gerrit +from louhi.common.utils import run + +logger = logging.getLogger() +logger.setLevel(logging.INFO) + + +def get_parser(): + """Constract the command line parser.""" + parser = argparse.ArgumentParser(description=__doc__) + + parser.add_argument('-b', + '--branch', + type=str, + help='branch name to use, instead of TOPIC.') + + parser.add_argument('--force', + action='store_true', + help='delete the branch if it already exists.') + + parser.add_argument( + '-s', + '--status', + type=str, + choices=[ + 'abandoned', 'closed', 'merged', 'new', 'pending', 'reviewed', + 'open' + ], + default='open', + help='(default: open) limit to changes with matching status; ' + 'see also --no-status.') + + parser.add_argument( + '--no-status', + dest="status", + action='store_const', + const=None, + help='don\'t limit to changes by status; see also --status.') + + parser.add_argument('--repo-path', + metavar='PATH', + type=str, + default='repo', + help='(default: repo) path to the repo executable.') + + parser.add_argument('topic', metavar='TOPIC') + + return parser + + +def main(): + """The main function.""" + args = get_parser().parse_args() + + branch = args.branch if args.branch else args.topic + + try: + git_cookie_file = run(['git', 'config', 'http.cookieFile'], + verbose=False).read().rstrip() + except subprocess.CalledProcessError: + logging.error('The git option http.cookieFile is not set.') + return 1 + + cookies = http.cookiejar.MozillaCookieJar() + with tempfile.NamedTemporaryFile('w', encoding='ascii') as temp_file: + # NB: MozillaCookieJar requires the file to start with the + # following line, so we add it in a temp file. + temp_file.write('# Netscape HTTP Cookie File\n') + with open(git_cookie_file, 'r', encoding='ascii') as cookie_file: + temp_file.write(cookie_file.read()) + temp_file.flush() + cookies.load(temp_file.name) + + changes = gerrit.query_topic(args.topic, + args.status, + access_cookie=cookies) + if not changes: + logging.error('No changes to download.') + return 1 + + branch_exists = run([args.repo_path, 'forall'] + list(changes) + [ + '--command', f'! git rev-parse --verify "{branch}" &>/dev/null ' + '|| echo "$REPO_PROJECT"' + ], + verbose=False).read().split('\n')[:-1] + if branch_exists: + if not args.force: + logging.error( + 'The branch %s alread exists in %s (use --force to ' + 'overwrite it.)', branch, branch_exists) + return 1 + + logging.info('Deleting branch %s from %s', branch, branch_exists) + run([args.repo_path, 'forall'] + branch_exists + [ + '--command', + f'if git rev-parse --verify "{branch}" &>/dev/null; then' + ' git checkout --detach HEAD;' + f' git branch --delete "{branch}" --force;' + 'fi' + ]) + + logging.info('Downloading changes: %s', dict(changes)) + run([args.repo_path, 'download', f'--branch={branch}', '--verbose'] + + [str(part) for change in changes.items() for part in change]) + return 0 + + +if __name__ == '__main__': + sys.exit(main())