blob: 6df24c690ebe6c358a2e9e1166253aa24ebe3d2c [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
Chris Sosadad0d322011-01-31 16:37:33 -080011
Aviv Keshetb7519e12016-10-04 00:50:00 -070012from chromite.lib import constants
Mike Frysingere8dcbfd2015-03-08 21:45:52 -040013from chromite.lib import commandline
Chris Sosac13bba52011-05-24 15:14:09 -070014from chromite.lib import cros_build_lib
Ralph Nathan03047282015-03-23 11:09:32 -070015from chromite.lib import cros_logging as logging
David James97d95872012-11-16 15:09:56 -080016from chromite.lib import git
Mike Frysingerfddaeb52012-11-20 11:17:31 -050017from chromite.lib import osutils
David James6450a0a2012-12-04 07:59:53 -080018from chromite.lib import parallel
Alex Deymo075c2292014-09-04 18:31:50 -070019from chromite.lib import portage_util
Chris Sosac13bba52011-05-24 15:14:09 -070020
Gabe Black71e963e2014-10-28 20:19:59 -070021# Commit message subject for uprevving Portage packages.
22GIT_COMMIT_SUBJECT = 'Marking set of ebuilds as stable'
David James15ed1302013-04-25 09:21:19 -070023
David James29e86d52013-04-19 09:41:29 -070024# Commit message for uprevving Portage packages.
25_GIT_COMMIT_MESSAGE = 'Marking 9999 ebuild for %s as stable.'
26
Chris Sosadad0d322011-01-31 16:37:33 -080027# Dictionary of valid commands with usage information.
28COMMAND_DICTIONARY = {
Mike Frysinger5cd8c742013-10-11 14:43:01 -040029 'commit': 'Marks given ebuilds as stable locally',
30 'push': 'Pushes previous marking of ebuilds to remote repo',
31}
Chris Sosadad0d322011-01-31 16:37:33 -080032
Chris Sosadad0d322011-01-31 16:37:33 -080033
Chris Sosadad0d322011-01-31 16:37:33 -080034# ======================= Global Helper Functions ========================
35
36
David James41124af2015-06-04 21:13:25 -070037def CleanStalePackages(srcroot, boards, package_atoms):
Chris Sosabf153872011-04-28 14:21:09 -070038 """Cleans up stale package info from a previous build.
Mike Frysinger5cd8c742013-10-11 14:43:01 -040039
Chris Sosabf153872011-04-28 14:21:09 -070040 Args:
David James41124af2015-06-04 21:13:25 -070041 srcroot: Root directory of the source tree.
David Jamescc09c9b2012-01-26 22:10:13 -080042 boards: Boards to clean the packages from.
Mike Frysingerde5ab0e2013-03-21 20:48:36 -040043 package_atoms: A list of package atoms to unmerge.
Chris Sosabf153872011-04-28 14:21:09 -070044 """
David James15ed1302013-04-25 09:21:19 -070045 if package_atoms:
Ralph Nathan03047282015-03-23 11:09:32 -070046 logging.info('Cleaning up stale packages %s.' % package_atoms)
David James15ed1302013-04-25 09:21:19 -070047
Mike Frysingerb7ab9b82012-04-04 16:22:43 -040048 # First unmerge all the packages for a board, then eclean it.
49 # We need these two steps to run in order (unmerge/eclean),
50 # but we can let all the boards run in parallel.
51 def _CleanStalePackages(board):
52 if board:
53 suffix = '-' + board
54 runcmd = cros_build_lib.RunCommand
55 else:
56 suffix = ''
57 runcmd = cros_build_lib.SudoRunCommand
Chris Sosadad0d322011-01-31 16:37:33 -080058
David James59a0a2b2013-03-22 14:04:44 -070059 emerge, eclean = 'emerge' + suffix, 'eclean' + suffix
60 if not osutils.FindMissingBinaries([emerge, eclean]):
David James63841a82014-01-16 14:39:24 -080061 if package_atoms:
62 # If nothing was found to be unmerged, emerge will exit(1).
63 result = runcmd([emerge, '-q', '--unmerge'] + package_atoms,
David James41124af2015-06-04 21:13:25 -070064 enter_chroot=True, extra_env={'CLEAN_DELAY': '0'},
65 error_code_ok=True, cwd=srcroot)
David James63841a82014-01-16 14:39:24 -080066 if not result.returncode in (0, 1):
67 raise cros_build_lib.RunCommandError('unexpected error', result)
David James59a0a2b2013-03-22 14:04:44 -070068 runcmd([eclean, '-d', 'packages'],
David James41124af2015-06-04 21:13:25 -070069 cwd=srcroot, enter_chroot=True,
David James59a0a2b2013-03-22 14:04:44 -070070 redirect_stdout=True, redirect_stderr=True)
Mike Frysingerb7ab9b82012-04-04 16:22:43 -040071
72 tasks = []
David Jamescc09c9b2012-01-26 22:10:13 -080073 for board in boards:
Mike Frysingerb7ab9b82012-04-04 16:22:43 -040074 tasks.append([board])
75 tasks.append([None])
76
David James6450a0a2012-12-04 07:59:53 -080077 parallel.RunTasksInProcessPool(_CleanStalePackages, tasks)
Chris Sosadad0d322011-01-31 16:37:33 -080078
79
Mike Frysinger2ebe3732012-05-08 17:04:12 -040080# TODO(build): This code needs to be gutted and rebased to cros_build_lib.
81def _DoWeHaveLocalCommits(stable_branch, tracking_branch, cwd):
Chris Sosadad0d322011-01-31 16:37:33 -080082 """Returns true if there are local commits."""
David James97d95872012-11-16 15:09:56 -080083 output = git.RunGit(
Paul Hobbs72d8e392015-10-21 17:24:23 -070084 cwd, ['rev-parse', stable_branch, tracking_branch]).output.split()
Brian Harring5b86b5e2012-05-25 18:27:40 -070085 return output[0] != output[1]
Chris Sosadad0d322011-01-31 16:37:33 -080086
87
Chris Sosadad0d322011-01-31 16:37:33 -080088# ======================= End Global Helper Functions ========================
89
90
Ningning Xia52009062016-05-09 14:33:51 -070091def PushChange(stable_branch, tracking_branch, dryrun, cwd,
92 staging_branch=None):
Chris Sosadad0d322011-01-31 16:37:33 -080093 """Pushes commits in the stable_branch to the remote git repository.
94
David Jamesee2da622012-02-23 09:32:16 -080095 Pushes local commits from calls to CommitChange to the remote git
David James66009462012-03-25 10:08:38 -070096 repository specified by current working directory. If changes are
97 found to commit, they will be merged to the merge branch and pushed.
98 In that case, the local repository will be left on the merge branch.
Chris Sosadad0d322011-01-31 16:37:33 -080099
100 Args:
101 stable_branch: The local branch with commits we want to push.
102 tracking_branch: The tracking branch of the local branch.
Chris Sosa62ad8522011-03-08 17:46:17 -0800103 dryrun: Use git push --dryrun to emulate a push.
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400104 cwd: The directory to run commands in.
Ningning Xia52009062016-05-09 14:33:51 -0700105 staging_branch: The staging branch to push for a failed PFQ run
Mike Frysinger1a736a82013-12-12 01:50:59 -0500106
Chris Sosadad0d322011-01-31 16:37:33 -0800107 Raises:
Mike Frysinger5cd8c742013-10-11 14:43:01 -0400108 OSError: Error occurred while pushing.
Chris Sosadad0d322011-01-31 16:37:33 -0800109 """
David James47959632015-10-23 07:56:01 -0700110 if not git.DoesCommitExistInRepo(cwd, stable_branch):
111 logging.debug('No branch created for %s. Exiting', cwd)
112 return
113
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400114 if not _DoWeHaveLocalCommits(stable_branch, tracking_branch, cwd):
David James47959632015-10-23 07:56:01 -0700115 logging.debug('No work found to push in %s. Exiting', cwd)
Chris Sosadad0d322011-01-31 16:37:33 -0800116 return
117
David James66009462012-03-25 10:08:38 -0700118 # For the commit queue, our local branch may contain commits that were
119 # just tested and pushed during the CommitQueueCompletion stage. Sync
120 # and rebase our local branch on top of the remote commits.
Paul Hobbs72d8e392015-10-21 17:24:23 -0700121 remote_ref = git.GetTrackingBranch(cwd,
122 branch=stable_branch,
123 for_push=True)
Paul Hobbs1e46be82015-10-30 13:46:02 -0700124 # SyncPushBranch rebases HEAD onto the updated remote. We need to checkout
125 # stable_branch here in order to update it.
126 git.RunGit(cwd, ['checkout', stable_branch])
Don Garrett99449592015-03-25 11:01:30 -0700127 git.SyncPushBranch(cwd, remote_ref.remote, remote_ref.ref)
David James66009462012-03-25 10:08:38 -0700128
129 # Check whether any local changes remain after the sync.
Don Garrett99449592015-03-25 11:01:30 -0700130 if not _DoWeHaveLocalCommits(stable_branch, remote_ref.ref, cwd):
Ralph Nathan03047282015-03-23 11:09:32 -0700131 logging.info('All changes already pushed for %s. Exiting', cwd)
David James66009462012-03-25 10:08:38 -0700132 return
133
Matt Tennantcb522052013-11-25 14:23:43 -0800134 # Add a failsafe check here. Only CLs from the 'chrome-bot' user should
135 # be involved here. If any other CLs are found then complain.
136 # In dryruns extra CLs are normal, though, and can be ignored.
137 bad_cl_cmd = ['log', '--format=short', '--perl-regexp',
138 '--author', '^(?!chrome-bot)', '%s..%s' % (
Don Garrett99449592015-03-25 11:01:30 -0700139 remote_ref.ref, stable_branch)]
Matt Tennantcb522052013-11-25 14:23:43 -0800140 bad_cls = git.RunGit(cwd, bad_cl_cmd).output
141 if bad_cls.strip() and not dryrun:
Ralph Nathan59900422015-03-24 10:41:17 -0700142 logging.error('The Uprev stage found changes from users other than '
143 'chrome-bot:\n\n%s', bad_cls)
Matt Tennantcb522052013-11-25 14:23:43 -0800144 raise AssertionError('Unexpected CLs found during uprev stage.')
145
Lev Rumyantsev25352ba2016-09-07 15:53:58 -0700146 if staging_branch is not None:
147 logging.info('PFQ FAILED. Pushing uprev change to staging branch %s',
148 staging_branch)
149
Mike Frysingere65f3752014-12-08 00:46:39 -0500150 description = git.RunGit(
151 cwd,
152 ['log', '--format=format:%s%n%n%b',
Don Garrett99449592015-03-25 11:01:30 -0700153 '%s..%s' % (remote_ref.ref, stable_branch)]).output
Gabe Black71e963e2014-10-28 20:19:59 -0700154 description = '%s\n\n%s' % (GIT_COMMIT_SUBJECT, description)
Ralph Nathan03047282015-03-23 11:09:32 -0700155 logging.info('For %s, using description %s', cwd, description)
Paul Hobbsf52ea8f2015-10-21 17:24:23 -0700156 git.CreatePushBranch(constants.MERGE_BRANCH, cwd,
157 remote_push_branch=remote_ref)
David James97d95872012-11-16 15:09:56 -0800158 git.RunGit(cwd, ['merge', '--squash', stable_branch])
159 git.RunGit(cwd, ['commit', '-m', description])
160 git.RunGit(cwd, ['config', 'push.default', 'tracking'])
Ningning Xia52009062016-05-09 14:33:51 -0700161 git.PushWithRetry(constants.MERGE_BRANCH, cwd, dryrun=dryrun,
162 staging_branch=staging_branch)
Chris Sosadad0d322011-01-31 16:37:33 -0800163
164
165class GitBranch(object):
166 """Wrapper class for a git branch."""
167
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400168 def __init__(self, branch_name, tracking_branch, cwd):
David Jamesc7c4ff52013-09-18 17:57:13 -0700169 """Sets up variables but does not create the branch.
170
Mike Frysinger5cd8c742013-10-11 14:43:01 -0400171 Args:
David Jamesc7c4ff52013-09-18 17:57:13 -0700172 branch_name: The name of the branch.
173 tracking_branch: The associated tracking branch.
174 cwd: The git repository to work in.
175 """
Chris Sosadad0d322011-01-31 16:37:33 -0800176 self.branch_name = branch_name
177 self.tracking_branch = tracking_branch
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400178 self.cwd = cwd
Chris Sosadad0d322011-01-31 16:37:33 -0800179
180 def CreateBranch(self):
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400181 self.Checkout()
Chris Sosadad0d322011-01-31 16:37:33 -0800182
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400183 def Checkout(self, branch=None):
Chris Sosadad0d322011-01-31 16:37:33 -0800184 """Function used to check out to another GitBranch."""
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400185 if not branch:
186 branch = self.branch_name
187 if branch == self.tracking_branch or self.Exists(branch):
188 git_cmd = ['git', 'checkout', '-f', branch]
Chris Sosadad0d322011-01-31 16:37:33 -0800189 else:
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400190 git_cmd = ['repo', 'start', branch, '.']
Yu-Ju Hong3add4432014-01-30 11:46:15 -0800191 cros_build_lib.RunCommand(git_cmd, print_cmd=False, cwd=self.cwd,
192 capture_output=True)
Chris Sosadad0d322011-01-31 16:37:33 -0800193
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400194 def Exists(self, branch=None):
Chris Sosadad0d322011-01-31 16:37:33 -0800195 """Returns True if the branch exists."""
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400196 if not branch:
197 branch = self.branch_name
David James67d73252013-09-19 17:33:12 -0700198 branches = git.RunGit(self.cwd, ['branch']).output
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400199 return branch in branches.split()
Chris Sosadad0d322011-01-31 16:37:33 -0800200
201
Mike Frysingere8dcbfd2015-03-08 21:45:52 -0400202def GetParser():
203 """Creates the argparse parser."""
204 parser = commandline.ArgumentParser()
205 parser.add_argument('--all', action='store_true',
206 help='Mark all packages as stable.')
207 parser.add_argument('-b', '--boards', default='',
208 help='Colon-separated list of boards.')
209 parser.add_argument('--drop_file',
210 help='File to list packages that were revved.')
211 parser.add_argument('--dryrun', action='store_true',
212 help='Passes dry-run to git push if pushing a change.')
Bertrand SIMONNET6af54302015-08-05 10:12:49 -0700213 parser.add_argument('--force', action='store_true',
214 help='Force the stabilization of blacklisted packages. '
215 '(only compatible with -p)')
Mike Frysingere8dcbfd2015-03-08 21:45:52 -0400216 parser.add_argument('-o', '--overlays',
217 help='Colon-separated list of overlays to modify.')
218 parser.add_argument('-p', '--packages',
219 help='Colon separated list of packages to rev.')
220 parser.add_argument('-r', '--srcroot', type='path',
221 default=os.path.join(constants.SOURCE_ROOT, 'src'),
222 help='Path to root src directory.')
223 parser.add_argument('--verbose', action='store_true',
224 help='Prints out debug info.')
Ningning Xia52009062016-05-09 14:33:51 -0700225 parser.add_argument('--staging_branch',
226 help='The staging branch to push changes')
Mike Frysingere8dcbfd2015-03-08 21:45:52 -0400227 parser.add_argument('command', choices=COMMAND_DICTIONARY.keys(),
228 help='Command to run.')
229 return parser
230
231
232def main(argv):
233 parser = GetParser()
234 options = parser.parse_args(argv)
Bertrand SIMONNET6af54302015-08-05 10:12:49 -0700235 options.Freeze()
Mike Frysingere8dcbfd2015-03-08 21:45:52 -0400236
Bertrand SIMONNET6af54302015-08-05 10:12:49 -0700237 if options.command == 'commit':
238 if not options.packages and not options.all:
239 parser.error('Please specify at least one package (--packages)')
240 if options.force and options.all:
241 parser.error('Cannot use --force with --all. You must specify a list of '
242 'packages you want to force uprev.')
243
Mike Frysingere8dcbfd2015-03-08 21:45:52 -0400244 if not os.path.isdir(options.srcroot):
245 parser.error('srcroot is not a valid path: %s' % options.srcroot)
Mike Frysingere8dcbfd2015-03-08 21:45:52 -0400246
Alex Deymo075c2292014-09-04 18:31:50 -0700247 portage_util.EBuild.VERBOSE = options.verbose
Chris Sosa62ad8522011-03-08 17:46:17 -0800248
Chris Sosa62ad8522011-03-08 17:46:17 -0800249 package_list = None
250 if options.packages:
251 package_list = options.packages.split(':')
252
Chris Sosa62ad8522011-03-08 17:46:17 -0800253 if options.overlays:
Chris Sosadad0d322011-01-31 16:37:33 -0800254 overlays = {}
Chris Sosa62ad8522011-03-08 17:46:17 -0800255 for path in options.overlays.split(':'):
Ryan Cui05a31ba2011-05-31 17:47:37 -0700256 if not os.path.isdir(path):
Chris Sosac13bba52011-05-24 15:14:09 -0700257 cros_build_lib.Die('Cannot find overlay: %s' % path)
Mike Frysinger359391c2016-11-15 16:13:34 -0500258 overlays[os.path.realpath(path)] = []
Chris Sosadad0d322011-01-31 16:37:33 -0800259 else:
Ralph Nathan446aee92015-03-23 14:44:56 -0700260 logging.warning('Missing --overlays argument')
Chris Sosadad0d322011-01-31 16:37:33 -0800261 overlays = {
Mike Frysingere65f3752014-12-08 00:46:39 -0500262 '%s/private-overlays/chromeos-overlay' % options.srcroot: [],
263 '%s/third_party/chromiumos-overlay' % options.srcroot: [],
Chris Sosadad0d322011-01-31 16:37:33 -0800264 }
265
David James97d95872012-11-16 15:09:56 -0800266 manifest = git.ManifestCheckout.Cached(options.srcroot)
Ryan Cui4656a3c2011-05-24 12:30:30 -0700267
Mike Frysingere8dcbfd2015-03-08 21:45:52 -0400268 if options.command == 'commit':
Bertrand SIMONNET6af54302015-08-05 10:12:49 -0700269 portage_util.BuildEBuildDictionary(overlays, options.all, package_list,
270 allow_blacklisted=options.force)
David James84e953c2013-04-23 18:44:06 -0700271
David James15ed1302013-04-25 09:21:19 -0700272 # Contains the array of packages we actually revved.
273 revved_packages = []
274 new_package_atoms = []
David Jamesa8457b52011-05-28 00:03:20 -0700275
David James41124af2015-06-04 21:13:25 -0700276 for overlay in overlays:
277 ebuilds = overlays[overlay]
278 if not os.path.isdir(overlay):
279 logging.warning('Skipping %s' % overlay)
280 continue
Chris Sosadad0d322011-01-31 16:37:33 -0800281
David James41124af2015-06-04 21:13:25 -0700282 # Note we intentionally work from the non push tracking branch;
283 # everything built thus far has been against it (meaning, http mirrors),
284 # thus we should honor that. During the actual push, the code switches
285 # to the correct urls, and does an appropriate rebasing.
286 tracking_branch = git.GetTrackingBranchViaManifest(
287 overlay, manifest=manifest).ref
Chris Sosadad0d322011-01-31 16:37:33 -0800288
David James41124af2015-06-04 21:13:25 -0700289 if options.command == 'push':
290 PushChange(constants.STABLE_EBUILD_BRANCH, tracking_branch,
Ningning Xia52009062016-05-09 14:33:51 -0700291 options.dryrun, cwd=overlay,
292 staging_branch=options.staging_branch)
David James41124af2015-06-04 21:13:25 -0700293 elif options.command == 'commit':
294 existing_commit = git.GetGitRepoRevision(overlay)
295 work_branch = GitBranch(constants.STABLE_EBUILD_BRANCH, tracking_branch,
296 cwd=overlay)
297 work_branch.CreateBranch()
298 if not work_branch.Exists():
299 cros_build_lib.Die('Unable to create stabilizing branch in %s' %
300 overlay)
Brian Harring609dc4e2012-05-07 02:17:44 -0700301
David James41124af2015-06-04 21:13:25 -0700302 # In the case of uprevving overlays that have patches applied to them,
303 # include the patched changes in the stabilizing branch.
304 git.RunGit(overlay, ['rebase', existing_commit])
David James15ed1302013-04-25 09:21:19 -0700305
David James41124af2015-06-04 21:13:25 -0700306 messages = []
307 for ebuild in ebuilds:
308 if options.verbose:
Aviv Keshet845602e2016-10-13 23:44:34 -0700309 logging.info('Working on %s, info %s', ebuild.package,
310 ebuild.cros_workon_vars)
David James41124af2015-06-04 21:13:25 -0700311 try:
Jason D. Clinton4f88f772017-12-24 14:51:03 -0700312 new_package = ebuild.RevWorkOnEBuild(options.srcroot, manifest)
David James41124af2015-06-04 21:13:25 -0700313 if new_package:
314 revved_packages.append(ebuild.package)
315 new_package_atoms.append('=%s' % new_package)
316 messages.append(_GIT_COMMIT_MESSAGE % ebuild.package)
317 except (OSError, IOError):
318 logging.warning(
319 'Cannot rev %s\n'
320 'Note you will have to go into %s '
321 'and reset the git repo yourself.' % (ebuild.package, overlay))
322 raise
David James15ed1302013-04-25 09:21:19 -0700323
David James41124af2015-06-04 21:13:25 -0700324 if messages:
325 portage_util.EBuild.CommitChange('\n\n'.join(messages), overlay)
David James15ed1302013-04-25 09:21:19 -0700326
Mike Frysingere8dcbfd2015-03-08 21:45:52 -0400327 if options.command == 'commit':
David James41124af2015-06-04 21:13:25 -0700328 chroot_path = os.path.join(options.srcroot, constants.DEFAULT_CHROOT_DIR)
329 if os.path.exists(chroot_path):
330 CleanStalePackages(options.srcroot, options.boards.split(':'),
331 new_package_atoms)
David James15ed1302013-04-25 09:21:19 -0700332 if options.drop_file:
333 osutils.WriteFile(options.drop_file, ' '.join(revved_packages))