blob: ce60e2eed1826f68759dba73fa7470a5bb15199b [file] [log] [blame]
Chris Sosadad0d322011-01-31 16:37:33 -08001#!/usr/bin/python
2
Chris Sosac13bba52011-05-24 15:14:09 -07003# Copyright (c) 2011 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
Ryan Cui4656a3c2011-05-24 12:30:30 -07009import constants
Chris Sosa8be39132011-04-14 12:09:24 -070010import filecmp
Chris Sosadad0d322011-01-31 16:37:33 -080011import fileinput
Chris Sosa62ad8522011-03-08 17:46:17 -080012import optparse
Chris Sosadad0d322011-01-31 16:37:33 -080013import os
14import re
15import shutil
16import subprocess
17import sys
18
Chris Sosa471532a2011-02-01 15:10:06 -080019if __name__ == '__main__':
Ryan Cui4656a3c2011-05-24 12:30:30 -070020 sys.path.append(constants.SOURCE_ROOT)
Chris Sosa471532a2011-02-01 15:10:06 -080021
Chris Sosac13bba52011-05-24 15:14:09 -070022from chromite.lib import cros_build_lib
23
Chris Sosadad0d322011-01-31 16:37:33 -080024
Chris Sosa62ad8522011-03-08 17:46:17 -080025# TODO(sosa): Remove during OO refactor.
26VERBOSE = False
Chris Sosadad0d322011-01-31 16:37:33 -080027
28# Takes two strings, package_name and commit_id.
29_GIT_COMMIT_MESSAGE = 'Marking 9999 ebuild for %s with commit %s as stable.'
30
31# Dictionary of valid commands with usage information.
32COMMAND_DICTIONARY = {
Chris Sosadad0d322011-01-31 16:37:33 -080033 'commit':
34 'Marks given ebuilds as stable locally',
35 'push':
36 'Pushes previous marking of ebuilds to remote repo',
37 }
38
Chris Sosadad0d322011-01-31 16:37:33 -080039
40def BestEBuild(ebuilds):
41 """Returns the newest EBuild from a list of EBuild objects."""
42 from portage.versions import vercmp
43 winner = ebuilds[0]
44 for ebuild in ebuilds[1:]:
45 if vercmp(winner.version, ebuild.version) < 0:
46 winner = ebuild
47 return winner
48
49# ======================= Global Helper Functions ========================
50
51
52def _Print(message):
53 """Verbose print function."""
Chris Sosac31bd2d2011-04-29 17:53:35 -070054 if VERBOSE:
Chris Sosac13bba52011-05-24 15:14:09 -070055 cros_build_lib.Info(message)
Chris Sosadad0d322011-01-31 16:37:33 -080056
57
Chris Sosabf153872011-04-28 14:21:09 -070058def CleanStalePackages(board, package_atoms):
59 """Cleans up stale package info from a previous build.
60 Args:
61 board: Board to clean the packages from.
62 package_atoms: The actual package atom to unmerge.
63 """
64 if package_atoms:
Chris Sosac13bba52011-05-24 15:14:09 -070065 cros_build_lib.Info('Cleaning up stale packages %s.' % package_atoms)
Chris Sosabf153872011-04-28 14:21:09 -070066 unmerge_board_cmd = ['emerge-%s' % board, '--unmerge']
67 unmerge_board_cmd.extend(package_atoms)
Chris Sosac13bba52011-05-24 15:14:09 -070068 cros_build_lib.RunCommand(unmerge_board_cmd)
Chris Sosadad0d322011-01-31 16:37:33 -080069
Chris Sosabf153872011-04-28 14:21:09 -070070 unmerge_host_cmd = ['sudo', 'emerge', '--unmerge']
71 unmerge_host_cmd.extend(package_atoms)
Chris Sosac13bba52011-05-24 15:14:09 -070072 cros_build_lib.RunCommand(unmerge_host_cmd)
Chris Sosadad0d322011-01-31 16:37:33 -080073
Chris Sosac13bba52011-05-24 15:14:09 -070074 cros_build_lib.RunCommand(['eclean-%s' % board, '-d', 'packages'],
75 redirect_stderr=True)
76 cros_build_lib.RunCommand(['sudo', 'eclean', '-d', 'packages'],
77 redirect_stderr=True)
Chris Sosadad0d322011-01-31 16:37:33 -080078
79
Chris Sosa95e85d92011-05-18 16:15:33 -070080class _BlackListManager(object):
81 """Small wrapper class to manage black lists for marking all packages."""
82 BLACK_LIST_FILE = os.path.join(os.path.dirname(os.path.realpath(__file__)),
83 'cros_mark_as_stable_blacklist')
84
85 def __init__(self):
86 """Initializes the black list manager."""
87 self.black_list_re_array = None
88 self._Initialize()
89
90 def _Initialize(self):
91 """Initializes the black list manager from a black list file."""
92 self.black_list_re_array = []
93 with open(self.BLACK_LIST_FILE) as file_handle:
94 for line in file_handle.readlines():
95 line = line.strip()
96 # Ignore comment lines.
97 if line and not line.startswith('#'):
98 line = line.rstrip()
99 package_array = line.split('/')
100 assert len(package_array) == 2, \
101 'Line %s does not match package format.' % line
102 category, package_name = package_array
103 self.black_list_re_array.append(
104 re.compile('.*/%s/%s/%s-.*\.ebuild' % (category, package_name,
105 package_name)))
106
107 def IsPackageBlackListed(self, path_to_ebuild):
108 """Returns True if the package given by the path is blacklisted."""
109 assert self.black_list_re_array != None, 'Black list not initialized.'
110
111 for re in self.black_list_re_array:
112 if re.match(path_to_ebuild):
113 return True
114
115 return False
116
117
118def _FindUprevCandidates(files, blacklist):
Chris Sosadad0d322011-01-31 16:37:33 -0800119 """Return a list of uprev candidates from specified list of files.
120
121 Usually an uprev candidate is a the stable ebuild in a cros_workon directory.
122 However, if no such stable ebuild exists (someone just checked in the 9999
123 ebuild), this is the unstable ebuild.
124
125 Args:
126 files: List of files.
127 """
128 workon_dir = False
129 stable_ebuilds = []
130 unstable_ebuilds = []
131 for path in files:
132 if path.endswith('.ebuild') and not os.path.islink(path):
133 ebuild = EBuild(path)
Chris Sosa95e85d92011-05-18 16:15:33 -0700134 if ebuild.is_workon and not blacklist.IsPackageBlackListed(path):
Chris Sosadad0d322011-01-31 16:37:33 -0800135 workon_dir = True
136 if ebuild.is_stable:
137 stable_ebuilds.append(ebuild)
138 else:
139 unstable_ebuilds.append(ebuild)
140
141 # If we found a workon ebuild in this directory, apply some sanity checks.
142 if workon_dir:
143 if len(unstable_ebuilds) > 1:
Chris Sosac13bba52011-05-24 15:14:09 -0700144 cros_build_lib.Die('Found multiple unstable ebuilds in %s' %
145 os.path.dirname(path))
Chris Sosadad0d322011-01-31 16:37:33 -0800146 if len(stable_ebuilds) > 1:
147 stable_ebuilds = [BestEBuild(stable_ebuilds)]
148
149 # Print a warning if multiple stable ebuilds are found in the same
150 # directory. Storing multiple stable ebuilds is error-prone because
151 # the older ebuilds will not get rev'd.
Chris Sosac13bba52011-05-24 15:14:09 -0700152 cros_build_lib.Warning('Found multiple stable ebuilds in %s' %
153 os.path.dirname(path))
Chris Sosadad0d322011-01-31 16:37:33 -0800154
155 if not unstable_ebuilds:
Chris Sosac13bba52011-05-24 15:14:09 -0700156 cros_build_lib.Die('Missing 9999 ebuild in %s' % os.path.dirname(path))
Chris Sosadad0d322011-01-31 16:37:33 -0800157 if not stable_ebuilds:
Chris Sosac13bba52011-05-24 15:14:09 -0700158 cros_build_lib.Warning('Missing stable ebuild in %s' %
159 os.path.dirname(path))
Chris Sosadad0d322011-01-31 16:37:33 -0800160 return unstable_ebuilds[0]
161
162 if stable_ebuilds:
163 return stable_ebuilds[0]
164 else:
165 return None
166
167
Chris Sosa95e85d92011-05-18 16:15:33 -0700168def _BuildEBuildDictionary(overlays, all, packages, blacklist):
Chris Sosadad0d322011-01-31 16:37:33 -0800169 """Build a dictionary of the ebuilds in the specified overlays.
170
171 overlays: A map which maps overlay directories to arrays of stable EBuilds
172 inside said directories.
173 all: Whether to include all ebuilds in the specified directories. If true,
174 then we gather all packages in the directories regardless of whether
175 they are in our set of packages.
176 packages: A set of the packages we want to gather.
177 """
178 for overlay in overlays:
Chris Sosa95e85d92011-05-18 16:15:33 -0700179 for package_dir, unused_dirs, files in os.walk(overlay):
Chris Sosadad0d322011-01-31 16:37:33 -0800180 # Add stable ebuilds to overlays[overlay].
181 paths = [os.path.join(package_dir, path) for path in files]
Chris Sosa95e85d92011-05-18 16:15:33 -0700182 ebuild = _FindUprevCandidates(paths, blacklist)
Chris Sosadad0d322011-01-31 16:37:33 -0800183
184 # If the --all option isn't used, we only want to update packages that
185 # are in packages.
186 if ebuild and (all or ebuild.package in packages):
187 overlays[overlay].append(ebuild)
188
189
190def _DoWeHaveLocalCommits(stable_branch, tracking_branch):
191 """Returns true if there are local commits."""
192 current_branch = _SimpleRunCommand('git branch | grep \*').split()[1]
193 if current_branch == stable_branch:
194 current_commit_id = _SimpleRunCommand('git rev-parse HEAD')
195 tracking_commit_id = _SimpleRunCommand('git rev-parse %s' % tracking_branch)
196 return current_commit_id != tracking_commit_id
197 else:
198 return False
199
200
Chris Sosa62ad8522011-03-08 17:46:17 -0800201def _CheckSaneArguments(package_list, command, options):
Chris Sosadad0d322011-01-31 16:37:33 -0800202 """Checks to make sure the flags are sane. Dies if arguments are not sane."""
203 if not command in COMMAND_DICTIONARY.keys():
204 _PrintUsageAndDie('%s is not a valid command' % command)
Chris Sosa62ad8522011-03-08 17:46:17 -0800205 if not options.packages and command == 'commit' and not options.all:
Chris Sosadad0d322011-01-31 16:37:33 -0800206 _PrintUsageAndDie('Please specify at least one package')
Chris Sosa62ad8522011-03-08 17:46:17 -0800207 if not options.board and command == 'commit':
Chris Sosadad0d322011-01-31 16:37:33 -0800208 _PrintUsageAndDie('Please specify a board')
Chris Sosa62ad8522011-03-08 17:46:17 -0800209 if not os.path.isdir(options.srcroot):
Chris Sosadad0d322011-01-31 16:37:33 -0800210 _PrintUsageAndDie('srcroot is not a valid path')
Chris Sosa62ad8522011-03-08 17:46:17 -0800211 options.srcroot = os.path.abspath(options.srcroot)
Chris Sosadad0d322011-01-31 16:37:33 -0800212
213
214def _PrintUsageAndDie(error_message=''):
215 """Prints optional error_message the usage and returns an error exit code."""
216 command_usage = 'Commands: \n'
217 # Add keys and usage information from dictionary.
218 commands = sorted(COMMAND_DICTIONARY.keys())
219 for command in commands:
220 command_usage += ' %s: %s\n' % (command, COMMAND_DICTIONARY[command])
221 commands_str = '|'.join(commands)
Chris Sosac13bba52011-05-24 15:14:09 -0700222 cros_build_lib.Warning('Usage: %s FLAGS [%s]\n\n%s' % (
223 sys.argv[0], commands_str, command_usage))
Chris Sosadad0d322011-01-31 16:37:33 -0800224 if error_message:
Chris Sosac13bba52011-05-24 15:14:09 -0700225 cros_build_lib.Die(error_message)
Chris Sosadad0d322011-01-31 16:37:33 -0800226 else:
227 sys.exit(1)
228
229
230def _SimpleRunCommand(command):
231 """Runs a shell command and returns stdout back to caller."""
232 _Print(' + %s' % command)
233 proc_handle = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True)
234 stdout = proc_handle.communicate()[0]
235 retcode = proc_handle.wait()
236 if retcode != 0:
237 _Print(stdout)
238 raise subprocess.CalledProcessError(retcode, command)
239 return stdout
240
241
242# ======================= End Global Helper Functions ========================
243
244
Chris Sosa62ad8522011-03-08 17:46:17 -0800245def PushChange(stable_branch, tracking_branch, dryrun):
Chris Sosadad0d322011-01-31 16:37:33 -0800246 """Pushes commits in the stable_branch to the remote git repository.
247
248 Pushes locals commits from calls to CommitChange to the remote git
249 repository specified by current working directory.
250
251 Args:
252 stable_branch: The local branch with commits we want to push.
253 tracking_branch: The tracking branch of the local branch.
Chris Sosa62ad8522011-03-08 17:46:17 -0800254 dryrun: Use git push --dryrun to emulate a push.
Chris Sosadad0d322011-01-31 16:37:33 -0800255 Raises:
256 OSError: Error occurred while pushing.
257 """
Chris Sosadad0d322011-01-31 16:37:33 -0800258 # Sanity check to make sure we're on a stabilizing branch before pushing.
259 if not _DoWeHaveLocalCommits(stable_branch, tracking_branch):
Chris Sosac13bba52011-05-24 15:14:09 -0700260 cros_build_lib.Info('Not work found to push. Exiting')
Chris Sosadad0d322011-01-31 16:37:33 -0800261 return
262
263 description = _SimpleRunCommand('git log --format=format:%s%n%n%b ' +
264 tracking_branch + '..')
265 description = 'Marking set of ebuilds as stable\n\n%s' % description
Chris Sosac13bba52011-05-24 15:14:09 -0700266 cros_build_lib.Info('Using description %s' % description)
Ryan Cui05a31ba2011-05-31 17:47:37 -0700267 merge_branch = GitBranch(constants.MERGE_BRANCH, tracking_branch)
Chris Sosac13bba52011-05-24 15:14:09 -0700268 if merge_branch.Exists():
269 merge_branch.Delete()
270 _SimpleRunCommand('repo sync .')
271 merge_branch.CreateBranch()
272 if not merge_branch.Exists():
273 cros_build_lib.Die('Unable to create merge branch.')
274 _SimpleRunCommand('git merge --squash %s' % stable_branch)
275 _SimpleRunCommand('git commit -m "%s"' % description)
276 _SimpleRunCommand('git config push.default tracking')
Ryan Cui05a31ba2011-05-31 17:47:37 -0700277 cros_build_lib.GitPushWithRetry(constants.MERGE_BRANCH, cwd='.',
278 dryrun=dryrun)
Chris Sosadad0d322011-01-31 16:37:33 -0800279
280
281class GitBranch(object):
282 """Wrapper class for a git branch."""
283
284 def __init__(self, branch_name, tracking_branch):
285 """Sets up variables but does not create the branch."""
286 self.branch_name = branch_name
287 self.tracking_branch = tracking_branch
288
289 def CreateBranch(self):
290 GitBranch.Checkout(self)
291
292 @classmethod
293 def Checkout(cls, target):
294 """Function used to check out to another GitBranch."""
295 if target.branch_name == target.tracking_branch or target.Exists():
296 git_cmd = 'git checkout %s -f' % target.branch_name
297 else:
Chris Sosa8cae72b2011-06-07 17:05:03 -0700298 git_cmd = 'repo start %s .' % target.branch_name
Chris Sosadad0d322011-01-31 16:37:33 -0800299 _SimpleRunCommand(git_cmd)
300
301 def Exists(self):
302 """Returns True if the branch exists."""
303 branch_cmd = 'git branch'
304 branches = _SimpleRunCommand(branch_cmd)
305 return self.branch_name in branches.split()
306
307 def Delete(self):
308 """Deletes the branch and returns the user to the master branch.
309
310 Returns True on success.
311 """
312 tracking_branch = GitBranch(self.tracking_branch, self.tracking_branch)
313 GitBranch.Checkout(tracking_branch)
Chris Sosa8cae72b2011-06-07 17:05:03 -0700314 delete_cmd = 'repo abandon %s' % self.branch_name
Chris Sosadad0d322011-01-31 16:37:33 -0800315 _SimpleRunCommand(delete_cmd)
316
317
318class EBuild(object):
319 """Wrapper class for information about an ebuild."""
320
321 def __init__(self, path):
322 """Sets up data about an ebuild from its path."""
323 from portage.versions import pkgsplit
324 unused_path, self.category, self.pkgname, filename = path.rsplit('/', 3)
325 unused_pkgname, self.version_no_rev, rev = pkgsplit(
326 filename.replace('.ebuild', ''))
327
328 self.ebuild_path_no_version = os.path.join(
329 os.path.dirname(path), self.pkgname)
330 self.ebuild_path_no_revision = '%s-%s' % (self.ebuild_path_no_version,
331 self.version_no_rev)
332 self.current_revision = int(rev.replace('r', ''))
333 self.version = '%s-%s' % (self.version_no_rev, rev)
334 self.package = '%s/%s' % (self.category, self.pkgname)
335 self.ebuild_path = path
336
337 self.is_workon = False
338 self.is_stable = False
339
340 for line in fileinput.input(path):
341 if line.startswith('inherit ') and 'cros-workon' in line:
342 self.is_workon = True
343 elif (line.startswith('KEYWORDS=') and '~' not in line and
344 ('amd64' in line or 'x86' in line or 'arm' in line)):
345 self.is_stable = True
346 fileinput.close()
347
Chris Sosa62ad8522011-03-08 17:46:17 -0800348 def GetCommitId(self, srcroot):
Chris Sosadad0d322011-01-31 16:37:33 -0800349 """Get the commit id for this ebuild."""
350 # Grab and evaluate CROS_WORKON variables from this ebuild.
351 unstable_ebuild = '%s-9999.ebuild' % self.ebuild_path_no_version
352 cmd = ('export CROS_WORKON_LOCALNAME="%s" CROS_WORKON_PROJECT="%s"; '
353 'eval $(grep -E "^CROS_WORKON" %s) && '
354 'echo $CROS_WORKON_PROJECT '
355 '$CROS_WORKON_LOCALNAME/$CROS_WORKON_SUBDIR'
356 % (self.pkgname, self.pkgname, unstable_ebuild))
357 project, subdir = _SimpleRunCommand(cmd).split()
358
359 # Calculate srcdir.
Chris Sosadad0d322011-01-31 16:37:33 -0800360 if self.category == 'chromeos-base':
361 dir = 'platform'
362 else:
363 dir = 'third_party'
Chris Sosac31bd2d2011-04-29 17:53:35 -0700364
Chris Sosadad0d322011-01-31 16:37:33 -0800365 srcdir = os.path.join(srcroot, dir, subdir)
366
367 if not os.path.isdir(srcdir):
Chris Sosac13bba52011-05-24 15:14:09 -0700368 cros_build_lib.Die('Cannot find commit id for %s' % self.ebuild_path)
Chris Sosadad0d322011-01-31 16:37:33 -0800369
370 # Verify that we're grabbing the commit id from the right project name.
371 # NOTE: chromeos-kernel has the wrong project name, so it fails this
372 # check.
373 # TODO(davidjames): Fix the project name in the chromeos-kernel ebuild.
Chris Sosae0a70482011-05-02 19:18:34 -0700374 cmd = ('cd %s && ( git config --get remote.cros.projectname || '
375 'git config --get remote.cros-internal.projectname )') % srcdir
Chris Sosac31bd2d2011-04-29 17:53:35 -0700376 actual_project = _SimpleRunCommand(cmd).rstrip()
Chris Sosadad0d322011-01-31 16:37:33 -0800377 if project not in (actual_project, 'chromeos-kernel'):
Chris Sosac13bba52011-05-24 15:14:09 -0700378 cros_build_lib.Die('Project name mismatch for %s (%s != %s)' % (
379 unstable_ebuild, project, actual_project))
Chris Sosadad0d322011-01-31 16:37:33 -0800380
381 # Get commit id.
382 output = _SimpleRunCommand('cd %s && git rev-parse HEAD' % srcdir)
383 if not output:
Chris Sosac13bba52011-05-24 15:14:09 -0700384 cros_build_lib.Die('Missing commit id for %s' % self.ebuild_path)
Chris Sosadad0d322011-01-31 16:37:33 -0800385 return output.rstrip()
386
387
388class EBuildStableMarker(object):
389 """Class that revs the ebuild and commits locally or pushes the change."""
390
391 def __init__(self, ebuild):
392 assert ebuild
393 self._ebuild = ebuild
394
395 @classmethod
396 def MarkAsStable(cls, unstable_ebuild_path, new_stable_ebuild_path,
397 commit_keyword, commit_value, redirect_file=None,
398 make_stable=True):
399 """Static function that creates a revved stable ebuild.
400
401 This function assumes you have already figured out the name of the new
402 stable ebuild path and then creates that file from the given unstable
403 ebuild and marks it as stable. If the commit_value is set, it also
404 set the commit_keyword=commit_value pair in the ebuild.
405
406 Args:
407 unstable_ebuild_path: The path to the unstable ebuild.
408 new_stable_ebuild_path: The path you want to use for the new stable
409 ebuild.
410 commit_keyword: Optional keyword to set in the ebuild to mark it as
411 stable.
412 commit_value: Value to set the above keyword to.
413 redirect_file: Optionally redirect output of new ebuild somewhere else.
414 make_stable: Actually make the ebuild stable.
415 """
416 shutil.copyfile(unstable_ebuild_path, new_stable_ebuild_path)
417 for line in fileinput.input(new_stable_ebuild_path, inplace=1):
418 # Has to be done here to get changes to sys.stdout from fileinput.input.
419 if not redirect_file:
420 redirect_file = sys.stdout
421 if line.startswith('KEYWORDS'):
422 # Actually mark this file as stable by removing ~'s.
423 if make_stable:
424 redirect_file.write(line.replace('~', ''))
425 else:
426 redirect_file.write(line)
427 elif line.startswith('EAPI'):
428 # Always add new commit_id after EAPI definition.
429 redirect_file.write(line)
430 if commit_keyword and commit_value:
431 redirect_file.write('%s="%s"\n' % (commit_keyword, commit_value))
432 elif not line.startswith(commit_keyword):
433 # Skip old commit_keyword definition.
434 redirect_file.write(line)
435 fileinput.close()
436
437 def RevWorkOnEBuild(self, commit_id, redirect_file=None):
438 """Revs a workon ebuild given the git commit hash.
439
440 By default this class overwrites a new ebuild given the normal
441 ebuild rev'ing logic. However, a user can specify a redirect_file
442 to redirect the new stable ebuild to another file.
443
444 Args:
445 commit_id: String corresponding to the commit hash of the developer
446 package to rev.
447 redirect_file: Optional file to write the new ebuild. By default
448 it is written using the standard rev'ing logic. This file must be
449 opened and closed by the caller.
450
451 Raises:
452 OSError: Error occurred while creating a new ebuild.
453 IOError: Error occurred while writing to the new revved ebuild file.
454 Returns:
455 If the revved package is different than the old ebuild, return the full
456 revved package name, including the version number. Otherwise, return None.
457 """
458 if self._ebuild.is_stable:
459 stable_version_no_rev = self._ebuild.version_no_rev
460 else:
461 # If given unstable ebuild, use 0.0.1 rather than 9999.
462 stable_version_no_rev = '0.0.1'
463
464 new_version = '%s-r%d' % (stable_version_no_rev,
465 self._ebuild.current_revision + 1)
466 new_stable_ebuild_path = '%s-%s.ebuild' % (
467 self._ebuild.ebuild_path_no_version, new_version)
468
469 _Print('Creating new stable ebuild %s' % new_stable_ebuild_path)
470 unstable_ebuild_path = ('%s-9999.ebuild' %
471 self._ebuild.ebuild_path_no_version)
472 if not os.path.exists(unstable_ebuild_path):
Chris Sosac13bba52011-05-24 15:14:09 -0700473 cros_build_lib.Die('Missing unstable ebuild: %s' % unstable_ebuild_path)
Chris Sosadad0d322011-01-31 16:37:33 -0800474
475 self.MarkAsStable(unstable_ebuild_path, new_stable_ebuild_path,
476 'CROS_WORKON_COMMIT', commit_id, redirect_file)
477
478 old_ebuild_path = self._ebuild.ebuild_path
Chris Sosa8be39132011-04-14 12:09:24 -0700479 if filecmp.cmp(old_ebuild_path, new_stable_ebuild_path, shallow=False):
Chris Sosadad0d322011-01-31 16:37:33 -0800480 os.unlink(new_stable_ebuild_path)
481 return None
482 else:
483 _Print('Adding new stable ebuild to git')
484 _SimpleRunCommand('git add %s' % new_stable_ebuild_path)
485
486 if self._ebuild.is_stable:
487 _Print('Removing old ebuild from git')
488 _SimpleRunCommand('git rm %s' % old_ebuild_path)
489
490 return '%s-%s' % (self._ebuild.package, new_version)
491
492 @classmethod
493 def CommitChange(cls, message):
494 """Commits current changes in git locally with given commit message.
495
496 Args:
497 message: the commit string to write when committing to git.
498
499 Raises:
500 OSError: Error occurred while committing.
501 """
Chris Sosac13bba52011-05-24 15:14:09 -0700502 cros_build_lib.Info('Committing changes with commit message: %s' % message)
Chris Sosadad0d322011-01-31 16:37:33 -0800503 git_commit_cmd = 'git commit -am "%s"' % message
504 _SimpleRunCommand(git_commit_cmd)
505
506
507def main(argv):
Chris Sosa62ad8522011-03-08 17:46:17 -0800508 parser = optparse.OptionParser('cros_mark_as_stable OPTIONS packages')
509 parser.add_option('--all', action='store_true',
510 help='Mark all packages as stable.')
511 parser.add_option('-b', '--board',
512 help='Board for which the package belongs.')
513 parser.add_option('--drop_file',
514 help='File to list packages that were revved.')
515 parser.add_option('--dryrun', action='store_true',
516 help='Passes dry-run to git push if pushing a change.')
517 parser.add_option('-o', '--overlays',
518 help='Colon-separated list of overlays to modify.')
519 parser.add_option('-p', '--packages',
520 help='Colon separated list of packages to rev.')
521 parser.add_option('-r', '--srcroot',
522 default='%s/trunk/src' % os.environ['HOME'],
523 help='Path to root src directory.')
Chris Sosa62ad8522011-03-08 17:46:17 -0800524 parser.add_option('--verbose', action='store_true',
525 help='Prints out debug info.')
526 (options, args) = parser.parse_args()
Chris Sosadad0d322011-01-31 16:37:33 -0800527
Chris Sosa62ad8522011-03-08 17:46:17 -0800528 global VERBOSE
529 VERBOSE = options.verbose
530
531 if len(args) != 1:
Ryan Cui05a31ba2011-05-31 17:47:37 -0700532 _PrintUsageAndDie('Must specify a valid command [commit, push]')
Chris Sosa62ad8522011-03-08 17:46:17 -0800533
534 command = args[0]
535 package_list = None
536 if options.packages:
537 package_list = options.packages.split(':')
538
539 _CheckSaneArguments(package_list, command, options)
540 if options.overlays:
Chris Sosadad0d322011-01-31 16:37:33 -0800541 overlays = {}
Chris Sosa62ad8522011-03-08 17:46:17 -0800542 for path in options.overlays.split(':'):
Ryan Cui05a31ba2011-05-31 17:47:37 -0700543 if not os.path.isdir(path):
Chris Sosac13bba52011-05-24 15:14:09 -0700544 cros_build_lib.Die('Cannot find overlay: %s' % path)
Chris Sosadad0d322011-01-31 16:37:33 -0800545 overlays[path] = []
546 else:
Chris Sosac13bba52011-05-24 15:14:09 -0700547 cros_build_lib.Warning('Missing --overlays argument')
Chris Sosadad0d322011-01-31 16:37:33 -0800548 overlays = {
Chris Sosa62ad8522011-03-08 17:46:17 -0800549 '%s/private-overlays/chromeos-overlay' % options.srcroot: [],
550 '%s/third_party/chromiumos-overlay' % options.srcroot: []
Chris Sosadad0d322011-01-31 16:37:33 -0800551 }
552
553 if command == 'commit':
Chris Sosa95e85d92011-05-18 16:15:33 -0700554 blacklist = _BlackListManager()
555 _BuildEBuildDictionary(overlays, options.all, package_list, blacklist)
Chris Sosadad0d322011-01-31 16:37:33 -0800556
Ryan Cui556aad52011-05-31 15:42:28 -0700557 tracking_branch = cros_build_lib.GetManifestDefaultBranch(options.srcroot)
558 tracking_branch = 'remotes/m/' + tracking_branch
Ryan Cui4656a3c2011-05-24 12:30:30 -0700559
David Jamesa8457b52011-05-28 00:03:20 -0700560 # Contains the array of packages we actually revved.
561 revved_packages = []
562 new_package_atoms = []
563
Chris Sosadad0d322011-01-31 16:37:33 -0800564 for overlay, ebuilds in overlays.items():
565 if not os.path.isdir(overlay):
Chris Sosac13bba52011-05-24 15:14:09 -0700566 cros_build_lib.Warning("Skipping %s" % overlay)
Chris Sosadad0d322011-01-31 16:37:33 -0800567 continue
568
569 # TODO(davidjames): Currently, all code that interacts with git depends on
570 # the cwd being set to the overlay directory. We should instead pass in
571 # this parameter so that we don't need to modify the cwd globally.
572 os.chdir(overlay)
573
Ryan Cui05a31ba2011-05-31 17:47:37 -0700574 if command == 'push':
575 PushChange(constants.STABLE_EBUILD_BRANCH, tracking_branch,
576 options.dryrun)
Chris Sosadad0d322011-01-31 16:37:33 -0800577 elif command == 'commit' and ebuilds:
Ryan Cuif0d57b42011-07-27 17:43:42 -0700578 existing_branch = cros_build_lib.GetCurrentBranch('.')
Ryan Cui05a31ba2011-05-31 17:47:37 -0700579 work_branch = GitBranch(constants.STABLE_EBUILD_BRANCH, tracking_branch)
Chris Sosadad0d322011-01-31 16:37:33 -0800580 work_branch.CreateBranch()
581 if not work_branch.Exists():
Chris Sosac13bba52011-05-24 15:14:09 -0700582 cros_build_lib.Die('Unable to create stabilizing branch in %s' %
583 overlay)
Chris Sosadad0d322011-01-31 16:37:33 -0800584
Ryan Cuif0d57b42011-07-27 17:43:42 -0700585 # In the case of uprevving overlays that have patches applied to them,
586 # include the patched changes in the stabilizing branch.
587 if existing_branch:
588 _SimpleRunCommand('git rebase %s' % existing_branch)
589
Chris Sosadad0d322011-01-31 16:37:33 -0800590 for ebuild in ebuilds:
591 try:
592 _Print('Working on %s' % ebuild.package)
593 worker = EBuildStableMarker(ebuild)
Chris Sosa62ad8522011-03-08 17:46:17 -0800594 commit_id = ebuild.GetCommitId(options.srcroot)
Chris Sosadad0d322011-01-31 16:37:33 -0800595 new_package = worker.RevWorkOnEBuild(commit_id)
596 if new_package:
597 message = _GIT_COMMIT_MESSAGE % (ebuild.package, commit_id)
598 worker.CommitChange(message)
599 revved_packages.append(ebuild.package)
600 new_package_atoms.append('=%s' % new_package)
601 except (OSError, IOError):
Ryan Cui556aad52011-05-31 15:42:28 -0700602 cros_build_lib.Warning('Cannot rev %s\n' % ebuild.package +
Chris Sosadad0d322011-01-31 16:37:33 -0800603 'Note you will have to go into %s '
604 'and reset the git repo yourself.' % overlay)
605 raise
606
David Jamesa8457b52011-05-28 00:03:20 -0700607 if command == 'commit':
608 CleanStalePackages(options.board, new_package_atoms)
609 if options.drop_file:
610 fh = open(options.drop_file, 'w')
611 fh.write(' '.join(revved_packages))
612 fh.close()
Chris Sosadad0d322011-01-31 16:37:33 -0800613
614
615if __name__ == '__main__':
616 main(sys.argv)