OLD | NEW |
---|---|
(Empty) | |
1 #!/usr/bin/python | |
2 # Copyright (c) 2011 The Chromium OS Authors. All rights reserved. | |
3 # Use of this source code is governed by a BSD-style license that can be | |
4 # found in the LICENSE file. | |
5 | |
6 """ | |
7 A script to generate and store the manifests for the canary builders to use. | |
8 """ | |
9 | |
10 import fnmatch | |
11 import logging | |
12 import logging.handlers | |
13 import optparse | |
Raja Aluri
2011/04/12 23:16:43
unused import
dgarrett
2011/04/13 17:55:18
Done.
| |
14 import os | |
15 import re | |
16 import shutil | |
17 import sys | |
Raja Aluri
2011/04/12 23:16:43
unused import
dgarrett
2011/04/13 17:55:18
Done.
| |
18 import tempfile | |
19 | |
20 import chromite.lib.cros_build_lib as cros_lib | |
21 | |
22 logging_format = '%(asctime)s - %(filename)s - %(levelname)-8s: %(message)s' | |
23 date_format = '%Y/%m/%d %H:%M:%S' | |
24 logging.basicConfig(level=logging.INFO, format=logging_format, | |
25 datefmt=date_format) | |
26 | |
27 # Pattern for matching build name format. E.g, 12.3.4.5,1.0.25.3 | |
28 VER_PATTERN = '(\d+).(\d+).(\d+).(\d+)' | |
29 | |
30 class VersionUpdateException(Exception): | |
31 """Exception gets thrown for failing to update the version file""" | |
32 pass | |
33 | |
34 | |
35 class InvalidOptionException(Exception): | |
36 """Exception gets thrown for invalid options""" | |
37 pass | |
38 | |
39 | |
40 class GitCommandException(Exception): | |
41 """Exception gets thrown for a git command that fails to execute.""" | |
42 pass | |
43 | |
44 class SrcCheckOutException(Exception): | |
45 """Exception gets thrown for failure to sync sources""" | |
46 pass | |
47 | |
48 class StatusUpdateException(Exception): | |
49 """Exception gets thrown for failure to update the status""" | |
50 pass | |
51 | |
52 class GenerateBuildSpecException(Exception): | |
53 """Exception gets thrown for failure to Generate a buildspec for the build""" | |
54 pass | |
55 | |
56 | |
57 class GitMirror(object): | |
58 """ A Class to deal with the source tree/mirror setup and sync repos | |
59 Args: | |
60 mirror_dir: location for setting up git mirror | |
61 git_server: gitserver URI information | |
Raja Aluri
2011/04/12 23:16:43
docstrings don't match the argument changes anymor
dgarrett
2011/04/13 17:55:18
Done.
| |
62 manifest_repo: repo to fetch the manifests from | |
63 manifest_file: manifest_file to use for syncing | |
64 """ | |
65 def __init__(self, mirror_dir, repo_url, manifest_file='full.xml'): | |
66 self.mirror_dir = mirror_dir | |
67 self.repo_url = repo_url | |
68 self.manifest_file = manifest_file | |
69 self.InitMirrorRepos() | |
70 | |
71 def InitMirrorRepos(self): | |
72 """Initialize the git mirroring""" | |
73 if not os.path.exists(self.mirror_dir): | |
74 os.makedirs(self.mirror_dir) | |
75 logging.debug('git mirror does not exist. Creating it for the first time') | |
76 | |
77 if not os.path.exists(os.path.join(self.mirror_dir, '.repo')): | |
78 cros_lib.RunCommand(['repo', 'init', '-u', self.repo_url, '-m', | |
79 self.manifest_file, '--mirror'], cwd=self.mirror_dir, | |
80 input='\n\ny\n') | |
81 | |
82 def SyncMirror(self): | |
83 """Sync/update the git mirror location""" | |
84 cros_lib.RunCommand(['repo', 'sync'], cwd=self.mirror_dir) | |
85 | |
86 | |
87 class VersionInfo(object): | |
88 """Class to encapsualte the chrome os version info | |
89 You can instantiate this class in two ways. | |
90 1)using a version file, specifically chromeos_version.sh, | |
91 which contains the version information. | |
92 2) just passing in the 4 version components (major, minor, sp and patch) | |
93 Args: | |
94 ver_maj: major version | |
95 ver_min: minor version | |
96 ver_sp: sp version | |
97 ver_patch: patch version | |
98 version_file: version file location. | |
99 """ | |
100 def __init__(self, ver_maj=0, ver_min=0, ver_sp=0, ver_patch=0, | |
101 incr_type=None, version_file=None): | |
102 self.ver_maj = ver_maj | |
103 self.ver_min = ver_min | |
104 self.ver_sp = ver_sp | |
105 self.ver_patch = ver_patch | |
106 self.incr_type = incr_type | |
107 if version_file: | |
108 self.version_file = version_file | |
109 self.load() | |
110 | |
111 def FindValue(self, key, line): | |
112 """Given the key find the value from the line, if it finds key = value | |
113 Args: | |
114 key: key to look for | |
115 line: string to search | |
116 returns: | |
117 None: on a non match | |
118 value: for a matching key | |
119 """ | |
120 regex = '.*(%s)\s*=\s*(\d+)$' % key | |
121 | |
122 match = re.match(regex, line) | |
123 if match: | |
124 return match.group(2) | |
125 return None | |
126 | |
127 def load(self): | |
128 """Read the version file and set the version components""" | |
129 with open(self.version_file, 'r') as version_fh: | |
130 for line in version_fh: | |
131 if not line.strip(): | |
132 continue | |
133 | |
134 match = self.FindValue('CHROMEOS_VERSION_MAJOR', line) | |
135 if match: | |
136 self.ver_maj = match | |
137 logging.debug('Set the major version to:%s', self.ver_maj) | |
138 continue | |
139 | |
140 match = self.FindValue('CHROMEOS_VERSION_MINOR', line) | |
141 if match: | |
142 self.ver_min = match | |
143 logging.debug('Set the minor version to:%s', self.ver_min) | |
144 continue | |
145 | |
146 match = self.FindValue('CHROMEOS_VERSION_BRANCH', line) | |
147 if match: | |
148 self.ver_sp = match | |
149 logging.debug('Set the sp version to:%s', self.ver_sp) | |
150 continue | |
151 | |
152 match = self.FindValue('CHROMEOS_VERSION_PATCH', line) | |
153 if match: | |
154 self.ver_patch = match | |
155 logging.debug('Set the patch version to:%s', self.ver_patch) | |
156 continue | |
157 logging.debug(self.VersionString()) | |
158 | |
159 def IncrementVersion(self): | |
160 """Updates the version file by incrementing the patch component""" | |
161 if not self.version_file: | |
162 raise VersionUpdateException('Cannot call IncrementVersion without ' | |
163 'an associated version_file') | |
164 if not self.incr_type: | |
165 raise VersionUpdateException('Need to specify the part of the version to' | |
166 'increment') | |
167 | |
168 if self.incr_type == 'branch': | |
169 self.ver_sp = str(int(self.ver_sp) + 1) | |
170 self.ver_patch = '0' | |
171 if self.incr_type == 'patch': | |
172 self.ver_patch = str(int(self.ver_patch) + 1) | |
173 temp_file = tempfile.mkstemp(suffix='mvp', prefix='tmp', dir=None, | |
174 text=True)[1] | |
175 with open(self.version_file, 'r') as source_version_fh: | |
176 with open(temp_file, 'w') as temp_fh: | |
177 for line in source_version_fh: | |
178 old_patch = self.FindValue('CHROMEOS_VERSION_PATCH', line) | |
179 if old_patch: | |
180 temp_fh.write(line.replace(old_patch, self.ver_patch, 1)) | |
181 continue | |
182 | |
183 old_sp = self.FindValue('CHROMEOS_VERSION_BRANCH', line) | |
184 if old_sp: | |
185 temp_fh.write(line.replace(old_sp, self.ver_sp, 1)) | |
186 continue | |
187 | |
188 temp_fh.write(line) | |
189 temp_fh.close() | |
190 source_version_fh.close() | |
191 shutil.copyfile(temp_file, self.version_file) | |
192 os.unlink(temp_file) | |
193 return self.VersionString() | |
194 | |
195 def VersionString(self): | |
196 """returns the version string""" | |
197 return '%s.%s.%s.%s' % (self.ver_maj, self.ver_min, self.ver_sp, | |
198 self.ver_patch) | |
199 def DirPrefix(self): | |
200 """returns the sub directory suffix in manifest-versions""" | |
201 return '%s.%s' % (self.ver_maj, self.ver_min) | |
202 | |
203 def BuildPrefix(self): | |
204 """returns the build prefix to match the buildspecs in manifest-versions""" | |
205 if self.incr_type == 'patch': | |
206 return '%s.%s.%s' % (self.ver_maj, self.ver_min, self.ver_sp) | |
207 | |
208 if self.incr_type == 'branch': | |
209 return '%s.%s' % (self.ver_maj, self.ver_min) | |
210 | |
211 return None | |
212 | |
213 | |
214 class SpecsInfo(object): | |
215 """Class that contains information about the buildspecs | |
216 Args: | |
217 working_dir: location of the buildspecs location | |
218 build_name: name of the build that we will be dealing with | |
219 dir_prefix: prefix of the version for sub-directory location | |
220 build_prefix: prefix of the build version for build specs matching | |
221 """ | |
222 def __init__(self, working_dir, ver_manifests, build_name, dir_prefix, | |
223 build_prefix): | |
224 self.working_dir = working_dir | |
225 self.ver_manifests = ver_manifests | |
226 self.build_name = build_name | |
227 self.dir_prefix = dir_prefix | |
228 self.build_prefix = build_prefix | |
229 self.root_dir = os.path.join(self.working_dir, self.ver_manifests) | |
230 self.all_specs_dir = os.path.join(self.root_dir, 'buildspecs', | |
231 self.dir_prefix) | |
232 self.pass_dir = os.path.join(self.root_dir, 'build-name', self.build_name, | |
233 'pass', self.dir_prefix) | |
234 self.fail_dir = os.path.join(self.root_dir, 'build-name', self.build_name, | |
235 'fail', self.dir_prefix) | |
236 self.inflight_dir = os.path.join(self.root_dir, 'build-name', | |
237 self.build_name, 'inflight', | |
238 self.dir_prefix) | |
239 logging.debug('Build Specs Dir = %s', self.all_specs_dir) | |
240 | |
241 | |
242 class BuildSpecs(object): | |
243 """ Class to manage buildspecs | |
244 Args: | |
245 working_dir:location where the ver_manifests repo is synced to | |
246 ver_manifests: Name of the ver_manifests repo. Default: manifest-versions | |
247 build_name: Name of the build that we are interested in. E.g., x86-alex | |
248 dir_prefix: prefix of the version numbers for sub directory look up | |
249 build_prefix: prefix of the build version for build specs matching | |
250 incr_type: part of the version to increment. 'patch or branch' | |
251 """ | |
252 def __init__(self, working_dir, ver_manifests, build_name, dir_prefix, | |
253 build_prefix, incr_type): | |
254 self.specs_info = SpecsInfo(working_dir, ver_manifests, build_name, | |
255 dir_prefix, build_prefix) | |
256 self.all = self.GetMatchingSpecs(self.specs_info.all_specs_dir, incr_type) | |
257 self.latest = '0.0.0.0' | |
258 self.incr_type = incr_type | |
259 | |
260 if self.all: | |
261 self.latest = self.all[-1] | |
262 self.all = set(self.GetMatchingSpecs(self.specs_info.all_specs_dir, | |
263 self.incr_type)) | |
264 self.passed = self.GetMatchingSpecs(self.specs_info.pass_dir, | |
265 self.incr_type) | |
266 self.failed = self.GetMatchingSpecs(self.specs_info.fail_dir, | |
267 self.incr_type) | |
268 self.inflight = self.GetMatchingSpecs(self.specs_info.inflight_dir, | |
269 self.incr_type) | |
270 self.processed = sorted((self.passed + self.failed + self.inflight), | |
271 key=lambda s: map(int, s.split('.'))) | |
272 self.last_processed = '0.0.0.0' | |
273 if self.processed: | |
274 self.last_processed = self.processed[-1] | |
275 logging.debug('Last processed build for %s is %s' % | |
276 (build_name, self.last_processed)) | |
277 | |
278 difference = list(self.all.difference(set(self.processed))) | |
279 | |
280 for build in difference[:]: | |
281 build1 = map(int, build.split(".")) | |
282 build2 = map(int, self.last_processed.split(".")) | |
283 | |
284 if cmp(build1 , build2) == 1: | |
285 logging.debug('Still need to build %s' % build) | |
286 else: | |
287 logging.debug('Ignoring build %s less than %s' % | |
288 (build, self.last_processed)) | |
289 difference.remove(build) | |
290 | |
291 self.unprocessed = sorted(difference, key=lambda s: map(int, s.split('.'))) | |
292 | |
293 def GetMatchingSpecs(self, specs_info, incr_type): | |
294 """Returns the sorted list of buildspecs that match '*.xml' | |
295 Args: | |
296 specs_info: SpecsInfo object for the buildspecs location information. | |
297 """ | |
298 matched_manifests = [] | |
299 if os.path.exists(specs_info): | |
300 all_manifests = os.listdir(specs_info) | |
301 match_string = self.specs_info.build_prefix + '.*.xml' | |
302 | |
303 if incr_type == 'branch': | |
304 match_string = self.specs_info.build_prefix + '.*.0.xml' | |
305 | |
306 matched_manifests = fnmatch.filter( | |
307 all_manifests, match_string) | |
308 matched_manifests = [os.path.splitext(m)[0] for m in matched_manifests] | |
309 | |
310 return matched_manifests | |
311 | |
312 def FindNextBuild(self, latest=False): | |
313 """"Find the next build to build from unprocessed buildspecs | |
314 Args: | |
315 latest: True of False. (Returns the last known build on latest =True | |
316 Returns: | |
317 Returns the next build to be built or None | |
318 """ | |
319 if not self.unprocessed: | |
320 return None | |
321 | |
322 if latest: | |
323 return self.unprocessed[-1] | |
324 | |
325 return self.unprocessed[0] | |
326 | |
327 def ExistsBuildSpec(self, build_number): | |
328 """"Find out if the build_number exists in existing buildspecs | |
329 Args: | |
330 build_number: build number to check for existence of build spec | |
331 Returns: | |
332 True if the buildspec exists, False if it doesn't | |
333 """ | |
334 return build_number in self.all | |
335 | |
336 | |
337 class BuildSpecsManager(object): | |
338 """A Class to manage buildspecs and their states | |
339 Args: | |
340 working_dir: location of the buildspecs repo | |
341 build_name: build_name e.g., x86-mario, x86-mario-factory | |
342 dir_prefix: version prefix to match for the sub directories | |
343 build_prefix: prefix of the build version for build specs matching | |
344 """ | |
345 | |
346 def __init__(self, working_dir, ver_manifests, build_name, dir_prefix, | |
347 build_prefix): | |
348 self.specs_info = SpecsInfo(working_dir, ver_manifests, build_name, | |
349 dir_prefix, build_prefix) | |
350 | |
351 def SetUpSymLinks(self, src_file, dest_file, remove_file=None): | |
352 """Setting up symlinks for various statuses in the git repo | |
353 Args: | |
354 status: status type to set to, (one of inflight, pass, fail) | |
355 src_file: source for the symlink | |
356 dest_file: destination for the symlink | |
357 remove_file: symlink that needs to be deleted for clearing the old state | |
358 """ | |
359 dest_dir = os.path.dirname(dest_file) | |
360 if os.path.lexists(dest_file): | |
361 os.unlink(dest_file) | |
362 | |
363 if not os.path.exists(dest_dir): | |
364 os.makedirs(dest_dir) | |
365 rel_src_file = os.path.relpath(src_file, dest_dir) | |
366 logging.debug('Linking %s to %s', rel_src_file, dest_file) | |
367 os.symlink(rel_src_file, dest_file) | |
368 | |
369 if remove_file and os.path.lexists(remove_file): | |
370 logging.debug('REMOVE: Removing %s', remove_file) | |
371 os.unlink(remove_file) | |
372 | |
373 | |
374 def SetInFlight(self, version): | |
375 """Marks a buildspec as inflight by creating a symlink in 'inflight' dir | |
376 Args: | |
377 version: version of the buildspec to mark as inflight | |
378 """ | |
379 dest_file = '%s.xml' % os.path.join(self.specs_info.inflight_dir, version) | |
380 src_file = '%s.xml' % os.path.join(self.specs_info.all_specs_dir, version) | |
381 logging.debug('Setting build in flight %s: %s', src_file, dest_file) | |
382 self.SetUpSymLinks(src_file, dest_file) | |
383 | |
384 def SetFailed(self, version): | |
385 """Marks a buildspec as failed by creating a symlink in 'fail' dir | |
386 Args: | |
387 version: version of the buildspec to mark as failed | |
388 """ | |
389 dest_file = '%s.xml' % os.path.join(self.specs_info.fail_dir, version) | |
390 src_file = '%s.xml' % os.path.join(self.specs_info.all_specs_dir, version) | |
391 remove_file = '%s.xml' % os.path.join(self.specs_info.inflight_dir, version) | |
392 logging.debug('Setting build to failed %s: %s', src_file, dest_file) | |
393 self.SetUpSymLinks(src_file, dest_file, remove_file) | |
394 | |
395 def SetPassed(self, version): | |
396 """Marks a buildspec as failed by creating a symlink in 'pass' dir | |
397 Args: | |
398 version: version of the buildspec to mark as passed | |
399 """ | |
400 dest_file = '%s.xml' % os.path.join(self.specs_info.pass_dir, version) | |
401 src_file = '%s.xml' % os.path.join(self.specs_info.all_specs_dir, version) | |
402 remove_file = '%s.xml' % os.path.join(self.specs_info.inflight_dir, version) | |
403 logging.debug('Setting build to passed %s: %s', src_file, dest_file) | |
404 self.SetUpSymLinks(src_file, dest_file, remove_file) | |
405 | |
406 | |
407 def CloneGitRepo(working_dir, repo_url): | |
408 """"Clone Given git repo | |
409 Args: | |
410 repo: git repo to clione | |
411 git_server: git server URL | |
Raja Aluri
2011/04/12 23:16:43
docstrings out of date
dgarrett
2011/04/13 17:55:18
Done.
| |
412 repo_dir: location where it should be cloned to | |
413 """ | |
414 if not os.path.exists(working_dir): | |
415 os.makedirs(working_dir) | |
416 cros_lib.RunCommand(['git', 'clone', repo_url], cwd=working_dir) | |
417 | |
418 def PushChanges(git_repo, message, use_repo=False, dry_run=True): | |
419 """Do the final commit into the git repo | |
420 Args: | |
421 git_repo: git repo to push | |
422 message: Commit message | |
Raja Aluri
2011/04/12 23:16:43
docstrings out of date
dgarrett
2011/04/13 17:55:18
Done.
| |
423 use_repo: use repo tool for pushing changes. Default: False | |
424 raises: GitCommandException | |
425 """ | |
426 branch = 'temp_auto_checkin_branch' | |
427 try: | |
428 if use_repo: | |
429 cros_lib.RunCommand(['repo', 'start', branch, '.'], cwd=git_repo) | |
430 cros_lib.RunCommand(['repo', 'sync', '.'], cwd=git_repo) | |
431 cros_lib.RunCommand(['git', 'config', 'push.default', 'tracking'], | |
432 cwd=git_repo) | |
433 else: | |
434 cros_lib.RunCommand(['git', 'pull', '--force'], cwd=git_repo) | |
435 cros_lib.RunCommand(['git', 'add', '-A'], cwd=git_repo) | |
436 cros_lib.RunCommand(['git', 'commit', '-am', message], cwd=git_repo) | |
437 | |
438 push_cmd = ['git', 'push', '--verbose'] | |
439 if dry_run: push_cmd.append('--dry-run') | |
440 cros_lib.RunCommand(push_cmd, cwd=git_repo) | |
441 except cros_lib.RunCommandError, e: | |
442 err_msg = 'Failed to commit to %s' % e.message | |
443 logging.error(err_msg) | |
444 git_status = cros_lib.RunCommand(['git', 'status'], cwd=git_repo) | |
445 logging.error('Current repo %s status: %s', git_repo, git_status) | |
446 CleanGitChanges(git_repo) | |
447 raise GitCommandException(err_msg) | |
448 finally: | |
449 if use_repo: | |
450 cros_lib.RunCommand(['repo', 'abandon', branch], cwd=git_repo) | |
451 | |
452 def CleanGitChanges(git_repo): | |
453 """"Clean git repo chanages | |
454 Args: | |
455 git_repo: location of the git repo to clean | |
456 raises: GitCommandException: when fails to clean | |
457 """ | |
458 try: | |
459 cros_lib.RunCommand(['git', 'clean', '-d', '-f'], cwd=git_repo) | |
460 cros_lib.RunCommand(['git', 'reset', '--hard', 'HEAD'], cwd=git_repo) | |
461 except cros_lib.RunCommandError, e: | |
462 err_msg = 'Failed to clean git repo %s' % e.message | |
463 logging.error(err_msg) | |
464 raise GitCommandException(err_msg) | |
465 | |
466 | |
467 def SetLogFileHandler(logfile): | |
468 """This sets the logging handler to a file. | |
469 define a Handler which writes INFO messages or higher to the sys.stderr | |
470 Add the log message handler to the logger | |
471 Args: | |
472 logfile: name of the logfile to open | |
473 """ | |
474 logfile_handler = logging.handlers.RotatingFileHandler(logfile, backupCount=5) | |
475 logfile_handler.setLevel(logging.DEBUG) | |
476 logfile_handler.setFormatter(logging.Formatter(logging_format)) | |
477 logging.getLogger().addHandler(logfile_handler) | |
478 | |
479 | |
480 def CheckOutSources(cros_source, repo_url, source_dir, branch, | |
481 manifest_file='full.xml', retries=0): | |
482 """"Fetches the sources from the git server using repo tool | |
483 Args: | |
484 cros_source: GitMirror Object | |
Raja Aluri
2011/04/12 23:16:43
docstrings out of date
dgarrett
2011/04/13 17:55:18
Done.
| |
485 source_dir: Location to sync the sources to | |
486 branch: branch to use for the manifest.xml for repo tool | |
487 manifest_repo: repo to use for manifest files | |
488 manifest_file: manifest file to use for the file definition | |
489 retries: Number of times to try to fetch sources | |
490 """ | |
491 while True: | |
492 if not os.path.exists(source_dir): | |
493 os.makedirs(source_dir) | |
494 try: | |
495 cros_source.SyncMirror() | |
496 cros_lib.RunCommand(['repo', 'init', '-u', repo_url, '-b', branch, '-m', | |
497 manifest_file, '--reference', | |
498 cros_source.mirror_dir], cwd=source_dir, | |
499 input='\n\ny\n') | |
500 cros_lib.RunCommand(['repo', 'sync'], cwd=source_dir) | |
501 break | |
502 except cros_lib.RunCommandError, e: | |
503 if retries < 1: | |
504 err_msg = 'Failed to sync sources %s' % e.message | |
505 logging.error(err_msg) | |
506 raise SrcCheckOutException(err_msg) | |
507 logging.info('Failed to sync sources %s', e.message) | |
508 logging.info('But will try %d times', retries) | |
509 CleanUP(source_dir) | |
510 retries = retries - 1 | |
511 | |
512 def ExportManifest(source_dir, output_file): | |
513 """Exports manifests file | |
514 Args: | |
515 source_dir: repo root | |
516 output_file: output_file to out put the manifest to | |
517 """ | |
518 cros_lib.RunCommand(['repo', 'manifest', '-r', '-o', output_file], | |
519 cwd=source_dir) | |
520 | |
521 | |
522 def SetupExistingBuildSpec(build_specs_manager, next_build, dry_run): | |
523 build_specs_manager.SetInFlight(next_build) | |
524 commit_message = 'Automatic: Start %s %s' % ( | |
525 build_specs_manager.specs_info.build_name, next_build) | |
526 PushChanges(build_specs_manager.specs_info.root_dir, commit_message, dry_run) | |
527 | |
528 | |
529 def GenerateNewBuildSpec(source_dir, branch, latest_build, build_specs_root, | |
530 build_specs_manager, cros_source, version_info, | |
531 increment_version, | |
532 dry_run): | |
533 """Generates a new buildspec for the builders to consume | |
534 Checks to see, if there are new changes that need to be built from the last | |
535 time another buildspec was created. Updates the version number in version | |
536 number file. Generates the manifest as the next buildspecs. pushes it to git | |
537 and returns the next build spec number | |
538 If there are no new changes returns None | |
539 Args: | |
540 source_dir: location of the source tree root | |
541 branch: branch to sync the sources to | |
542 latest_build: latest known build to match the latest sources against | |
543 build_specs_root: location of the build specs root | |
544 build_specs_manager: BuildSpecsManager object | |
545 cros_source: GitMirror object | |
546 version_info: VersionInfo Object | |
547 increment_version: True or False for incrementing the chromeos_version.sh | |
548 dry_run: if True don't push changes externally | |
549 Returns: | |
550 next build number: on new changes or | |
551 None: on no new changes | |
552 """ | |
553 temp_manifest_file = tempfile.mkstemp(suffix='mvp', text=True)[1] | |
554 | |
555 ExportManifest(source_dir, temp_manifest_file) | |
556 | |
557 latest_spec_file = '%s.xml' % os.path.join( | |
558 build_specs_manager.specs_info.all_specs_dir, latest_build) | |
559 | |
560 logging.debug('Calling DiffManifests with %s, %s', latest_spec_file, | |
561 temp_manifest_file) | |
562 if not (latest_build == '0.0.0.0' or | |
563 DiffManifests(temp_manifest_file, latest_spec_file)): | |
564 return None | |
565 | |
566 next_version = version_info.VersionString() | |
567 | |
568 if increment_version: | |
569 next_version = version_info.IncrementVersion() | |
570 logging.debug('Incremented version number to %s', next_version) | |
571 message = 'Automatic: Updating the new version number %s' % next_version | |
572 PushChanges(os.path.dirname(version_info.version_file), message, | |
dgarrett
2011/04/12 23:21:56
This line originally passed use_repo=True to PushC
Raja Aluri
2011/04/12 23:26:04
That's correct though.IT should use repo. This fil
| |
573 True, dry_run) | |
574 CheckOutSources(cros_source, source_dir, branch) | |
575 | |
Raja Aluri
2011/04/12 23:16:43
cros_source, repo_url, source_dir, branch,
dgarrett
2011/04/13 17:55:18
You were right, but the correct answer is that cro
| |
576 next_spec_file = '%s.xml' % os.path.join( | |
577 build_specs_manager.specs_info.all_specs_dir, next_version) | |
578 if not os.path.exists(os.path.dirname(next_spec_file)): | |
579 os.makedirs(os.path.dirname(next_spec_file)) | |
580 ExportManifest(source_dir, next_spec_file) | |
581 message = 'Automatic: Buildspec %s. Created by %s' % ( | |
582 next_version, build_specs_manager.specs_info.build_name) | |
583 logging.debug('Created New Build Spec %s', message) | |
584 | |
585 build_specs_manager.SetInFlight(next_version) | |
586 PushChanges(build_specs_root, message, dry_run) | |
587 return next_version | |
588 | |
589 | |
590 def DiffManifests(manifest1, manifest2): | |
591 """Diff two manifest files. excluding the revisions to manifest-verisons.git | |
592 Args: | |
593 manifest1: First manifest file to compare | |
594 manifest2: Second manifest file to compare | |
Raja Aluri
2011/04/12 23:16:43
docstrings out of date
dgarrett
2011/04/13 17:55:18
This one's still current.
| |
595 Returns: | |
596 True: If the manifests are different | |
597 False: If the manifests are same | |
598 """ | |
599 black_list = ['manifest-versions'] | |
600 blacklist_pattern = re.compile(r'|'.join(black_list)) | |
601 with open(manifest1, 'r') as manifest1_fh: | |
602 with open(manifest2, 'r') as manifest2_fh: | |
603 for (line1, line2) in zip(manifest1_fh, manifest2_fh): | |
604 if blacklist_pattern.search(line1): | |
605 logging.debug('%s ignored %s', line1, line2) | |
606 continue | |
607 | |
608 if line1 != line2: | |
609 return True | |
610 return False | |
611 | |
612 | |
613 def SetStatus(build_version, status, working_dir, ver_manifests, build_name, | |
614 dry_run): | |
615 """Set the status of a particular build by moving the build_spec files around | |
616 Args: | |
617 build_version: version of the build that we should set the status for | |
618 status: status to set. One of 'pass|fail' | |
619 working_dir: working dir where our version-manifests repos are synced | |
620 ver_manifests: name of the version manifests repo | |
621 build_name: Name of the build that we are settting the status for | |
622 dry_run: if True don't push changes externally | |
623 """ | |
624 logging.info('Setting Status for %s as %s', build_version, status) | |
625 match = re.search(VER_PATTERN, build_version) | |
626 logging.debug('Matched %s - %s - %s - %s', match.group(1), match.group(2), | |
627 match.group(3), match.group(4)) | |
628 version_info = VersionInfo(ver_maj=match.group(1), ver_min=match.group(2), | |
629 ver_sp=match.group(3), ver_patch=match.group(4)) | |
630 logging.debug('Using Version: %s', version_info.VersionString()) | |
631 logging.debug('Using Directory Prefix: %s', version_info.DirPrefix()) | |
632 logging.debug('Using Build Prefix: %s', version_info.BuildPrefix()) | |
633 | |
634 build_specs_manager = BuildSpecsManager(working_dir, ver_manifests, | |
635 build_name, version_info.DirPrefix(), | |
636 version_info.BuildPrefix()) | |
637 if status == 'pass': | |
638 build_specs_manager.SetPassed(build_version) | |
639 if status == 'fail': | |
640 build_specs_manager.SetFailed(build_version) | |
641 | |
642 commit_message = 'Automatic checkin: status = %s build_version %s for %s' % ( | |
643 status, build_version, build_name) | |
644 PushChanges(build_specs_manager.specs_info.root_dir, commit_message, | |
645 dry_run=dry_run) | |
646 | |
647 | |
648 def RemoveDirs(dir_name): | |
649 """Remove directories recursively, if they exist""" | |
650 if os.path.exists(dir_name): | |
651 shutil.rmtree(dir_name) | |
652 | |
653 def CleanUP(source_dir=None, working_dir=None, git_mirror_dir=None): | |
654 """Clean up of all the directories that are created by this script | |
655 Args: | |
656 source_dir: directory where the sources are. Default: None | |
657 working_dir: directory where manifest-versions is. Default: None | |
658 git_mirror_dir: directory where git mirror is setup. Default: None | |
659 """ | |
660 if source_dir: | |
661 logging.debug('Cleaning source dir = %s', source_dir) | |
662 RemoveDirs(source_dir) | |
663 | |
664 if working_dir: | |
665 logging.debug('Cleaning working dir = %s', working_dir) | |
666 RemoveDirs(working_dir) | |
667 | |
668 if git_mirror_dir: | |
669 logging.debug('Cleaning git mirror dir = %s', git_mirror_dir) | |
670 RemoveDirs(git_mirror_dir) | |
671 | |
672 def GenerateWorkload(tmp_dir, source_repo, manifest_repo, | |
673 branch, version_file, | |
674 board, incr_type, latest=False, | |
Raja Aluri
2011/04/12 23:16:43
board and build_name are not necessarily equivalen
| |
675 dry_run=False, | |
676 retries=0): | |
677 """Function to see, if there are any new builds that need to be built. | |
678 Args: | |
679 tmp_dir: Temporary working directory, best shared with GenerateWorkload | |
Raja Aluri
2011/04/12 23:16:43
best shared with GenerateWorkload
doesn't convey m
dgarrett
2011/04/13 17:55:18
Done.
dgarrett
2011/04/13 17:55:18
Done.
| |
680 source_repo: git URL to repo with source code | |
681 manifest_repo: git URL to repo with manifests/status values | |
682 branch: Which branch to build ('master' or mainline) | |
683 version_file: location of chromeos_version.sh | |
684 board: name of the board | |
685 incr_type: part of the version number to increment 'patch or branch' | |
686 latest: Whether we need to handout the latest build. Default: False | |
687 dry_run: if True don't push changes externally | |
688 retries: Number of retries for updating the status | |
689 Returns: | |
690 next_build: a string of the next build number for the builder to consume | |
691 or None in case of no need to build. | |
692 Raises: | |
693 GenerateBuildSpecException in case of failure to generate a buildspec | |
694 """ | |
695 | |
696 ver_manifests=os.path.basename(manifest_repo) | |
697 | |
698 git_mirror_dir = os.path.join(tmp_dir, 'mirror') | |
699 source_dir = os.path.join(tmp_dir, 'source') | |
700 working_dir = os.path.join(tmp_dir, 'working') | |
701 | |
702 while True: | |
703 try: | |
704 cros_source = GitMirror(git_mirror_dir, source_repo) | |
705 CheckOutSources(cros_source, source_repo, source_dir, branch) | |
706 | |
707 # Let's be conservative and remove the version_manifests directory. | |
708 RemoveDirs(working_dir) | |
709 CloneGitRepo(working_dir, manifest_repo) | |
710 | |
711 version_file = os.path.join(source_dir, version_file) | |
712 logging.debug('Using VERSION _FILE = %s', version_file) | |
713 version_info = VersionInfo(version_file=version_file, incr_type=incr_type) | |
714 build_specs = BuildSpecs(working_dir, ver_manifests, board, | |
715 version_info.DirPrefix(), | |
716 version_info.BuildPrefix(), | |
717 version_info.incr_type) | |
718 current_build = version_info.VersionString() | |
719 logging.debug('Current build in %s: %s', version_file, current_build) | |
720 | |
721 build_specs_manager = BuildSpecsManager( | |
722 working_dir, ver_manifests, board, version_info.DirPrefix(), | |
723 version_info.BuildPrefix()) | |
724 | |
725 next_build = build_specs.FindNextBuild(latest) | |
726 if next_build: | |
727 logging.debug('Using an existing build spec: %s', next_build) | |
728 SetupExistingBuildSpec(build_specs_manager, next_build, dry_run) | |
729 return next_build | |
730 | |
731 # If the build spec doesn't exist for the current version in chromeos_ | |
732 # version.sh. we should just create the build_spec for that version, | |
733 # instead of incrementing chromeos_version.sh | |
734 | |
735 increment_version = build_specs.ExistsBuildSpec(current_build) | |
736 return GenerateNewBuildSpec(source_dir, branch, build_specs.latest, | |
737 build_specs.specs_info.root_dir, | |
738 build_specs_manager, cros_source, | |
739 version_info, increment_version, | |
740 dry_run) | |
741 | |
742 except (cros_lib.RunCommandError, GitCommandException), e: | |
743 if retries < 1: | |
744 err_msg = 'Failed to generate buildspec. error: %s' % e | |
745 logging.error(err_msg) | |
746 raise GenerateBuildSpecException(err_msg) | |
747 | |
748 logging.info('Failed to generate a buildspec for consumption: %s', | |
749 e.message) | |
750 logging.info('But will try %d times', retries) | |
751 CleanUP(source_dir, working_dir) | |
752 retries = retries - 1 | |
753 | |
754 def UpdateStatus(tmp_dir, manifest_repo, board, | |
755 build_version, success, dry_run=False, retries=0): | |
756 """Updates the status of the build | |
757 Args: | |
758 tmp_dir: Temporary working directory, best shared with GenerateWorkload | |
759 manifest_repo: git URL to repo with manifests/status values | |
760 board: name of the board | |
761 build_version: value returned by GenerateWorkload for this build | |
762 success: True for success, False for failure | |
763 dry_run: if True don't push changes externally | |
764 retries: Number of retries for updating the status | |
765 Raises: | |
766 GenerateBuildSpecExtension in case of failure to generate a buildspec | |
767 """ | |
768 | |
769 working_dir = os.path.join(tmp_dir, 'working') | |
770 ver_manifests=os.path.basename(manifest_repo) | |
771 ver_manifests_dir = os.path.join(working_dir, ver_manifests) | |
772 | |
773 if success: | |
774 status = 'pass' | |
775 else: | |
776 status = 'fail' | |
777 | |
778 while True: | |
779 try: | |
780 if not os.path.exists(ver_manifests_dir): | |
781 CloneGitRepo(ver_manifests, git_server, working_dir) | |
Raja Aluri
2011/04/12 23:16:43
Function mismatch
dgarrett
2011/04/13 17:55:18
Good catch!
| |
782 cros_lib.RunCommand(['git', 'checkout', 'master'], cwd=ver_manifests_dir) | |
783 logging.debug('Updating the status info for %s to %s', board, | |
784 status) | |
785 SetStatus(build_version, | |
786 status, | |
787 working_dir, | |
788 ver_manifests, | |
789 board, | |
Raja Aluri
2011/04/12 23:16:43
argument formatting is not compatible with the res
dgarrett
2011/04/13 17:55:18
Done.
| |
790 dry_run) | |
791 logging.debug('Updated the status info for %s to %s', board, | |
792 status) | |
793 break | |
794 except (GitCommandException, cros_lib.RunCommandError), e: | |
795 if retries <= 0: | |
796 logging.error('Failed to update the status for %s to %s', board, | |
797 status) | |
798 err_msg = 'with the following error: %s' % e.message | |
799 logging.error(err_msg) | |
800 raise StatusUpdateException(err_msg) | |
801 | |
802 logging.info('Failed to update the status for %s to %s', board, | |
803 status) | |
804 logging.info('But will try %d times', retries) | |
805 RemoveDirs(ver_manifests_dir) | |
806 retries = retries - 1 | |
OLD | NEW |