blob: 2db641c7db8905af918e43c0dba769f047c63f08 [file] [log] [blame]
Mike Frysingere58c0e22017-10-04 15:43:30 -04001# -*- coding: utf-8 -*-
Mike Frysinger110750a2012-03-26 14:19:20 -04002# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Chris Sosadad0d322011-01-31 16:37:33 -08003# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""This module uprevs a given package's ebuild to the next revision."""
7
Mike Frysinger383367e2014-09-16 15:06:17 -04008from __future__ import print_function
9
Chris Sosadad0d322011-01-31 16:37:33 -080010import os
Mike Frysinger32781402020-04-19 06:34:17 -040011import sys
Chris Sosadad0d322011-01-31 16:37:33 -080012
Aviv Keshetb7519e12016-10-04 00:50:00 -070013from chromite.lib import constants
Mike Frysingere8dcbfd2015-03-08 21:45:52 -040014from chromite.lib import commandline
Chris Sosac13bba52011-05-24 15:14:09 -070015from chromite.lib import cros_build_lib
Ralph Nathan03047282015-03-23 11:09:32 -070016from chromite.lib import cros_logging as logging
David James97d95872012-11-16 15:09:56 -080017from chromite.lib import git
Mike Frysingerfddaeb52012-11-20 11:17:31 -050018from chromite.lib import osutils
David James6450a0a2012-12-04 07:59:53 -080019from chromite.lib import parallel
Alex Deymo075c2292014-09-04 18:31:50 -070020from chromite.lib import portage_util
Lann Martinb26e1292018-08-09 13:59:19 -060021from chromite.lib import repo_util
Chris Sosac13bba52011-05-24 15:14:09 -070022
Don Garrett3dbb2932018-10-02 14:41:26 -070023from chromite.cbuildbot import manifest_version
24
Mike Frysinger32781402020-04-19 06:34:17 -040025
26assert sys.version_info >= (3, 6), 'This module requires Python 3.6+'
27
28
Gabe Black71e963e2014-10-28 20:19:59 -070029# Commit message subject for uprevving Portage packages.
30GIT_COMMIT_SUBJECT = 'Marking set of ebuilds as stable'
David James15ed1302013-04-25 09:21:19 -070031
David James29e86d52013-04-19 09:41:29 -070032# Commit message for uprevving Portage packages.
33_GIT_COMMIT_MESSAGE = 'Marking 9999 ebuild for %s as stable.'
34
Chris Sosadad0d322011-01-31 16:37:33 -080035# Dictionary of valid commands with usage information.
36COMMAND_DICTIONARY = {
Mike Frysinger5cd8c742013-10-11 14:43:01 -040037 'commit': 'Marks given ebuilds as stable locally',
38 'push': 'Pushes previous marking of ebuilds to remote repo',
39}
Chris Sosadad0d322011-01-31 16:37:33 -080040
Chris Sosadad0d322011-01-31 16:37:33 -080041
Chris Sosadad0d322011-01-31 16:37:33 -080042# ======================= Global Helper Functions ========================
43
44
David James41124af2015-06-04 21:13:25 -070045def CleanStalePackages(srcroot, boards, package_atoms):
Chris Sosabf153872011-04-28 14:21:09 -070046 """Cleans up stale package info from a previous build.
Mike Frysinger5cd8c742013-10-11 14:43:01 -040047
Chris Sosabf153872011-04-28 14:21:09 -070048 Args:
David James41124af2015-06-04 21:13:25 -070049 srcroot: Root directory of the source tree.
David Jamescc09c9b2012-01-26 22:10:13 -080050 boards: Boards to clean the packages from.
Mike Frysingerde5ab0e2013-03-21 20:48:36 -040051 package_atoms: A list of package atoms to unmerge.
Chris Sosabf153872011-04-28 14:21:09 -070052 """
David James15ed1302013-04-25 09:21:19 -070053 if package_atoms:
Lann Martinffb95162018-08-28 12:02:54 -060054 logging.info('Cleaning up stale packages %s.', package_atoms)
David James15ed1302013-04-25 09:21:19 -070055
Mike Frysingerb7ab9b82012-04-04 16:22:43 -040056 # First unmerge all the packages for a board, then eclean it.
57 # We need these two steps to run in order (unmerge/eclean),
58 # but we can let all the boards run in parallel.
59 def _CleanStalePackages(board):
60 if board:
61 suffix = '-' + board
Mike Frysinger45602c72019-09-22 02:15:11 -040062 runcmd = cros_build_lib.run
Mike Frysingerb7ab9b82012-04-04 16:22:43 -040063 else:
64 suffix = ''
Mike Frysinger45602c72019-09-22 02:15:11 -040065 runcmd = cros_build_lib.sudo_run
Chris Sosadad0d322011-01-31 16:37:33 -080066
David James59a0a2b2013-03-22 14:04:44 -070067 emerge, eclean = 'emerge' + suffix, 'eclean' + suffix
68 if not osutils.FindMissingBinaries([emerge, eclean]):
David James63841a82014-01-16 14:39:24 -080069 if package_atoms:
70 # If nothing was found to be unmerged, emerge will exit(1).
Don Garrett7100fff2018-10-23 11:07:13 -070071 result = runcmd([emerge, '-q', '--unmerge'] + list(package_atoms),
David James41124af2015-06-04 21:13:25 -070072 enter_chroot=True, extra_env={'CLEAN_DELAY': '0'},
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -050073 check=False, cwd=srcroot)
Mike Frysinger01504112019-08-24 19:35:24 -040074 if result.returncode not in (0, 1):
David James63841a82014-01-16 14:39:24 -080075 raise cros_build_lib.RunCommandError('unexpected error', result)
David James59a0a2b2013-03-22 14:04:44 -070076 runcmd([eclean, '-d', 'packages'],
David James41124af2015-06-04 21:13:25 -070077 cwd=srcroot, enter_chroot=True,
Mike Frysinger0282d222019-12-17 17:15:48 -050078 stdout=True, stderr=True)
Mike Frysingerb7ab9b82012-04-04 16:22:43 -040079
80 tasks = []
David Jamescc09c9b2012-01-26 22:10:13 -080081 for board in boards:
Mike Frysingerb7ab9b82012-04-04 16:22:43 -040082 tasks.append([board])
83 tasks.append([None])
84
David James6450a0a2012-12-04 07:59:53 -080085 parallel.RunTasksInProcessPool(_CleanStalePackages, tasks)
Chris Sosadad0d322011-01-31 16:37:33 -080086
87
Mike Frysinger2ebe3732012-05-08 17:04:12 -040088# TODO(build): This code needs to be gutted and rebased to cros_build_lib.
89def _DoWeHaveLocalCommits(stable_branch, tracking_branch, cwd):
Chris Sosadad0d322011-01-31 16:37:33 -080090 """Returns true if there are local commits."""
David James97d95872012-11-16 15:09:56 -080091 output = git.RunGit(
Paul Hobbs72d8e392015-10-21 17:24:23 -070092 cwd, ['rev-parse', stable_branch, tracking_branch]).output.split()
Brian Harring5b86b5e2012-05-25 18:27:40 -070093 return output[0] != output[1]
Chris Sosadad0d322011-01-31 16:37:33 -080094
95
Chris Sosadad0d322011-01-31 16:37:33 -080096# ======================= End Global Helper Functions ========================
97
98
Ningning Xia52009062016-05-09 14:33:51 -070099def PushChange(stable_branch, tracking_branch, dryrun, cwd,
100 staging_branch=None):
Chris Sosadad0d322011-01-31 16:37:33 -0800101 """Pushes commits in the stable_branch to the remote git repository.
102
David Jamesee2da622012-02-23 09:32:16 -0800103 Pushes local commits from calls to CommitChange to the remote git
David James66009462012-03-25 10:08:38 -0700104 repository specified by current working directory. If changes are
105 found to commit, they will be merged to the merge branch and pushed.
106 In that case, the local repository will be left on the merge branch.
Chris Sosadad0d322011-01-31 16:37:33 -0800107
108 Args:
109 stable_branch: The local branch with commits we want to push.
110 tracking_branch: The tracking branch of the local branch.
Chris Sosa62ad8522011-03-08 17:46:17 -0800111 dryrun: Use git push --dryrun to emulate a push.
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400112 cwd: The directory to run commands in.
Ningning Xia52009062016-05-09 14:33:51 -0700113 staging_branch: The staging branch to push for a failed PFQ run
Mike Frysinger1a736a82013-12-12 01:50:59 -0500114
Chris Sosadad0d322011-01-31 16:37:33 -0800115 Raises:
Mike Frysinger5cd8c742013-10-11 14:43:01 -0400116 OSError: Error occurred while pushing.
Chris Sosadad0d322011-01-31 16:37:33 -0800117 """
David James47959632015-10-23 07:56:01 -0700118 if not git.DoesCommitExistInRepo(cwd, stable_branch):
119 logging.debug('No branch created for %s. Exiting', cwd)
120 return
121
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400122 if not _DoWeHaveLocalCommits(stable_branch, tracking_branch, cwd):
David James47959632015-10-23 07:56:01 -0700123 logging.debug('No work found to push in %s. Exiting', cwd)
Chris Sosadad0d322011-01-31 16:37:33 -0800124 return
125
David James66009462012-03-25 10:08:38 -0700126 # For the commit queue, our local branch may contain commits that were
127 # just tested and pushed during the CommitQueueCompletion stage. Sync
128 # and rebase our local branch on top of the remote commits.
Paul Hobbs72d8e392015-10-21 17:24:23 -0700129 remote_ref = git.GetTrackingBranch(cwd,
130 branch=stable_branch,
131 for_push=True)
Paul Hobbs1e46be82015-10-30 13:46:02 -0700132 # SyncPushBranch rebases HEAD onto the updated remote. We need to checkout
133 # stable_branch here in order to update it.
134 git.RunGit(cwd, ['checkout', stable_branch])
Don Garrett99449592015-03-25 11:01:30 -0700135 git.SyncPushBranch(cwd, remote_ref.remote, remote_ref.ref)
David James66009462012-03-25 10:08:38 -0700136
137 # Check whether any local changes remain after the sync.
Don Garrett99449592015-03-25 11:01:30 -0700138 if not _DoWeHaveLocalCommits(stable_branch, remote_ref.ref, cwd):
Ralph Nathan03047282015-03-23 11:09:32 -0700139 logging.info('All changes already pushed for %s. Exiting', cwd)
David James66009462012-03-25 10:08:38 -0700140 return
141
Andrew Lamb7ef2c0b2019-07-17 09:43:27 -0600142 # Add a failsafe check here. Only CLs from the 'chrome-bot' or
143 # 'chromeos-ci-prod' user should be involved here. If any other CLs are
144 # found then complain. In dryruns extra CLs are normal, though, and can
145 # be ignored.
146 bad_cl_cmd = [
147 'log', '--format=short', '--perl-regexp', '--author',
148 '^(?!chrome-bot|chromeos-ci-prod)',
149 '%s..%s' % (remote_ref.ref, stable_branch)
150 ]
Matt Tennantcb522052013-11-25 14:23:43 -0800151 bad_cls = git.RunGit(cwd, bad_cl_cmd).output
152 if bad_cls.strip() and not dryrun:
Andrew Lamb7ef2c0b2019-07-17 09:43:27 -0600153 logging.error(
154 'The Uprev stage found changes from users other than '
155 'chrome-bot or chromeos-ci-prod:\n\n%s', bad_cls)
Matt Tennantcb522052013-11-25 14:23:43 -0800156 raise AssertionError('Unexpected CLs found during uprev stage.')
157
Lev Rumyantsev25352ba2016-09-07 15:53:58 -0700158 if staging_branch is not None:
159 logging.info('PFQ FAILED. Pushing uprev change to staging branch %s',
160 staging_branch)
161
Mike Frysingere65f3752014-12-08 00:46:39 -0500162 description = git.RunGit(
163 cwd,
164 ['log', '--format=format:%s%n%n%b',
Don Garrett99449592015-03-25 11:01:30 -0700165 '%s..%s' % (remote_ref.ref, stable_branch)]).output
Gabe Black71e963e2014-10-28 20:19:59 -0700166 description = '%s\n\n%s' % (GIT_COMMIT_SUBJECT, description)
Ralph Nathan03047282015-03-23 11:09:32 -0700167 logging.info('For %s, using description %s', cwd, description)
Paul Hobbsf52ea8f2015-10-21 17:24:23 -0700168 git.CreatePushBranch(constants.MERGE_BRANCH, cwd,
169 remote_push_branch=remote_ref)
David James97d95872012-11-16 15:09:56 -0800170 git.RunGit(cwd, ['merge', '--squash', stable_branch])
171 git.RunGit(cwd, ['commit', '-m', description])
172 git.RunGit(cwd, ['config', 'push.default', 'tracking'])
Mike Nicholsa6818c52018-04-09 11:05:42 -0600173 git.PushBranch(constants.MERGE_BRANCH, cwd, dryrun=dryrun,
Sean Abraham11b57f82019-09-20 19:33:17 +0000174 staging_branch=staging_branch)
Chris Sosadad0d322011-01-31 16:37:33 -0800175
176
177class GitBranch(object):
178 """Wrapper class for a git branch."""
179
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400180 def __init__(self, branch_name, tracking_branch, cwd):
David Jamesc7c4ff52013-09-18 17:57:13 -0700181 """Sets up variables but does not create the branch.
182
Mike Frysinger5cd8c742013-10-11 14:43:01 -0400183 Args:
David Jamesc7c4ff52013-09-18 17:57:13 -0700184 branch_name: The name of the branch.
185 tracking_branch: The associated tracking branch.
186 cwd: The git repository to work in.
187 """
Chris Sosadad0d322011-01-31 16:37:33 -0800188 self.branch_name = branch_name
189 self.tracking_branch = tracking_branch
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400190 self.cwd = cwd
Chris Sosadad0d322011-01-31 16:37:33 -0800191
192 def CreateBranch(self):
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400193 self.Checkout()
Chris Sosadad0d322011-01-31 16:37:33 -0800194
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400195 def Checkout(self, branch=None):
Chris Sosadad0d322011-01-31 16:37:33 -0800196 """Function used to check out to another GitBranch."""
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400197 if not branch:
198 branch = self.branch_name
199 if branch == self.tracking_branch or self.Exists(branch):
Lann Martinb26e1292018-08-09 13:59:19 -0600200 git.RunGit(self.cwd, ['checkout', '-f', branch], quiet=True)
Chris Sosadad0d322011-01-31 16:37:33 -0800201 else:
Lann Martinb26e1292018-08-09 13:59:19 -0600202 repo = repo_util.Repository.MustFind(self.cwd)
203 repo.StartBranch(branch, projects=['.'], cwd=self.cwd)
Chris Sosadad0d322011-01-31 16:37:33 -0800204
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400205 def Exists(self, branch=None):
Chris Sosadad0d322011-01-31 16:37:33 -0800206 """Returns True if the branch exists."""
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400207 if not branch:
208 branch = self.branch_name
David James67d73252013-09-19 17:33:12 -0700209 branches = git.RunGit(self.cwd, ['branch']).output
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400210 return branch in branches.split()
Chris Sosadad0d322011-01-31 16:37:33 -0800211
212
Mike Frysingere8dcbfd2015-03-08 21:45:52 -0400213def GetParser():
214 """Creates the argparse parser."""
215 parser = commandline.ArgumentParser()
216 parser.add_argument('--all', action='store_true',
217 help='Mark all packages as stable.')
218 parser.add_argument('-b', '--boards', default='',
219 help='Colon-separated list of boards.')
Mike Nicholsf7f13a82019-01-11 17:11:04 +0000220 parser.add_argument('--drop_file',
221 help='File to list packages that were revved.')
Mike Frysingere8dcbfd2015-03-08 21:45:52 -0400222 parser.add_argument('--dryrun', action='store_true',
223 help='Passes dry-run to git push if pushing a change.')
Bertrand SIMONNET6af54302015-08-05 10:12:49 -0700224 parser.add_argument('--force', action='store_true',
Nicolas Boichatde92a6d2021-03-31 16:43:50 +0800225 help='Force the stabilization of packages marked for '
226 'manual uprev. '
Bertrand SIMONNET6af54302015-08-05 10:12:49 -0700227 '(only compatible with -p)')
Sam McNallyeb5e2052018-09-05 16:34:55 +1000228 parser.add_argument('--list_revisions', action='store_true',
229 help='List all revisions included in the commit message.')
Mike Frysingere8dcbfd2015-03-08 21:45:52 -0400230 parser.add_argument('-o', '--overlays',
231 help='Colon-separated list of overlays to modify.')
Don Garrettf9eff952018-08-10 16:50:04 -0700232 parser.add_argument('--overlay-type',
233 help='Populates --overlays based on "public", "private"'
234 ', or "both".')
Mike Frysingere8dcbfd2015-03-08 21:45:52 -0400235 parser.add_argument('-p', '--packages',
236 help='Colon separated list of packages to rev.')
Don Garrett4fef8c32018-08-10 18:04:01 -0700237 parser.add_argument('--buildroot', type='path',
238 help='Path to buildroot.')
Mike Frysingere8dcbfd2015-03-08 21:45:52 -0400239 parser.add_argument('-r', '--srcroot', type='path',
Don Garrett4fef8c32018-08-10 18:04:01 -0700240 help='Path to root src. Deprecated in favor of '
241 '--buildroot')
Ningning Xia52009062016-05-09 14:33:51 -0700242 parser.add_argument('--staging_branch',
243 help='The staging branch to push changes')
Mike Frysinger1f4478c2019-10-20 18:33:17 -0400244 parser.add_argument('command', choices=sorted(COMMAND_DICTIONARY),
Mike Frysingere8dcbfd2015-03-08 21:45:52 -0400245 help='Command to run.')
246 return parser
247
248
249def main(argv):
250 parser = GetParser()
251 options = parser.parse_args(argv)
Don Garrett4fef8c32018-08-10 18:04:01 -0700252
253 # TODO: Remove this code in favor of a simple default on buildroot when
254 # srcroot is removed.
255 if options.srcroot and not options.buildroot:
256 # Convert /<repo>/src -> <repo>
257 options.buildroot = os.path.dirname(options.srcroot)
258 if not options.buildroot:
259 options.buildroot = constants.SOURCE_ROOT
260 options.srcroot = None
261
Bertrand SIMONNET6af54302015-08-05 10:12:49 -0700262 options.Freeze()
Mike Frysingere8dcbfd2015-03-08 21:45:52 -0400263
Bertrand SIMONNET6af54302015-08-05 10:12:49 -0700264 if options.command == 'commit':
265 if not options.packages and not options.all:
266 parser.error('Please specify at least one package (--packages)')
267 if options.force and options.all:
268 parser.error('Cannot use --force with --all. You must specify a list of '
269 'packages you want to force uprev.')
270
Don Garrett4fef8c32018-08-10 18:04:01 -0700271 if not os.path.isdir(options.buildroot):
272 parser.error('buildroot is not a valid path: %s' % options.buildroot)
Mike Frysingere8dcbfd2015-03-08 21:45:52 -0400273
Don Garrettf9eff952018-08-10 16:50:04 -0700274 if options.overlay_type and options.overlays:
275 parser.error('Cannot use --overlay-type with --overlays.')
276
Alex Deymo075c2292014-09-04 18:31:50 -0700277 portage_util.EBuild.VERBOSE = options.verbose
Chris Sosa62ad8522011-03-08 17:46:17 -0800278
Chris Sosa62ad8522011-03-08 17:46:17 -0800279 package_list = None
280 if options.packages:
281 package_list = options.packages.split(':')
282
Ningning Xiadb884322018-01-26 16:27:06 -0800283 overlays = []
Chris Sosa62ad8522011-03-08 17:46:17 -0800284 if options.overlays:
Chris Sosa62ad8522011-03-08 17:46:17 -0800285 for path in options.overlays.split(':'):
Ryan Cui05a31ba2011-05-31 17:47:37 -0700286 if not os.path.isdir(path):
Chris Sosac13bba52011-05-24 15:14:09 -0700287 cros_build_lib.Die('Cannot find overlay: %s' % path)
Ningning Xiadb884322018-01-26 16:27:06 -0800288 overlays.append(os.path.realpath(path))
Don Garrettf9eff952018-08-10 16:50:04 -0700289 elif options.overlay_type:
290 overlays = portage_util.FindOverlays(
Don Garrett4fef8c32018-08-10 18:04:01 -0700291 options.overlay_type, buildroot=options.buildroot)
Chris Sosadad0d322011-01-31 16:37:33 -0800292 else:
Ralph Nathan446aee92015-03-23 14:44:56 -0700293 logging.warning('Missing --overlays argument')
Ningning Xiadb884322018-01-26 16:27:06 -0800294 overlays.extend([
Don Garrett4fef8c32018-08-10 18:04:01 -0700295 '%s/src/private-overlays/chromeos-overlay' % options.buildroot,
296 '%s/src/third_party/chromiumos-overlay' % options.buildroot])
Chris Sosadad0d322011-01-31 16:37:33 -0800297
Don Garrett4fef8c32018-08-10 18:04:01 -0700298 manifest = git.ManifestCheckout.Cached(options.buildroot)
Ryan Cui4656a3c2011-05-24 12:30:30 -0700299
Ningning Xia419e4eb2018-02-05 10:30:36 -0800300 # Dict mapping from each overlay to its tracking branch.
301 overlay_tracking_branch = {}
302 # Dict mapping from each git repository (project) to a list of its overlays.
303 git_project_overlays = {}
304
305 for overlay in overlays:
306 remote_ref = git.GetTrackingBranchViaManifest(overlay, manifest=manifest)
307 overlay_tracking_branch[overlay] = remote_ref.ref
308 git_project_overlays.setdefault(remote_ref.project_name, []).append(overlay)
309
310 if options.command == 'push':
311 _WorkOnPush(options, overlay_tracking_branch, git_project_overlays)
312 elif options.command == 'commit':
313 _WorkOnCommit(options, overlays, overlay_tracking_branch,
314 git_project_overlays, manifest, package_list)
315
316
317def _WorkOnPush(options, overlay_tracking_branch, git_project_overlays):
318 """Push uprevs of overlays belonging to differet git projects in parallel.
319
320 Args:
321 options: The options object returned by the argument parser.
322 overlay_tracking_branch: A dict mapping from each overlay to its tracking
323 branch.
324 git_project_overlays: A dict mapping from each git repository to a list of
325 its overlays.
326 """
327 inputs = [[options, overlays_per_project, overlay_tracking_branch]
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400328 for overlays_per_project in git_project_overlays.values()]
Ningning Xia419e4eb2018-02-05 10:30:36 -0800329 parallel.RunTasksInProcessPool(_PushOverlays, inputs)
330
331
332def _PushOverlays(options, overlays, overlay_tracking_branch):
333 """Push uprevs for overlays in sequence.
334
335 Args:
336 options: The options object returned by the argument parser.
337 overlays: A list of overlays to push uprevs in sequence.
338 overlay_tracking_branch: A dict mapping from each overlay to its tracking
339 branch.
340 """
341 for overlay in overlays:
342 if not os.path.isdir(overlay):
343 logging.warning('Skipping %s, which is not a directory.', overlay)
344 continue
345
346 tracking_branch = overlay_tracking_branch[overlay]
347 PushChange(constants.STABLE_EBUILD_BRANCH, tracking_branch, options.dryrun,
348 cwd=overlay, staging_branch=options.staging_branch)
349
350
351def _WorkOnCommit(options, overlays, overlay_tracking_branch,
352 git_project_overlays, manifest, package_list):
353 """Commit uprevs of overlays belonging to different git projects in parallel.
354
355 Args:
356 options: The options object returned by the argument parser.
357 overlays: A list of overlays to work on.
358 overlay_tracking_branch: A dict mapping from each overlay to its tracking
359 branch.
360 git_project_overlays: A dict mapping from each git repository to a list of
361 its overlays.
362 manifest: The manifest of the given source root.
363 package_list: A list of packages passed from commandline to work on.
364 """
Mike Frysinger62ff8d72020-05-19 03:06:51 -0400365 # We cleaned up self referential ebuilds by this version, but don't enforce
366 # the check on older ones to avoid breaking factory/firmware branches.
367 root_version = manifest_version.VersionInfo.from_repo(options.buildroot)
368 no_self_repos_version = manifest_version.VersionInfo('13099.0.0')
369 reject_self_repo = root_version >= no_self_repos_version
370
Ningning Xia419e4eb2018-02-05 10:30:36 -0800371 overlay_ebuilds = _GetOverlayToEbuildsMap(options, overlays, package_list)
David James84e953c2013-04-23 18:44:06 -0700372
Ningning Xia783efc02018-01-24 13:39:51 -0800373 with parallel.Manager() as manager:
374 # Contains the array of packages we actually revved.
375 revved_packages = manager.list()
376 new_package_atoms = manager.list()
David Jamesa8457b52011-05-28 00:03:20 -0700377
Ningning Xia419e4eb2018-02-05 10:30:36 -0800378 inputs = [[options, manifest, overlays_per_project, overlay_tracking_branch,
Mike Frysinger62ff8d72020-05-19 03:06:51 -0400379 overlay_ebuilds, revved_packages, new_package_atoms,
380 reject_self_repo]
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400381 for overlays_per_project in git_project_overlays.values()]
Ningning Xia419e4eb2018-02-05 10:30:36 -0800382 parallel.RunTasksInProcessPool(_CommitOverlays, inputs)
Chris Sosadad0d322011-01-31 16:37:33 -0800383
Don Garrett4fef8c32018-08-10 18:04:01 -0700384 chroot_path = os.path.join(options.buildroot, constants.DEFAULT_CHROOT_DIR)
Ningning Xia419e4eb2018-02-05 10:30:36 -0800385 if os.path.exists(chroot_path):
Don Garrett4fef8c32018-08-10 18:04:01 -0700386 CleanStalePackages(options.buildroot, options.boards.split(':'),
Ningning Xia419e4eb2018-02-05 10:30:36 -0800387 new_package_atoms)
Mike Nicholsf7f13a82019-01-11 17:11:04 +0000388 if options.drop_file:
389 osutils.WriteFile(options.drop_file, ' '.join(revved_packages))
Chris Sosadad0d322011-01-31 16:37:33 -0800390
Brian Harring609dc4e2012-05-07 02:17:44 -0700391
Ningning Xia419e4eb2018-02-05 10:30:36 -0800392def _GetOverlayToEbuildsMap(options, overlays, package_list):
393 """Get ebuilds for overlays.
David James15ed1302013-04-25 09:21:19 -0700394
Ningning Xia783efc02018-01-24 13:39:51 -0800395 Args:
Ningning Xia783efc02018-01-24 13:39:51 -0800396 options: The options object returned by the argument parser.
Ningning Xia419e4eb2018-02-05 10:30:36 -0800397 overlays: A list of overlays to work on.
398 package_list: A list of packages passed from commandline to work on.
399
400 Returns:
401 A dict mapping each overlay to a list of ebuilds belonging to it.
402 """
Don Garrett3dbb2932018-10-02 14:41:26 -0700403 root_version = manifest_version.VersionInfo.from_repo(options.buildroot)
404 subdir_removal = manifest_version.VersionInfo('10363.0.0')
405 require_subdir_support = root_version < subdir_removal
406
Ningning Xia419e4eb2018-02-05 10:30:36 -0800407 overlay_ebuilds = {}
Don Garrett3dbb2932018-10-02 14:41:26 -0700408 inputs = [[overlay, options.all, package_list, options.force,
409 require_subdir_support]
Ningning Xia419e4eb2018-02-05 10:30:36 -0800410 for overlay in overlays]
411 result = parallel.RunTasksInProcessPool(
412 portage_util.GetOverlayEBuilds, inputs)
413 for idx, ebuilds in enumerate(result):
414 overlay_ebuilds[overlays[idx]] = ebuilds
415
416 return overlay_ebuilds
417
418
419def _CommitOverlays(options, manifest, overlays, overlay_tracking_branch,
Mike Frysinger62ff8d72020-05-19 03:06:51 -0400420 overlay_ebuilds, revved_packages, new_package_atoms,
421 reject_self_repo=True):
Ningning Xia419e4eb2018-02-05 10:30:36 -0800422 """Commit uprevs for overlays in sequence.
423
424 Args:
425 options: The options object returned by the argument parser.
426 manifest: The manifest of the given source root.
427 overlays: A list over overlays to commit.
428 overlay_tracking_branch: A dict mapping from each overlay to its tracking
429 branch.
430 overlay_ebuilds: A dict mapping overlays to their ebuilds.
Ningning Xia783efc02018-01-24 13:39:51 -0800431 revved_packages: A shared list of revved packages.
432 new_package_atoms: A shared list of new package atoms.
Mike Frysinger62ff8d72020-05-19 03:06:51 -0400433 reject_self_repo: Whether to abort if the ebuild lives in the same git
434 repo as it is tracking for uprevs.
Ningning Xia783efc02018-01-24 13:39:51 -0800435 """
Ningning Xia419e4eb2018-02-05 10:30:36 -0800436 for overlay in overlays:
437 if not os.path.isdir(overlay):
438 logging.warning('Skipping %s, which is not a directory.', overlay)
439 continue
Ningning Xia783efc02018-01-24 13:39:51 -0800440
Ningning Xia419e4eb2018-02-05 10:30:36 -0800441 # Note we intentionally work from the non push tracking branch;
442 # everything built thus far has been against it (meaning, http mirrors),
443 # thus we should honor that. During the actual push, the code switches
444 # to the correct urls, and does an appropriate rebasing.
445 tracking_branch = overlay_tracking_branch[overlay]
Ningning Xia783efc02018-01-24 13:39:51 -0800446
Ningning Xia783efc02018-01-24 13:39:51 -0800447 existing_commit = git.GetGitRepoRevision(overlay)
Lann Martine0ef98c2018-06-11 13:12:24 -0600448
449 # Make sure we run in the top-level git directory in case we are
450 # adding/removing an overlay in existing_commit.
451 git_root = git.FindGitTopLevel(overlay)
452 if git_root is None:
453 cros_build_lib.Die('No git repo at overlay directory %s.', overlay)
454
Ningning Xia783efc02018-01-24 13:39:51 -0800455 work_branch = GitBranch(constants.STABLE_EBUILD_BRANCH, tracking_branch,
Lann Martine0ef98c2018-06-11 13:12:24 -0600456 cwd=git_root)
Ningning Xia783efc02018-01-24 13:39:51 -0800457 work_branch.CreateBranch()
458 if not work_branch.Exists():
459 cros_build_lib.Die('Unable to create stabilizing branch in %s' %
460 overlay)
461
462 # In the case of uprevving overlays that have patches applied to them,
463 # include the patched changes in the stabilizing branch.
Lann Martine0ef98c2018-06-11 13:12:24 -0600464 git.RunGit(git_root, ['rebase', existing_commit])
Ningning Xia783efc02018-01-24 13:39:51 -0800465
Ningning Xiadb884322018-01-26 16:27:06 -0800466 ebuilds = overlay_ebuilds.get(overlay, [])
Ningning Xia783efc02018-01-24 13:39:51 -0800467 if ebuilds:
Ningning Xia419e4eb2018-02-05 10:30:36 -0800468 with parallel.Manager() as manager:
469 # Contains the array of packages we actually revved.
470 messages = manager.list()
471 ebuild_paths_to_add = manager.list()
472 ebuild_paths_to_remove = manager.list()
Ningning Xia783efc02018-01-24 13:39:51 -0800473
Ningning Xia419e4eb2018-02-05 10:30:36 -0800474 inputs = [[overlay, ebuild, manifest, options, ebuild_paths_to_add,
475 ebuild_paths_to_remove, messages, revved_packages,
Mike Frysinger62ff8d72020-05-19 03:06:51 -0400476 new_package_atoms, reject_self_repo]
477 for ebuild in ebuilds]
Ningning Xia419e4eb2018-02-05 10:30:36 -0800478 parallel.RunTasksInProcessPool(_WorkOnEbuild, inputs)
Ningning Xia783efc02018-01-24 13:39:51 -0800479
Ningning Xia419e4eb2018-02-05 10:30:36 -0800480 if ebuild_paths_to_add:
481 logging.info('Adding new stable ebuild paths %s in overlay %s.',
482 ebuild_paths_to_add, overlay)
483 git.RunGit(overlay, ['add'] + list(ebuild_paths_to_add))
David James15ed1302013-04-25 09:21:19 -0700484
Ningning Xia419e4eb2018-02-05 10:30:36 -0800485 if ebuild_paths_to_remove:
486 logging.info('Removing old ebuild paths %s in overlay %s.',
487 ebuild_paths_to_remove, overlay)
488 git.RunGit(overlay, ['rm', '-f'] + list(ebuild_paths_to_remove))
489
490 if messages:
491 portage_util.EBuild.CommitChange('\n\n'.join(messages), overlay)
David James15ed1302013-04-25 09:21:19 -0700492
Ningning Xia783efc02018-01-24 13:39:51 -0800493
494def _WorkOnEbuild(overlay, ebuild, manifest, options, ebuild_paths_to_add,
495 ebuild_paths_to_remove, messages, revved_packages,
Mike Frysinger62ff8d72020-05-19 03:06:51 -0400496 new_package_atoms, reject_self_repo=True):
Ningning Xia783efc02018-01-24 13:39:51 -0800497 """Work on a single ebuild.
498
499 Args:
500 overlay: The overlay where the ebuild belongs to.
501 ebuild: The ebuild to work on.
502 manifest: The manifest of the given source root.
503 options: The options object returned by the argument parser.
504 ebuild_paths_to_add: New stable ebuild paths to add to git.
505 ebuild_paths_to_remove: Old ebuild paths to remove from git.
506 messages: A share list of commit messages.
507 revved_packages: A shared list of revved packages.
508 new_package_atoms: A shared list of new package atoms.
Mike Frysinger62ff8d72020-05-19 03:06:51 -0400509 reject_self_repo: Whether to abort if the ebuild lives in the same git
510 repo as it is tracking for uprevs.
Ningning Xia783efc02018-01-24 13:39:51 -0800511 """
512 if options.verbose:
513 logging.info('Working on %s, info %s', ebuild.package,
514 ebuild.cros_workon_vars)
Mike Frysinger00ba05a2020-06-12 02:44:02 -0400515 if not ebuild.cros_workon_vars:
516 logging.warning('%s: unable to parse workon settings', ebuild.ebuild_path)
517
Ningning Xia783efc02018-01-24 13:39:51 -0800518 try:
Don Garrett4fef8c32018-08-10 18:04:01 -0700519 result = ebuild.RevWorkOnEBuild(os.path.join(options.buildroot, 'src'),
Mike Frysinger62ff8d72020-05-19 03:06:51 -0400520 manifest, reject_self_repo=reject_self_repo)
Ningning Xia783efc02018-01-24 13:39:51 -0800521 if result:
522 new_package, ebuild_path_to_add, ebuild_path_to_remove = result
523
524 if ebuild_path_to_add:
525 ebuild_paths_to_add.append(ebuild_path_to_add)
526 if ebuild_path_to_remove:
527 ebuild_paths_to_remove.append(ebuild_path_to_remove)
528
529 messages.append(_GIT_COMMIT_MESSAGE % ebuild.package)
Sam McNallyeb5e2052018-09-05 16:34:55 +1000530
531 if options.list_revisions:
532 info = ebuild.GetSourceInfo(os.path.join(options.buildroot, 'src'),
Mike Frysinger62ff8d72020-05-19 03:06:51 -0400533 manifest, reject_self_repo=reject_self_repo)
Sam McNallyeb5e2052018-09-05 16:34:55 +1000534 srcdirs = [os.path.join(options.buildroot, 'src', srcdir)
535 for srcdir in ebuild.cros_workon_vars.localname]
536 old_commit_ids = dict(
537 zip(srcdirs, ebuild.cros_workon_vars.commit.split(',')))
538 git_log = []
539 for srcdir in info.srcdirs:
540 old_commit_id = old_commit_ids.get(srcdir)
541 new_commit_id = ebuild.GetCommitId(srcdir)
542 if not old_commit_id or old_commit_id == new_commit_id:
543 continue
544
545 logs = git.RunGit(srcdir, [
546 'log', '%s..%s' % (old_commit_id[:8], new_commit_id[:8]),
547 '--pretty=format:%h %<(63,trunc)%s'])
548 git_log.append('$ ' + logs.cmdstr)
549 git_log.extend(line.strip() for line in logs.output.splitlines())
550 if git_log:
551 messages.append('\n'.join(git_log))
552
Ningning Xia783efc02018-01-24 13:39:51 -0800553 revved_packages.append(ebuild.package)
554 new_package_atoms.append('=%s' % new_package)
David Burger8cf4a762020-03-09 10:33:38 -0600555 except portage_util.InvalidUprevSourceError as e:
556 logging.error('An error occurred while uprevving %s: %s',
557 ebuild.package, e)
558 raise
Alex Klein99920662019-10-14 15:30:15 -0600559 except portage_util.EbuildVersionError as e:
560 logging.warning('Unable to rev %s: %s', ebuild.package, e)
561 raise
Mike Frysingerb32cc472020-05-15 00:17:54 -0400562 except OSError:
Ningning Xia783efc02018-01-24 13:39:51 -0800563 logging.warning(
564 'Cannot rev %s\n'
565 'Note you will have to go into %s '
Ningning Xia419e4eb2018-02-05 10:30:36 -0800566 'and reset the git repo yourself.', ebuild.package, overlay)
Ningning Xia783efc02018-01-24 13:39:51 -0800567 raise