1# Copyright 2018 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5"""Constants and util methods to interact with skylab inventory repo.""" 6 7import logging 8import re 9 10import common 11 12from autotest_lib.client.common_lib import revision_control 13from chromite.lib import gob_util 14 15try: 16 from skylab_inventory import text_manager 17except ImportError: 18 pass 19 20 21INTERNAL_GERRIT_HOST = 'chrome-internal-review.googlesource.com' 22INTERNAL_GERRIT_HOST_URL = 'https://%s' % INTERNAL_GERRIT_HOST 23# The git url of the internal skylab_inventory 24INTERNAL_INVENTORY_REPO_URL = ('https://chrome-internal.googlesource.com/' 25 'chromeos/infra_internal/skylab_inventory.git') 26INTERNAL_INVENTORY_CHANGE_PATTERN = ( 27 r'https://chrome-internal-review.googlesource.com/c/chromeos/' 28 'infra_internal/skylab_inventory/\\+/([0-9]*)') 29MSG_INVALID_IN_SKYLAB = 'This is currently not supported with --skylab.' 30MSG_ONLY_VALID_IN_SKYLAB = 'This only applies to actions on skylab inventory.' 31 32 33class SkylabInventoryNotImported(Exception): 34 """skylab_inventory is not imported.""" 35 36 37class InventoryRepoChangeNotFound(Exception): 38 """Error raised when no inventory repo change number is found.""" 39 40 41class InventoryRepoDirNotClean(Exception): 42 """Error raised when the given inventory_repo_dir contains local changes.""" 43 44 45def get_cl_url(change_number): 46 return INTERNAL_GERRIT_HOST_URL + '/' + str(change_number) 47 48 49def get_cl_message(change_number): 50 return ('Please submit the CL at %s to make the change effective.' % 51 get_cl_url(change_number)) 52 53 54def construct_commit_message(subject, bug=None, test=None): 55 """Construct commit message for skylab inventory repo commit. 56 57 @param subject: Commit message subject. 58 @param bug: Bug number of the commit. 59 @param test: Tests of the commit. 60 61 @return: A commit message string. 62 """ 63 return '\n'.join([subject, '', 'BUG=%s' % bug, 'TEST=%s' % test]) 64 65 66def extract_inventory_change(output): 67 """Extract the change number from the output. 68 69 @param output: The git command output containing the change gerrit url. 70 71 @return: The change number (int) of the inventory change. 72 """ 73 m = re.search(INTERNAL_INVENTORY_CHANGE_PATTERN, output) 74 75 if not m: 76 raise InventoryRepoChangeNotFound( 77 'Could not extract CL number from "%r"' % output) 78 79 return int(m.group(1)) 80 81 82def submit_inventory_change(change_number): 83 """Set review labels and submit the inventory change. 84 85 @param change_number: The change number (int) of the inventory change. 86 """ 87 logging.info('Setting review labels for %s.', 88 get_cl_url(change_number)) 89 gob_util.SetReview( 90 INTERNAL_GERRIT_HOST, 91 change=change_number, 92 labels={'Code-Review': 2, 'Verified': 1}, 93 msg='Set TBR by "atest --skylab"', 94 notify='OWNER') 95 96 logging.info('Submitting the change.') 97 gob_util.SubmitChange( 98 INTERNAL_GERRIT_HOST, 99 change=change_number) 100 101 102class InventoryRepo(object): 103 """Class to present a inventory repository.""" 104 105 106 def __init__(self, inventory_repo_dir): 107 self.inventory_repo_dir = inventory_repo_dir 108 self.git_repo = None 109 110 111 def initialize(self): 112 """Initialize inventory repo at the given dir.""" 113 self.git_repo = revision_control.GitRepo( 114 self.inventory_repo_dir, 115 giturl=INTERNAL_INVENTORY_REPO_URL, 116 abs_work_tree=self.inventory_repo_dir) 117 118 if self.git_repo.is_repo_initialized(): 119 if self.git_repo.status(): 120 raise InventoryRepoDirNotClean( 121 'The inventory_repo_dir "%s" contains uncommitted ' 122 'changes. Please clean up the local repo directory or ' 123 'use another clean directory.' % self.inventory_repo_dir) 124 125 logging.info('Inventory repo was already initialized, start ' 126 'pulling.') 127 self.git_repo.checkout('master') 128 self.git_repo.pull() 129 else: 130 logging.info('No inventory repo was found, start cloning.') 131 self.git_repo.clone(shallow=True) 132 133 134 def get_data_dir(self, data_subdir='skylab'): 135 """Get path to the data dir.""" 136 return text_manager.get_data_dir(self.inventory_repo_dir, data_subdir) 137 138 139 def upload_change(self, commit_message, draft=False, dryrun=False, 140 submit=False): 141 """Commit and upload the change to gerrit. 142 143 @param commit_message: Commit message of the CL to upload. 144 @param draft: Boolean indicating whether to upload the CL as a draft. 145 @param dryrun: Boolean indicating whether to run upload as a dryrun. 146 @param submit: Boolean indicating whether to submit the CL directly. 147 148 @return: Change number (int) of the CL if it's uploaded to Gerrit. 149 """ 150 self.git_repo.commit(commit_message) 151 152 remote = self.git_repo.remote() 153 output = self.git_repo.upload_cl( 154 remote, 'master', draft=draft, dryrun=dryrun) 155 156 if not dryrun: 157 change_number = extract_inventory_change(output) 158 159 if submit: 160 submit_inventory_change(change_number) 161 162 return change_number 163