Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(252)

Side by Side Diff: cros_mark_as_stable.py

Issue 5172003: The major change is refactoring to make functions more accessible for other modules. (Closed) Base URL: http://git.chromium.org/git/crosutils.git@master
Patch Set: white space Created 10 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | cros_mark_as_stable_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 2
3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. 3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be 4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file. 5 # found in the LICENSE file.
6 6
7 """This module uprevs a given package's ebuild to the next revision.""" 7 """This module uprevs a given package's ebuild to the next revision."""
8 8
9 9
10 import fileinput 10 import fileinput
11 import gflags 11 import gflags
12 import os 12 import os
13 import re 13 import re
14 import shutil 14 import shutil
15 import subprocess 15 import subprocess
16 import sys 16 import sys
17 17
18 sys.path.append(os.path.join(os.path.dirname(__file__), 'lib')) 18 sys.path.append(os.path.join(os.path.dirname(__file__), 'lib'))
19 from cros_build_lib import Info, RunCommand, Warning, Die 19 from cros_build_lib import Info, RunCommand, Warning, Die
20 20
21 21 gflags.DEFINE_boolean('all', False,
22 'Mark all packages as stable.')
22 gflags.DEFINE_string('board', '', 23 gflags.DEFINE_string('board', '',
23 'Board for which the package belongs.', short_name='b') 24 'Board for which the package belongs.', short_name='b')
24 gflags.DEFINE_string('overlays', '', 25 gflags.DEFINE_string('overlays', '',
25 'Colon-separated list of overlays to modify.', 26 'Colon-separated list of overlays to modify.',
26 short_name='o') 27 short_name='o')
27 gflags.DEFINE_string('packages', '', 28 gflags.DEFINE_string('packages', '',
28 'Colon-separated list of packages to mark as stable.', 29 'Colon-separated list of packages to mark as stable.',
29 short_name='p') 30 short_name='p')
30 gflags.DEFINE_string('push_options', '', 31 gflags.DEFINE_string('push_options', '',
31 'Options to use with git-cl push using push command.') 32 'Options to use with git-cl push using push command.')
32 gflags.DEFINE_string('srcroot', '%s/trunk/src' % os.environ['HOME'], 33 gflags.DEFINE_string('srcroot', '%s/trunk/src' % os.environ['HOME'],
33 'Path to root src directory.', 34 'Path to root src directory.',
34 short_name='r') 35 short_name='r')
35 gflags.DEFINE_string('tracking_branch', 'cros/master', 36 gflags.DEFINE_string('tracking_branch', 'cros/master',
36 'Used with commit to specify branch to track against.', 37 'Used with commit to specify branch to track against.',
37 short_name='t') 38 short_name='t')
38 gflags.DEFINE_boolean('all', False,
39 'Mark all packages as stable.')
40 gflags.DEFINE_boolean('verbose', False, 39 gflags.DEFINE_boolean('verbose', False,
41 'Prints out verbose information about what is going on.', 40 'Prints out verbose information about what is going on.',
42 short_name='v') 41 short_name='v')
43 42
44 43
45 # Takes two strings, package_name and commit_id. 44 # Takes two strings, package_name and commit_id.
46 _GIT_COMMIT_MESSAGE = \ 45 _GIT_COMMIT_MESSAGE = \
47 'Marking 9999 ebuild for %s with commit %s as stable.' 46 'Marking 9999 ebuild for %s with commit %s as stable.'
48 47
49 # Dictionary of valid commands with usage information. 48 # Dictionary of valid commands with usage information.
50 _COMMAND_DICTIONARY = { 49 COMMAND_DICTIONARY = {
51 'clean': 50 'clean':
52 'Cleans up previous calls to either commit or push', 51 'Cleans up previous calls to either commit or push',
53 'commit': 52 'commit':
54 'Marks given ebuilds as stable locally', 53 'Marks given ebuilds as stable locally',
55 'push': 54 'push':
56 'Pushes previous marking of ebuilds to remote repo', 55 'Pushes previous marking of ebuilds to remote repo',
57 } 56 }
58 57
59 # Name used for stabilizing branch. 58 # Name used for stabilizing branch.
60 _STABLE_BRANCH_NAME = 'stabilizing_branch' 59 _STABLE_BRANCH_NAME = 'stabilizing_branch'
61 60
61
62 def BestEBuild(ebuilds):
63 """Returns the newest EBuild from a list of EBuild objects."""
64 from portage.versions import vercmp
65 winner = ebuilds[0]
66 for ebuild in ebuilds[1:]:
67 if vercmp(winner.version, ebuild.version) < 0:
68 winner = ebuild
69 return winner
70
62 # ======================= Global Helper Functions ======================== 71 # ======================= Global Helper Functions ========================
63 72
64 73
65 def _Print(message): 74 def _Print(message):
66 """Verbose print function.""" 75 """Verbose print function."""
67 if gflags.FLAGS.verbose: 76 if gflags.FLAGS.verbose:
68 Info(message) 77 Info(message)
69 78
70 79
71 def _CleanStalePackages(board, package_array): 80 def _CleanStalePackages(board, package_array):
72 """Cleans up stale package info from a previous build.""" 81 """Cleans up stale package info from a previous build."""
73 Info('Cleaning up stale packages %s.' % package_array) 82 Info('Cleaning up stale packages %s.' % package_array)
74 unmerge_board_cmd = ['emerge-%s' % board, '--unmerge'] 83 unmerge_board_cmd = ['emerge-%s' % board, '--unmerge']
75 unmerge_board_cmd.extend(package_array) 84 unmerge_board_cmd.extend(package_array)
76 RunCommand(unmerge_board_cmd) 85 RunCommand(unmerge_board_cmd)
77 86
78 unmerge_host_cmd = ['sudo', 'emerge', '--unmerge'] 87 unmerge_host_cmd = ['sudo', 'emerge', '--unmerge']
79 unmerge_host_cmd.extend(package_array) 88 unmerge_host_cmd.extend(package_array)
80 RunCommand(unmerge_host_cmd) 89 RunCommand(unmerge_host_cmd)
81 90
82 RunCommand(['eclean-%s' % board, '-d', 'packages'], redirect_stderr=True) 91 RunCommand(['eclean-%s' % board, '-d', 'packages'], redirect_stderr=True)
83 RunCommand(['sudo', 'eclean', '-d', 'packages'], redirect_stderr=True) 92 RunCommand(['sudo', 'eclean', '-d', 'packages'], redirect_stderr=True)
84 93
85 94
86 def _BestEBuild(ebuilds):
87 """Returns the newest EBuild from a list of EBuild objects."""
88 from portage.versions import vercmp
89 winner = ebuilds[0]
90 for ebuild in ebuilds[1:]:
91 if vercmp(winner.version, ebuild.version) < 0:
92 winner = ebuild
93 return winner
94
95
96 def _FindUprevCandidates(files): 95 def _FindUprevCandidates(files):
97 """Return a list of uprev candidates from specified list of files. 96 """Return a list of uprev candidates from specified list of files.
98 97
99 Usually an uprev candidate is a the stable ebuild in a cros_workon directory. 98 Usually an uprev candidate is a the stable ebuild in a cros_workon directory.
100 However, if no such stable ebuild exists (someone just checked in the 9999 99 However, if no such stable ebuild exists (someone just checked in the 9999
101 ebuild), this is the unstable ebuild. 100 ebuild), this is the unstable ebuild.
102 101
103 Args: 102 Args:
104 files: List of files. 103 files: List of files.
105 """ 104 """
106 workon_dir = False 105 workon_dir = False
107 stable_ebuilds = [] 106 stable_ebuilds = []
108 unstable_ebuilds = [] 107 unstable_ebuilds = []
109 for path in files: 108 for path in files:
110 if path.endswith('.ebuild') and not os.path.islink(path): 109 if path.endswith('.ebuild') and not os.path.islink(path):
111 ebuild = _EBuild(path) 110 ebuild = EBuild(path)
112 if ebuild.is_workon: 111 if ebuild.is_workon:
113 workon_dir = True 112 workon_dir = True
114 if ebuild.is_stable: 113 if ebuild.is_stable:
115 stable_ebuilds.append(ebuild) 114 stable_ebuilds.append(ebuild)
116 else: 115 else:
117 unstable_ebuilds.append(ebuild) 116 unstable_ebuilds.append(ebuild)
118 117
119 # If we found a workon ebuild in this directory, apply some sanity checks. 118 # If we found a workon ebuild in this directory, apply some sanity checks.
120 if workon_dir: 119 if workon_dir:
121 if len(unstable_ebuilds) > 1: 120 if len(unstable_ebuilds) > 1:
122 Die('Found multiple unstable ebuilds in %s' % os.path.dirname(path)) 121 Die('Found multiple unstable ebuilds in %s' % os.path.dirname(path))
123 if len(stable_ebuilds) > 1: 122 if len(stable_ebuilds) > 1:
124 stable_ebuilds = [_BestEBuild(stable_ebuilds)] 123 stable_ebuilds = [BestEBuild(stable_ebuilds)]
125 124
126 # Print a warning if multiple stable ebuilds are found in the same 125 # Print a warning if multiple stable ebuilds are found in the same
127 # directory. Storing multiple stable ebuilds is error-prone because 126 # directory. Storing multiple stable ebuilds is error-prone because
128 # the older ebuilds will not get rev'd. 127 # the older ebuilds will not get rev'd.
129 # 128 #
130 # We make a special exception for x11-drivers/xf86-video-msm for legacy 129 # We make a special exception for x11-drivers/xf86-video-msm for legacy
131 # reasons. 130 # reasons.
132 if stable_ebuilds[0].package != 'x11-drivers/xf86-video-msm': 131 if stable_ebuilds[0].package != 'x11-drivers/xf86-video-msm':
133 Warning('Found multiple stable ebuilds in %s' % os.path.dirname(path)) 132 Warning('Found multiple stable ebuilds in %s' % os.path.dirname(path))
134 133
(...skipping 24 matching lines...) Expand all
159 # Add stable ebuilds to overlays[overlay]. 158 # Add stable ebuilds to overlays[overlay].
160 paths = [os.path.join(package_dir, path) for path in files] 159 paths = [os.path.join(package_dir, path) for path in files]
161 ebuild = _FindUprevCandidates(paths) 160 ebuild = _FindUprevCandidates(paths)
162 161
163 # If the --all option isn't used, we only want to update packages that 162 # If the --all option isn't used, we only want to update packages that
164 # are in packages. 163 # are in packages.
165 if ebuild and (all or ebuild.package in packages): 164 if ebuild and (all or ebuild.package in packages):
166 overlays[overlay].append(ebuild) 165 overlays[overlay].append(ebuild)
167 166
168 167
169 def _CheckOnStabilizingBranch(): 168 def _CheckOnStabilizingBranch(stable_branch):
170 """Returns true if the git branch is on the stabilizing branch.""" 169 """Returns true if the git branch is on the stabilizing branch."""
171 current_branch = _SimpleRunCommand('git branch | grep \*').split()[1] 170 current_branch = _SimpleRunCommand('git branch | grep \*').split()[1]
172 return current_branch == _STABLE_BRANCH_NAME 171 return current_branch == stable_branch
173 172
174 173
175 def _CheckSaneArguments(package_list, command): 174 def _CheckSaneArguments(package_list, command):
176 """Checks to make sure the flags are sane. Dies if arguments are not sane.""" 175 """Checks to make sure the flags are sane. Dies if arguments are not sane."""
177 if not command in _COMMAND_DICTIONARY.keys(): 176 if not command in COMMAND_DICTIONARY.keys():
178 _PrintUsageAndDie('%s is not a valid command' % command) 177 _PrintUsageAndDie('%s is not a valid command' % command)
179 if not gflags.FLAGS.packages and command == 'commit' and not gflags.FLAGS.all: 178 if not gflags.FLAGS.packages and command == 'commit' and not gflags.FLAGS.all:
180 _PrintUsageAndDie('Please specify at least one package') 179 _PrintUsageAndDie('Please specify at least one package')
181 if not gflags.FLAGS.board and command == 'commit': 180 if not gflags.FLAGS.board and command == 'commit':
182 _PrintUsageAndDie('Please specify a board') 181 _PrintUsageAndDie('Please specify a board')
183 if not os.path.isdir(gflags.FLAGS.srcroot): 182 if not os.path.isdir(gflags.FLAGS.srcroot):
184 _PrintUsageAndDie('srcroot is not a valid path') 183 _PrintUsageAndDie('srcroot is not a valid path')
185 gflags.FLAGS.srcroot = os.path.abspath(gflags.FLAGS.srcroot) 184 gflags.FLAGS.srcroot = os.path.abspath(gflags.FLAGS.srcroot)
186 185
187 186
188 def _Clean():
189 """Cleans up uncommitted changes on either stabilizing branch or master."""
190 _SimpleRunCommand('git reset HEAD --hard')
191 _SimpleRunCommand('git checkout %s' % gflags.FLAGS.tracking_branch)
192
193
194 def _PrintUsageAndDie(error_message=''): 187 def _PrintUsageAndDie(error_message=''):
195 """Prints optional error_message the usage and returns an error exit code.""" 188 """Prints optional error_message the usage and returns an error exit code."""
196 command_usage = 'Commands: \n' 189 command_usage = 'Commands: \n'
197 # Add keys and usage information from dictionary. 190 # Add keys and usage information from dictionary.
198 commands = sorted(_COMMAND_DICTIONARY.keys()) 191 commands = sorted(COMMAND_DICTIONARY.keys())
199 for command in commands: 192 for command in commands:
200 command_usage += ' %s: %s\n' % (command, _COMMAND_DICTIONARY[command]) 193 command_usage += ' %s: %s\n' % (command, COMMAND_DICTIONARY[command])
201 commands_str = '|'.join(commands) 194 commands_str = '|'.join(commands)
202 Warning('Usage: %s FLAGS [%s]\n\n%s\nFlags:%s' % (sys.argv[0], commands_str, 195 Warning('Usage: %s FLAGS [%s]\n\n%s\nFlags:%s' % (sys.argv[0], commands_str,
203 command_usage, gflags.FLAGS)) 196 command_usage, gflags.FLAGS))
204 if error_message: 197 if error_message:
205 Die(error_message) 198 Die(error_message)
206 else: 199 else:
207 sys.exit(1) 200 sys.exit(1)
208 201
209 def _PushChange():
210 """Pushes changes to the git repository.
211
212 Pushes locals commits from calls to CommitChange to the remote git
213 repository specified by os.pwd.
214
215 Raises:
216 OSError: Error occurred while pushing.
217 """
218
219 # TODO(sosa) - Add logic for buildbot to check whether other slaves have
220 # completed and push this change only if they have.
221
222 # Sanity check to make sure we're on a stabilizing branch before pushing.
223 if not _CheckOnStabilizingBranch():
224 Info('Not on branch %s so no work found to push. Exiting' % \
225 _STABLE_BRANCH_NAME)
226 return
227
228 description = _SimpleRunCommand('git log --format=format:%s%n%n%b ' +
229 gflags.FLAGS.tracking_branch + '..')
230 description = 'Marking set of ebuilds as stable\n\n%s' % description
231 merge_branch_name = 'merge_branch'
232 _SimpleRunCommand('git remote update')
233 merge_branch = _GitBranch(merge_branch_name)
234 merge_branch.CreateBranch()
235 if not merge_branch.Exists():
236 Die('Unable to create merge branch.')
237 _SimpleRunCommand('git merge --squash %s' % _STABLE_BRANCH_NAME)
238 _SimpleRunCommand('git commit -m "%s"' % description)
239 # Ugh. There has got to be an easier way to push to a tracking branch
240 _SimpleRunCommand('git config push.default tracking')
241 _SimpleRunCommand('git push')
242
243 202
244 def _SimpleRunCommand(command): 203 def _SimpleRunCommand(command):
245 """Runs a shell command and returns stdout back to caller.""" 204 """Runs a shell command and returns stdout back to caller."""
246 _Print(' + %s' % command) 205 _Print(' + %s' % command)
247 proc_handle = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True) 206 proc_handle = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True)
248 stdout = proc_handle.communicate()[0] 207 stdout = proc_handle.communicate()[0]
249 retcode = proc_handle.wait() 208 retcode = proc_handle.wait()
250 if retcode != 0: 209 if retcode != 0:
251 _Print(stdout) 210 _Print(stdout)
252 raise subprocess.CalledProcessError(retcode, command) 211 raise subprocess.CalledProcessError(retcode, command)
253 return stdout 212 return stdout
254 213
255 214
256 # ======================= End Global Helper Functions ======================== 215 # ======================= End Global Helper Functions ========================
257 216
258 217
259 class _GitBranch(object): 218 def Clean(tracking_branch):
219 """Cleans up uncommitted changes.
220
221 Args:
222 tracking_branch: The tracking branch we want to return to after the call.
223 """
224 _SimpleRunCommand('git reset HEAD --hard')
225 _SimpleRunCommand('git checkout %s' % tracking_branch)
226
227
228 def PushChange(stable_branch, tracking_branch):
229 """Pushes commits in the stable_branch to the remote git repository.
230
231 Pushes locals commits from calls to CommitChange to the remote git
232 repository specified by current working directory.
233
234 Args:
235 stable_branch: The local branch with commits we want to push.
236 tracking_branch: The tracking branch of the local branch.
237 Raises:
238 OSError: Error occurred while pushing.
239 """
240 # Sanity check to make sure we're on a stabilizing branch before pushing.
241 if not _CheckOnStabilizingBranch(stable_branch):
242 Info('Not on branch %s so no work found to push. Exiting' % \
243 stable_branch)
244 return
245
246 description = _SimpleRunCommand('git log --format=format:%s%n%n%b ' +
247 tracking_branch)
248 description = 'Marking set of ebuilds as stable\n\n%s' % description
249 merge_branch_name = 'merge_branch'
250 _SimpleRunCommand('git remote update')
251 merge_branch = GitBranch(merge_branch_name)
252 merge_branch.CreateBranch()
253 if not merge_branch.Exists():
254 Die('Unable to create merge branch.')
255
256 _SimpleRunCommand('git merge --squash %s' % stable_branch)
257 _SimpleRunCommand('git commit -m "%s"' % description)
258 # Ugh. There has got to be an easier way to push to a tracking branch
259 _SimpleRunCommand('git config push.default tracking')
260 _SimpleRunCommand('git push')
261
262
263 class GitBranch(object):
260 """Wrapper class for a git branch.""" 264 """Wrapper class for a git branch."""
261 265
262 def __init__(self, branch_name): 266 def __init__(self, branch_name, tracking_branch):
263 """Sets up variables but does not create the branch.""" 267 """Sets up variables but does not create the branch."""
264 self.branch_name = branch_name 268 self.branch_name = branch_name
269 self.tracking_branch = tracking_branch
265 270
266 def CreateBranch(self): 271 def CreateBranch(self):
267 """Creates a new git branch or replaces an existing one.""" 272 """Creates a new git branch or replaces an existing one."""
268 if self.Exists(): 273 if self.Exists():
269 self.Delete() 274 self.Delete()
270 self._Checkout(self.branch_name) 275 self._Checkout(self.branch_name)
271 276
272 def _Checkout(self, target, create=True): 277 def _Checkout(self, target, create=True):
273 """Function used internally to create and move between branches.""" 278 """Function used internally to create and move between branches."""
274 if create: 279 if create:
275 git_cmd = 'git checkout -b %s %s' % (target, gflags.FLAGS.tracking_branch) 280 git_cmd = 'git checkout -b %s %s' % (target, self.tracking_branch)
276 else: 281 else:
277 git_cmd = 'git checkout %s' % target 282 git_cmd = 'git checkout %s' % target
278 _SimpleRunCommand(git_cmd) 283 _SimpleRunCommand(git_cmd)
279 284
280 def Exists(self): 285 def Exists(self):
281 """Returns True if the branch exists.""" 286 """Returns True if the branch exists."""
282 branch_cmd = 'git branch' 287 branch_cmd = 'git branch'
283 branches = _SimpleRunCommand(branch_cmd) 288 branches = _SimpleRunCommand(branch_cmd)
284 return self.branch_name in branches.split() 289 return self.branch_name in branches.split()
285 290
286 def Delete(self): 291 def Delete(self):
287 """Deletes the branch and returns the user to the master branch. 292 """Deletes the branch and returns the user to the master branch.
288 293
289 Returns True on success. 294 Returns True on success.
290 """ 295 """
291 self._Checkout(gflags.FLAGS.tracking_branch, create=False) 296 self._Checkout(self.tracking_branch, create=False)
292 delete_cmd = 'git branch -D %s' % self.branch_name 297 delete_cmd = 'git branch -D %s' % self.branch_name
293 _SimpleRunCommand(delete_cmd) 298 _SimpleRunCommand(delete_cmd)
294 299
295 300
296 class _EBuild(object): 301 class EBuild(object):
297 """Wrapper class for an ebuild.""" 302 """Wrapper class for information about an ebuild."""
298 303
299 def __init__(self, path): 304 def __init__(self, path):
300 """Initializes all data about an ebuild. 305 """Sets up data about an ebuild from its path."""
306 from portage.versions import pkgsplit
307 unused_path, self.category, self.pkgname, filename = path.rsplit('/', 3)
308 unused_pkgname, version_no_rev, rev = pkgsplit(
309 filename.replace('.ebuild', ''))
301 310
302 Uses equery to find the ebuild path and sets data about an ebuild for 311 self.ebuild_path_no_version = os.path.join(
303 easy reference. 312 os.path.dirname(path), self.pkgname)
304 """ 313 self.ebuild_path_no_revision = '%s-%s' % (self.ebuild_path_no_version,
305 from portage.versions import pkgsplit 314 version_no_rev)
306 self.ebuild_path = path 315 self.current_revision = int(rev.replace('r', ''))
307 (self.ebuild_path_no_revision,
308 self.ebuild_path_no_version,
309 self.current_revision) = self._ParseEBuildPath(self.ebuild_path)
310 _, self.category, pkgpath, filename = path.rsplit('/', 3)
311 filename_no_suffix = os.path.join(filename.replace('.ebuild', ''))
312 self.pkgname, version_no_rev, rev = pkgsplit(filename_no_suffix)
313 self.version = '%s-%s' % (version_no_rev, rev) 316 self.version = '%s-%s' % (version_no_rev, rev)
314 self.package = '%s/%s' % (self.category, self.pkgname) 317 self.package = '%s/%s' % (self.category, self.pkgname)
318 self.ebuild_path = path
319
315 self.is_workon = False 320 self.is_workon = False
316 self.is_stable = False 321 self.is_stable = False
317 322
318 for line in fileinput.input(path): 323 for line in fileinput.input(path):
319 if line.startswith('inherit ') and 'cros-workon' in line: 324 if line.startswith('inherit ') and 'cros-workon' in line:
320 self.is_workon = True 325 self.is_workon = True
321 elif (line.startswith('KEYWORDS=') and '~' not in line and 326 elif (line.startswith('KEYWORDS=') and '~' not in line and
322 ('amd64' in line or 'x86' in line or 'arm' in line)): 327 ('amd64' in line or 'x86' in line or 'arm' in line)):
323 self.is_stable = True 328 self.is_stable = True
324 fileinput.close() 329 fileinput.close()
325 330
326 def GetCommitId(self): 331 def GetCommitId(self):
327 """Get the commit id for this ebuild.""" 332 """Get the commit id for this ebuild."""
328
329 # Grab and evaluate CROS_WORKON variables from this ebuild. 333 # Grab and evaluate CROS_WORKON variables from this ebuild.
330 unstable_ebuild = '%s-9999.ebuild' % self.ebuild_path_no_version 334 unstable_ebuild = '%s-9999.ebuild' % self.ebuild_path_no_version
331 cmd = ('export CROS_WORKON_LOCALNAME="%s" CROS_WORKON_PROJECT="%s"; ' 335 cmd = ('export CROS_WORKON_LOCALNAME="%s" CROS_WORKON_PROJECT="%s"; '
332 'eval $(grep -E "^CROS_WORKON" %s) && ' 336 'eval $(grep -E "^CROS_WORKON" %s) && '
333 'echo $CROS_WORKON_PROJECT ' 337 'echo $CROS_WORKON_PROJECT '
334 '$CROS_WORKON_LOCALNAME/$CROS_WORKON_SUBDIR' 338 '$CROS_WORKON_LOCALNAME/$CROS_WORKON_SUBDIR'
335 % (self.pkgname, self.pkgname, unstable_ebuild)) 339 % (self.pkgname, self.pkgname, unstable_ebuild))
336 project, subdir = _SimpleRunCommand(cmd).split() 340 project, subdir = _SimpleRunCommand(cmd).split()
337 341
338 # Calculate srcdir. 342 # Calculate srcdir.
(...skipping 22 matching lines...) Expand all
361 if project not in (actual_project, 'chromeos-kernel'): 365 if project not in (actual_project, 'chromeos-kernel'):
362 Die('Project name mismatch for %s (%s != %s)' % (unstable_ebuild, project, 366 Die('Project name mismatch for %s (%s != %s)' % (unstable_ebuild, project,
363 actual_project)) 367 actual_project))
364 368
365 # Get commit id. 369 # Get commit id.
366 output = _SimpleRunCommand('cd %s && git rev-parse HEAD' % srcdir) 370 output = _SimpleRunCommand('cd %s && git rev-parse HEAD' % srcdir)
367 if not output: 371 if not output:
368 Die('Missing commit id for %s' % self.ebuild_path) 372 Die('Missing commit id for %s' % self.ebuild_path)
369 return output.rstrip() 373 return output.rstrip()
370 374
371 @classmethod
372 def _ParseEBuildPath(cls, ebuild_path):
373 """Static method that parses the path of an ebuild
374
375 Returns a tuple containing the (ebuild path without the revision
376 string, without the version string, and the current revision number for
377 the ebuild).
378 """
379 # Get the ebuild name without the revision string.
380 (ebuild_no_rev, _, rev_string) = ebuild_path.rpartition('-')
381
382 # Verify the revision string starts with the revision character.
383 if rev_string.startswith('r'):
384 # Get the ebuild name without the revision and version strings.
385 ebuild_no_version = ebuild_no_rev.rpartition('-')[0]
386 rev_string = rev_string[1:].rpartition('.ebuild')[0]
387 else:
388 # Has no revision so we stripped the version number instead.
389 ebuild_no_version = ebuild_no_rev
390 ebuild_no_rev = ebuild_path.rpartition('9999.ebuild')[0] + '0.0.1'
391 rev_string = '0'
392 revision = int(rev_string)
393 return (ebuild_no_rev, ebuild_no_version, revision)
394
395 375
396 class EBuildStableMarker(object): 376 class EBuildStableMarker(object):
397 """Class that revs the ebuild and commits locally or pushes the change.""" 377 """Class that revs the ebuild and commits locally or pushes the change."""
398 378
399 def __init__(self, ebuild): 379 def __init__(self, ebuild):
380 assert ebuild
400 self._ebuild = ebuild 381 self._ebuild = ebuild
401 382
402 def RevEBuild(self, commit_id='', redirect_file=None): 383 @classmethod
403 """Revs an ebuild given the git commit id. 384 def MarkAsStable(cls, unstable_ebuild_path, new_stable_ebuild_path,
385 commit_keyword, commit_value, redirect_file=None):
386 """Static function that creates a revved stable ebuild.
387
388 This function assumes you have already figured out the name of the new
389 stable ebuild path and then creates that file from the given unstable
390 ebuild and marks it as stable. If the commit_value is set, it also
391 set the commit_keyword=commit_value pair in the ebuild.
392
393 Args:
394 unstable_ebuild_path: The path to the unstable ebuild.
395 new_stable_ebuild_path: The path you want to use for the new stable
396 ebuild.
397 commit_keyword: Optional keyword to set in the ebuild to mark it as
398 stable.
399 commit_value: Value to set the above keyword to.
400 redirect_file: Optionally redirect output of new ebuild somewhere else.
401 """
402 shutil.copyfile(unstable_ebuild_path, new_stable_ebuild_path)
403 for line in fileinput.input(new_stable_ebuild_path, inplace=1):
404 # Has to be done here to get changes to sys.stdout from fileinput.input.
405 if not redirect_file:
406 redirect_file = sys.stdout
407 if line.startswith('KEYWORDS'):
408 # Actually mark this file as stable by removing ~'s.
409 redirect_file.write(line.replace('~', ''))
410 elif line.startswith('EAPI'):
411 # Always add new commit_id after EAPI definition.
412 redirect_file.write(line)
413 if commit_keyword and commit_value:
414 redirect_file.write('%s="%s"\n' % (commit_keyword, commit_value))
415
davidjames 2010/11/18 21:56:32 Extra newline.
416 elif not line.startswith(commit_keyword):
417 # Skip old commit_keyword definition.
418 redirect_file.write(line)
419 fileinput.close()
420
421 def RevWorkOnEBuild(self, commit_id, redirect_file=None):
422 """Revs a workon ebuild given the git commit hash.
404 423
405 By default this class overwrites a new ebuild given the normal 424 By default this class overwrites a new ebuild given the normal
406 ebuild rev'ing logic. However, a user can specify a redirect_file 425 ebuild rev'ing logic. However, a user can specify a redirect_file
407 to redirect the new stable ebuild to another file. 426 to redirect the new stable ebuild to another file.
408 427
409 Args: 428 Args:
410 commit_id: String corresponding to the commit hash of the developer 429 commit_id: String corresponding to the commit hash of the developer
411 package to rev. 430 package to rev.
412 redirect_file: Optional file to write the new ebuild. By default 431 redirect_file: Optional file to write the new ebuild. By default
413 it is written using the standard rev'ing logic. This file must be 432 it is written using the standard rev'ing logic. This file must be
414 opened and closed by the caller. 433 opened and closed by the caller.
415 434
416 Raises: 435 Raises:
417 OSError: Error occurred while creating a new ebuild. 436 OSError: Error occurred while creating a new ebuild.
418 IOError: Error occurred while writing to the new revved ebuild file. 437 IOError: Error occurred while writing to the new revved ebuild file.
419 Returns: 438 Returns:
420 True if the revved package is different than the old ebuild. 439 True if the revved package is different than the old ebuild.
421 """ 440 """
422 # TODO(sosa): Change to a check. 441 if self._ebuild.is_stable:
423 if not self._ebuild: 442 new_stable_ebuild_path = '%s-r%d.ebuild' % (
424 Die('Invalid ebuild given to EBuildStableMarker') 443 self._ebuild.ebuild_path_no_revision,
444 self._ebuild.current_revision + 1)
445 else:
446 # If given unstable ebuild, use 0.0.1 rather than 9999.
447 new_stable_ebuild_path = '%s-0.0.1-r%d.ebuild' % (
448 self._ebuild.ebuild_path_no_version,
449 self._ebuild.current_revision + 1)
425 450
426 new_ebuild_path = '%s-r%d.ebuild' % (self._ebuild.ebuild_path_no_revision, 451 _Print('Creating new stable ebuild %s' % new_stable_ebuild_path)
427 self._ebuild.current_revision + 1) 452 unstable_ebuild_path = ('%s-9999.ebuild' %
453 self._ebuild.ebuild_path_no_version)
454 if not os.path.exists(unstable_ebuild_path):
455 Die('Missing unstable ebuild: %s' % unstable_ebuild_path)
428 456
429 _Print('Creating new stable ebuild %s' % new_ebuild_path) 457 self.MarkAsStable(unstable_ebuild_path, new_stable_ebuild_path,
430 workon_ebuild = '%s-9999.ebuild' % self._ebuild.ebuild_path_no_version 458 'CROS_WORKON_COMMIT', commit_id, redirect_file)
431 if not os.path.exists(workon_ebuild):
432 Die('Missing 9999 ebuild: %s' % workon_ebuild)
433 shutil.copyfile(workon_ebuild, new_ebuild_path)
434
435 for line in fileinput.input(new_ebuild_path, inplace=1):
436 # Has to be done here to get changes to sys.stdout from fileinput.input.
437 if not redirect_file:
438 redirect_file = sys.stdout
439 if line.startswith('KEYWORDS'):
440 # Actually mark this file as stable by removing ~'s.
441 redirect_file.write(line.replace('~', ''))
442 elif line.startswith('EAPI'):
443 # Always add new commit_id after EAPI definition.
444 redirect_file.write(line)
445 redirect_file.write('CROS_WORKON_COMMIT="%s"\n' % commit_id)
446 elif not line.startswith('CROS_WORKON_COMMIT'):
447 # Skip old CROS_WORKON_COMMIT definition.
448 redirect_file.write(line)
449 fileinput.close()
450 459
451 old_ebuild_path = self._ebuild.ebuild_path 460 old_ebuild_path = self._ebuild.ebuild_path
452 diff_cmd = ['diff', '-Bu', old_ebuild_path, new_ebuild_path] 461 diff_cmd = ['diff', '-Bu', old_ebuild_path, new_stable_ebuild_path]
453 if 0 == RunCommand(diff_cmd, exit_code=True, redirect_stdout=True, 462 if 0 == RunCommand(diff_cmd, exit_code=True, redirect_stdout=True,
454 redirect_stderr=True, print_cmd=gflags.FLAGS.verbose): 463 redirect_stderr=True, print_cmd=gflags.FLAGS.verbose):
455 os.unlink(new_ebuild_path) 464 os.unlink(new_stable_ebuild_path)
456 return False 465 return False
457 else: 466 else:
458 _Print('Adding new stable ebuild to git') 467 _Print('Adding new stable ebuild to git')
459 _SimpleRunCommand('git add %s' % new_ebuild_path) 468 _SimpleRunCommand('git add %s' % new_stable_ebuild_path)
460 469
461 if self._ebuild.is_stable: 470 if self._ebuild.is_stable:
462 _Print('Removing old ebuild from git') 471 _Print('Removing old ebuild from git')
463 _SimpleRunCommand('git rm %s' % old_ebuild_path) 472 _SimpleRunCommand('git rm %s' % old_ebuild_path)
464 473
465 return True 474 return True
466 475
467 def CommitChange(self, message): 476 @classmethod
468 """Commits current changes in git locally. 477 def CommitChange(cls, message):
469 478 """Commits current changes in git locally with given commit message.
470 This method will take any changes from invocations to RevEBuild
471 and commits them locally in the git repository that contains os.pwd.
472 479
473 Args: 480 Args:
474 message: the commit string to write when committing to git. 481 message: the commit string to write when committing to git.
475 482
476 Raises: 483 Raises:
477 OSError: Error occurred while committing. 484 OSError: Error occurred while committing.
478 """ 485 """
479 _Print('Committing changes for %s with commit message %s' % \ 486 Info('Committing changes with commit message: %s' % message)
480 (self._ebuild.package, message))
481 git_commit_cmd = 'git commit -am "%s"' % message 487 git_commit_cmd = 'git commit -am "%s"' % message
482 _SimpleRunCommand(git_commit_cmd) 488 _SimpleRunCommand(git_commit_cmd)
483 489
484 490
485 def main(argv): 491 def main(argv):
486 try: 492 try:
487 argv = gflags.FLAGS(argv) 493 argv = gflags.FLAGS(argv)
488 if len(argv) != 2: 494 if len(argv) != 2:
489 _PrintUsageAndDie('Must specify a valid command') 495 _PrintUsageAndDie('Must specify a valid command')
490 else: 496 else:
(...skipping 23 matching lines...) Expand all
514 if not os.path.isdir(overlay): 520 if not os.path.isdir(overlay):
515 Warning("Skipping %s" % overlay) 521 Warning("Skipping %s" % overlay)
516 continue 522 continue
517 523
518 # TODO(davidjames): Currently, all code that interacts with git depends on 524 # TODO(davidjames): Currently, all code that interacts with git depends on
519 # the cwd being set to the overlay directory. We should instead pass in 525 # the cwd being set to the overlay directory. We should instead pass in
520 # this parameter so that we don't need to modify the cwd globally. 526 # this parameter so that we don't need to modify the cwd globally.
521 os.chdir(overlay) 527 os.chdir(overlay)
522 528
523 if command == 'clean': 529 if command == 'clean':
524 _Clean() 530 Clean(gflags.FLAGS.tracking_branch)
525 elif command == 'push': 531 elif command == 'push':
526 _PushChange() 532 PushChange(_STABLE_BRANCH_NAME, gflags.FLAGS.tracking_branch)
527 elif command == 'commit' and ebuilds: 533 elif command == 'commit' and ebuilds:
528 work_branch = _GitBranch(_STABLE_BRANCH_NAME) 534 work_branch = GitBranch(_STABLE_BRANCH_NAME, gflags.FLAGS.tracking_branch)
529 work_branch.CreateBranch() 535 work_branch.CreateBranch()
530 if not work_branch.Exists(): 536 if not work_branch.Exists():
531 Die('Unable to create stabilizing branch in %s' % overlay) 537 Die('Unable to create stabilizing branch in %s' % overlay)
532 538
533 # Contains the array of packages we actually revved. 539 # Contains the array of packages we actually revved.
534 revved_packages = [] 540 revved_packages = []
535 for ebuild in ebuilds: 541 for ebuild in ebuilds:
536 try: 542 try:
537 _Print('Working on %s' % ebuild.package) 543 _Print('Working on %s' % ebuild.package)
538 worker = EBuildStableMarker(ebuild) 544 worker = EBuildStableMarker(ebuild)
539 commit_id = ebuild.GetCommitId() 545 commit_id = ebuild.GetCommitId()
540 if worker.RevEBuild(commit_id): 546 if worker.RevWorkOnEBuild(commit_id):
541 message = _GIT_COMMIT_MESSAGE % (ebuild.package, commit_id) 547 message = _GIT_COMMIT_MESSAGE % (ebuild.package, commit_id)
542 worker.CommitChange(message) 548 worker.CommitChange(message)
543 revved_packages.append(ebuild.package) 549 revved_packages.append(ebuild.package)
544 550
545 except (OSError, IOError): 551 except (OSError, IOError):
546 Warning('Cannot rev %s\n' % ebuild.package, 552 Warning('Cannot rev %s\n' % ebuild.package,
547 'Note you will have to go into %s ' 553 'Note you will have to go into %s '
548 'and reset the git repo yourself.' % overlay) 554 'and reset the git repo yourself.' % overlay)
549 raise 555 raise
550 556
551 if revved_packages: 557 if revved_packages:
552 _CleanStalePackages(gflags.FLAGS.board, revved_packages) 558 _CleanStalePackages(gflags.FLAGS.board, revved_packages)
553 else: 559 else:
554 work_branch.Delete() 560 work_branch.Delete()
555 561
556 562
557 if __name__ == '__main__': 563 if __name__ == '__main__':
558 main(sys.argv) 564 main(sys.argv)
OLDNEW
« no previous file with comments | « no previous file | cros_mark_as_stable_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698