blob: 00940b374c78438b8581a829861b283213dd163c [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
Mike Frysinger110750a2012-03-26 14:19:20 -04009import multiprocessing
Chris Sosa62ad8522011-03-08 17:46:17 -080010import optparse
Chris Sosadad0d322011-01-31 16:37:33 -080011import os
Chris Sosadad0d322011-01-31 16:37:33 -080012import sys
13
Mike Frysinger6cb624a2012-05-24 18:17:38 -040014from chromite.buildbot import constants
Mike Frysingerb7ab9b82012-04-04 16:22:43 -040015from chromite.buildbot import cbuildbot_background as background
David James66009462012-03-25 10:08:38 -070016from chromite.buildbot import portage_utilities
Chris Sosac13bba52011-05-24 15:14:09 -070017from chromite.lib import cros_build_lib
18
Chris Sosadad0d322011-01-31 16:37:33 -080019
Chris Sosa62ad8522011-03-08 17:46:17 -080020# TODO(sosa): Remove during OO refactor.
21VERBOSE = False
Chris Sosadad0d322011-01-31 16:37:33 -080022
Chris Sosadad0d322011-01-31 16:37:33 -080023# Dictionary of valid commands with usage information.
24COMMAND_DICTIONARY = {
Chris Sosadad0d322011-01-31 16:37:33 -080025 'commit':
26 'Marks given ebuilds as stable locally',
27 'push':
28 'Pushes previous marking of ebuilds to remote repo',
29 }
30
Chris Sosadad0d322011-01-31 16:37:33 -080031
Chris Sosadad0d322011-01-31 16:37:33 -080032# ======================= Global Helper Functions ========================
33
34
35def _Print(message):
36 """Verbose print function."""
Chris Sosac31bd2d2011-04-29 17:53:35 -070037 if VERBOSE:
Chris Sosac13bba52011-05-24 15:14:09 -070038 cros_build_lib.Info(message)
Chris Sosadad0d322011-01-31 16:37:33 -080039
40
David Jamescc09c9b2012-01-26 22:10:13 -080041def CleanStalePackages(boards, package_atoms):
Chris Sosabf153872011-04-28 14:21:09 -070042 """Cleans up stale package info from a previous build.
43 Args:
David Jamescc09c9b2012-01-26 22:10:13 -080044 boards: Boards to clean the packages from.
Chris Sosabf153872011-04-28 14:21:09 -070045 package_atoms: The actual package atom to unmerge.
46 """
47 if package_atoms:
Chris Sosac13bba52011-05-24 15:14:09 -070048 cros_build_lib.Info('Cleaning up stale packages %s.' % package_atoms)
Chris Sosadad0d322011-01-31 16:37:33 -080049
Mike Frysingerb7ab9b82012-04-04 16:22:43 -040050 # First unmerge all the packages for a board, then eclean it.
51 # We need these two steps to run in order (unmerge/eclean),
52 # but we can let all the boards run in parallel.
53 def _CleanStalePackages(board):
54 if board:
55 suffix = '-' + board
56 runcmd = cros_build_lib.RunCommand
57 else:
58 suffix = ''
59 runcmd = cros_build_lib.SudoRunCommand
Chris Sosadad0d322011-01-31 16:37:33 -080060
Mike Frysingerb7ab9b82012-04-04 16:22:43 -040061 if package_atoms:
62 runcmd(['emerge' + suffix, '-q', '--unmerge'] + package_atoms);
63 runcmd(['eclean' + suffix, '-d', 'packages'],
64 redirect_stdout=True, redirect_stderr=True)
65
66 tasks = []
David Jamescc09c9b2012-01-26 22:10:13 -080067 for board in boards:
Mike Frysingerb7ab9b82012-04-04 16:22:43 -040068 tasks.append([board])
69 tasks.append([None])
70
71 background.RunTasksInProcessPool(_CleanStalePackages, tasks)
Chris Sosadad0d322011-01-31 16:37:33 -080072
73
Mike Frysinger2ebe3732012-05-08 17:04:12 -040074# TODO(build): This code needs to be gutted and rebased to cros_build_lib.
75def _DoWeHaveLocalCommits(stable_branch, tracking_branch, cwd):
Chris Sosadad0d322011-01-31 16:37:33 -080076 """Returns true if there are local commits."""
Brian Harring609dc4e2012-05-07 02:17:44 -070077 current_branch = cros_build_lib.GetCurrentBranch(cwd)
78
79 if current_branch != stable_branch:
Chris Sosadad0d322011-01-31 16:37:33 -080080 return False
Brian Harring609dc4e2012-05-07 02:17:44 -070081 output = cros_build_lib.RunGitCommand(
82 cwd, ['rev-parse', 'HEAD', tracking_branch]).output.split()
Brian Harring5b86b5e2012-05-25 18:27:40 -070083 return output[0] != output[1]
Chris Sosadad0d322011-01-31 16:37:33 -080084
85
Chris Sosa62ad8522011-03-08 17:46:17 -080086def _CheckSaneArguments(package_list, command, options):
Chris Sosadad0d322011-01-31 16:37:33 -080087 """Checks to make sure the flags are sane. Dies if arguments are not sane."""
88 if not command in COMMAND_DICTIONARY.keys():
89 _PrintUsageAndDie('%s is not a valid command' % command)
Chris Sosa62ad8522011-03-08 17:46:17 -080090 if not options.packages and command == 'commit' and not options.all:
Chris Sosadad0d322011-01-31 16:37:33 -080091 _PrintUsageAndDie('Please specify at least one package')
David Jamescc09c9b2012-01-26 22:10:13 -080092 if not options.boards and command == 'commit':
Chris Sosadad0d322011-01-31 16:37:33 -080093 _PrintUsageAndDie('Please specify a board')
Chris Sosa62ad8522011-03-08 17:46:17 -080094 if not os.path.isdir(options.srcroot):
Chris Sosadad0d322011-01-31 16:37:33 -080095 _PrintUsageAndDie('srcroot is not a valid path')
Chris Sosa62ad8522011-03-08 17:46:17 -080096 options.srcroot = os.path.abspath(options.srcroot)
Chris Sosadad0d322011-01-31 16:37:33 -080097
98
99def _PrintUsageAndDie(error_message=''):
100 """Prints optional error_message the usage and returns an error exit code."""
101 command_usage = 'Commands: \n'
102 # Add keys and usage information from dictionary.
103 commands = sorted(COMMAND_DICTIONARY.keys())
104 for command in commands:
105 command_usage += ' %s: %s\n' % (command, COMMAND_DICTIONARY[command])
106 commands_str = '|'.join(commands)
Chris Sosac13bba52011-05-24 15:14:09 -0700107 cros_build_lib.Warning('Usage: %s FLAGS [%s]\n\n%s' % (
108 sys.argv[0], commands_str, command_usage))
Chris Sosadad0d322011-01-31 16:37:33 -0800109 if error_message:
Chris Sosac13bba52011-05-24 15:14:09 -0700110 cros_build_lib.Die(error_message)
Chris Sosadad0d322011-01-31 16:37:33 -0800111 else:
112 sys.exit(1)
113
114
Chris Sosadad0d322011-01-31 16:37:33 -0800115# ======================= End Global Helper Functions ========================
116
117
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400118def PushChange(stable_branch, tracking_branch, dryrun, cwd):
Chris Sosadad0d322011-01-31 16:37:33 -0800119 """Pushes commits in the stable_branch to the remote git repository.
120
David Jamesee2da622012-02-23 09:32:16 -0800121 Pushes local commits from calls to CommitChange to the remote git
David James66009462012-03-25 10:08:38 -0700122 repository specified by current working directory. If changes are
123 found to commit, they will be merged to the merge branch and pushed.
124 In that case, the local repository will be left on the merge branch.
Chris Sosadad0d322011-01-31 16:37:33 -0800125
126 Args:
127 stable_branch: The local branch with commits we want to push.
128 tracking_branch: The tracking branch of the local branch.
Chris Sosa62ad8522011-03-08 17:46:17 -0800129 dryrun: Use git push --dryrun to emulate a push.
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400130 cwd: The directory to run commands in.
Chris Sosadad0d322011-01-31 16:37:33 -0800131 Raises:
132 OSError: Error occurred while pushing.
133 """
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400134 if not _DoWeHaveLocalCommits(stable_branch, tracking_branch, cwd):
Brian Harring609dc4e2012-05-07 02:17:44 -0700135 cros_build_lib.Info('No work found to push in %s. Exiting', cwd)
Chris Sosadad0d322011-01-31 16:37:33 -0800136 return
137
David James66009462012-03-25 10:08:38 -0700138 # For the commit queue, our local branch may contain commits that were
139 # just tested and pushed during the CommitQueueCompletion stage. Sync
140 # and rebase our local branch on top of the remote commits.
Brian Harring609dc4e2012-05-07 02:17:44 -0700141 remote, push_branch = cros_build_lib.GetTrackingBranch(cwd, for_push=True)
David James66009462012-03-25 10:08:38 -0700142 cros_build_lib.SyncPushBranch(cwd, remote, push_branch)
143
144 # Check whether any local changes remain after the sync.
Brian Harring609dc4e2012-05-07 02:17:44 -0700145 if not _DoWeHaveLocalCommits(stable_branch, push_branch, cwd):
146 cros_build_lib.Info('All changes already pushed for %s. Exiting', cwd)
David James66009462012-03-25 10:08:38 -0700147 return
148
Mike Frysinger7dafd0e2012-05-08 15:47:16 -0400149 description = cros_build_lib.RunCommandCaptureOutput(
Brian Harring609dc4e2012-05-07 02:17:44 -0700150 ['git', 'log', '--format=format:%s%n%n%b', '%s..%s' % (
151 push_branch, stable_branch)], cwd=cwd).output
Chris Sosadad0d322011-01-31 16:37:33 -0800152 description = 'Marking set of ebuilds as stable\n\n%s' % description
Brian Harring609dc4e2012-05-07 02:17:44 -0700153 cros_build_lib.Info('For %s, using description %s', cwd, description)
David James66009462012-03-25 10:08:38 -0700154 cros_build_lib.CreatePushBranch(constants.MERGE_BRANCH, cwd)
Brian Harring609dc4e2012-05-07 02:17:44 -0700155 cros_build_lib.RunGitCommand(cwd, ['merge', '--squash', stable_branch])
156 cros_build_lib.RunGitCommand(cwd, ['commit', '-m', description])
157 cros_build_lib.RunGitCommand(cwd, ['config', 'push.default', 'tracking'])
158 cros_build_lib.GitPushWithRetry(constants.MERGE_BRANCH, cwd,
Ryan Cui05a31ba2011-05-31 17:47:37 -0700159 dryrun=dryrun)
Chris Sosadad0d322011-01-31 16:37:33 -0800160
161
162class GitBranch(object):
163 """Wrapper class for a git branch."""
164
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400165 def __init__(self, branch_name, tracking_branch, cwd):
Chris Sosadad0d322011-01-31 16:37:33 -0800166 """Sets up variables but does not create the branch."""
167 self.branch_name = branch_name
168 self.tracking_branch = tracking_branch
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400169 self.cwd = cwd
Chris Sosadad0d322011-01-31 16:37:33 -0800170
171 def CreateBranch(self):
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400172 self.Checkout()
Chris Sosadad0d322011-01-31 16:37:33 -0800173
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400174 def Checkout(self, branch=None):
Chris Sosadad0d322011-01-31 16:37:33 -0800175 """Function used to check out to another GitBranch."""
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400176 if not branch:
177 branch = self.branch_name
178 if branch == self.tracking_branch or self.Exists(branch):
179 git_cmd = ['git', 'checkout', '-f', branch]
Chris Sosadad0d322011-01-31 16:37:33 -0800180 else:
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400181 git_cmd = ['repo', 'start', branch, '.']
182 cros_build_lib.RunCommandCaptureOutput(git_cmd, print_cmd=False,
183 cwd=self.cwd)
Chris Sosadad0d322011-01-31 16:37:33 -0800184
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400185 def Exists(self, branch=None):
Chris Sosadad0d322011-01-31 16:37:33 -0800186 """Returns True if the branch exists."""
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400187 if not branch:
188 branch = self.branch_name
Mike Frysinger7dafd0e2012-05-08 15:47:16 -0400189 branches = cros_build_lib.RunCommandCaptureOutput(['git', 'branch'],
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400190 print_cmd=False,
191 cwd=self.cwd).output
192 return branch in branches.split()
Chris Sosadad0d322011-01-31 16:37:33 -0800193
194
Mike Frysinger368bbb72012-05-23 15:57:10 -0400195def main(argv):
Chris Sosa62ad8522011-03-08 17:46:17 -0800196 parser = optparse.OptionParser('cros_mark_as_stable OPTIONS packages')
197 parser.add_option('--all', action='store_true',
198 help='Mark all packages as stable.')
David Jamescc09c9b2012-01-26 22:10:13 -0800199 parser.add_option('-b', '--boards',
200 help='Colon-separated list of boards')
Chris Sosa62ad8522011-03-08 17:46:17 -0800201 parser.add_option('--drop_file',
202 help='File to list packages that were revved.')
203 parser.add_option('--dryrun', action='store_true',
204 help='Passes dry-run to git push if pushing a change.')
205 parser.add_option('-o', '--overlays',
206 help='Colon-separated list of overlays to modify.')
207 parser.add_option('-p', '--packages',
208 help='Colon separated list of packages to rev.')
209 parser.add_option('-r', '--srcroot',
210 default='%s/trunk/src' % os.environ['HOME'],
211 help='Path to root src directory.')
Chris Sosa62ad8522011-03-08 17:46:17 -0800212 parser.add_option('--verbose', action='store_true',
213 help='Prints out debug info.')
214 (options, args) = parser.parse_args()
Chris Sosadad0d322011-01-31 16:37:33 -0800215
Chris Sosa62ad8522011-03-08 17:46:17 -0800216 global VERBOSE
217 VERBOSE = options.verbose
J. Richard Barnette2fa9fd62011-12-02 17:10:10 -0800218 portage_utilities.EBuild.VERBOSE = options.verbose
Chris Sosa62ad8522011-03-08 17:46:17 -0800219
220 if len(args) != 1:
Ryan Cui05a31ba2011-05-31 17:47:37 -0700221 _PrintUsageAndDie('Must specify a valid command [commit, push]')
Chris Sosa62ad8522011-03-08 17:46:17 -0800222
223 command = args[0]
224 package_list = None
225 if options.packages:
226 package_list = options.packages.split(':')
227
228 _CheckSaneArguments(package_list, command, options)
229 if options.overlays:
Chris Sosadad0d322011-01-31 16:37:33 -0800230 overlays = {}
Chris Sosa62ad8522011-03-08 17:46:17 -0800231 for path in options.overlays.split(':'):
Ryan Cui05a31ba2011-05-31 17:47:37 -0700232 if not os.path.isdir(path):
Chris Sosac13bba52011-05-24 15:14:09 -0700233 cros_build_lib.Die('Cannot find overlay: %s' % path)
Chris Sosadad0d322011-01-31 16:37:33 -0800234 overlays[path] = []
235 else:
Chris Sosac13bba52011-05-24 15:14:09 -0700236 cros_build_lib.Warning('Missing --overlays argument')
Chris Sosadad0d322011-01-31 16:37:33 -0800237 overlays = {
Chris Sosa62ad8522011-03-08 17:46:17 -0800238 '%s/private-overlays/chromeos-overlay' % options.srcroot: [],
239 '%s/third_party/chromiumos-overlay' % options.srcroot: []
Chris Sosadad0d322011-01-31 16:37:33 -0800240 }
241
242 if command == 'commit':
J. Richard Barnettef6697cf2011-11-18 12:42:08 -0800243 portage_utilities.BuildEBuildDictionary(
J. Richard Barnetted422f622011-11-17 09:39:46 -0800244 overlays, options.all, package_list)
Chris Sosadad0d322011-01-31 16:37:33 -0800245
Brian Harring609dc4e2012-05-07 02:17:44 -0700246 manifest = cros_build_lib.ManifestCheckout.Cached(options.srcroot)
Ryan Cui4656a3c2011-05-24 12:30:30 -0700247
David Jamesa8457b52011-05-28 00:03:20 -0700248 # Contains the array of packages we actually revved.
249 revved_packages = []
250 new_package_atoms = []
251
Mike Frysingerb9bd2f82012-05-08 14:55:23 -0400252 # Slight optimization hack: process the chromiumos overlay before any other
253 # cros-workon overlay first so we can do background cache generation in it.
254 # A perfect solution would walk all the overlays, figure out any dependencies
255 # between them (with layout.conf), and then process them in dependency order.
256 # However, this operation isn't slow enough to warrant that level of
257 # complexity, so we'll just special case the main overlay.
258 #
259 # Similarly, generate the cache in the portage-stable tree asap. We know
260 # we won't have any cros-workon packages in there, so generating the cache
261 # is the only thing it'll be doing. The chromiumos overlay instead might
262 # have revbumping to do before it can generate the cache.
Mike Frysinger110750a2012-03-26 14:19:20 -0400263 keys = overlays.keys()
Mike Frysingerb9bd2f82012-05-08 14:55:23 -0400264 for overlay in ('/third_party/chromiumos-overlay',
265 '/third_party/portage-stable'):
266 for k in keys:
267 if k.endswith(overlay):
268 keys.remove(k)
269 keys.insert(0, k)
270 break
Chris Sosadad0d322011-01-31 16:37:33 -0800271
Mike Frysinger110750a2012-03-26 14:19:20 -0400272 cache_queue = multiprocessing.Queue()
273 with background.BackgroundTaskRunner(cache_queue,
274 portage_utilities.RegenCache):
275 for overlay in keys:
276 ebuilds = overlays[overlay]
277 if not os.path.isdir(overlay):
278 cros_build_lib.Warning("Skipping %s" % overlay)
279 continue
Chris Sosadad0d322011-01-31 16:37:33 -0800280
Brian Harringe08e4422012-05-30 12:40:50 -0700281 # Note we intentionally work from the non push tracking branch;
282 # everything built thus far has been against it (meaning, http mirrors),
283 # thus we should honor that. During the actual push, the code switches
284 # to the correct urls, and does an appropriate rebasing.
Brian Harring609dc4e2012-05-07 02:17:44 -0700285 tracking_branch = cros_build_lib.GetTrackingBranchViaManifest(
Brian Harringe08e4422012-05-30 12:40:50 -0700286 overlay, manifest=manifest)[1]
Brian Harring609dc4e2012-05-07 02:17:44 -0700287
Mike Frysinger110750a2012-03-26 14:19:20 -0400288 if command == 'push':
289 PushChange(constants.STABLE_EBUILD_BRANCH, tracking_branch,
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400290 options.dryrun, cwd=overlay)
Mike Frysinger303083d2012-07-16 14:57:24 -0400291 elif command == 'commit':
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400292 existing_branch = cros_build_lib.GetCurrentBranch(overlay)
293 work_branch = GitBranch(constants.STABLE_EBUILD_BRANCH, tracking_branch,
294 cwd=overlay)
Mike Frysinger110750a2012-03-26 14:19:20 -0400295 work_branch.CreateBranch()
296 if not work_branch.Exists():
297 cros_build_lib.Die('Unable to create stabilizing branch in %s' %
298 overlay)
Ryan Cuif0d57b42011-07-27 17:43:42 -0700299
Mike Frysinger110750a2012-03-26 14:19:20 -0400300 # In the case of uprevving overlays that have patches applied to them,
301 # include the patched changes in the stabilizing branch.
302 if existing_branch:
Mike Frysinger7dafd0e2012-05-08 15:47:16 -0400303 cros_build_lib.RunCommand(['git', 'rebase', existing_branch],
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400304 print_cmd=False, cwd=overlay)
Mike Frysinger110750a2012-03-26 14:19:20 -0400305
306 for ebuild in ebuilds:
307 try:
308 _Print('Working on %s' % ebuild.package)
309 new_package = ebuild.RevWorkOnEBuild(options.srcroot)
310 if new_package:
311 revved_packages.append(ebuild.package)
312 new_package_atoms.append('=%s' % new_package)
313 except (OSError, IOError):
314 cros_build_lib.Warning('Cannot rev %s\n' % ebuild.package +
315 'Note you will have to go into %s '
316 'and reset the git repo yourself.' % overlay)
317 raise
318
319 # Regenerate caches if need be. We do this all the time to
320 # catch when users make changes without updating cache files.
321 cache_queue.put([overlay])
Chris Sosadad0d322011-01-31 16:37:33 -0800322
David Jamesa8457b52011-05-28 00:03:20 -0700323 if command == 'commit':
David Jamescc09c9b2012-01-26 22:10:13 -0800324 CleanStalePackages(options.boards.split(':'), new_package_atoms)
David Jamesa8457b52011-05-28 00:03:20 -0700325 if options.drop_file:
326 fh = open(options.drop_file, 'w')
327 fh.write(' '.join(revved_packages))
328 fh.close()