blob: 6ad0afe82a5537c8f86a54ed7ddf4715c6a0bcd6 [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2017 The ChromiumOS Authors
Steven Bennettsddf9bcd2017-06-14 14:07:43 -07002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Ben Pastene6a8361a2021-08-19 19:52:05 -07005"""Update the CHROMEOS_LKGM file in a chromium repository.
6
Jack Neus51d2e342022-09-14 16:40:08 +00007This script will upload an LKGM CL and potentially submit it to the CQ.
Ben Pastene6a8361a2021-08-19 19:52:05 -07008"""
Steven Bennettsddf9bcd2017-06-14 14:07:43 -07009
Chris McDonald59650c32021-07-20 15:29:28 -060010import logging
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +090011from typing import Optional
Steven Bennettsddf9bcd2017-06-14 14:07:43 -070012
Ram Chandrasekar60f69f32022-06-03 22:49:30 +000013from chromite.lib import chromeos_version
Achuith Bhandarkarf561c152018-08-27 19:05:39 -070014from chromite.lib import commandline
Steven Bennettsddf9bcd2017-06-14 14:07:43 -070015from chromite.lib import constants
Ben Pastene5a40b102019-11-22 17:06:31 -080016from chromite.lib import gerrit
Ben Pastene769b1212021-12-14 18:26:50 -080017from chromite.lib import gob_util
Anuj Jamwal258529f2023-09-08 17:32:55 +000018from chromite.utils import hostname_util
Steven Bennettsddf9bcd2017-06-14 14:07:43 -070019
20
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +090021# Gerrit hashtag for the LKGM Uprev CLs.
22HASHTAG = "chrome-lkgm"
23
24
Ben Pastene3ae39bd2022-01-04 15:20:05 -080025class LKGMNotValid(Exception):
Alex Klein9e7b29e2023-04-11 16:10:31 -060026 """The LKGM version is unset or not newer than the current value."""
Steven Bennettsddf9bcd2017-06-14 14:07:43 -070027
28
Ben Pastene3ae39bd2022-01-04 15:20:05 -080029class LKGMFileNotFound(Exception):
Alex Klein1699fab2022-09-08 08:46:06 -060030 """Raised if the LKGM file is not found."""
Steven Bennettsddf9bcd2017-06-14 14:07:43 -070031
32
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +090033class ChromeLKGMCleaner:
34 """Responsible for cleaning up the existing LKGM CLs if necessary.
Steven Bennettsddf9bcd2017-06-14 14:07:43 -070035
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +090036 In Particular, this class does:
Alex Klein9e7b29e2023-04-11 16:10:31 -060037 - abandoning the obsolete CLs
38 - rebasing the merge-conflicted CLs
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +090039 """
Steven Bennettsddf9bcd2017-06-14 14:07:43 -070040
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +090041 def __init__(
42 self,
43 branch: str,
44 current_lkgm: chromeos_version.VersionInfo,
45 user_email: str,
46 dryrun: bool = False,
47 buildbucket_id: Optional[str] = None,
48 ):
Alex Klein1699fab2022-09-08 08:46:06 -060049 self._dryrun = dryrun
50 self._branch = branch
Alex Klein1699fab2022-09-08 08:46:06 -060051 self._gerrit_helper = gerrit.GetCrosExternal()
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +090052 self._buildbucket_id = buildbucket_id
Ben Pastene3ae39bd2022-01-04 15:20:05 -080053
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +090054 self._user_email = user_email
Achuith Bhandarkarec8d7a72018-03-01 16:56:22 -080055
Alex Klein1699fab2022-09-08 08:46:06 -060056 # Strip any chrome branch from the lkgm version.
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +090057 self._current_lkgm = current_lkgm
Achuith Bhandarkardc3a3512018-03-16 15:10:57 -070058
Yoshiki Iguchiab2c4c22022-09-22 18:43:21 +090059 def ProcessObsoleteLKGMRolls(self):
60 """Clean up all obsolete LKGM roll CLs by abandoning or rebasing.
Ben Pastene5a40b102019-11-22 17:06:31 -080061
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +090062 This method finds the LKGM roll CLs that were trying changing to an
63 older version than the current LKGM version, and abandons them.
Alex Klein1699fab2022-09-08 08:46:06 -060064 """
65 query_params = {
66 "project": constants.CHROMIUM_SRC_PROJECT,
67 "branch": self._branch,
68 "file": constants.PATH_TO_CHROME_LKGM,
Alex Klein1699fab2022-09-08 08:46:06 -060069 "status": "open",
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +090070 "hashtag": HASHTAG,
71 # Use 'owner' rather than 'uploader' or 'author' since those last
72 # two can be overwritten when the gardener resolves a merge-conflict
73 # and uploads a new patchset.
Alex Klein1699fab2022-09-08 08:46:06 -060074 "owner": self._user_email,
75 }
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +090076 open_changes = self._gerrit_helper.Query(**query_params)
77 if not open_changes:
Alex Klein1699fab2022-09-08 08:46:06 -060078 logging.info("No old LKGM rolls detected.")
79 return
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +090080
81 logging.info(
82 "Retrieved the current LKGM version: %s",
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +090083 self._current_lkgm.VersionString(),
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +090084 )
85
86 build_link = ""
87 if self._buildbucket_id:
88 build_link = (
89 "\nUpdated by"
90 f" https://ci.chromium.org/b/{self._buildbucket_id}\n"
91 )
92
93 for change in open_changes:
94 logging.info(
95 "Found a open LKGM roll CL: %s (crrev.com/c/%s).",
96 change.subject,
97 change.gerrit_number,
98 )
Yoshiki Iguchiab2c4c22022-09-22 18:43:21 +090099
100 # Retrieve the version that this CL tries to roll to.
101 roll_to_string = change.GetFileContents(
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +0900102 constants.PATH_TO_CHROME_LKGM
103 )
Yoshiki Iguchiab2c4c22022-09-22 18:43:21 +0900104 if roll_to_string is None:
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +0900105 logging.info("=> No LKGM change found in this CL.")
106 continue
107
Yoshiki Iguchiab2c4c22022-09-22 18:43:21 +0900108 roll_to = chromeos_version.VersionInfo(roll_to_string)
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900109 if roll_to <= self._current_lkgm:
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +0900110 # The target version that the CL is changing to is older than
111 # the current. The roll CL is useless so that it'd be abandoned.
Alex Klein1699fab2022-09-08 08:46:06 -0600112 logging.info(
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +0900113 "=> This CL is an older LKGM roll than current: Abandoning"
Alex Klein1699fab2022-09-08 08:46:06 -0600114 )
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +0900115 if not self._dryrun:
116 abandon_message = (
117 "The newer LKGM"
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900118 f" ({self._current_lkgm.VersionString()}) roll than"
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +0900119 f" this CL has been landed.{build_link}"
120 )
121 self._gerrit_helper.AbandonChange(
122 change,
123 msg=abandon_message,
124 )
Yoshiki Iguchiab2c4c22022-09-22 18:43:21 +0900125 continue
126
127 mergeable = change.IsMergeable()
128 if mergeable is None:
129 logging.info("=> Failed to get the mergeable state of the CL.")
130 continue
131
132 # This CL may be in "merge conflict" state. Resolve.
133 if not mergeable:
134 # Retrieve the version that this CL tries to roll from.
135 roll_from_string = change.GetOriginalFileContents(
136 constants.PATH_TO_CHROME_LKGM
Alex Klein1699fab2022-09-08 08:46:06 -0600137 )
Yoshiki Iguchiab2c4c22022-09-22 18:43:21 +0900138 roll_from = chromeos_version.VersionInfo(
139 roll_from_string.strip()
140 )
141
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900142 if roll_from == self._current_lkgm:
Yoshiki Iguchiab2c4c22022-09-22 18:43:21 +0900143 # The CL should not be in the merge-conflict state.
144 # mergeable=False might come from other reason.
145 logging.info(
146 "=> This CL tries to roll from the same LKGM. "
147 "Doing nothing."
148 )
149 continue
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900150 elif roll_from >= self._current_lkgm:
Yoshiki Iguchiab2c4c22022-09-22 18:43:21 +0900151 # This should not happen.
152 logging.info(
153 "=> This CL tries to roll from a newer LKGM. Maybe"
154 "LKGM in Chromium code has been rolled back. Anyway, "
155 "rebasing forcibly."
156 )
157
158 else:
159 logging.info(
160 "=> This CL tries to roll from the older LKGM. "
161 "Rebasing."
162 )
163
164 # Resolve the conflict by rebasing.
165 if not self._dryrun:
166 change.Rebase(allow_conflicts=True)
167 self._gerrit_helper.ChangeEdit(
168 change.gerrit_number,
169 "chromeos/CHROMEOS_LKGM",
170 roll_to_string,
171 )
172 continue
173
174 logging.info("=> This CL is not in the merge-conflict state.")
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +0900175
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900176 def Run(self):
177 self.ProcessObsoleteLKGMRolls()
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +0900178
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +0900179
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900180class ChromeLKGMCommitter:
Alex Klein9e7b29e2023-04-11 16:10:31 -0600181 """Committer object responsible for obtaining and committing a new LKGM."""
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +0900182
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900183 # The list of trybots we require LKGM updates to run and pass on before
184 # landing. Since they're internal trybots, the CQ won't automatically
Ben Pastenec71e3e02023-07-11 17:52:57 -0700185 # trigger them, so we have to explicitly tell it to. If you add a new
186 # internal builder here, make sure it's also listed in
187 # https://source.chromium.org/chromium/chromium/src/+/main:infra/config/subprojects/chrome/try.star.
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900188 _PRESUBMIT_BOTS = (
189 "chromeos-betty-pi-arc-chrome",
190 "chromeos-eve-chrome",
Eric Lok637d9272023-07-07 00:57:20 +0000191 "chromeos-jacuzzi-chrome",
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900192 "chromeos-octopus-chrome",
193 "chromeos-reven-chrome",
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900194 )
195 # Files needed in a local checkout to successfully update the LKGM. The
196 # OWNERS file allows the --tbr-owners mechanism to select an appropriate
Alex Klein9e7b29e2023-04-11 16:10:31 -0600197 # OWNER to TBR. TRANSLATION_OWNERS is necessary to parse CHROMEOS_OWNERS
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900198 # file since it has the reference.
199 _NEEDED_FILES = (
200 constants.PATH_TO_CHROME_CHROMEOS_OWNERS,
201 constants.PATH_TO_CHROME_LKGM,
202 "tools/translation/TRANSLATION_OWNERS",
203 )
204 # First line of the commit message for all LKGM CLs.
205 _COMMIT_MSG_HEADER = "Automated Commit: LKGM %(lkgm)s for chromeos."
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +0900206
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900207 def __init__(
208 self,
209 lkgm: str,
210 branch: str,
211 current_lkgm: chromeos_version.VersionInfo,
212 dryrun: bool = False,
213 buildbucket_id: Optional[str] = None,
214 ):
215 self._dryrun = dryrun
216 self._branch = branch
217 self._buildbucket_id = buildbucket_id
218 self._gerrit_helper = gerrit.GetCrosExternal()
219
220 # Strip any chrome branch from the lkgm version.
221 self._lkgm = chromeos_version.VersionInfo(lkgm).VersionString()
Jack Neusd9a0bd92022-12-12 16:20:55 +0000222 if self._dryrun:
223 self._lkgm = "9999999.99.99"
224 logging.info("dry run, using version %s", self._lkgm)
225
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900226 self._commit_msg_header = self._COMMIT_MSG_HEADER % {"lkgm": self._lkgm}
227 self._current_lkgm = current_lkgm
228
229 if not self._lkgm:
230 raise LKGMNotValid("LKGM not provided.")
231 logging.info("lkgm=%s", lkgm)
232
233 def Run(self):
234 self.UpdateLKGM()
235
236 @property
237 def lkgm_file(self):
238 return self._committer.FullPath(constants.PATH_TO_CHROME_LKGM)
Alex Klein1699fab2022-09-08 08:46:06 -0600239
Alex Klein1699fab2022-09-08 08:46:06 -0600240 def UpdateLKGM(self):
241 """Updates the LKGM file with the new version."""
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900242 if chromeos_version.VersionInfo(self._lkgm) <= self._current_lkgm:
Jack Neusb8a9feb2022-11-18 16:28:55 +0000243 raise LKGMNotValid(
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +0900244 f"LKGM version ({self._lkgm}) is not newer than current version"
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900245 f" ({self._current_lkgm.VersionString()})."
Alex Klein1699fab2022-09-08 08:46:06 -0600246 )
247
Ben Pastene6a8361a2021-08-19 19:52:05 -0700248 logging.info(
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +0900249 "Updating LKGM version: %s (was %s),",
250 self._lkgm,
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900251 self._current_lkgm.VersionString(),
Alex Klein1699fab2022-09-08 08:46:06 -0600252 )
253 change = self._gerrit_helper.CreateChange(
254 "chromium/src", self._branch, self.ComposeCommitMsg(), False
255 )
256 self._gerrit_helper.ChangeEdit(
257 change.gerrit_number, "chromeos/CHROMEOS_LKGM", self._lkgm
258 )
Ben Pastene6a8361a2021-08-19 19:52:05 -0700259
Jack Neus624c8a52022-09-13 17:33:03 +0000260 if self._dryrun:
261 logging.info(
262 "Would have applied CQ+2 to crrev.com/c/%s",
263 change.gerrit_number,
264 )
Jack Neus4b03d3c2022-09-16 20:21:17 +0000265 self._gerrit_helper.AbandonChange(
266 change,
267 msg="Dry run",
268 )
Yoshiki Iguchif4300842022-09-14 12:14:03 +0900269 return
270
271 labels = {
272 "Bot-Commit": 1,
273 "Commit-Queue": 2,
274 }
Jack Neus624c8a52022-09-13 17:33:03 +0000275 logging.info(
276 "Applying %s to crrev.com/c/%s", labels, change.gerrit_number
277 )
Alex Klein1699fab2022-09-08 08:46:06 -0600278 self._gerrit_helper.SetReview(
Yoshiki Iguchidad24252022-09-22 14:21:30 +0900279 change.gerrit_number,
280 labels=labels,
281 notify="NONE",
282 ready=True,
283 reviewers=[constants.CHROME_GARDENER_REVIEW_EMAIL],
Alex Klein1699fab2022-09-08 08:46:06 -0600284 )
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900285 self._gerrit_helper.SetHashtags(change.gerrit_number, [HASHTAG], [])
Ben Pastene6a8361a2021-08-19 19:52:05 -0700286
Alex Klein1699fab2022-09-08 08:46:06 -0600287 def ComposeCommitMsg(self):
288 """Constructs and returns the commit message for the LKGM update."""
Jack Neus51d2e342022-09-14 16:40:08 +0000289 dry_run_message = (
290 "This CL was created during a dry run and is not "
291 "intended to be committed.\n"
292 )
Alex Klein1699fab2022-09-08 08:46:06 -0600293 commit_msg_template = (
Jack Neus51d2e342022-09-14 16:40:08 +0000294 "%(header)s\n%(build_link)s\n%(message)s\n%(cq_includes)s"
Alex Klein1699fab2022-09-08 08:46:06 -0600295 )
296 cq_includes = ""
Jack Neus7329df52022-09-28 18:53:28 +0000297 if self._branch == "main":
298 for bot in self._PRESUBMIT_BOTS:
299 cq_includes += "CQ_INCLUDE_TRYBOTS=luci.chrome.try:%s\n" % bot
Alex Klein1699fab2022-09-08 08:46:06 -0600300 build_link = ""
301 if self._buildbucket_id:
302 build_link = "\nUploaded by https://ci.chromium.org/b/%s\n" % (
303 self._buildbucket_id
304 )
305 return commit_msg_template % dict(
306 header=self._commit_msg_header,
307 cq_includes=cq_includes,
308 build_link=build_link,
Jack Neus51d2e342022-09-14 16:40:08 +0000309 message=dry_run_message if self._dryrun else "",
Alex Klein1699fab2022-09-08 08:46:06 -0600310 )
Ben Pastened3b93d42019-10-10 09:56:29 -0700311
Achuith Bhandarkar1b9180f2018-02-22 19:12:09 -0800312
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900313def GetCurrentLKGM(branch: str) -> chromeos_version.VersionInfo:
314 """Returns the current LKGM version on the branch.
315
Alex Klein9e7b29e2023-04-11 16:10:31 -0600316 On the first call, this method retrieves the LKGM version from Gitiles
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900317 server and returns it. On subsequent calls, this method returns the
318 cached LKGM version.
319
320 Raises:
Alex Klein9e7b29e2023-04-11 16:10:31 -0600321 LKGMNotValid: if the retrieved LKGM version from the repository is
322 invalid.
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900323 """
324 current_lkgm = gob_util.GetFileContents(
325 constants.CHROMIUM_GOB_URL,
326 constants.PATH_TO_CHROME_LKGM,
327 ref=branch,
328 )
329 if current_lkgm is None:
330 raise LKGMNotValid(
331 "The retrieved LKGM version from the repository is invalid:"
332 f" {current_lkgm}."
333 )
334
335 return chromeos_version.VersionInfo(current_lkgm.strip())
336
337
Achuith Bhandarkarb6f40252019-12-09 16:31:34 -0800338def GetOpts(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600339 """Returns a dictionary of parsed options.
Achuith Bhandarkar1b9180f2018-02-22 19:12:09 -0800340
Alex Klein1699fab2022-09-08 08:46:06 -0600341 Args:
Alex Klein9e7b29e2023-04-11 16:10:31 -0600342 argv: raw command line.
Achuith Bhandarkardc3a3512018-03-16 15:10:57 -0700343
Alex Klein1699fab2022-09-08 08:46:06 -0600344 Returns:
Alex Klein9e7b29e2023-04-11 16:10:31 -0600345 Dictionary of parsed options.
Alex Klein1699fab2022-09-08 08:46:06 -0600346 """
347 parser = commandline.ArgumentParser(description=__doc__, add_help=False)
348 parser.add_argument(
349 "--dryrun",
350 action="store_true",
351 default=False,
352 help="Don't commit changes or send out emails.",
353 )
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900354 parser.add_argument("--lkgm", help="LKGM version to update to.")
Alex Klein1699fab2022-09-08 08:46:06 -0600355 parser.add_argument(
356 "--buildbucket-id",
357 help="Buildbucket ID of the build that ran this script. "
358 "Will be linked in the commit message if specified.",
359 )
360 parser.add_argument(
361 "--branch",
362 default="main",
363 help="Branch to upload change to, e.g. "
364 "refs/branch-heads/5112. Defaults to main.",
365 )
366 return parser.parse_args(argv)
Steven Bennettsddf9bcd2017-06-14 14:07:43 -0700367
Ben Pastene3ae39bd2022-01-04 15:20:05 -0800368
Steven Bennettsddf9bcd2017-06-14 14:07:43 -0700369def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600370 opts = GetOpts(argv)
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900371 current_lkgm = GetCurrentLKGM(opts.branch)
372
373 if opts.lkgm is not None:
374 committer = ChromeLKGMCommitter(
375 opts.lkgm,
376 opts.branch,
377 current_lkgm,
378 opts.dryrun,
379 opts.buildbucket_id,
380 )
381 committer.Run()
382
383 # We need to know the account used by the builder to upload git CLs when
384 # listing up CLs.
385 user_email = ""
Anuj Jamwal258529f2023-09-08 17:32:55 +0000386 if hostname_util.host_is_ci_builder(golo_only=True):
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900387 user_email = "chromeos-commit-bot@chromium.org"
Anuj Jamwal258529f2023-09-08 17:32:55 +0000388 elif hostname_util.host_is_ci_builder(gce_only=True):
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900389 user_email = "3su6n15k.default@developer.gserviceaccount.com"
390 else:
391 raise LKGMFileNotFound("Failed to determine an appropriate user email.")
392
393 cleaner = ChromeLKGMCleaner(
394 opts.branch,
395 current_lkgm,
396 user_email,
397 opts.dryrun,
398 opts.buildbucket_id,
Alex Klein1699fab2022-09-08 08:46:06 -0600399 )
Yoshiki Iguchid5bc7922022-09-22 14:37:48 +0900400 cleaner.Run()
401
Alex Klein1699fab2022-09-08 08:46:06 -0600402 return 0