blob: 5503aa7ee3809d11ffb06ea4819ee7bbf79a1c67 [file] [log] [blame]
Gilad Arnold84eb60c2012-11-05 23:46:10 -08001# Copyright (c) 2012 The Chromium OS 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.
4
5"""Modules for obtaining Chrome OS release info."""
6
7
8import ConfigParser
9import bisect
10import os
11
12
13_RELEASE_CONFIG_FILE = os.path.join(os.getcwd(), 'release_config.ini')
14
15# Prefix for brachpoint definitions in the config file.
16_CONF_BRANCH_SECTION = 'BRANCH'
17_CONF_BRANCH_POINTS_OPT = 'branch_points'
18_CONF_BRANCH_POINT_OPT_PREFIX = 'bp_'
19_CONF_NEXT_BRANCH_OPT = 'next_branch'
20
21
22class ReleaseError(BaseException):
23 """Errors related to release and branch inference."""
24 pass
25
26
27class ReleaseInfo(object):
28 """Provides reference information about Chrome OS releases.
29
30 Currently, this class serves for mapping between releases and branches /
31 release milestones. The information lives in a .ini file at the current
32 directory, which has a single section [BRANCH] containing
33
34 branch_points: comma-separated list of release branches (e.g. R10, R11,
35 ...)
36
37 bp_XYZ: for each branch listed above, a variable that maps to the Chrome
38 OS release at that branchpoint (e.g. bp_r10: 0.10.156.0). Note that .ini
39 file variables are case-insensitive.
40
41 next_branch: the name of the current (unforked) branch (e.g. R24)
42
Gilad Arnoldc33f6482012-12-20 13:12:22 -080043 It is also worth noting that a branch point X.Y.Z (alternatively, W.X.Y.Z)
44 of some branch R denotes the build number X (repsectively, W) that
45 constitutes the said branch. Therefore, it is only from build X+1 (W+1) and
46 onward that releases will be tagged with R+1.
47
Gilad Arnold84eb60c2012-11-05 23:46:10 -080048 """
49 def __init__(self):
50 self._release_config = None
51 self._branchpoint_dict = None
52 self._next_branch = None
53 self._sorted_branchpoint_list = None
Gilad Arnoldc33f6482012-12-20 13:12:22 -080054 self._sorted_shifted_branchpoint_rel_key_list = None
Gilad Arnold84eb60c2012-11-05 23:46:10 -080055
56 def initialize(self):
57 """Read release config and initialize lookup data structures."""
58 self._release_config = ConfigParser.ConfigParser()
59 try:
60 self._release_config.readfp(open(_RELEASE_CONFIG_FILE))
61
62 # Build branchpoint dictionary.
63 branchpoint_list_str = self._release_config.get(
64 _CONF_BRANCH_SECTION, _CONF_BRANCH_POINTS_OPT)
65 if branchpoint_list_str:
66 branchpoint_list = map(str.strip,
67 branchpoint_list_str.split(','))
68 else:
69 branchpoint_list = []
70
71 self._branchpoint_dict = {}
72 for branchpoint in branchpoint_list:
73 self._branchpoint_dict[branchpoint] = (
74 self._release_config.get(
75 _CONF_BRANCH_SECTION,
76 _CONF_BRANCH_POINT_OPT_PREFIX + branchpoint))
77
78 # Get next branch name.
79 self._next_branch = self._release_config.get(_CONF_BRANCH_SECTION,
80 _CONF_NEXT_BRANCH_OPT)
81 if not self._next_branch:
82 raise ReleaseError("missing `%s' option" %
83 _CONF_NEXT_BRANCH_OPT)
84 except IOError, e:
85 raise ReleaseError('failed to open release config file (%s): %s' %
Gilad Arnold03901082012-11-19 07:29:02 -080086 (_RELEASE_CONFIG_FILE, e))
Gilad Arnold84eb60c2012-11-05 23:46:10 -080087 except ConfigParser.Error, e:
Gilad Arnold03901082012-11-19 07:29:02 -080088 raise ReleaseError('failed to load release config: %s' % e)
Gilad Arnold84eb60c2012-11-05 23:46:10 -080089
90 # Infer chronologically sorted list of branchpoints.
91 self._sorted_branchpoint_list = self._branchpoint_dict.items()
92 self._sorted_branchpoint_list.append((self._next_branch, '99999.0.0'))
93 self._sorted_branchpoint_list.sort(
94 key=lambda (branch, release): self._release_key(release))
95
96 # Also store a sorted list of branchpoint release keys, for easy lookup.
Gilad Arnoldc33f6482012-12-20 13:12:22 -080097 self._sorted_shifted_branchpoint_rel_key_list = [
98 self._release_key(self._next_build_number_release(release))
99 for (branch, release) in self._sorted_branchpoint_list]
100
101
102 def _next_build_number_release(self, release):
103 """Returns the release of the next build following a given release.
104
105 Given a release number 'X.Y.Z' (new scheme) or '0.X.Y.Z' (old scheme)
106 it will return 'X+1.0.0' or '0.X+1.0.0', respectively.
107
108 @param release: the release number in dotted notation (string)
109
110 @return The release number of the next build.
111
112 @raise ReleaseError if the release is malformed.
113
114 """
115 release_components = release.split('.')
116 if len(release_components) == 4 and release_components[0] == '0':
117 prepend = '0.'
118 x = int(release_components[1])
119 elif len(release_components) != 3:
120 raise ReleaseError('invalid release number: %s' % release)
121 else:
122 prepend = ''
123 x = int(release_components[0])
124
125 return '%s%s.0.0' % (prepend, x + 1)
Gilad Arnold84eb60c2012-11-05 23:46:10 -0800126
127
128 def _release_key(self, release):
129 """Convert a Chrome OS release string into an integer key.
130
131 This translates a release string 'X.Y.Z' (new scheme) or 'W.X.Y.Z' (old
132 scheme where W = 0) into an integer whose value equals X * 10^7 + Y *
133 10^3 + Z, assuming that Y < 10^4 and Z < 10^3, and will scale safely to
134 any foreseeable major release number (X).
135
136 @param release: the release number in dotted notation (string)
137
138 @return A unique integer key representing the release.
139
140 @raise ReleaseError if the release is malformed.
141
142 """
143 release_components = release.split('.')
144 if len(release_components) == 4 and release_components[0] == '0':
145 release_components = release_components[1:]
146 elif len(release_components) != 3:
147 raise ReleaseError('invalid release number: %s' % release)
148 x, y, z = [int(s) for s in release_components]
149 return x * 10000000 + y * 1000 + z
150
151
152 def get_branch_list(self):
153 """Retruns chronologically sorted list of branch names."""
154 return [branch for (branch, release) in self._sorted_branchpoint_list]
155
156
157 def get_branch(self, release):
158 """Returns the branch name of a given release version. """
Gilad Arnoldc33f6482012-12-20 13:12:22 -0800159 i = bisect.bisect_left(self._sorted_shifted_branchpoint_rel_key_list,
Gilad Arnold84eb60c2012-11-05 23:46:10 -0800160 self._release_key(release))
161 return self._sorted_branchpoint_list[i][0] if i else None
162
163
164 def get_branchpoint_release(self, branch):
165 """Returns the branchpoint release of a given branch.
166
167 Returns None if given name is the next branch.
168
169 @raise KeyError if branch name not known
170
171 """
172 if branch == self._next_branch:
173 return None
174 return self._branchpoint_dict[branch]