blob: 74899a967f400b2ddddec249eae69d9f6aad0347 [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
Steven Bennettsddf9bcd2017-06-14 14:07:43 -070011
Ram Chandrasekar60f69f32022-06-03 22:49:30 +000012from chromite.lib import chromeos_version
Achuith Bhandarkarf561c152018-08-27 19:05:39 -070013from chromite.lib import commandline
Steven Bennettsddf9bcd2017-06-14 14:07:43 -070014from chromite.lib import constants
Ben Pastene3ae39bd2022-01-04 15:20:05 -080015from chromite.lib import cros_build_lib
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
Steven Bennettsddf9bcd2017-06-14 14:07:43 -070018
19
Ben Pastene3ae39bd2022-01-04 15:20:05 -080020class LKGMNotValid(Exception):
Alex Klein1699fab2022-09-08 08:46:06 -060021 """Raised if the LKGM version is unset or not newer than the current value."""
Steven Bennettsddf9bcd2017-06-14 14:07:43 -070022
23
Ben Pastene3ae39bd2022-01-04 15:20:05 -080024class LKGMFileNotFound(Exception):
Alex Klein1699fab2022-09-08 08:46:06 -060025 """Raised if the LKGM file is not found."""
Steven Bennettsddf9bcd2017-06-14 14:07:43 -070026
27
Achuith Bhandarkar1b9180f2018-02-22 19:12:09 -080028class ChromeLKGMCommitter(object):
Alex Klein1699fab2022-09-08 08:46:06 -060029 """Committer object responsible for obtaining a new LKGM and committing it."""
Steven Bennettsddf9bcd2017-06-14 14:07:43 -070030
Alex Klein1699fab2022-09-08 08:46:06 -060031 # 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 Bennettsddf9bcd2017-06-14 14:07:43 -070053
Alex Klein1699fab2022-09-08 08:46:06 -060054 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 Pastene3ae39bd2022-01-04 15:20:05 -080059
Alex Klein1699fab2022-09-08 08:46:06 -060060 # 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 Bhandarkarec8d7a72018-03-01 16:56:22 -080067
Alex Klein1699fab2022-09-08 08:46:06 -060068 # 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 Iguchif8ab0112022-09-15 16:57:26 +090071 self._current_lkgm = None
Steven Bennettsddf9bcd2017-06-14 14:07:43 -070072
Alex Klein1699fab2022-09-08 08:46:06 -060073 if not self._lkgm:
74 raise LKGMNotValid("LKGM not provided.")
75 logging.info("lkgm=%s", lkgm)
Achuith Bhandarkardc3a3512018-03-16 15:10:57 -070076
Alex Klein1699fab2022-09-08 08:46:06 -060077 def Run(self):
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +090078 self.AbandonObsoleteLKGMRolls()
Jack Neus624c8a52022-09-13 17:33:03 +000079 self.UpdateLKGM()
Achuith Bhandarkardc3a3512018-03-16 15:10:57 -070080
Alex Klein1699fab2022-09-08 08:46:06 -060081 @property
82 def lkgm_file(self):
83 return self._committer.FullPath(constants.PATH_TO_CHROME_LKGM)
Achuith Bhandarkardc3a3512018-03-16 15:10:57 -070084
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +090085 def AbandonObsoleteLKGMRolls(self):
86 """Abandon all obsolete LKGM roll CLs.
Ben Pastene5a40b102019-11-22 17:06:31 -080087
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +090088 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 Klein1699fab2022-09-08 08:46:06 -060090 """
91 query_params = {
92 "project": constants.CHROMIUM_SRC_PROJECT,
93 "branch": self._branch,
94 "file": constants.PATH_TO_CHROME_LKGM,
Alex Klein1699fab2022-09-08 08:46:06 -060095 "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 Iguchif8ab0112022-09-15 16:57:26 +0900101 open_changes = self._gerrit_helper.Query(**query_params)
102 if not open_changes:
Alex Klein1699fab2022-09-08 08:46:06 -0600103 logging.info("No old LKGM rolls detected.")
104 return
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +0900105
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 Klein1699fab2022-09-08 08:46:06 -0600135 logging.info(
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +0900136 "=> This CL is an older LKGM roll than current: Abandoning"
Alex Klein1699fab2022-09-08 08:46:06 -0600137 )
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +0900138 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 Klein1699fab2022-09-08 08:46:06 -0600148 else:
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +0900149 # Do nothing with the roll CLs that tries changing to newer
150 # version.
151 # TODO(yoshiki): rebase the roll CL.
Alex Klein1699fab2022-09-08 08:46:06 -0600152 logging.info(
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +0900153 "=> This CL is a newer LKGM roll than current: Do nothing."
Alex Klein1699fab2022-09-08 08:46:06 -0600154 )
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +0900155
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 Klein1699fab2022-09-08 08:46:06 -0600183
Alex Klein1699fab2022-09-08 08:46:06 -0600184 def UpdateLKGM(self):
185 """Updates the LKGM file with the new version."""
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +0900186 if chromeos_version.VersionInfo(self._lkgm) <= self.GetCurrentLKGM():
Alex Klein1699fab2022-09-08 08:46:06 -0600187 raise LKGMNotValid(
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +0900188 f"LKGM version ({self._lkgm}) is not newer than current version"
189 f" ({self.GetCurrentLKGM().VersionString()})."
Alex Klein1699fab2022-09-08 08:46:06 -0600190 )
191
Ben Pastene6a8361a2021-08-19 19:52:05 -0700192 logging.info(
Yoshiki Iguchif8ab0112022-09-15 16:57:26 +0900193 "Updating LKGM version: %s (was %s),",
194 self._lkgm,
195 self.GetCurrentLKGM().VersionString(),
Alex Klein1699fab2022-09-08 08:46:06 -0600196 )
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 Pastene6a8361a2021-08-19 19:52:05 -0700203
Jack Neus624c8a52022-09-13 17:33:03 +0000204 if self._dryrun:
205 logging.info(
206 "Would have applied CQ+2 to crrev.com/c/%s",
207 change.gerrit_number,
208 )
Jack Neus4b03d3c2022-09-16 20:21:17 +0000209 self._gerrit_helper.AbandonChange(
210 change,
211 msg="Dry run",
212 )
Yoshiki Iguchif4300842022-09-14 12:14:03 +0900213 return
214
215 labels = {
216 "Bot-Commit": 1,
217 "Commit-Queue": 2,
218 }
Jack Neus624c8a52022-09-13 17:33:03 +0000219 logging.info(
220 "Applying %s to crrev.com/c/%s", labels, change.gerrit_number
221 )
Alex Klein1699fab2022-09-08 08:46:06 -0600222 self._gerrit_helper.SetReview(
Yoshiki Iguchidad24252022-09-22 14:21:30 +0900223 change.gerrit_number,
224 labels=labels,
225 notify="NONE",
226 ready=True,
227 reviewers=[constants.CHROME_GARDENER_REVIEW_EMAIL],
Alex Klein1699fab2022-09-08 08:46:06 -0600228 )
229 self._gerrit_helper.SetHashtags(
230 change.gerrit_number, ["chrome-lkgm"], []
231 )
Ben Pastene6a8361a2021-08-19 19:52:05 -0700232
Alex Klein1699fab2022-09-08 08:46:06 -0600233 def ComposeCommitMsg(self):
234 """Constructs and returns the commit message for the LKGM update."""
Jack Neus51d2e342022-09-14 16:40:08 +0000235 dry_run_message = (
236 "This CL was created during a dry run and is not "
237 "intended to be committed.\n"
238 )
Alex Klein1699fab2022-09-08 08:46:06 -0600239 commit_msg_template = (
Jack Neus51d2e342022-09-14 16:40:08 +0000240 "%(header)s\n%(build_link)s\n%(message)s\n%(cq_includes)s"
Alex Klein1699fab2022-09-08 08:46:06 -0600241 )
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 Neus51d2e342022-09-14 16:40:08 +0000254 message=dry_run_message if self._dryrun else "",
Alex Klein1699fab2022-09-08 08:46:06 -0600255 )
Ben Pastened3b93d42019-10-10 09:56:29 -0700256
Achuith Bhandarkar1b9180f2018-02-22 19:12:09 -0800257
Achuith Bhandarkarb6f40252019-12-09 16:31:34 -0800258def GetOpts(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600259 """Returns a dictionary of parsed options.
Achuith Bhandarkar1b9180f2018-02-22 19:12:09 -0800260
Alex Klein1699fab2022-09-08 08:46:06 -0600261 Args:
262 argv: raw command line.
Achuith Bhandarkardc3a3512018-03-16 15:10:57 -0700263
Alex Klein1699fab2022-09-08 08:46:06 -0600264 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 Bennettsddf9bcd2017-06-14 14:07:43 -0700289
Ben Pastene3ae39bd2022-01-04 15:20:05 -0800290
Steven Bennettsddf9bcd2017-06-14 14:07:43 -0700291def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600292 opts = GetOpts(argv)
293 committer = ChromeLKGMCommitter(
294 opts.lkgm, opts.branch, opts.dryrun, opts.buildbucket_id
295 )
296 committer.Run()
297 return 0