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 |