blob: 384a058ce960eb3197ef8c0c6201b9081536d351 [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.
Chris Sosabf153872011-04-28 14:21:09 -070037 package_atoms: The actual package atom to unmerge.
38 """
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
Mike Frysingerb7ab9b82012-04-04 16:22:43 -040053 if package_atoms:
Mike Frysinger0a647fc2012-08-06 14:36:05 -040054 runcmd(['emerge' + suffix, '-q', '--unmerge'] + package_atoms,
55 extra_env={'CLEAN_DELAY': '0'})
Mike Frysingerb7ab9b82012-04-04 16:22:43 -040056 runcmd(['eclean' + suffix, '-d', 'packages'],
57 redirect_stdout=True, redirect_stderr=True)
58
59 tasks = []
David Jamescc09c9b2012-01-26 22:10:13 -080060 for board in boards:
Mike Frysingerb7ab9b82012-04-04 16:22:43 -040061 tasks.append([board])
62 tasks.append([None])
63
David James6450a0a2012-12-04 07:59:53 -080064 parallel.RunTasksInProcessPool(_CleanStalePackages, tasks)
Chris Sosadad0d322011-01-31 16:37:33 -080065
66
Mike Frysinger2ebe3732012-05-08 17:04:12 -040067# TODO(build): This code needs to be gutted and rebased to cros_build_lib.
68def _DoWeHaveLocalCommits(stable_branch, tracking_branch, cwd):
Chris Sosadad0d322011-01-31 16:37:33 -080069 """Returns true if there are local commits."""
David James97d95872012-11-16 15:09:56 -080070 current_branch = git.GetCurrentBranch(cwd)
Brian Harring609dc4e2012-05-07 02:17:44 -070071
72 if current_branch != stable_branch:
Chris Sosadad0d322011-01-31 16:37:33 -080073 return False
David James97d95872012-11-16 15:09:56 -080074 output = git.RunGit(
Brian Harring609dc4e2012-05-07 02:17:44 -070075 cwd, ['rev-parse', 'HEAD', tracking_branch]).output.split()
Brian Harring5b86b5e2012-05-25 18:27:40 -070076 return output[0] != output[1]
Chris Sosadad0d322011-01-31 16:37:33 -080077
78
David James1b363582012-12-17 11:53:11 -080079def _CheckSaneArguments(command, options):
Chris Sosadad0d322011-01-31 16:37:33 -080080 """Checks to make sure the flags are sane. Dies if arguments are not sane."""
81 if not command in COMMAND_DICTIONARY.keys():
82 _PrintUsageAndDie('%s is not a valid command' % command)
Chris Sosa62ad8522011-03-08 17:46:17 -080083 if not options.packages and command == 'commit' and not options.all:
Chris Sosadad0d322011-01-31 16:37:33 -080084 _PrintUsageAndDie('Please specify at least one package')
Mike Frysinger8fd67dc2012-12-03 23:51:18 -050085 if options.boards:
86 cros_build_lib.AssertInsideChroot()
Chris Sosa62ad8522011-03-08 17:46:17 -080087 if not os.path.isdir(options.srcroot):
Chris Sosadad0d322011-01-31 16:37:33 -080088 _PrintUsageAndDie('srcroot is not a valid path')
Chris Sosa62ad8522011-03-08 17:46:17 -080089 options.srcroot = os.path.abspath(options.srcroot)
Chris Sosadad0d322011-01-31 16:37:33 -080090
91
92def _PrintUsageAndDie(error_message=''):
93 """Prints optional error_message the usage and returns an error exit code."""
94 command_usage = 'Commands: \n'
95 # Add keys and usage information from dictionary.
96 commands = sorted(COMMAND_DICTIONARY.keys())
97 for command in commands:
98 command_usage += ' %s: %s\n' % (command, COMMAND_DICTIONARY[command])
99 commands_str = '|'.join(commands)
Chris Sosac13bba52011-05-24 15:14:09 -0700100 cros_build_lib.Warning('Usage: %s FLAGS [%s]\n\n%s' % (
101 sys.argv[0], commands_str, command_usage))
Chris Sosadad0d322011-01-31 16:37:33 -0800102 if error_message:
Chris Sosac13bba52011-05-24 15:14:09 -0700103 cros_build_lib.Die(error_message)
Chris Sosadad0d322011-01-31 16:37:33 -0800104 else:
105 sys.exit(1)
106
107
Chris Sosadad0d322011-01-31 16:37:33 -0800108# ======================= End Global Helper Functions ========================
109
110
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400111def PushChange(stable_branch, tracking_branch, dryrun, cwd):
Chris Sosadad0d322011-01-31 16:37:33 -0800112 """Pushes commits in the stable_branch to the remote git repository.
113
David Jamesee2da622012-02-23 09:32:16 -0800114 Pushes local commits from calls to CommitChange to the remote git
David James66009462012-03-25 10:08:38 -0700115 repository specified by current working directory. If changes are
116 found to commit, they will be merged to the merge branch and pushed.
117 In that case, the local repository will be left on the merge branch.
Chris Sosadad0d322011-01-31 16:37:33 -0800118
119 Args:
120 stable_branch: The local branch with commits we want to push.
121 tracking_branch: The tracking branch of the local branch.
Chris Sosa62ad8522011-03-08 17:46:17 -0800122 dryrun: Use git push --dryrun to emulate a push.
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400123 cwd: The directory to run commands in.
Chris Sosadad0d322011-01-31 16:37:33 -0800124 Raises:
125 OSError: Error occurred while pushing.
126 """
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400127 if not _DoWeHaveLocalCommits(stable_branch, tracking_branch, cwd):
Brian Harring609dc4e2012-05-07 02:17:44 -0700128 cros_build_lib.Info('No work found to push in %s. Exiting', cwd)
Chris Sosadad0d322011-01-31 16:37:33 -0800129 return
130
David James66009462012-03-25 10:08:38 -0700131 # For the commit queue, our local branch may contain commits that were
132 # just tested and pushed during the CommitQueueCompletion stage. Sync
133 # and rebase our local branch on top of the remote commits.
David James97d95872012-11-16 15:09:56 -0800134 remote, push_branch = git.GetTrackingBranch(cwd, for_push=True)
135 git.SyncPushBranch(cwd, remote, push_branch)
David James66009462012-03-25 10:08:38 -0700136
137 # Check whether any local changes remain after the sync.
Brian Harring609dc4e2012-05-07 02:17:44 -0700138 if not _DoWeHaveLocalCommits(stable_branch, push_branch, cwd):
139 cros_build_lib.Info('All changes already pushed for %s. Exiting', cwd)
David James66009462012-03-25 10:08:38 -0700140 return
141
Mike Frysinger7dafd0e2012-05-08 15:47:16 -0400142 description = cros_build_lib.RunCommandCaptureOutput(
Brian Harring609dc4e2012-05-07 02:17:44 -0700143 ['git', 'log', '--format=format:%s%n%n%b', '%s..%s' % (
144 push_branch, stable_branch)], cwd=cwd).output
Chris Sosadad0d322011-01-31 16:37:33 -0800145 description = 'Marking set of ebuilds as stable\n\n%s' % description
Brian Harring609dc4e2012-05-07 02:17:44 -0700146 cros_build_lib.Info('For %s, using description %s', cwd, description)
David James97d95872012-11-16 15:09:56 -0800147 git.CreatePushBranch(constants.MERGE_BRANCH, cwd)
148 git.RunGit(cwd, ['merge', '--squash', stable_branch])
149 git.RunGit(cwd, ['commit', '-m', description])
150 git.RunGit(cwd, ['config', 'push.default', 'tracking'])
151 git.PushWithRetry(constants.MERGE_BRANCH, cwd, dryrun=dryrun)
Chris Sosadad0d322011-01-31 16:37:33 -0800152
153
154class GitBranch(object):
155 """Wrapper class for a git branch."""
156
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400157 def __init__(self, branch_name, tracking_branch, cwd):
Chris Sosadad0d322011-01-31 16:37:33 -0800158 """Sets up variables but does not create the branch."""
159 self.branch_name = branch_name
160 self.tracking_branch = tracking_branch
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400161 self.cwd = cwd
Chris Sosadad0d322011-01-31 16:37:33 -0800162
163 def CreateBranch(self):
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400164 self.Checkout()
Chris Sosadad0d322011-01-31 16:37:33 -0800165
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400166 def Checkout(self, branch=None):
Chris Sosadad0d322011-01-31 16:37:33 -0800167 """Function used to check out to another GitBranch."""
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400168 if not branch:
169 branch = self.branch_name
170 if branch == self.tracking_branch or self.Exists(branch):
171 git_cmd = ['git', 'checkout', '-f', branch]
Chris Sosadad0d322011-01-31 16:37:33 -0800172 else:
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400173 git_cmd = ['repo', 'start', branch, '.']
174 cros_build_lib.RunCommandCaptureOutput(git_cmd, print_cmd=False,
175 cwd=self.cwd)
Chris Sosadad0d322011-01-31 16:37:33 -0800176
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400177 def Exists(self, branch=None):
Chris Sosadad0d322011-01-31 16:37:33 -0800178 """Returns True if the branch exists."""
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400179 if not branch:
180 branch = self.branch_name
Mike Frysinger7dafd0e2012-05-08 15:47:16 -0400181 branches = cros_build_lib.RunCommandCaptureOutput(['git', 'branch'],
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400182 print_cmd=False,
183 cwd=self.cwd).output
184 return branch in branches.split()
Chris Sosadad0d322011-01-31 16:37:33 -0800185
186
David James1b363582012-12-17 11:53:11 -0800187def main(_argv):
Chris Sosa62ad8522011-03-08 17:46:17 -0800188 parser = optparse.OptionParser('cros_mark_as_stable OPTIONS packages')
189 parser.add_option('--all', action='store_true',
190 help='Mark all packages as stable.')
Mike Frysinger587bd562012-11-19 16:41:39 -0500191 parser.add_option('-b', '--boards', default='',
David Jamescc09c9b2012-01-26 22:10:13 -0800192 help='Colon-separated list of boards')
Chris Sosa62ad8522011-03-08 17:46:17 -0800193 parser.add_option('--drop_file',
194 help='File to list packages that were revved.')
195 parser.add_option('--dryrun', action='store_true',
196 help='Passes dry-run to git push if pushing a change.')
197 parser.add_option('-o', '--overlays',
198 help='Colon-separated list of overlays to modify.')
199 parser.add_option('-p', '--packages',
200 help='Colon separated list of packages to rev.')
201 parser.add_option('-r', '--srcroot',
Mike Frysingerfddaeb52012-11-20 11:17:31 -0500202 default=os.path.join(constants.SOURCE_ROOT, 'src'),
Chris Sosa62ad8522011-03-08 17:46:17 -0800203 help='Path to root src directory.')
Chris Sosa62ad8522011-03-08 17:46:17 -0800204 parser.add_option('--verbose', action='store_true',
205 help='Prints out debug info.')
206 (options, args) = parser.parse_args()
Chris Sosadad0d322011-01-31 16:37:33 -0800207
J. Richard Barnette2fa9fd62011-12-02 17:10:10 -0800208 portage_utilities.EBuild.VERBOSE = options.verbose
Chris Sosa62ad8522011-03-08 17:46:17 -0800209
210 if len(args) != 1:
Ryan Cui05a31ba2011-05-31 17:47:37 -0700211 _PrintUsageAndDie('Must specify a valid command [commit, push]')
Chris Sosa62ad8522011-03-08 17:46:17 -0800212
213 command = args[0]
214 package_list = None
215 if options.packages:
216 package_list = options.packages.split(':')
217
David James1b363582012-12-17 11:53:11 -0800218 _CheckSaneArguments(command, options)
Chris Sosa62ad8522011-03-08 17:46:17 -0800219 if options.overlays:
Chris Sosadad0d322011-01-31 16:37:33 -0800220 overlays = {}
Chris Sosa62ad8522011-03-08 17:46:17 -0800221 for path in options.overlays.split(':'):
Ryan Cui05a31ba2011-05-31 17:47:37 -0700222 if not os.path.isdir(path):
Chris Sosac13bba52011-05-24 15:14:09 -0700223 cros_build_lib.Die('Cannot find overlay: %s' % path)
Chris Sosadad0d322011-01-31 16:37:33 -0800224 overlays[path] = []
225 else:
Chris Sosac13bba52011-05-24 15:14:09 -0700226 cros_build_lib.Warning('Missing --overlays argument')
Chris Sosadad0d322011-01-31 16:37:33 -0800227 overlays = {
Chris Sosa62ad8522011-03-08 17:46:17 -0800228 '%s/private-overlays/chromeos-overlay' % options.srcroot: [],
229 '%s/third_party/chromiumos-overlay' % options.srcroot: []
Chris Sosadad0d322011-01-31 16:37:33 -0800230 }
231
232 if command == 'commit':
J. Richard Barnettef6697cf2011-11-18 12:42:08 -0800233 portage_utilities.BuildEBuildDictionary(
J. Richard Barnetted422f622011-11-17 09:39:46 -0800234 overlays, options.all, package_list)
Chris Sosadad0d322011-01-31 16:37:33 -0800235
David James97d95872012-11-16 15:09:56 -0800236 manifest = git.ManifestCheckout.Cached(options.srcroot)
Ryan Cui4656a3c2011-05-24 12:30:30 -0700237
David Jamesa8457b52011-05-28 00:03:20 -0700238 # Contains the array of packages we actually revved.
239 revved_packages = []
240 new_package_atoms = []
241
Mike Frysingerb9bd2f82012-05-08 14:55:23 -0400242 # Slight optimization hack: process the chromiumos overlay before any other
243 # cros-workon overlay first so we can do background cache generation in it.
244 # A perfect solution would walk all the overlays, figure out any dependencies
245 # between them (with layout.conf), and then process them in dependency order.
246 # However, this operation isn't slow enough to warrant that level of
247 # complexity, so we'll just special case the main overlay.
248 #
249 # Similarly, generate the cache in the portage-stable tree asap. We know
250 # we won't have any cros-workon packages in there, so generating the cache
251 # is the only thing it'll be doing. The chromiumos overlay instead might
252 # have revbumping to do before it can generate the cache.
Mike Frysinger110750a2012-03-26 14:19:20 -0400253 keys = overlays.keys()
Mike Frysingerb9bd2f82012-05-08 14:55:23 -0400254 for overlay in ('/third_party/chromiumos-overlay',
255 '/third_party/portage-stable'):
256 for k in keys:
257 if k.endswith(overlay):
258 keys.remove(k)
259 keys.insert(0, k)
260 break
Chris Sosadad0d322011-01-31 16:37:33 -0800261
Brian Harring9fdd23d2012-12-07 12:09:08 -0800262 with parallel.BackgroundTaskRunner(portage_utilities.RegenCache) as queue:
Mike Frysinger110750a2012-03-26 14:19:20 -0400263 for overlay in keys:
264 ebuilds = overlays[overlay]
265 if not os.path.isdir(overlay):
266 cros_build_lib.Warning("Skipping %s" % overlay)
267 continue
Chris Sosadad0d322011-01-31 16:37:33 -0800268
Brian Harringe08e4422012-05-30 12:40:50 -0700269 # Note we intentionally work from the non push tracking branch;
270 # everything built thus far has been against it (meaning, http mirrors),
271 # thus we should honor that. During the actual push, the code switches
272 # to the correct urls, and does an appropriate rebasing.
David James97d95872012-11-16 15:09:56 -0800273 tracking_branch = git.GetTrackingBranchViaManifest(
Brian Harringe08e4422012-05-30 12:40:50 -0700274 overlay, manifest=manifest)[1]
Brian Harring609dc4e2012-05-07 02:17:44 -0700275
Mike Frysinger110750a2012-03-26 14:19:20 -0400276 if command == 'push':
277 PushChange(constants.STABLE_EBUILD_BRANCH, tracking_branch,
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400278 options.dryrun, cwd=overlay)
Mike Frysinger303083d2012-07-16 14:57:24 -0400279 elif command == 'commit':
David James97d95872012-11-16 15:09:56 -0800280 existing_branch = git.GetCurrentBranch(overlay)
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400281 work_branch = GitBranch(constants.STABLE_EBUILD_BRANCH, tracking_branch,
282 cwd=overlay)
Mike Frysinger110750a2012-03-26 14:19:20 -0400283 work_branch.CreateBranch()
284 if not work_branch.Exists():
285 cros_build_lib.Die('Unable to create stabilizing branch in %s' %
286 overlay)
Ryan Cuif0d57b42011-07-27 17:43:42 -0700287
Mike Frysinger110750a2012-03-26 14:19:20 -0400288 # In the case of uprevving overlays that have patches applied to them,
289 # include the patched changes in the stabilizing branch.
290 if existing_branch:
Mike Frysinger7dafd0e2012-05-08 15:47:16 -0400291 cros_build_lib.RunCommand(['git', 'rebase', existing_branch],
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400292 print_cmd=False, cwd=overlay)
Mike Frysinger110750a2012-03-26 14:19:20 -0400293
294 for ebuild in ebuilds:
David James1b363582012-12-17 11:53:11 -0800295 if options.verbose:
296 cros_build_lib.Info('Working on %s', ebuild.package)
Mike Frysinger110750a2012-03-26 14:19:20 -0400297 try:
Mike Frysinger110750a2012-03-26 14:19:20 -0400298 new_package = ebuild.RevWorkOnEBuild(options.srcroot)
299 if new_package:
300 revved_packages.append(ebuild.package)
301 new_package_atoms.append('=%s' % new_package)
302 except (OSError, IOError):
303 cros_build_lib.Warning('Cannot rev %s\n' % ebuild.package +
304 'Note you will have to go into %s '
305 'and reset the git repo yourself.' % overlay)
306 raise
307
Mike Frysingerfddaeb52012-11-20 11:17:31 -0500308 if cros_build_lib.IsInsideChroot():
309 # Regenerate caches if need be. We do this all the time to
310 # catch when users make changes without updating cache files.
Brian Harring9fdd23d2012-12-07 12:09:08 -0800311 queue.put([overlay])
Chris Sosadad0d322011-01-31 16:37:33 -0800312
David Jamesa8457b52011-05-28 00:03:20 -0700313 if command == 'commit':
Mike Frysingerfddaeb52012-11-20 11:17:31 -0500314 if cros_build_lib.IsInsideChroot():
315 CleanStalePackages(options.boards.split(':'), new_package_atoms)
David Jamesa8457b52011-05-28 00:03:20 -0700316 if options.drop_file:
Mike Frysingerfddaeb52012-11-20 11:17:31 -0500317 osutils.WriteFile(options.drop_file, ' '.join(revved_packages))