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

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

Powered by Google App Engine
This is Rietveld 408576698