blob: e9361ddac45ac251fcf2ae8c2804a7fc55a365d9 [file] [log] [blame]
Chris Sosadad0d322011-01-31 16:37:33 -08001#!/usr/bin/python
2
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
5# found in the LICENSE file.
6
7"""This module uprevs Chrome for cbuildbot.
8
9After calling, it prints outs CHROME_VERSION_ATOM=(version atom string). A
10caller could then use this atom with emerge to build the newly uprevved version
11of Chrome e.g.
12
13./cros_mark_chrome_as_stable tot
14Returns chrome-base/chromeos-chrome-8.0.552.0_alpha_r1
15
16emerge-x86-generic =chrome-base/chromeos-chrome-8.0.552.0_alpha_r1
17"""
18
Ryan Cui05a31ba2011-05-31 17:47:37 -070019import constants
Chris Sosa8be39132011-04-14 12:09:24 -070020import filecmp
Chris Sosadad0d322011-01-31 16:37:33 -080021import optparse
22import os
23import re
24import sys
25import urllib
26
Chris Sosadad0d322011-01-31 16:37:33 -080027import cros_mark_as_stable
Chris Sosadad0d322011-01-31 16:37:33 -080028from cros_build_lib import RunCommand, Info, Warning
29
Chris Sosad44c3f92011-05-10 16:05:25 -070030BASE_CHROME_SVN_URL = 'svn://svn.chromium.org/chrome/'
Chris Sosadad0d322011-01-31 16:37:33 -080031
32# Command for which chrome ebuild to uprev.
33TIP_OF_TRUNK, LATEST_RELEASE, STICKY = 'tot', 'latest_release', 'stable_release'
34CHROME_REV = [TIP_OF_TRUNK, LATEST_RELEASE, STICKY]
35
36# Helper regex's for finding ebuilds.
37_CHROME_VERSION_REGEX = '\d+\.\d+\.\d+\.\d+'
38_NON_STICKY_REGEX = '%s[(_rc.*)|(_alpha.*)]+' % _CHROME_VERSION_REGEX
39
40# Dir where all the action happens.
41_CHROME_OVERLAY_DIR = ('%(srcroot)s/third_party/chromiumos-overlay'
42 '/chromeos-base/chromeos-chrome')
43
44_GIT_COMMIT_MESSAGE = ('Marking %(chrome_rev)s for chrome ebuild with version '
45 '%(chrome_version)s as stable.')
46
47
48def _GetSvnUrl():
49 """Returns the path to the svn url for the given chrome branch."""
50 return os.path.join(BASE_CHROME_SVN_URL, 'trunk')
51
52
53def _GetTipOfTrunkSvnRevision():
54 """Returns the current svn revision for the chrome tree."""
55 svn_url = _GetSvnUrl()
56 svn_info = RunCommand(['svn', 'info', svn_url], redirect_stdout=True)
57
58 revision_re = re.compile('^Revision:\s+(\d+).*')
59 for line in svn_info.splitlines():
60 match = revision_re.search(line)
61 if match:
62 svn_revision = match.group(1)
63 Info('Using SVN Revision %s' % svn_revision)
64 return svn_revision
65
66 raise Exception('Could not find revision information from %s' % svn_url)
67
68
69def _GetTipOfTrunkVersion():
70 """Returns the current Chrome version."""
Chris Sosad44c3f92011-05-10 16:05:25 -070071 svn_url = os.path.join(_GetSvnUrl(), 'src', 'chrome', 'VERSION')
72 chrome_version_info = RunCommand(
73 ['svn', 'cat', svn_url],
74 redirect_stdout=True,
75 error_message='Could not read version file at %s.' % svn_url)
Chris Sosadad0d322011-01-31 16:37:33 -080076
77 chrome_version_array = []
Chris Sosadad0d322011-01-31 16:37:33 -080078 for line in chrome_version_info.splitlines():
79 chrome_version_array.append(line.rpartition('=')[2])
80
81 return '.'.join(chrome_version_array)
82
83
84def _GetLatestRelease(branch=None):
85 """Gets the latest release version from the buildspec_url for the branch.
86
87 Args:
88 branch: If set, gets the latest release for branch, otherwise latest
89 release.
90 Returns:
91 Latest version string.
92 """
Chris Sosad44c3f92011-05-10 16:05:25 -070093 buildspec_url = os.path.join(BASE_CHROME_SVN_URL, 'releases')
Chris Sosadad0d322011-01-31 16:37:33 -080094 svn_ls = RunCommand(['svn', 'ls', buildspec_url], redirect_stdout=True)
95 sorted_ls = RunCommand(['sort', '--version-sort'], input=svn_ls,
96 redirect_stdout=True)
97 if branch:
98 chrome_version_re = re.compile('^%s\.\d+.*' % branch)
99 else:
100 chrome_version_re = re.compile('^[0-9]+\..*')
101 for chrome_version in sorted_ls.splitlines():
102 if chrome_version_re.match(chrome_version):
103 current_version = chrome_version
104
105 return current_version.rstrip('/')
106
107
108def _GetStickyEBuild(stable_ebuilds):
109 """Returns the sticky ebuild."""
110 sticky_ebuilds = []
111 non_sticky_re = re.compile(_NON_STICKY_REGEX)
112 for ebuild in stable_ebuilds:
113 if not non_sticky_re.match(ebuild.version):
114 sticky_ebuilds.append(ebuild)
115
116 if not sticky_ebuilds:
117 raise Exception('No sticky ebuilds found')
118 elif len(sticky_ebuilds) > 1:
119 Warning('More than one sticky ebuild found')
120
121 return cros_mark_as_stable.BestEBuild(sticky_ebuilds)
122
123
124class ChromeEBuild(cros_mark_as_stable.EBuild):
125 """Thin sub-class of EBuild that adds a chrome_version field."""
126 chrome_version_re = re.compile('.*chromeos-chrome-(%s|9999).*' % (
127 _CHROME_VERSION_REGEX))
128 chrome_version = ''
129
130 def __init__(self, path):
131 cros_mark_as_stable.EBuild.__init__(self, path)
132 re_match = self.chrome_version_re.match(self.ebuild_path_no_revision)
133 if re_match:
134 self.chrome_version = re_match.group(1)
135
136 def __cmp__(self, other):
137 """Use ebuild paths for comparison."""
138 if self.ebuild_path == other.ebuild_path:
139 return 0
140 elif self.ebuild_path > other.ebuild_path:
141 return 1
142 else:
143 return (-1)
144
145 def __str__(self):
146 return self.ebuild_path
147
148
149def FindChromeCandidates(overlay_dir):
150 """Return a tuple of chrome's unstable ebuild and stable ebuilds.
151
152 Args:
153 overlay_dir: The path to chrome's portage overlay dir.
154 Returns:
155 Tuple [unstable_ebuild, stable_ebuilds].
156 Raises:
157 Exception: if no unstable ebuild exists for Chrome.
158 """
159 stable_ebuilds = []
160 unstable_ebuilds = []
161 for path in [
162 os.path.join(overlay_dir, entry) for entry in os.listdir(overlay_dir)]:
163 if path.endswith('.ebuild'):
164 ebuild = ChromeEBuild(path)
165 if not ebuild.chrome_version:
166 Warning('Poorly formatted ebuild found at %s' % path)
167 else:
168 if '9999' in ebuild.version:
169 unstable_ebuilds.append(ebuild)
170 else:
171 stable_ebuilds.append(ebuild)
172
173 # Apply some sanity checks.
174 if not unstable_ebuilds:
175 raise Exception('Missing 9999 ebuild for %s' % overlay_dir)
176 if not stable_ebuilds:
177 Warning('Missing stable ebuild for %s' % overlay_dir)
178
179 return cros_mark_as_stable.BestEBuild(unstable_ebuilds), stable_ebuilds
180
181
182def FindChromeUprevCandidate(stable_ebuilds, chrome_rev, sticky_branch):
183 """Finds the Chrome uprev candidate for the given chrome_rev.
184
185 Using the pre-flight logic, this means the stable ebuild you are uprevving
186 from. The difference here is that the version could be different and in
187 that case we want to find it to delete it.
188
189 Args:
190 stable_ebuilds: A list of stable ebuilds.
191 chrome_rev: The chrome_rev designating which candidate to find.
192 sticky_branch: The the branch that is currently sticky with Major/Minor
193 components. For example: 9.0.553
194 Returns:
195 Returns the EBuild, otherwise None if none found.
196 """
197 candidates = []
198 if chrome_rev == TIP_OF_TRUNK:
199 chrome_branch_re = re.compile('%s.*_alpha.*' % _CHROME_VERSION_REGEX)
200 for ebuild in stable_ebuilds:
201 if chrome_branch_re.search(ebuild.version):
202 candidates.append(ebuild)
203
204 elif chrome_rev == STICKY:
205 chrome_branch_re = re.compile('%s\..*' % sticky_branch)
206 for ebuild in stable_ebuilds:
207 if chrome_branch_re.search(ebuild.version):
208 candidates.append(ebuild)
209
210 else:
211 chrome_branch_re = re.compile('%s.*_rc.*' % _CHROME_VERSION_REGEX)
212 for ebuild in stable_ebuilds:
213 if chrome_branch_re.search(ebuild.version) and (
214 not ebuild.chrome_version.startswith(sticky_branch)):
215 candidates.append(ebuild)
216
217 if candidates:
218 return cros_mark_as_stable.BestEBuild(candidates)
219 else:
220 return None
221
222
223def MarkChromeEBuildAsStable(stable_candidate, unstable_ebuild, chrome_rev,
224 chrome_version, commit, overlay_dir,
225 sticky_ebuild):
226 """Uprevs the chrome ebuild specified by chrome_rev.
227
228 This is the main function that uprevs the chrome_rev from a stable candidate
229 to its new version.
230
231 Args:
232 stable_candidate: ebuild that corresponds to the stable ebuild we are
233 revving from. If None, builds the a new ebuild given the version
234 and logic for chrome_rev type with revision set to 1.
235 unstable_ebuild: ebuild corresponding to the unstable ebuild for chrome.
236 chrome_rev: one of CHROME_REV
237 TIP_OF_TRUNK - Requires commit value. Revs the ebuild for the TOT
238 version and uses the portage suffix of _alpha.
239 LATEST_RELEASE - This uses the portage suffix of _rc as they are release
240 candidates for the next sticky version.
241 STICKY - Revs the sticky version.
242 chrome_version: The \d.\d.\d.\d version of Chrome.
243 commit: Used with TIP_OF_TRUNK. The svn revision of chrome.
244 overlay_dir: Path to the chromeos-chrome package dir.
245 sticky_ebuild: EBuild class for the sticky ebuild.
246 Returns:
247 Full portage version atom (including rc's, etc) that was revved.
248 """
Chris Sosa8be39132011-04-14 12:09:24 -0700249 def IsTheNewEBuildRedundant(new_ebuild, stable_ebuild):
250 """Returns True if the new ebuild is redundant.
251
252 This is True if there if the current stable ebuild is the exact same copy
253 of the new one OR the chrome versions are the same and we're revving
254 LATEST_RELEASE (as we don't care about 9999 changes for it).
255 """
256 if not stable_ebuild:
257 return False
258
259 if stable_candidate.chrome_version == new_ebuild.chrome_version:
260 if chrome_rev == LATEST_RELEASE:
261 return True
262 else:
263 return filecmp.cmp(
264 new_ebuild.ebuild_path, stable_ebuild.ebuild_path, shallow=False)
265
Chris Sosadad0d322011-01-31 16:37:33 -0800266 base_path = os.path.join(overlay_dir, 'chromeos-chrome-%s' % chrome_version)
267 # Case where we have the last stable candidate with same version just rev.
268 if stable_candidate and stable_candidate.chrome_version == chrome_version:
269 new_ebuild_path = '%s-r%d.ebuild' % (
270 stable_candidate.ebuild_path_no_revision,
271 stable_candidate.current_revision + 1)
272 else:
273 if chrome_rev == TIP_OF_TRUNK:
274 portage_suffix = '_alpha'
275 else:
276 portage_suffix = '_rc'
277
278 new_ebuild_path = base_path + ('%s-r1.ebuild' % portage_suffix)
279
280 # Mark latest release and sticky branches as stable.
281 mark_stable = chrome_rev != TIP_OF_TRUNK
282
283 cros_mark_as_stable.EBuildStableMarker.MarkAsStable(
284 unstable_ebuild.ebuild_path, new_ebuild_path, 'CROS_SVN_COMMIT', commit,
285 make_stable=mark_stable)
286 new_ebuild = ChromeEBuild(new_ebuild_path)
Chris Sosa8be39132011-04-14 12:09:24 -0700287
288 # Determine whether this is ebuild is redundant.
289 if IsTheNewEBuildRedundant(new_ebuild, stable_candidate):
290 Info('Previous ebuild with same version found and ebuild is redundant.')
291 os.unlink(new_ebuild_path)
292 return None
Chris Sosadad0d322011-01-31 16:37:33 -0800293
294 RunCommand(['git', 'add', new_ebuild_path])
295 if stable_candidate and stable_candidate != sticky_ebuild:
296 RunCommand(['git', 'rm', stable_candidate.ebuild_path])
297
298 cros_mark_as_stable.EBuildStableMarker.CommitChange(
299 _GIT_COMMIT_MESSAGE % {'chrome_rev': chrome_rev,
300 'chrome_version': chrome_version})
301
302 new_ebuild = ChromeEBuild(new_ebuild_path)
303 return '%s-%s' % (new_ebuild.package, new_ebuild.version)
304
305
306def main():
307 usage = '%s OPTIONS [%s]' % (__file__, '|'.join(CHROME_REV))
308 parser = optparse.OptionParser(usage)
Chris Sosabf153872011-04-28 14:21:09 -0700309 parser.add_option('-b', '--board', default='x86-generic')
Chris Sosadad0d322011-01-31 16:37:33 -0800310 parser.add_option('-s', '--srcroot', default=os.path.join(os.environ['HOME'],
311 'trunk', 'src'),
312 help='Path to the src directory')
313 parser.add_option('-t', '--tracking_branch', default='cros/master',
314 help='Branch we are tracking changes against')
315 (options, args) = parser.parse_args()
316
317 if len(args) != 1 or args[0] not in CHROME_REV:
318 parser.error('Commit requires arg set to one of %s.' % CHROME_REV)
319
320 overlay_dir = os.path.abspath(_CHROME_OVERLAY_DIR %
321 {'srcroot': options.srcroot})
322 chrome_rev = args[0]
323 version_to_uprev = None
324 commit_to_use = None
325
326 (unstable_ebuild, stable_ebuilds) = FindChromeCandidates(overlay_dir)
327 sticky_ebuild = _GetStickyEBuild(stable_ebuilds)
328 sticky_version = sticky_ebuild.chrome_version
329 sticky_branch = sticky_version.rpartition('.')[0]
330
331 if chrome_rev == TIP_OF_TRUNK:
332 version_to_uprev = _GetTipOfTrunkVersion()
333 commit_to_use = _GetTipOfTrunkSvnRevision()
334 elif chrome_rev == LATEST_RELEASE:
335 version_to_uprev = _GetLatestRelease()
336 # Don't rev on stable branch for latest_release.
337 if re.match('%s\.\d+' % sticky_branch, version_to_uprev):
338 Info('Latest release is sticky branch. Nothing to do.')
339 return
340 else:
341 version_to_uprev = _GetLatestRelease(sticky_branch)
342
343 stable_candidate = FindChromeUprevCandidate(stable_ebuilds, chrome_rev,
344 sticky_branch)
345
346 if stable_candidate:
347 Info('Stable candidate found %s' % stable_candidate)
348 else:
349 Info('No stable candidate found.')
350
Chris Sosa8049f3b2011-05-02 20:09:28 -0700351 tracking_branch = 'remotes/m/%s' % os.path.basename(options.tracking_branch)
Chris Sosadad0d322011-01-31 16:37:33 -0800352 os.chdir(overlay_dir)
Ryan Cui05a31ba2011-05-31 17:47:37 -0700353 work_branch = cros_mark_as_stable.GitBranch(constants.STABLE_EBUILD_BRANCH,
354 tracking_branch)
Chris Sosadad0d322011-01-31 16:37:33 -0800355 work_branch.CreateBranch()
356 chrome_version_atom = MarkChromeEBuildAsStable(
357 stable_candidate, unstable_ebuild, chrome_rev, version_to_uprev,
358 commit_to_use, overlay_dir, sticky_ebuild)
359 # Explicit print to communicate to caller.
360 if chrome_version_atom:
Chris Sosabf153872011-04-28 14:21:09 -0700361 cros_mark_as_stable.CleanStalePackages(options.board, [chrome_version_atom])
Chris Sosadad0d322011-01-31 16:37:33 -0800362 print 'CHROME_VERSION_ATOM=%s' % chrome_version_atom
363
364
365if __name__ == '__main__':
366 main()