Mike Frysinger | f1ba7ad | 2022-09-12 05:42:57 -0400 | [diff] [blame] | 1 | # Copyright 2017 The ChromiumOS Authors |
Steven Bennetts | ddf9bcd | 2017-06-14 14:07:43 -0700 | [diff] [blame] | 2 | # Use of this source code is governed by a BSD-style license that can be |
| 3 | # found in the LICENSE file. |
| 4 | |
Ben Pastene | 6a8361a | 2021-08-19 19:52:05 -0700 | [diff] [blame] | 5 | """Update the CHROMEOS_LKGM file in a chromium repository. |
| 6 | |
Jack Neus | 51d2e34 | 2022-09-14 16:40:08 +0000 | [diff] [blame] | 7 | This script will upload an LKGM CL and potentially submit it to the CQ. |
Ben Pastene | 6a8361a | 2021-08-19 19:52:05 -0700 | [diff] [blame] | 8 | """ |
Steven Bennetts | ddf9bcd | 2017-06-14 14:07:43 -0700 | [diff] [blame] | 9 | |
Chris McDonald | 59650c3 | 2021-07-20 15:29:28 -0600 | [diff] [blame] | 10 | import logging |
Steven Bennetts | ddf9bcd | 2017-06-14 14:07:43 -0700 | [diff] [blame] | 11 | |
Ram Chandrasekar | 60f69f3 | 2022-06-03 22:49:30 +0000 | [diff] [blame] | 12 | from chromite.lib import chromeos_version |
Achuith Bhandarkar | f561c15 | 2018-08-27 19:05:39 -0700 | [diff] [blame] | 13 | from chromite.lib import commandline |
Steven Bennetts | ddf9bcd | 2017-06-14 14:07:43 -0700 | [diff] [blame] | 14 | from chromite.lib import constants |
Ben Pastene | 3ae39bd | 2022-01-04 15:20:05 -0800 | [diff] [blame] | 15 | from chromite.lib import cros_build_lib |
Ben Pastene | 5a40b10 | 2019-11-22 17:06:31 -0800 | [diff] [blame] | 16 | from chromite.lib import gerrit |
Ben Pastene | 769b121 | 2021-12-14 18:26:50 -0800 | [diff] [blame] | 17 | from chromite.lib import gob_util |
Steven Bennetts | ddf9bcd | 2017-06-14 14:07:43 -0700 | [diff] [blame] | 18 | |
| 19 | |
Ben Pastene | 3ae39bd | 2022-01-04 15:20:05 -0800 | [diff] [blame] | 20 | class LKGMNotValid(Exception): |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 21 | """Raised if the LKGM version is unset or not newer than the current value.""" |
Steven Bennetts | ddf9bcd | 2017-06-14 14:07:43 -0700 | [diff] [blame] | 22 | |
| 23 | |
Ben Pastene | 3ae39bd | 2022-01-04 15:20:05 -0800 | [diff] [blame] | 24 | class LKGMFileNotFound(Exception): |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 25 | """Raised if the LKGM file is not found.""" |
Steven Bennetts | ddf9bcd | 2017-06-14 14:07:43 -0700 | [diff] [blame] | 26 | |
| 27 | |
Achuith Bhandarkar | 1b9180f | 2018-02-22 19:12:09 -0800 | [diff] [blame] | 28 | class ChromeLKGMCommitter(object): |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 29 | """Committer object responsible for obtaining a new LKGM and committing it.""" |
Steven Bennetts | ddf9bcd | 2017-06-14 14:07:43 -0700 | [diff] [blame] | 30 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 31 | # The list of trybots we require LKGM updates to run and pass on before |
| 32 | # landing. Since they're internal trybots, the CQ won't automatically trigger |
| 33 | # them, so we have to explicitly tell it to. |
| 34 | _PRESUBMIT_BOTS = [ |
| 35 | "chromeos-betty-pi-arc-chrome", |
| 36 | "chromeos-eve-chrome", |
| 37 | "chromeos-kevin-chrome", |
| 38 | "chromeos-octopus-chrome", |
| 39 | "chromeos-reven-chrome", |
| 40 | "lacros-amd64-generic-chrome", |
| 41 | ] |
| 42 | # Files needed in a local checkout to successfully update the LKGM. The OWNERS |
| 43 | # file allows the --tbr-owners mechanism to select an appropriate OWNER to |
| 44 | # TBR. TRANSLATION_OWNERS is necesssary to parse CHROMEOS_OWNERS file since |
| 45 | # it has the reference. |
| 46 | _NEEDED_FILES = [ |
| 47 | constants.PATH_TO_CHROME_CHROMEOS_OWNERS, |
| 48 | constants.PATH_TO_CHROME_LKGM, |
| 49 | "tools/translation/TRANSLATION_OWNERS", |
| 50 | ] |
| 51 | # First line of the commit message for all LKGM CLs. |
| 52 | _COMMIT_MSG_HEADER = "Automated Commit: LKGM %(lkgm)s for chromeos." |
Steven Bennetts | ddf9bcd | 2017-06-14 14:07:43 -0700 | [diff] [blame] | 53 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 54 | def __init__(self, lkgm, branch, dryrun=False, buildbucket_id=None): |
| 55 | self._dryrun = dryrun |
| 56 | self._branch = branch |
| 57 | self._buildbucket_id = buildbucket_id |
| 58 | self._gerrit_helper = gerrit.GetCrosExternal() |
Ben Pastene | 3ae39bd | 2022-01-04 15:20:05 -0800 | [diff] [blame] | 59 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 60 | # We need to use the account used by the builder to upload git CLs when |
| 61 | # generating CLs. |
| 62 | self._user_email = None |
| 63 | if cros_build_lib.HostIsCIBuilder(golo_only=True): |
| 64 | self._user_email = "chromeos-commit-bot@chromium.org" |
| 65 | elif cros_build_lib.HostIsCIBuilder(gce_only=True): |
| 66 | self._user_email = "3su6n15k.default@developer.gserviceaccount.com" |
Achuith Bhandarkar | ec8d7a7 | 2018-03-01 16:56:22 -0800 | [diff] [blame] | 67 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 68 | # Strip any chrome branch from the lkgm version. |
| 69 | self._lkgm = chromeos_version.VersionInfo(lkgm).VersionString() |
| 70 | self._commit_msg_header = self._COMMIT_MSG_HEADER % {"lkgm": self._lkgm} |
Yoshiki Iguchi | f8ab011 | 2022-09-15 16:57:26 +0900 | [diff] [blame] | 71 | self._current_lkgm = None |
Steven Bennetts | ddf9bcd | 2017-06-14 14:07:43 -0700 | [diff] [blame] | 72 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 73 | if not self._lkgm: |
| 74 | raise LKGMNotValid("LKGM not provided.") |
| 75 | logging.info("lkgm=%s", lkgm) |
Achuith Bhandarkar | dc3a351 | 2018-03-16 15:10:57 -0700 | [diff] [blame] | 76 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 77 | def Run(self): |
Yoshiki Iguchi | f8ab011 | 2022-09-15 16:57:26 +0900 | [diff] [blame] | 78 | self.AbandonObsoleteLKGMRolls() |
Jack Neus | 624c8a5 | 2022-09-13 17:33:03 +0000 | [diff] [blame] | 79 | self.UpdateLKGM() |
Achuith Bhandarkar | dc3a351 | 2018-03-16 15:10:57 -0700 | [diff] [blame] | 80 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 81 | @property |
| 82 | def lkgm_file(self): |
| 83 | return self._committer.FullPath(constants.PATH_TO_CHROME_LKGM) |
Achuith Bhandarkar | dc3a351 | 2018-03-16 15:10:57 -0700 | [diff] [blame] | 84 | |
Yoshiki Iguchi | f8ab011 | 2022-09-15 16:57:26 +0900 | [diff] [blame] | 85 | def AbandonObsoleteLKGMRolls(self): |
| 86 | """Abandon all obsolete LKGM roll CLs. |
Ben Pastene | 5a40b10 | 2019-11-22 17:06:31 -0800 | [diff] [blame] | 87 | |
Yoshiki Iguchi | f8ab011 | 2022-09-15 16:57:26 +0900 | [diff] [blame] | 88 | This method finds the LKGM roll CLs that were trying changing to an |
| 89 | older version than the current LKGM version, and abandons them. |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 90 | """ |
| 91 | query_params = { |
| 92 | "project": constants.CHROMIUM_SRC_PROJECT, |
| 93 | "branch": self._branch, |
| 94 | "file": constants.PATH_TO_CHROME_LKGM, |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 95 | "status": "open", |
| 96 | # Use 'owner' rather than 'uploader' or 'author' since those last two |
| 97 | # can be overwritten when the gardener resolves a merge-conflict and |
| 98 | # uploads a new patchset. |
| 99 | "owner": self._user_email, |
| 100 | } |
Yoshiki Iguchi | f8ab011 | 2022-09-15 16:57:26 +0900 | [diff] [blame] | 101 | open_changes = self._gerrit_helper.Query(**query_params) |
| 102 | if not open_changes: |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 103 | logging.info("No old LKGM rolls detected.") |
| 104 | return |
Yoshiki Iguchi | f8ab011 | 2022-09-15 16:57:26 +0900 | [diff] [blame] | 105 | |
| 106 | logging.info( |
| 107 | "Retrieved the current LKGM version: %s", |
| 108 | self.GetCurrentLKGM().VersionString(), |
| 109 | ) |
| 110 | |
| 111 | build_link = "" |
| 112 | if self._buildbucket_id: |
| 113 | build_link = ( |
| 114 | "\nUpdated by" |
| 115 | f" https://ci.chromium.org/b/{self._buildbucket_id}\n" |
| 116 | ) |
| 117 | |
| 118 | for change in open_changes: |
| 119 | logging.info( |
| 120 | "Found a open LKGM roll CL: %s (crrev.com/c/%s).", |
| 121 | change.subject, |
| 122 | change.gerrit_number, |
| 123 | ) |
| 124 | version_string = change.GetFileContents( |
| 125 | constants.PATH_TO_CHROME_LKGM |
| 126 | ) |
| 127 | if version_string is None: |
| 128 | logging.info("=> No LKGM change found in this CL.") |
| 129 | continue |
| 130 | |
| 131 | version = chromeos_version.VersionInfo(version_string) |
| 132 | if version <= self.GetCurrentLKGM(): |
| 133 | # The target version that the CL is changing to is older than |
| 134 | # the current. The roll CL is useless so that it'd be abandoned. |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 135 | logging.info( |
Yoshiki Iguchi | f8ab011 | 2022-09-15 16:57:26 +0900 | [diff] [blame] | 136 | "=> This CL is an older LKGM roll than current: Abandoning" |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 137 | ) |
Yoshiki Iguchi | f8ab011 | 2022-09-15 16:57:26 +0900 | [diff] [blame] | 138 | if not self._dryrun: |
| 139 | abandon_message = ( |
| 140 | "The newer LKGM" |
| 141 | f" ({self.GetCurrentLKGM().VersionString()}) roll than" |
| 142 | f" this CL has been landed.{build_link}" |
| 143 | ) |
| 144 | self._gerrit_helper.AbandonChange( |
| 145 | change, |
| 146 | msg=abandon_message, |
| 147 | ) |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 148 | else: |
Yoshiki Iguchi | f8ab011 | 2022-09-15 16:57:26 +0900 | [diff] [blame] | 149 | # Do nothing with the roll CLs that tries changing to newer |
| 150 | # version. |
| 151 | # TODO(yoshiki): rebase the roll CL. |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 152 | logging.info( |
Yoshiki Iguchi | f8ab011 | 2022-09-15 16:57:26 +0900 | [diff] [blame] | 153 | "=> This CL is a newer LKGM roll than current: Do nothing." |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 154 | ) |
Yoshiki Iguchi | f8ab011 | 2022-09-15 16:57:26 +0900 | [diff] [blame] | 155 | |
| 156 | def GetCurrentLKGM(self): |
| 157 | """Returns the current LKGM version on the branch. |
| 158 | |
| 159 | On the first call, this method retrieves the LKGM version from Gltlies |
| 160 | server and returns it. On subsequent calls, this method returns the |
| 161 | cached LKGM version. |
| 162 | |
| 163 | Raises: |
| 164 | LKGMNotValid: if the retrieved LKGM version from the repository is |
| 165 | invalid. |
| 166 | """ |
| 167 | if self._current_lkgm is not None: |
| 168 | return self._current_lkgm |
| 169 | |
| 170 | current_lkgm = gob_util.GetFileContents( |
| 171 | constants.CHROMIUM_GOB_URL, |
| 172 | constants.PATH_TO_CHROME_LKGM, |
| 173 | ref=self._branch, |
| 174 | ) |
| 175 | if current_lkgm is None: |
| 176 | raise LKGMNotValid( |
| 177 | "The retrieved LKGM version from the repository is invalid:" |
| 178 | f" {self._current_lkgm}." |
| 179 | ) |
| 180 | |
| 181 | self._current_lkgm = chromeos_version.VersionInfo(current_lkgm.strip()) |
| 182 | return self._current_lkgm |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 183 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 184 | def UpdateLKGM(self): |
| 185 | """Updates the LKGM file with the new version.""" |
Yoshiki Iguchi | f8ab011 | 2022-09-15 16:57:26 +0900 | [diff] [blame] | 186 | if chromeos_version.VersionInfo(self._lkgm) <= self.GetCurrentLKGM(): |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 187 | raise LKGMNotValid( |
Yoshiki Iguchi | f8ab011 | 2022-09-15 16:57:26 +0900 | [diff] [blame] | 188 | f"LKGM version ({self._lkgm}) is not newer than current version" |
| 189 | f" ({self.GetCurrentLKGM().VersionString()})." |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 190 | ) |
| 191 | |
Ben Pastene | 6a8361a | 2021-08-19 19:52:05 -0700 | [diff] [blame] | 192 | logging.info( |
Yoshiki Iguchi | f8ab011 | 2022-09-15 16:57:26 +0900 | [diff] [blame] | 193 | "Updating LKGM version: %s (was %s),", |
| 194 | self._lkgm, |
| 195 | self.GetCurrentLKGM().VersionString(), |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 196 | ) |
| 197 | change = self._gerrit_helper.CreateChange( |
| 198 | "chromium/src", self._branch, self.ComposeCommitMsg(), False |
| 199 | ) |
| 200 | self._gerrit_helper.ChangeEdit( |
| 201 | change.gerrit_number, "chromeos/CHROMEOS_LKGM", self._lkgm |
| 202 | ) |
Ben Pastene | 6a8361a | 2021-08-19 19:52:05 -0700 | [diff] [blame] | 203 | |
Jack Neus | 624c8a5 | 2022-09-13 17:33:03 +0000 | [diff] [blame] | 204 | if self._dryrun: |
| 205 | logging.info( |
| 206 | "Would have applied CQ+2 to crrev.com/c/%s", |
| 207 | change.gerrit_number, |
| 208 | ) |
Jack Neus | 4b03d3c | 2022-09-16 20:21:17 +0000 | [diff] [blame] | 209 | self._gerrit_helper.AbandonChange( |
| 210 | change, |
| 211 | msg="Dry run", |
| 212 | ) |
Yoshiki Iguchi | f430084 | 2022-09-14 12:14:03 +0900 | [diff] [blame] | 213 | return |
| 214 | |
| 215 | labels = { |
| 216 | "Bot-Commit": 1, |
| 217 | "Commit-Queue": 2, |
| 218 | } |
Jack Neus | 624c8a5 | 2022-09-13 17:33:03 +0000 | [diff] [blame] | 219 | logging.info( |
| 220 | "Applying %s to crrev.com/c/%s", labels, change.gerrit_number |
| 221 | ) |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 222 | self._gerrit_helper.SetReview( |
Yoshiki Iguchi | dad2425 | 2022-09-22 14:21:30 +0900 | [diff] [blame^] | 223 | change.gerrit_number, |
| 224 | labels=labels, |
| 225 | notify="NONE", |
| 226 | ready=True, |
| 227 | reviewers=[constants.CHROME_GARDENER_REVIEW_EMAIL], |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 228 | ) |
| 229 | self._gerrit_helper.SetHashtags( |
| 230 | change.gerrit_number, ["chrome-lkgm"], [] |
| 231 | ) |
Ben Pastene | 6a8361a | 2021-08-19 19:52:05 -0700 | [diff] [blame] | 232 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 233 | def ComposeCommitMsg(self): |
| 234 | """Constructs and returns the commit message for the LKGM update.""" |
Jack Neus | 51d2e34 | 2022-09-14 16:40:08 +0000 | [diff] [blame] | 235 | dry_run_message = ( |
| 236 | "This CL was created during a dry run and is not " |
| 237 | "intended to be committed.\n" |
| 238 | ) |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 239 | commit_msg_template = ( |
Jack Neus | 51d2e34 | 2022-09-14 16:40:08 +0000 | [diff] [blame] | 240 | "%(header)s\n%(build_link)s\n%(message)s\n%(cq_includes)s" |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 241 | ) |
| 242 | cq_includes = "" |
| 243 | for bot in self._PRESUBMIT_BOTS: |
| 244 | cq_includes += "CQ_INCLUDE_TRYBOTS=luci.chrome.try:%s\n" % bot |
| 245 | build_link = "" |
| 246 | if self._buildbucket_id: |
| 247 | build_link = "\nUploaded by https://ci.chromium.org/b/%s\n" % ( |
| 248 | self._buildbucket_id |
| 249 | ) |
| 250 | return commit_msg_template % dict( |
| 251 | header=self._commit_msg_header, |
| 252 | cq_includes=cq_includes, |
| 253 | build_link=build_link, |
Jack Neus | 51d2e34 | 2022-09-14 16:40:08 +0000 | [diff] [blame] | 254 | message=dry_run_message if self._dryrun else "", |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 255 | ) |
Ben Pastene | d3b93d4 | 2019-10-10 09:56:29 -0700 | [diff] [blame] | 256 | |
Achuith Bhandarkar | 1b9180f | 2018-02-22 19:12:09 -0800 | [diff] [blame] | 257 | |
Achuith Bhandarkar | b6f4025 | 2019-12-09 16:31:34 -0800 | [diff] [blame] | 258 | def GetOpts(argv): |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 259 | """Returns a dictionary of parsed options. |
Achuith Bhandarkar | 1b9180f | 2018-02-22 19:12:09 -0800 | [diff] [blame] | 260 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 261 | Args: |
| 262 | argv: raw command line. |
Achuith Bhandarkar | dc3a351 | 2018-03-16 15:10:57 -0700 | [diff] [blame] | 263 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 264 | Returns: |
| 265 | Dictionary of parsed options. |
| 266 | """ |
| 267 | parser = commandline.ArgumentParser(description=__doc__, add_help=False) |
| 268 | parser.add_argument( |
| 269 | "--dryrun", |
| 270 | action="store_true", |
| 271 | default=False, |
| 272 | help="Don't commit changes or send out emails.", |
| 273 | ) |
| 274 | parser.add_argument( |
| 275 | "--lkgm", required=True, help="LKGM version to update to." |
| 276 | ) |
| 277 | parser.add_argument( |
| 278 | "--buildbucket-id", |
| 279 | help="Buildbucket ID of the build that ran this script. " |
| 280 | "Will be linked in the commit message if specified.", |
| 281 | ) |
| 282 | parser.add_argument( |
| 283 | "--branch", |
| 284 | default="main", |
| 285 | help="Branch to upload change to, e.g. " |
| 286 | "refs/branch-heads/5112. Defaults to main.", |
| 287 | ) |
| 288 | return parser.parse_args(argv) |
Steven Bennetts | ddf9bcd | 2017-06-14 14:07:43 -0700 | [diff] [blame] | 289 | |
Ben Pastene | 3ae39bd | 2022-01-04 15:20:05 -0800 | [diff] [blame] | 290 | |
Steven Bennetts | ddf9bcd | 2017-06-14 14:07:43 -0700 | [diff] [blame] | 291 | def main(argv): |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 292 | opts = GetOpts(argv) |
| 293 | committer = ChromeLKGMCommitter( |
| 294 | opts.lkgm, opts.branch, opts.dryrun, opts.buildbucket_id |
| 295 | ) |
| 296 | committer.Run() |
| 297 | return 0 |