Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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) |
| OLD | NEW |