blob: 1d5c46377060bd70795543b855515d6cee69322a [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2019 The ChromiumOS Authors
Alex Kleinf4047132019-05-30 11:25:38 -06002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Simplified cros_mark_as_stable script."""
6
Chris McDonald59650c32021-07-20 15:29:28 -06007import logging
Alex Kleinf4047132019-05-30 11:25:38 -06008import os
Christian Flachfb8c9b32022-02-28 15:16:18 +01009from typing import Dict, List, Optional
Alex Kleinf4047132019-05-30 11:25:38 -060010
Ram Chandrasekar60f69f32022-06-03 22:49:30 +000011from chromite.lib import chromeos_version
Alex Kleinf4047132019-05-30 11:25:38 -060012from chromite.lib import commandline
Chris McDonald59650c32021-07-20 15:29:28 -060013from chromite.lib import constants
Alex Kleinf4047132019-05-30 11:25:38 -060014from chromite.lib import cros_build_lib
Alex Kleinf4047132019-05-30 11:25:38 -060015from chromite.lib import git
16from chromite.lib import osutils
17from chromite.lib import parallel
18from chromite.lib import portage_util
19
Mike Frysinger2688ef62020-02-16 00:00:46 -050020
Alex Kleinf4047132019-05-30 11:25:38 -060021# Commit message subject for uprevving Portage packages.
Alex Klein1699fab2022-09-08 08:46:06 -060022GIT_COMMIT_SUBJECT = "Marking set of ebuilds as stable"
Alex Kleinf4047132019-05-30 11:25:38 -060023
24# Commit message for uprevving Portage packages.
Alex Klein1699fab2022-09-08 08:46:06 -060025_GIT_COMMIT_MESSAGE = "Marking 9999 ebuild for %s as stable."
Alex Kleinf4047132019-05-30 11:25:38 -060026
27
Christian Flachfb8c9b32022-02-28 15:16:18 +010028def GetParser() -> commandline.ArgumentParser:
Alex Klein1699fab2022-09-08 08:46:06 -060029 """Build the argument parser."""
30 parser = commandline.ArgumentParser(description=__doc__)
Alex Kleinf4047132019-05-30 11:25:38 -060031
Alex Klein1699fab2022-09-08 08:46:06 -060032 parser.add_argument(
33 "--all", action="store_true", help="Mark all packages as stable."
34 )
35 parser.add_argument("--boards", help="Colon-separated list of boards.")
36 parser.add_argument("--chroot", type="path", help="The chroot path.")
37 parser.add_argument(
38 "--force",
39 action="store_true",
40 help="Force the stabilization of manually uprevved "
41 "packages. (only compatible with -p)",
42 )
43 parser.add_argument(
44 "--force-version",
45 type=str,
46 help="Force the version of manually uprevved packages.",
47 )
48 parser.add_argument(
49 "--overlay-type",
50 required=True,
51 choices=["public", "private", "both"],
52 help='Populates --overlays based on "public", "private", ' 'or "both".',
53 )
54 parser.add_argument(
55 "--packages", help="Colon separated list of packages to rev."
56 )
57 parser.add_argument(
58 "--dump-files",
59 action="store_true",
60 help="Dump the revved packages, new files list, and "
61 "removed files list files. This is mostly for"
62 "debugging right now.",
63 )
Alex Kleinf4047132019-05-30 11:25:38 -060064
Alex Klein1699fab2022-09-08 08:46:06 -060065 return parser
Alex Kleinf4047132019-05-30 11:25:38 -060066
67
Christian Flachfb8c9b32022-02-28 15:16:18 +010068def _ParseArguments(argv: List[str]) -> commandline.ArgumentNamespace:
Alex Klein1699fab2022-09-08 08:46:06 -060069 """Parse and validate arguments."""
70 parser = GetParser()
71 options = parser.parse_args(argv)
Alex Kleinf4047132019-05-30 11:25:38 -060072
Alex Klein1699fab2022-09-08 08:46:06 -060073 # Parse, cleanup, and populate options.
74 if options.packages:
75 options.packages = options.packages.split(":")
76 if options.boards:
77 options.boards = options.boards.split(":")
78 options.overlays = portage_util.FindOverlays(options.overlay_type)
Alex Kleinf4047132019-05-30 11:25:38 -060079
Alex Klein1699fab2022-09-08 08:46:06 -060080 # Verify options.
81 if not options.packages and not options.all:
82 parser.error("Please specify at least one package (--packages)")
83 if options.force and options.all:
84 parser.error(
85 "Cannot use --force with --all. You must specify a list of "
86 "packages you want to force uprev."
87 )
Alex Kleinf4047132019-05-30 11:25:38 -060088
Alex Klein1699fab2022-09-08 08:46:06 -060089 options.Freeze()
90 return options
Alex Kleinf4047132019-05-30 11:25:38 -060091
92
Christian Flachfb8c9b32022-02-28 15:16:18 +010093def main(argv: List[str]) -> None:
Alex Klein1699fab2022-09-08 08:46:06 -060094 options = _ParseArguments(argv)
Alex Kleinf4047132019-05-30 11:25:38 -060095
Alex Klein1699fab2022-09-08 08:46:06 -060096 if not options.boards:
97 overlays = portage_util.FindOverlays(options.overlay_type)
Alex Kleinf4047132019-05-30 11:25:38 -060098 else:
Alex Klein1699fab2022-09-08 08:46:06 -060099 overlays = set()
100 for board in options.boards:
101 board_overlays = portage_util.FindOverlays(
102 options.overlay_type, board=board
103 )
104 overlays = overlays.union(board_overlays)
Alex Kleinf4047132019-05-30 11:25:38 -0600105
Alex Klein1699fab2022-09-08 08:46:06 -0600106 overlays = list(overlays)
Alex Kleinf4047132019-05-30 11:25:38 -0600107
Alex Klein1699fab2022-09-08 08:46:06 -0600108 manifest = git.ManifestCheckout.Cached(constants.SOURCE_ROOT)
Alex Kleinf4047132019-05-30 11:25:38 -0600109
Alex Klein1699fab2022-09-08 08:46:06 -0600110 _WorkOnCommit(options, overlays, manifest, options.packages or None)
Alex Kleinf4047132019-05-30 11:25:38 -0600111
112
Alex Klein1699fab2022-09-08 08:46:06 -0600113def CleanStalePackages(
114 boards: List[str], package_atoms: List[str], chroot: str
115) -> None:
116 """Cleans up stale package info from a previous build.
Alex Kleinf4047132019-05-30 11:25:38 -0600117
Alex Klein1699fab2022-09-08 08:46:06 -0600118 Args:
Alex Klein68b270c2023-04-14 14:42:50 -0600119 boards: Boards to clean the packages from.
120 package_atoms: A list of package atoms to unmerge.
121 chroot: The chroot path.
Alex Klein1699fab2022-09-08 08:46:06 -0600122 """
123 if package_atoms:
124 logging.info("Cleaning up stale packages %s.", package_atoms)
Alex Kleinf4047132019-05-30 11:25:38 -0600125
Alex Klein1699fab2022-09-08 08:46:06 -0600126 # First unmerge all the packages for a board, then eclean it.
127 # We need these two steps to run in order (unmerge/eclean),
128 # but we can let all the boards run in parallel.
129 def _CleanStalePackages(board: str):
130 if board:
131 suffix = "-" + board
132 runcmd = cros_build_lib.run
133 else:
134 suffix = ""
135 runcmd = cros_build_lib.sudo_run
Alex Kleinf4047132019-05-30 11:25:38 -0600136
Alex Klein1699fab2022-09-08 08:46:06 -0600137 chroot_args = ["--chroot", chroot] if chroot else None
138 emerge, eclean = "emerge" + suffix, "eclean" + suffix
139 if not osutils.FindMissingBinaries([emerge, eclean]):
140 if package_atoms:
141 # If nothing was found to be unmerged, emerge will exit(1).
142 result = runcmd(
143 [emerge, "-q", "--unmerge"] + list(package_atoms),
144 enter_chroot=True,
145 chroot_args=chroot_args,
146 extra_env={"CLEAN_DELAY": "0"},
147 check=False,
148 cwd=constants.SOURCE_ROOT,
149 )
150 if result.returncode not in (0, 1):
151 raise cros_build_lib.RunCommandError(
152 "unexpected error", result
153 )
154 runcmd(
155 [eclean, "-d", "packages"],
156 cwd=constants.SOURCE_ROOT,
157 enter_chroot=True,
158 chroot_args=chroot_args,
159 stdout=True,
160 stderr=True,
161 )
Alex Kleinf4047132019-05-30 11:25:38 -0600162
Alex Klein1699fab2022-09-08 08:46:06 -0600163 tasks = []
164 for board in boards:
165 tasks.append([board])
166 tasks.append([None])
Alex Kleinf4047132019-05-30 11:25:38 -0600167
Alex Klein1699fab2022-09-08 08:46:06 -0600168 parallel.RunTasksInProcessPool(_CleanStalePackages, tasks)
Alex Kleinf4047132019-05-30 11:25:38 -0600169
170
Alex Klein1699fab2022-09-08 08:46:06 -0600171def _WorkOnCommit(
172 options: commandline.ArgumentNamespace,
173 overlays: List[str],
174 manifest: git.ManifestCheckout,
175 package_list: Optional[List[str]],
176) -> None:
Alex Klein68b270c2023-04-14 14:42:50 -0600177 """Commit uprevs of overlays in different git projects in parallel.
Alex Kleinf4047132019-05-30 11:25:38 -0600178
Alex Klein1699fab2022-09-08 08:46:06 -0600179 Args:
Alex Klein68b270c2023-04-14 14:42:50 -0600180 options: The options object returned by the argument parser.
181 overlays: A list of overlays to work on.
182 manifest: The manifest of the given source root.
183 package_list: A list of packages passed from commandline to work on.
Alex Klein1699fab2022-09-08 08:46:06 -0600184 """
185 overlay_ebuilds = _GetOverlayToEbuildsMap(
186 overlays, package_list, options.force
187 )
Alex Kleinf4047132019-05-30 11:25:38 -0600188
189 with parallel.Manager() as manager:
Alex Klein1699fab2022-09-08 08:46:06 -0600190 # Contains the array of packages we actually revved.
191 revved_packages = manager.list()
192 new_package_atoms = manager.list()
193 new_ebuild_files = manager.list()
194 removed_ebuild_files = manager.list()
Alex Kleinf4047132019-05-30 11:25:38 -0600195
Alex Klein1699fab2022-09-08 08:46:06 -0600196 inputs = [
197 [
198 manifest,
199 [overlay],
200 overlay_ebuilds,
201 revved_packages,
202 new_package_atoms,
203 new_ebuild_files,
204 removed_ebuild_files,
205 options,
206 ]
207 for overlay in overlays
208 ]
209 parallel.RunTasksInProcessPool(_UprevOverlays, inputs)
210
211 if options.chroot and os.path.exists(options.chroot):
212 CleanStalePackages(
213 options.boards or [], new_package_atoms, options.chroot
214 )
215
216 if options.dump_files:
217 osutils.WriteFile(
218 "/tmp/revved_packages", "\n".join(revved_packages)
219 )
220 osutils.WriteFile(
221 "/tmp/new_ebuild_files", "\n".join(new_ebuild_files)
222 )
223 osutils.WriteFile(
224 "/tmp/removed_ebuild_files", "\n".join(removed_ebuild_files)
225 )
Alex Kleinf4047132019-05-30 11:25:38 -0600226
227
Alex Klein1699fab2022-09-08 08:46:06 -0600228def _GetOverlayToEbuildsMap(
229 overlays: List[str], package_list: Optional[List[str]], force: bool
230) -> Dict:
231 """Get ebuilds for overlays.
Alex Kleinf4047132019-05-30 11:25:38 -0600232
Alex Klein1699fab2022-09-08 08:46:06 -0600233 Args:
Alex Klein68b270c2023-04-14 14:42:50 -0600234 overlays: A list of overlays to work on.
235 package_list: A list of packages passed from commandline to work on.
236 force: Whether to use packages even if in manual uprev list.
Alex Kleinf4047132019-05-30 11:25:38 -0600237
Alex Klein1699fab2022-09-08 08:46:06 -0600238 Returns:
Alex Klein68b270c2023-04-14 14:42:50 -0600239 A dict mapping each overlay to a list of ebuilds belonging to it.
Alex Klein1699fab2022-09-08 08:46:06 -0600240 """
241 root_version = chromeos_version.VersionInfo.from_repo(constants.SOURCE_ROOT)
242 subdir_removal = chromeos_version.VersionInfo("10363.0.0")
243 require_subdir_support = root_version < subdir_removal
244 use_all = not package_list
Alex Kleinf4047132019-05-30 11:25:38 -0600245
Alex Klein1699fab2022-09-08 08:46:06 -0600246 overlay_ebuilds = {}
247 inputs = [
248 [overlay, use_all, package_list, force, require_subdir_support]
249 for overlay in overlays
250 ]
251 result = parallel.RunTasksInProcessPool(
252 portage_util.GetOverlayEBuilds, inputs
253 )
254 for idx, ebuilds in enumerate(result):
255 overlay_ebuilds[overlays[idx]] = ebuilds
Alex Kleinf4047132019-05-30 11:25:38 -0600256
Alex Klein1699fab2022-09-08 08:46:06 -0600257 return overlay_ebuilds
258
259
260def _UprevOverlays(
261 manifest: git.ManifestCheckout,
262 overlays: List[str],
263 overlay_ebuilds: Dict,
264 revved_packages: List[str],
265 new_package_atoms: List[str],
266 new_ebuild_files: List[str],
267 removed_ebuild_files: List[str],
268 options: commandline.ArgumentNamespace,
269) -> None:
270 """Execute uprevs for overlays in sequence.
271
272 Args:
Alex Klein68b270c2023-04-14 14:42:50 -0600273 manifest: The manifest of the given source root.
274 overlays: A list over overlays to commit.
275 overlay_ebuilds: A dict mapping overlays to their ebuilds.
276 revved_packages: A shared list of revved packages.
277 new_package_atoms: A shared list of new package atoms.
278 new_ebuild_files: New stable ebuild paths.
279 removed_ebuild_files: Old ebuild paths that were removed.
280 options: The options object returned by the argument parser.
Alex Klein1699fab2022-09-08 08:46:06 -0600281 """
282 for overlay in overlays:
283 if not os.path.isdir(overlay):
284 logging.warning("Skipping %s, which is not a directory.", overlay)
285 continue
286
287 ebuilds = overlay_ebuilds.get(overlay, [])
288 if not ebuilds:
289 continue
290
291 with parallel.Manager() as manager:
292 # Contains the array of packages we actually revved.
293 messages = manager.list()
294
295 inputs = [
296 [
297 overlay,
298 ebuild,
299 manifest,
300 new_ebuild_files,
301 removed_ebuild_files,
302 messages,
303 revved_packages,
304 new_package_atoms,
305 options,
306 ]
307 for ebuild in ebuilds
308 ]
309 parallel.RunTasksInProcessPool(_WorkOnEbuild, inputs)
310
311
312def _WorkOnEbuild(
313 overlay: str,
314 ebuild: portage_util.EBuild,
315 manifest: git.ManifestCheckout,
316 new_ebuild_files: List[str],
317 removed_ebuild_files: List[str],
318 messages: List[str],
319 revved_packages: List[str],
320 new_package_atoms: List[str],
321 options: commandline.ArgumentNamespace,
322) -> None:
323 """Work on a single ebuild.
324
325 Args:
Alex Klein68b270c2023-04-14 14:42:50 -0600326 overlay: The overlay where the ebuild belongs to.
327 ebuild: The ebuild to work on.
328 manifest: The manifest of the given source root.
329 new_ebuild_files: New stable ebuild paths that were created.
330 removed_ebuild_files: Old ebuild paths that were removed.
331 messages: A share list of commit messages.
332 revved_packages: A shared list of revved packages.
333 new_package_atoms: A shared list of new package atoms.
334 options: The options object returned by the argument parser.
Alex Klein1699fab2022-09-08 08:46:06 -0600335 """
336 logging.debug(
337 "Working on %s, info %s", ebuild.package, ebuild.cros_workon_vars
338 )
339 try:
340 result = ebuild.RevWorkOnEBuild(
341 os.path.join(constants.SOURCE_ROOT, "src"),
342 manifest,
343 new_version=options.force_version,
344 )
345 except portage_util.InvalidUprevSourceError as e:
346 logging.error(
347 "An error occurred while uprevving %s: %s", ebuild.package, e
348 )
349 raise
350 except OSError:
351 logging.warning(
352 "Cannot rev %s\n"
353 "Note you will have to go into %s "
354 "and reset the git repo yourself.",
355 ebuild.package,
356 overlay,
357 )
358 raise
359
360 if result:
361 new_package, ebuild_path_to_add, ebuild_path_to_remove = result
362
363 if ebuild_path_to_add:
364 new_ebuild_files.append(ebuild_path_to_add)
365 if ebuild_path_to_remove:
366 osutils.SafeUnlink(ebuild_path_to_remove)
367 removed_ebuild_files.append(ebuild_path_to_remove)
368
369 messages.append(_GIT_COMMIT_MESSAGE % ebuild.package)
370 revved_packages.append(ebuild.package)
371 new_package_atoms.append("=%s" % new_package)