blob: 78c9388c4084979de794b34b88d248e558fc7190 [file] [log] [blame]
Chris Sosadad0d322011-01-31 16:37:33 -08001#!/usr/bin/python
2
Mike Frysinger110750a2012-03-26 14:19:20 -04003# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Chris Sosadad0d322011-01-31 16:37:33 -08004# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""This module uprevs a given package's ebuild to the next revision."""
8
Chris Sosa62ad8522011-03-08 17:46:17 -08009import optparse
Chris Sosadad0d322011-01-31 16:37:33 -080010import os
Chris Sosadad0d322011-01-31 16:37:33 -080011import sys
12
Mike Frysinger6cb624a2012-05-24 18:17:38 -040013from chromite.buildbot import constants
David James66009462012-03-25 10:08:38 -070014from chromite.buildbot import portage_utilities
Chris Sosac13bba52011-05-24 15:14:09 -070015from chromite.lib import cros_build_lib
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
Chris Sosac13bba52011-05-24 15:14:09 -070019
Chris Sosadad0d322011-01-31 16:37:33 -080020
Chris Sosadad0d322011-01-31 16:37:33 -080021# Dictionary of valid commands with usage information.
22COMMAND_DICTIONARY = {
Chris Sosadad0d322011-01-31 16:37:33 -080023 'commit':
24 'Marks given ebuilds as stable locally',
25 'push':
26 'Pushes previous marking of ebuilds to remote repo',
27 }
28
Chris Sosadad0d322011-01-31 16:37:33 -080029
Chris Sosadad0d322011-01-31 16:37:33 -080030# ======================= Global Helper Functions ========================
31
32
David Jamescc09c9b2012-01-26 22:10:13 -080033def CleanStalePackages(boards, package_atoms):
Chris Sosabf153872011-04-28 14:21:09 -070034 """Cleans up stale package info from a previous build.
35 Args:
David Jamescc09c9b2012-01-26 22:10:13 -080036 boards: Boards to clean the packages from.
Mike Frysingerde5ab0e2013-03-21 20:48:36 -040037 package_atoms: A list of package atoms to unmerge.
Chris Sosabf153872011-04-28 14:21:09 -070038 """
39 if package_atoms:
Chris Sosac13bba52011-05-24 15:14:09 -070040 cros_build_lib.Info('Cleaning up stale packages %s.' % package_atoms)
Chris Sosadad0d322011-01-31 16:37:33 -080041
Mike Frysingerb7ab9b82012-04-04 16:22:43 -040042 # First unmerge all the packages for a board, then eclean it.
43 # We need these two steps to run in order (unmerge/eclean),
44 # but we can let all the boards run in parallel.
45 def _CleanStalePackages(board):
46 if board:
47 suffix = '-' + board
48 runcmd = cros_build_lib.RunCommand
49 else:
50 suffix = ''
51 runcmd = cros_build_lib.SudoRunCommand
Chris Sosadad0d322011-01-31 16:37:33 -080052
David James59a0a2b2013-03-22 14:04:44 -070053 emerge, eclean = 'emerge' + suffix, 'eclean' + suffix
54 if not osutils.FindMissingBinaries([emerge, eclean]):
Mike Frysingerde5ab0e2013-03-21 20:48:36 -040055 # If nothing was found to be unmerged, emerge will exit(1).
David James59a0a2b2013-03-22 14:04:44 -070056 result = runcmd([emerge, '-q', '--unmerge'] + package_atoms,
Mike Frysingerde5ab0e2013-03-21 20:48:36 -040057 extra_env={'CLEAN_DELAY': '0'}, error_code_ok=True)
58 if not result.returncode in (0, 1):
59 raise cros_build_lib.RunCommandError('unexpected error', result)
David James59a0a2b2013-03-22 14:04:44 -070060 runcmd([eclean, '-d', 'packages'],
61 redirect_stdout=True, redirect_stderr=True)
Mike Frysingerb7ab9b82012-04-04 16:22:43 -040062
63 tasks = []
David Jamescc09c9b2012-01-26 22:10:13 -080064 for board in boards:
Mike Frysingerb7ab9b82012-04-04 16:22:43 -040065 tasks.append([board])
66 tasks.append([None])
67
David James6450a0a2012-12-04 07:59:53 -080068 parallel.RunTasksInProcessPool(_CleanStalePackages, tasks)
Chris Sosadad0d322011-01-31 16:37:33 -080069
70
Mike Frysinger2ebe3732012-05-08 17:04:12 -040071# TODO(build): This code needs to be gutted and rebased to cros_build_lib.
72def _DoWeHaveLocalCommits(stable_branch, tracking_branch, cwd):
Chris Sosadad0d322011-01-31 16:37:33 -080073 """Returns true if there are local commits."""
David James97d95872012-11-16 15:09:56 -080074 current_branch = git.GetCurrentBranch(cwd)
Brian Harring609dc4e2012-05-07 02:17:44 -070075
76 if current_branch != stable_branch:
Chris Sosadad0d322011-01-31 16:37:33 -080077 return False
David James97d95872012-11-16 15:09:56 -080078 output = git.RunGit(
Brian Harring609dc4e2012-05-07 02:17:44 -070079 cwd, ['rev-parse', 'HEAD', tracking_branch]).output.split()
Brian Harring5b86b5e2012-05-25 18:27:40 -070080 return output[0] != output[1]
Chris Sosadad0d322011-01-31 16:37:33 -080081
82
David James1b363582012-12-17 11:53:11 -080083def _CheckSaneArguments(command, options):
Chris Sosadad0d322011-01-31 16:37:33 -080084 """Checks to make sure the flags are sane. Dies if arguments are not sane."""
85 if not command in COMMAND_DICTIONARY.keys():
86 _PrintUsageAndDie('%s is not a valid command' % command)
Chris Sosa62ad8522011-03-08 17:46:17 -080087 if not options.packages and command == 'commit' and not options.all:
Chris Sosadad0d322011-01-31 16:37:33 -080088 _PrintUsageAndDie('Please specify at least one package')
Mike Frysinger8fd67dc2012-12-03 23:51:18 -050089 if options.boards:
90 cros_build_lib.AssertInsideChroot()
Chris Sosa62ad8522011-03-08 17:46:17 -080091 if not os.path.isdir(options.srcroot):
Chris Sosadad0d322011-01-31 16:37:33 -080092 _PrintUsageAndDie('srcroot is not a valid path')
Chris Sosa62ad8522011-03-08 17:46:17 -080093 options.srcroot = os.path.abspath(options.srcroot)
Chris Sosadad0d322011-01-31 16:37:33 -080094
95
96def _PrintUsageAndDie(error_message=''):
97 """Prints optional error_message the usage and returns an error exit code."""
98 command_usage = 'Commands: \n'
99 # Add keys and usage information from dictionary.
100 commands = sorted(COMMAND_DICTIONARY.keys())
101 for command in commands:
102 command_usage += ' %s: %s\n' % (command, COMMAND_DICTIONARY[command])
103 commands_str = '|'.join(commands)
Chris Sosac13bba52011-05-24 15:14:09 -0700104 cros_build_lib.Warning('Usage: %s FLAGS [%s]\n\n%s' % (
105 sys.argv[0], commands_str, command_usage))
Chris Sosadad0d322011-01-31 16:37:33 -0800106 if error_message:
Chris Sosac13bba52011-05-24 15:14:09 -0700107 cros_build_lib.Die(error_message)
Chris Sosadad0d322011-01-31 16:37:33 -0800108 else:
109 sys.exit(1)
110
111
Chris Sosadad0d322011-01-31 16:37:33 -0800112# ======================= End Global Helper Functions ========================
113
114
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400115def PushChange(stable_branch, tracking_branch, dryrun, cwd):
Chris Sosadad0d322011-01-31 16:37:33 -0800116 """Pushes commits in the stable_branch to the remote git repository.
117
David Jamesee2da622012-02-23 09:32:16 -0800118 Pushes local commits from calls to CommitChange to the remote git
David James66009462012-03-25 10:08:38 -0700119 repository specified by current working directory. If changes are
120 found to commit, they will be merged to the merge branch and pushed.
121 In that case, the local repository will be left on the merge branch.
Chris Sosadad0d322011-01-31 16:37:33 -0800122
123 Args:
124 stable_branch: The local branch with commits we want to push.
125 tracking_branch: The tracking branch of the local branch.
Chris Sosa62ad8522011-03-08 17:46:17 -0800126 dryrun: Use git push --dryrun to emulate a push.
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400127 cwd: The directory to run commands in.
Chris Sosadad0d322011-01-31 16:37:33 -0800128 Raises:
129 OSError: Error occurred while pushing.
130 """
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400131 if not _DoWeHaveLocalCommits(stable_branch, tracking_branch, cwd):
Brian Harring609dc4e2012-05-07 02:17:44 -0700132 cros_build_lib.Info('No work found to push in %s. Exiting', cwd)
Chris Sosadad0d322011-01-31 16:37:33 -0800133 return
134
David James66009462012-03-25 10:08:38 -0700135 # For the commit queue, our local branch may contain commits that were
136 # just tested and pushed during the CommitQueueCompletion stage. Sync
137 # and rebase our local branch on top of the remote commits.
David James97d95872012-11-16 15:09:56 -0800138 remote, push_branch = git.GetTrackingBranch(cwd, for_push=True)
139 git.SyncPushBranch(cwd, remote, push_branch)
David James66009462012-03-25 10:08:38 -0700140
141 # Check whether any local changes remain after the sync.
Brian Harring609dc4e2012-05-07 02:17:44 -0700142 if not _DoWeHaveLocalCommits(stable_branch, push_branch, cwd):
143 cros_build_lib.Info('All changes already pushed for %s. Exiting', cwd)
David James66009462012-03-25 10:08:38 -0700144 return
145
Mike Frysinger7dafd0e2012-05-08 15:47:16 -0400146 description = cros_build_lib.RunCommandCaptureOutput(
Brian Harring609dc4e2012-05-07 02:17:44 -0700147 ['git', 'log', '--format=format:%s%n%n%b', '%s..%s' % (
148 push_branch, stable_branch)], cwd=cwd).output
Chris Sosadad0d322011-01-31 16:37:33 -0800149 description = 'Marking set of ebuilds as stable\n\n%s' % description
Brian Harring609dc4e2012-05-07 02:17:44 -0700150 cros_build_lib.Info('For %s, using description %s', cwd, description)
David James97d95872012-11-16 15:09:56 -0800151 git.CreatePushBranch(constants.MERGE_BRANCH, cwd)
152 git.RunGit(cwd, ['merge', '--squash', stable_branch])
153 git.RunGit(cwd, ['commit', '-m', description])
154 git.RunGit(cwd, ['config', 'push.default', 'tracking'])
155 git.PushWithRetry(constants.MERGE_BRANCH, cwd, dryrun=dryrun)
Chris Sosadad0d322011-01-31 16:37:33 -0800156
157
158class GitBranch(object):
159 """Wrapper class for a git branch."""
160
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400161 def __init__(self, branch_name, tracking_branch, cwd):
Chris Sosadad0d322011-01-31 16:37:33 -0800162 """Sets up variables but does not create the branch."""
163 self.branch_name = branch_name
164 self.tracking_branch = tracking_branch
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400165 self.cwd = cwd
Chris Sosadad0d322011-01-31 16:37:33 -0800166
167 def CreateBranch(self):
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400168 self.Checkout()
Chris Sosadad0d322011-01-31 16:37:33 -0800169
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400170 def Checkout(self, branch=None):
Chris Sosadad0d322011-01-31 16:37:33 -0800171 """Function used to check out to another GitBranch."""
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400172 if not branch:
173 branch = self.branch_name
174 if branch == self.tracking_branch or self.Exists(branch):
175 git_cmd = ['git', 'checkout', '-f', branch]
Chris Sosadad0d322011-01-31 16:37:33 -0800176 else:
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400177 git_cmd = ['repo', 'start', branch, '.']
178 cros_build_lib.RunCommandCaptureOutput(git_cmd, print_cmd=False,
179 cwd=self.cwd)
Chris Sosadad0d322011-01-31 16:37:33 -0800180
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400181 def Exists(self, branch=None):
Chris Sosadad0d322011-01-31 16:37:33 -0800182 """Returns True if the branch exists."""
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400183 if not branch:
184 branch = self.branch_name
Mike Frysinger7dafd0e2012-05-08 15:47:16 -0400185 branches = cros_build_lib.RunCommandCaptureOutput(['git', 'branch'],
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400186 print_cmd=False,
187 cwd=self.cwd).output
188 return branch in branches.split()
Chris Sosadad0d322011-01-31 16:37:33 -0800189
190
David James1b363582012-12-17 11:53:11 -0800191def main(_argv):
Chris Sosa62ad8522011-03-08 17:46:17 -0800192 parser = optparse.OptionParser('cros_mark_as_stable OPTIONS packages')
193 parser.add_option('--all', action='store_true',
194 help='Mark all packages as stable.')
Mike Frysinger587bd562012-11-19 16:41:39 -0500195 parser.add_option('-b', '--boards', default='',
David Jamescc09c9b2012-01-26 22:10:13 -0800196 help='Colon-separated list of boards')
Chris Sosa62ad8522011-03-08 17:46:17 -0800197 parser.add_option('--drop_file',
198 help='File to list packages that were revved.')
199 parser.add_option('--dryrun', action='store_true',
200 help='Passes dry-run to git push if pushing a change.')
201 parser.add_option('-o', '--overlays',
202 help='Colon-separated list of overlays to modify.')
203 parser.add_option('-p', '--packages',
204 help='Colon separated list of packages to rev.')
205 parser.add_option('-r', '--srcroot',
Mike Frysingerfddaeb52012-11-20 11:17:31 -0500206 default=os.path.join(constants.SOURCE_ROOT, 'src'),
Chris Sosa62ad8522011-03-08 17:46:17 -0800207 help='Path to root src directory.')
Chris Sosa62ad8522011-03-08 17:46:17 -0800208 parser.add_option('--verbose', action='store_true',
209 help='Prints out debug info.')
210 (options, args) = parser.parse_args()
Chris Sosadad0d322011-01-31 16:37:33 -0800211
J. Richard Barnette2fa9fd62011-12-02 17:10:10 -0800212 portage_utilities.EBuild.VERBOSE = options.verbose
Chris Sosa62ad8522011-03-08 17:46:17 -0800213
214 if len(args) != 1:
Ryan Cui05a31ba2011-05-31 17:47:37 -0700215 _PrintUsageAndDie('Must specify a valid command [commit, push]')
Chris Sosa62ad8522011-03-08 17:46:17 -0800216
217 command = args[0]
218 package_list = None
219 if options.packages:
220 package_list = options.packages.split(':')
221
David James1b363582012-12-17 11:53:11 -0800222 _CheckSaneArguments(command, options)
Chris Sosa62ad8522011-03-08 17:46:17 -0800223 if options.overlays:
Chris Sosadad0d322011-01-31 16:37:33 -0800224 overlays = {}
Chris Sosa62ad8522011-03-08 17:46:17 -0800225 for path in options.overlays.split(':'):
Ryan Cui05a31ba2011-05-31 17:47:37 -0700226 if not os.path.isdir(path):
Chris Sosac13bba52011-05-24 15:14:09 -0700227 cros_build_lib.Die('Cannot find overlay: %s' % path)
Chris Sosadad0d322011-01-31 16:37:33 -0800228 overlays[path] = []
229 else:
Chris Sosac13bba52011-05-24 15:14:09 -0700230 cros_build_lib.Warning('Missing --overlays argument')
Chris Sosadad0d322011-01-31 16:37:33 -0800231 overlays = {
Chris Sosa62ad8522011-03-08 17:46:17 -0800232 '%s/private-overlays/chromeos-overlay' % options.srcroot: [],
233 '%s/third_party/chromiumos-overlay' % options.srcroot: []
Chris Sosadad0d322011-01-31 16:37:33 -0800234 }
235
236 if command == 'commit':
J. Richard Barnettef6697cf2011-11-18 12:42:08 -0800237 portage_utilities.BuildEBuildDictionary(
J. Richard Barnetted422f622011-11-17 09:39:46 -0800238 overlays, options.all, package_list)
Chris Sosadad0d322011-01-31 16:37:33 -0800239
David James97d95872012-11-16 15:09:56 -0800240 manifest = git.ManifestCheckout.Cached(options.srcroot)
Ryan Cui4656a3c2011-05-24 12:30:30 -0700241
David Jamesa8457b52011-05-28 00:03:20 -0700242 # Contains the array of packages we actually revved.
243 revved_packages = []
244 new_package_atoms = []
245
Mike Frysingerb9bd2f82012-05-08 14:55:23 -0400246 # Slight optimization hack: process the chromiumos overlay before any other
247 # cros-workon overlay first so we can do background cache generation in it.
248 # A perfect solution would walk all the overlays, figure out any dependencies
249 # between them (with layout.conf), and then process them in dependency order.
250 # However, this operation isn't slow enough to warrant that level of
251 # complexity, so we'll just special case the main overlay.
252 #
253 # Similarly, generate the cache in the portage-stable tree asap. We know
254 # we won't have any cros-workon packages in there, so generating the cache
255 # is the only thing it'll be doing. The chromiumos overlay instead might
256 # have revbumping to do before it can generate the cache.
Mike Frysinger110750a2012-03-26 14:19:20 -0400257 keys = overlays.keys()
Mike Frysingerb9bd2f82012-05-08 14:55:23 -0400258 for overlay in ('/third_party/chromiumos-overlay',
259 '/third_party/portage-stable'):
260 for k in keys:
261 if k.endswith(overlay):
262 keys.remove(k)
263 keys.insert(0, k)
264 break
Chris Sosadad0d322011-01-31 16:37:33 -0800265
Brian Harring9fdd23d2012-12-07 12:09:08 -0800266 with parallel.BackgroundTaskRunner(portage_utilities.RegenCache) as queue:
Mike Frysinger110750a2012-03-26 14:19:20 -0400267 for overlay in keys:
268 ebuilds = overlays[overlay]
269 if not os.path.isdir(overlay):
270 cros_build_lib.Warning("Skipping %s" % overlay)
271 continue
Chris Sosadad0d322011-01-31 16:37:33 -0800272
Brian Harringe08e4422012-05-30 12:40:50 -0700273 # Note we intentionally work from the non push tracking branch;
274 # everything built thus far has been against it (meaning, http mirrors),
275 # thus we should honor that. During the actual push, the code switches
276 # to the correct urls, and does an appropriate rebasing.
David James97d95872012-11-16 15:09:56 -0800277 tracking_branch = git.GetTrackingBranchViaManifest(
Brian Harringe08e4422012-05-30 12:40:50 -0700278 overlay, manifest=manifest)[1]
Brian Harring609dc4e2012-05-07 02:17:44 -0700279
Mike Frysinger110750a2012-03-26 14:19:20 -0400280 if command == 'push':
281 PushChange(constants.STABLE_EBUILD_BRANCH, tracking_branch,
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400282 options.dryrun, cwd=overlay)
Mike Frysinger303083d2012-07-16 14:57:24 -0400283 elif command == 'commit':
David James97d95872012-11-16 15:09:56 -0800284 existing_branch = git.GetCurrentBranch(overlay)
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400285 work_branch = GitBranch(constants.STABLE_EBUILD_BRANCH, tracking_branch,
286 cwd=overlay)
Mike Frysinger110750a2012-03-26 14:19:20 -0400287 work_branch.CreateBranch()
288 if not work_branch.Exists():
289 cros_build_lib.Die('Unable to create stabilizing branch in %s' %
290 overlay)
Ryan Cuif0d57b42011-07-27 17:43:42 -0700291
Mike Frysinger110750a2012-03-26 14:19:20 -0400292 # In the case of uprevving overlays that have patches applied to them,
293 # include the patched changes in the stabilizing branch.
294 if existing_branch:
Mike Frysinger7dafd0e2012-05-08 15:47:16 -0400295 cros_build_lib.RunCommand(['git', 'rebase', existing_branch],
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400296 print_cmd=False, cwd=overlay)
Mike Frysinger110750a2012-03-26 14:19:20 -0400297
298 for ebuild in ebuilds:
David James1b363582012-12-17 11:53:11 -0800299 if options.verbose:
300 cros_build_lib.Info('Working on %s', ebuild.package)
Mike Frysinger110750a2012-03-26 14:19:20 -0400301 try:
Mike Frysinger110750a2012-03-26 14:19:20 -0400302 new_package = ebuild.RevWorkOnEBuild(options.srcroot)
303 if new_package:
304 revved_packages.append(ebuild.package)
305 new_package_atoms.append('=%s' % new_package)
306 except (OSError, IOError):
307 cros_build_lib.Warning('Cannot rev %s\n' % ebuild.package +
308 'Note you will have to go into %s '
309 'and reset the git repo yourself.' % overlay)
310 raise
311
Mike Frysingerfddaeb52012-11-20 11:17:31 -0500312 if cros_build_lib.IsInsideChroot():
313 # Regenerate caches if need be. We do this all the time to
314 # catch when users make changes without updating cache files.
Brian Harring9fdd23d2012-12-07 12:09:08 -0800315 queue.put([overlay])
Chris Sosadad0d322011-01-31 16:37:33 -0800316
David Jamesa8457b52011-05-28 00:03:20 -0700317 if command == 'commit':
Mike Frysingerfddaeb52012-11-20 11:17:31 -0500318 if cros_build_lib.IsInsideChroot():
319 CleanStalePackages(options.boards.split(':'), new_package_atoms)
David Jamesa8457b52011-05-28 00:03:20 -0700320 if options.drop_file:
Mike Frysingerfddaeb52012-11-20 11:17:31 -0500321 osutils.WriteFile(options.drop_file, ' '.join(revved_packages))