Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / common / extensions / docs / server2 / branch_utility.py
blob955cfbb6b8abd1f50301fcf1502e78b75cfe21f3
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 import json
6 import logging
7 import operator
9 from environment_wrappers import CreateUrlFetcher
10 import url_constants
13 class ChannelInfo(object):
14 '''Represents a Chrome channel with three pieces of information. |channel| is
15 one of 'stable', 'beta', 'dev', or 'master'. |branch| and |version| correspond
16 with each other, and represent different releases of Chrome. Note that
17 |branch| and |version| can occasionally be the same for separate channels
18 (i.e. 'beta' and 'dev'), so all three fields are required to uniquely
19 identify a channel.
20 '''
22 def __init__(self, channel, branch, version):
23 assert isinstance(channel, basestring), channel
24 assert isinstance(branch, basestring), branch
25 # TODO(kalman): Assert that this is a string. One day Chromium will probably
26 # be served out of a git repository and the versions will no longer be ints.
27 assert isinstance(version, int) or version == 'master', version
28 self.channel = channel
29 self.branch = branch
30 self.version = version
32 def __eq__(self, other):
33 return self.__dict__ == other.__dict__
35 def __ne__(self, other):
36 return not (self == other)
38 def __repr__(self):
39 return '%s%s' % (type(self).__name__, repr(self.__dict__))
41 def __str__(self):
42 return repr(self)
45 class BranchUtility(object):
46 '''Provides methods for working with Chrome channel, branch, and version
47 data served from OmahaProxy.
48 '''
50 def __init__(self, fetch_url, history_url, fetcher, object_store_creator):
51 self._fetcher = fetcher
52 def create_object_store(category):
53 return object_store_creator.Create(BranchUtility, category=category)
54 self._branch_object_store = create_object_store('branch')
55 self._version_object_store = create_object_store('version')
56 self._fetch_result = self._fetcher.FetchAsync(fetch_url)
57 self._history_result = self._fetcher.FetchAsync(history_url)
59 @staticmethod
60 def Create(object_store_creator):
61 return BranchUtility(url_constants.OMAHA_PROXY_URL,
62 url_constants.OMAHA_DEV_HISTORY,
63 CreateUrlFetcher(),
64 object_store_creator)
66 @staticmethod
67 def GetAllChannelNames():
68 return ('stable', 'beta', 'dev', 'master')
70 @staticmethod
71 def NewestChannel(channels):
72 channels = set(channels)
73 for channel in reversed(BranchUtility.GetAllChannelNames()):
74 if channel in channels:
75 return channel
77 def Newer(self, channel_info):
78 '''Given a ChannelInfo object, returns a new ChannelInfo object
79 representing the next most recent Chrome version/branch combination.
80 '''
81 if channel_info.channel == 'master':
82 return None
83 if channel_info.channel == 'stable':
84 stable_info = self.GetChannelInfo('stable')
85 if channel_info.version < stable_info.version:
86 return self.GetStableChannelInfo(channel_info.version + 1)
87 names = self.GetAllChannelNames()
88 return self.GetAllChannelInfo()[names.index(channel_info.channel) + 1]
90 def Older(self, channel_info):
91 '''Given a ChannelInfo object, returns a new ChannelInfo object
92 representing the previous Chrome version/branch combination.
93 '''
94 if channel_info.channel == 'stable':
95 if channel_info.version <= 5:
96 # BranchUtility can't access branch data from before Chrome version 5.
97 return None
98 return self.GetStableChannelInfo(channel_info.version - 1)
99 names = self.GetAllChannelNames()
100 return self.GetAllChannelInfo()[names.index(channel_info.channel) - 1]
102 @staticmethod
103 def SplitChannelNameFromPath(path):
104 '''Splits the channel name out of |path|, returning the tuple
105 (channel_name, real_path). If the channel cannot be determined then returns
106 (None, path).
108 if '/' in path:
109 first, second = path.split('/', 1)
110 else:
111 first, second = (path, '')
112 if first in BranchUtility.GetAllChannelNames():
113 return (first, second)
114 return (None, path)
116 def GetAllBranches(self):
117 return tuple((channel, self.GetChannelInfo(channel).branch)
118 for channel in BranchUtility.GetAllChannelNames())
120 def GetAllVersions(self):
121 return tuple(self.GetChannelInfo(channel).version
122 for channel in BranchUtility.GetAllChannelNames())
124 def GetAllChannelInfo(self):
125 return tuple(self.GetChannelInfo(channel)
126 for channel in BranchUtility.GetAllChannelNames())
129 def GetChannelInfo(self, channel):
130 version = self._ExtractFromVersionJson(channel, 'version')
131 if version != 'master':
132 version = int(version)
133 return ChannelInfo(channel,
134 self._ExtractFromVersionJson(channel, 'branch'),
135 version)
137 def GetStableChannelInfo(self, version):
138 '''Given a |version| corresponding to a 'stable' version of Chrome, returns
139 a ChannelInfo object representing that version.
141 return ChannelInfo('stable', self.GetBranchForVersion(version), version)
143 def _ExtractFromVersionJson(self, channel_name, data_type):
144 '''Returns the branch or version number for a channel name.
146 if channel_name == 'master':
147 return 'master'
149 if data_type == 'branch':
150 object_store = self._branch_object_store
151 elif data_type == 'version':
152 object_store = self._version_object_store
154 data = object_store.Get(channel_name).Get()
155 if data is not None:
156 return data
158 try:
159 version_json = json.loads(self._fetch_result.Get().content)
160 except Exception as e:
161 # This can happen if omahaproxy is misbehaving, which we've seen before.
162 # Quick hack fix: just serve from master until it's fixed.
163 logging.error('Failed to fetch or parse branch from omahaproxy: %s! '
164 'Falling back to "master".' % e)
165 return 'master'
167 numbers = {}
168 for entry in version_json:
169 if entry['os'] not in ('win', 'linux', 'mac', 'cros'):
170 continue
171 for version in entry['versions']:
172 if version['channel'] != channel_name:
173 continue
174 if data_type == 'branch':
175 number = version['version'].split('.')[2]
176 elif data_type == 'version':
177 number = version['version'].split('.')[0]
178 if number not in numbers:
179 numbers[number] = 0
180 else:
181 numbers[number] += 1
183 sorted_numbers = sorted(numbers.iteritems(),
184 key=operator.itemgetter(1),
185 reverse=True)
186 object_store.Set(channel_name, sorted_numbers[0][0])
187 return sorted_numbers[0][0]
189 def GetBranchForVersion(self, version):
190 '''Returns the most recent branch for a given chrome version number using
191 data stored on omahaproxy (see url_constants).
193 if version == 'master':
194 return 'master'
196 branch = self._branch_object_store.Get(str(version)).Get()
197 if branch is not None:
198 return branch
200 version_json = json.loads(self._history_result.Get().content)
201 for entry in version_json:
202 version_title = entry['version'].split('.')
203 if version_title[0] == str(version):
204 self._branch_object_store.Set(str(version), version_title[2])
205 return version_title[2]
207 raise ValueError('The branch for %s could not be found.' % version)
209 def GetChannelForVersion(self, version):
210 '''Returns the name of the development channel corresponding to a given
211 version number.
213 for channel_info in self.GetAllChannelInfo():
214 if channel_info.channel == 'stable' and version <= channel_info.version:
215 return channel_info.channel
216 if version == channel_info.version:
217 return channel_info.channel
219 def GetLatestVersionNumber(self):
220 '''Returns the most recent version number found using data stored on
221 omahaproxy.
223 latest_version = self._version_object_store.Get('latest').Get()
224 if latest_version is not None:
225 return latest_version
227 version_json = json.loads(self._history_result.Get().content)
228 latest_version = 0
229 for entry in version_json:
230 version_title = entry['version'].split('.')
231 version = int(version_title[0])
232 if version > latest_version:
233 latest_version = version
235 self._version_object_store.Set('latest', latest_version)
236 return latest_version