1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# Copyright 2020 The Chromium OS Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7"""Git helper functions.""" 8 9from __future__ import print_function 10 11import collections 12import os 13import re 14import subprocess 15import tempfile 16 17CommitContents = collections.namedtuple('CommitContents', ['url', 'cl_number']) 18 19 20def InChroot(): 21 """Returns True if currently in the chroot.""" 22 return 'CROS_WORKON_SRCROOT' in os.environ 23 24 25def VerifyOutsideChroot(): 26 """Checks whether the script invoked was executed in the chroot. 27 28 Raises: 29 AssertionError: The script was run inside the chroot. 30 """ 31 32 assert not InChroot(), 'Script should be run outside the chroot.' 33 34 35def CreateBranch(repo, branch): 36 """Creates a branch in the given repo. 37 38 Args: 39 repo: The absolute path to the repo. 40 branch: The name of the branch to create. 41 42 Raises: 43 ValueError: Failed to create a repo in that directory. 44 """ 45 46 if not os.path.isdir(repo): 47 raise ValueError('Invalid directory path provided: %s' % repo) 48 49 subprocess.check_output(['git', '-C', repo, 'reset', 'HEAD', '--hard']) 50 51 subprocess.check_output(['repo', 'start', branch], cwd=repo) 52 53 54def DeleteBranch(repo, branch): 55 """Deletes a branch in the given repo. 56 57 Args: 58 repo: The absolute path of the repo. 59 branch: The name of the branch to delete. 60 61 Raises: 62 ValueError: Failed to delete the repo in that directory. 63 """ 64 65 if not os.path.isdir(repo): 66 raise ValueError('Invalid directory path provided: %s' % repo) 67 68 subprocess.check_output(['git', '-C', repo, 'checkout', 'cros/main']) 69 70 subprocess.check_output(['git', '-C', repo, 'reset', 'HEAD', '--hard']) 71 72 subprocess.check_output(['git', '-C', repo, 'branch', '-D', branch]) 73 74 75def UploadChanges(repo, branch, commit_messages, reviewers=None, cc=None): 76 """Uploads the changes in the specifed branch of the given repo for review. 77 78 Args: 79 repo: The absolute path to the repo where changes were made. 80 branch: The name of the branch to upload. 81 commit_messages: A string of commit message(s) (i.e. '[message]' 82 of the changes made. 83 reviewers: A list of reviewers to add to the CL. 84 cc: A list of contributors to CC about the CL. 85 86 Returns: 87 A nametuple that has two (key, value) pairs, where the first pair is the 88 Gerrit commit URL and the second pair is the change list number. 89 90 Raises: 91 ValueError: Failed to create a commit or failed to upload the 92 changes for review. 93 """ 94 95 if not os.path.isdir(repo): 96 raise ValueError('Invalid path provided: %s' % repo) 97 98 # Create a git commit. 99 with tempfile.NamedTemporaryFile(mode='w+t') as f: 100 f.write('\n'.join(commit_messages)) 101 f.flush() 102 103 subprocess.check_output(['git', 'commit', '-F', f.name], cwd=repo) 104 105 # Upload the changes for review. 106 git_args = [ 107 'repo', 108 'upload', 109 '--yes', 110 f'--reviewers={",".join(reviewers)}' if reviewers else '--ne', 111 '--no-verify', 112 f'--br={branch}', 113 ] 114 115 if cc: 116 git_args.append(f'--cc={",".join(cc)}') 117 118 out = subprocess.check_output( 119 git_args, 120 stderr=subprocess.STDOUT, 121 cwd=repo, 122 encoding='utf-8', 123 ) 124 125 print(out) 126 127 found_url = re.search( 128 r'https://chromium-review.googlesource.com/c/' 129 r'chromiumos/overlays/chromiumos-overlay/\+/([0-9]+)', out.rstrip()) 130 131 if not found_url: 132 raise ValueError('Failed to find change list URL.') 133 134 return CommitContents( 135 url=found_url.group(0), cl_number=int(found_url.group(1))) 136