Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(82)

Side by Side Diff: buildbot/manifest_version.py

Issue 6691047: Merge MVP script into cbuildbot. (Closed) Base URL: http://git.chromium.org/git/chromite.git@master
Patch Set: Fix up manifest_version docstrings and limited unittesting Created 9 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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
14 import os
15 import re
16 import shutil
17 import sys
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
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
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
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
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):
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)
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 """Generates a new buildspec for the builders to consume
533 Checks to see, if there are new changes that need to be built from the last
534 time another buildspec was created. Updates the version number in version
535 number file. Generates the manifest as the next buildspecs. pushes it to git
536 and returns the next build spec number
537 If there are no new changes returns None
538 Args:
539 source_dir: location of the source tree root
540 branch: branch to sync the sources to
541 latest_build: latest known build to match the latest sources against
542 build_specs_root: location of the build specs root
543 build_specs_manager: BuildSpecsManager object
544 cros_source: GitMirror object
545 version_info: VersionInfo Object
546 increment_version: True or False for incrementing the chromeos_version.sh
547 Returns:
548 next build number: on new changes or
549 None: on no new changes
550 """
551 temp_manifest_file = tempfile.mkstemp(suffix='mvp', text=True)[1]
552
553 ExportManifest(source_dir, temp_manifest_file)
554
555 latest_spec_file = '%s.xml' % os.path.join(
556 build_specs_manager.specs_info.all_specs_dir, latest_build)
557
558 logging.debug('Calling DiffManifests with %s, %s', latest_spec_file,
559 temp_manifest_file)
560 if not (latest_build == '0.0.0.0' or
561 DiffManifests(temp_manifest_file, latest_spec_file)):
562 return None
563
564 next_version = version_info.VersionString()
565
566 if increment_version:
567 next_version = version_info.IncrementVersion()
568 logging.debug('Incremented version number to %s', next_version)
569 message = 'Automatic: Updating the new version number %s' % next_version
570 PushChanges(os.path.dirname(version_info.version_file), message, True)
571 CheckOutSources(cros_source, source_dir, branch)
572
573 next_spec_file = '%s.xml' % os.path.join(
574 build_specs_manager.specs_info.all_specs_dir, next_version)
575 if not os.path.exists(os.path.dirname(next_spec_file)):
576 os.makedirs(os.path.dirname(next_spec_file))
577 ExportManifest(source_dir, next_spec_file)
578 message = 'Automatic: Buildspec %s. Created by %s' % (
579 next_version, build_specs_manager.specs_info.build_name)
580 logging.debug('Created New Build Spec %s', message)
581
582 build_specs_manager.SetInFlight(next_version)
583 PushChanges(build_specs_root, message)
584 return next_version
585
586
587 def DiffManifests(manifest1, manifest2):
588 """Diff two manifest files. excluding the revisions to manifest-verisons.git
589 Args:
590 manifest1: First manifest file to compare
591 manifest2: Second manifest file to compare
592 Returns:
593 True: If the manifests are different
594 False: If the manifests are same
595 """
596 black_list = ['manifest-versions']
597 blacklist_pattern = re.compile(r'|'.join(black_list))
598 with open(manifest1, 'r') as manifest1_fh:
599 with open(manifest2, 'r') as manifest2_fh:
600 for (line1, line2) in zip(manifest1_fh, manifest2_fh):
601 if blacklist_pattern.search(line1):
602 logging.debug('%s ignored %s', line1, line2)
603 continue
604
605 if line1 != line2:
606 return True
607 return False
608
609
610 def SetStatus(build_version, status, working_dir, ver_manifests, build_name):
611 """Set the status of a particular build by moving the build_spec files around
612 Args:
613 build_version: version of the build that we should set the status for
614 status: status to set. One of 'pass|fail'
615 working_dir: working dir where our version-manifests repos are synced
616 ver_manifests: name of the version manifests repo
617 build_name: Name of the build that we are settting the status for
618 """
619 logging.info('Setting Status for %s as %s', build_version, status)
620 match = re.search(VER_PATTERN, build_version)
621 logging.debug('Matched %s - %s - %s - %s', match.group(1), match.group(2),
622 match.group(3), match.group(4))
623 version_info = VersionInfo(ver_maj=match.group(1), ver_min=match.group(2),
624 ver_sp=match.group(3), ver_patch=match.group(4))
625 logging.debug('Using Version: %s', version_info.VersionString())
626 logging.debug('Using Directory Prefix: %s', version_info.DirPrefix())
627 logging.debug('Using Build Prefix: %s', version_info.BuildPrefix())
628
629 build_specs_manager = BuildSpecsManager(working_dir, ver_manifests,
630 build_name, version_info.DirPrefix(),
631 version_info.BuildPrefix())
632 if status == 'pass':
633 build_specs_manager.SetPassed(build_version)
634 if status == 'fail':
635 build_specs_manager.SetFailed(build_version)
636
637 commit_message = 'Automatic checkin: status = %s build_version %s for %s' % (
638 status, build_version, build_name)
639 PushChanges(build_specs_manager.specs_info.root_dir, commit_message)
640
641
642 def RemoveDirs(dir_name):
643 """Remove directories recursively, if they exist"""
644 if os.path.exists(dir_name):
645 shutil.rmtree(dir_name)
646
647 def CleanUP(source_dir=None, working_dir=None, git_mirror_dir=None):
648 """Clean up of all the directories that are created by this script
649 Args:
650 source_dir: directory where the sources are. Default: None
651 working_dir: directory where manifest-versions is. Default: None
652 git_mirror_dir: directory where git mirror is setup. Default: None
653 """
654 if source_dir:
655 logging.debug('Cleaning source dir = %s', source_dir)
656 RemoveDirs(source_dir)
657
658 if working_dir:
659 logging.debug('Cleaning working dir = %s', working_dir)
660 RemoveDirs(working_dir)
661
662 if git_mirror_dir:
663 logging.debug('Cleaning git mirror dir = %s', git_mirror_dir)
664 RemoveDirs(git_mirror_dir)
665
666 def GenerateWorkload(tmp_dir, source_repo, manifest_repo,
667 branch, version_file,
668 board, incr_type, latest=False,
669 retries=0):
670 """Function to see, if there are any new builds that need to be built.
671 Args:
672 tmp_dir: Temporary working directory, best shared with GenerateWorkload
673 source_repo: git URL to repo with source code
674 manifest_repo: git URL to repo with manifests/status values
675 branch: Which branch to build ('master' or mainline)
676 version_file: location of chromeos_version.sh
677 board: name of the board
678 incr_type: part of the version number to increment 'patch or branch'
679 latest: Whether we need to handout the latest build. Default: False
680 retries: Number of retries for updating the status
681 Returns:
682 next_build: a string of the next build number for the builder to consume
683 or None in case of no need to build.
684 Raises:
685 GenerateBuildSpecException in case of failure to generate a buildspec
686 """
687
688 ver_manifests=os.path.basename(manifest_repo)
689
690 git_mirror_dir = os.path.join(tmp_dir, 'mirror')
691 source_dir = os.path.join(tmp_dir, 'source')
692 working_dir = os.path.join(tmp_dir, 'working')
693
694 while True:
695 try:
696 cros_source = GitMirror(git_mirror_dir, source_repo)
697 CheckOutSources(cros_source, source_repo, source_dir, branch)
698
699 # Let's be conservative and remove the version_manifests directory.
700 RemoveDirs(working_dir)
701 CloneGitRepo(working_dir, manifest_repo)
702
703 version_file = os.path.join(source_dir, version_file)
704 logging.debug('Using VERSION _FILE = %s', version_file)
705 version_info = VersionInfo(version_file=version_file, incr_type=incr_type)
706 build_specs = BuildSpecs(working_dir, ver_manifests, board,
707 version_info.DirPrefix(),
708 version_info.BuildPrefix(),
709 version_info.incr_type)
710 current_build = version_info.VersionString()
711 logging.debug('Current build in %s: %s', version_file, current_build)
712
713 build_specs_manager = BuildSpecsManager(
714 working_dir, ver_manifests, board, version_info.DirPrefix(),
715 version_info.BuildPrefix())
716
717 next_build = build_specs.FindNextBuild(latest)
718 if next_build:
719 logging.debug('Using an existing build spec: %s', next_build)
720 SetupExistingBuildSpec(build_specs_manager, next_build)
721 return next_build
722
723 # If the build spec doesn't exist for the current version in chromeos_
724 # version.sh. we should just create the build_spec for that version,
725 # instead of incrementing chromeos_version.sh
726
727 increment_version = build_specs.ExistsBuildSpec(current_build)
728 return GenerateNewBuildSpec(source_dir, branch, build_specs.latest,
729 build_specs.specs_info.root_dir,
730 build_specs_manager, cros_source,
731 version_info, increment_version)
732
733 except (cros_lib.RunCommandError, GitCommandException), e:
734 if retries < 1:
735 err_msg = 'Failed to generate buildspec. error: %s' % e
736 logging.error(err_msg)
737 raise GenerateBuildSpecException(err_msg)
738
739 logging.info('Failed to generate a buildspec for consumption: %s',
740 e.message)
741 logging.info('But will try %d times', retries)
742 CleanUP(source_dir, working_dir)
743 retries = retries - 1
744
745 def UpdateStatus(tmp_dir, manifest_repo, board,
746 build_version, success, retries=0):
747 """Updates the status of the build
748 Args:
749 tmp_dir: Temporary working directory, best shared with GenerateWorkload
750 manifest_repo: git URL to repo with manifests/status values
751 board: name of the board
752 build_version: value returned by GenerateWorkload for this build
753 success: True for success, False for failure
754 retries: Number of retries for updating the status
755 Raises:
756 GenerateBuildSpecExtension in case of failure to generate a buildspec
757 """
758
759 working_dir = os.path.join(tmp_dir, 'working')
760 ver_manifests=os.path.basename(manifest_repo)
761 ver_manifests_dir = os.path.join(working_dir, ver_manifests)
762
763 if success:
764 status = 'pass'
765 else:
766 status = 'fail'
767
768 while True:
769 try:
770 if not os.path.exists(ver_manifests_dir):
771 CloneGitRepo(ver_manifests, git_server, working_dir)
772 cros_lib.RunCommand(['git', 'checkout', 'master'], cwd=ver_manifests_dir)
773 logging.debug('Updating the status info for %s to %s', board,
774 status)
775 SetStatus(build_version, status, working_dir, ver_manifests, board)
776 logging.debug('Updated the status info for %s to %s', board,
777 status)
778 break
779 except (GitCommandException, cros_lib.RunCommandError), e:
780 if retries <= 0:
781 logging.error('Failed to update the status for %s to %s', board,
782 status)
783 err_msg = 'with the following error: %s' % e.message
784 logging.error(err_msg)
785 raise StatusUpdateException(err_msg)
786
787 logging.info('Failed to update the status for %s to %s', board,
788 status)
789 logging.info('But will try %d times', retries)
790 RemoveDirs(ver_manifests_dir)
791 retries = retries - 1
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698