blob: fea12eefd19f81ffaf078dce3d2bfd59d0a7c68d [file] [log] [blame]
Mike Frysingere58c0e22017-10-04 15:43:30 -04001# -*- coding: utf-8 -*-
Don Garrettc4114cc2016-11-01 20:04:06 -07002# Copyright 2016 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"""Bootstrap for cbuildbot.
7
8This script is intended to checkout chromite on the branch specified by -b or
9--branch (as normally accepted by cbuildbot), and then invoke cbuildbot. Most
10arguments are not parsed, only passed along. If a branch is not specified, this
11script will use 'master'.
12
13Among other things, this allows us to invoke build configs that exist on a given
14branch, but not on TOT.
15"""
16
17from __future__ import print_function
18
Benjamin Gordon90b2dd92018-04-12 14:04:21 -060019import base64
Don Garrett125d4dc2017-04-25 16:26:03 -070020import functools
Don Garrettc4114cc2016-11-01 20:04:06 -070021import os
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -070022import time
Don Garrettc4114cc2016-11-01 20:04:06 -070023
24from chromite.cbuildbot import repository
Don Garrett597ddff2017-02-17 18:29:37 -080025from chromite.cbuildbot.stages import sync_stages
Lann Martinebae73d2018-05-21 17:12:00 -060026from chromite.lib import boto_compat
Benjamin Gordon90b2dd92018-04-12 14:04:21 -060027from chromite.lib import build_summary
Don Garrett86881cb2017-02-15 15:41:55 -080028from chromite.lib import config_lib
Don Garretta50bf492017-09-28 18:33:02 -070029from chromite.lib import constants
Don Garrettc4114cc2016-11-01 20:04:06 -070030from chromite.lib import cros_build_lib
31from chromite.lib import cros_logging as logging
Benjamin Gordon74645232018-05-04 17:40:42 -060032from chromite.lib import cros_sdk_lib
Don Garrettacbb2392017-05-11 18:27:41 -070033from chromite.lib import metrics
Don Garrettc4114cc2016-11-01 20:04:06 -070034from chromite.lib import osutils
Don Garrettacbb2392017-05-11 18:27:41 -070035from chromite.lib import ts_mon_config
Don Garrett86881cb2017-02-15 15:41:55 -080036from chromite.scripts import cbuildbot
Don Garrettc4114cc2016-11-01 20:04:06 -070037
Don Garrett60967922017-04-12 18:51:44 -070038# This number should be incremented when we change the layout of the buildroot
39# in a non-backwards compatible way. This wipes all buildroots.
Don Garrettbf90cdf2017-05-19 15:54:02 -070040BUILDROOT_BUILDROOT_LAYOUT = 2
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -070041_DISTFILES_CACHE_EXPIRY_HOURS = 8 * 24
Don Garrett60967922017-04-12 18:51:44 -070042
Don Garrettacbb2392017-05-11 18:27:41 -070043# Metrics reported to Monarch.
Mike Nicholscb29fbd2018-07-24 11:09:33 -060044METRIC_PREFIX = 'chromeos/chromite/cbuildbot_launch/'
45METRIC_ACTIVE = METRIC_PREFIX + 'active'
46METRIC_INVOKED = METRIC_PREFIX + 'invoked'
47METRIC_COMPLETED = METRIC_PREFIX + 'completed'
48METRIC_PREP = METRIC_PREFIX + 'prep_completed'
49METRIC_CLEAN = METRIC_PREFIX + 'clean_buildroot_durations'
50METRIC_INITIAL = METRIC_PREFIX + 'initial_checkout_durations'
51METRIC_CBUILDBOT = METRIC_PREFIX + 'cbuildbot_durations'
52METRIC_CBUILDBOT_INSTANCE = METRIC_PREFIX + 'cbuildbot_instance_durations'
53METRIC_CLOBBER = METRIC_PREFIX + 'clobber'
54METRIC_BRANCH_CLEANUP = METRIC_PREFIX + 'branch_cleanup'
55METRIC_DISTFILES_CLEANUP = METRIC_PREFIX + 'distfiles_cleanup'
56METRIC_CHROOT_CLEANUP = METRIC_PREFIX + 'chroot_cleanup'
Don Garrettacbb2392017-05-11 18:27:41 -070057
Benjamin Gordon90b2dd92018-04-12 14:04:21 -060058# Builder state
59BUILDER_STATE_FILENAME = '.cbuildbot_build_state.json'
60
Don Garrett60967922017-04-12 18:51:44 -070061
Don Garrett125d4dc2017-04-25 16:26:03 -070062def StageDecorator(functor):
63 """A Decorator that adds buildbot stage tags around a method.
64
Don Garrettacbb2392017-05-11 18:27:41 -070065 It uses the method name as the stage name, and assumes failure on a true
66 return value, or an exception.
Don Garrett125d4dc2017-04-25 16:26:03 -070067 """
68 @functools.wraps(functor)
69 def wrapped_functor(*args, **kwargs):
70 try:
71 logging.PrintBuildbotStepName(functor.__name__)
Don Garrettacbb2392017-05-11 18:27:41 -070072 result = functor(*args, **kwargs)
Don Garrett125d4dc2017-04-25 16:26:03 -070073 except Exception:
74 logging.PrintBuildbotStepFailure()
75 raise
76
Don Garrettacbb2392017-05-11 18:27:41 -070077 if result:
78 logging.PrintBuildbotStepFailure()
79 return result
80
Don Garrett125d4dc2017-04-25 16:26:03 -070081 return wrapped_functor
82
83
Don Garrettb5fc08b2017-11-20 21:51:16 +000084def field(fields, **kwargs):
Don Garrettacbb2392017-05-11 18:27:41 -070085 """Helper for inserting more fields into a metrics fields dictionary.
86
87 Args:
88 fields: Dictionary of metrics fields.
89 kwargs: Each argument is a key/value pair to insert into dict.
90
91 Returns:
92 Copy of original dictionary with kwargs set as fields.
93 """
94 f = fields.copy()
95 f.update(kwargs)
96 return f
97
Don Garretta50bf492017-09-28 18:33:02 -070098
99def PrependPath(prepend):
100 """Generate path with new directory at the beginning.
101
102 Args:
103 prepend: Directory to add at the beginning of the path.
104
105 Returns:
106 Extended path as a string.
107 """
108 return os.pathsep.join([prepend, os.environ.get('PATH', os.defpath)])
109
Aviv Keshetc38eebe2018-09-27 23:19:58 +0000110
Don Garrett86881cb2017-02-15 15:41:55 -0800111def PreParseArguments(argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700112 """Extract the branch name from cbuildbot command line arguments.
113
Don Garrettc4114cc2016-11-01 20:04:06 -0700114 Args:
115 argv: The command line arguments to parse.
116
117 Returns:
118 Branch as a string ('master' if nothing is specified).
119 """
Don Garrett86881cb2017-02-15 15:41:55 -0800120 parser = cbuildbot.CreateParser()
Mike Frysinger80bba8a2017-08-18 15:28:36 -0400121 options = cbuildbot.ParseCommandLine(parser, argv)
Don Garrettd1d90dd2017-06-13 17:35:52 -0700122 options.Freeze()
Don Garrett86881cb2017-02-15 15:41:55 -0800123
124 # This option isn't required for cbuildbot, but is for us.
125 if not options.buildroot:
126 cros_build_lib.Die('--buildroot is a required option.')
127
128 return options
Don Garrettc4114cc2016-11-01 20:04:06 -0700129
Aviv Keshetc38eebe2018-09-27 23:19:58 +0000130
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600131def GetCurrentBuildState(options, branch):
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600132 """Extract information about the current build state from command-line args.
133
134 Args:
135 options: A parsed options object from a cbuildbot parser.
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600136 branch: The name of the branch this builder was called with.
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600137
138 Returns:
139 A BuildSummary object describing the current build.
140 """
141 build_state = build_summary.BuildSummary(
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600142 status=constants.BUILDER_STATUS_INFLIGHT,
143 buildroot_layout=BUILDROOT_BUILDROOT_LAYOUT,
144 branch=branch)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600145 if options.buildnumber:
146 build_state.build_number = options.buildnumber
147 if options.buildbucket_id:
148 build_state.buildbucket_id = options.buildbucket_id
149 if options.master_build_id:
150 build_state.master_build_id = options.master_build_id
151 return build_state
152
153
154def GetLastBuildState(root):
155 """Fetch the state of the last build run from |root|.
156
157 If the saved state file can't be read or doesn't contain valid JSON, a default
158 state will be returned.
159
160 Args:
161 root: Root of the working directory tree as a string.
162
163 Returns:
164 A BuildSummary object representing the previous build.
165 """
166 state_file = os.path.join(root, BUILDER_STATE_FILENAME)
167
168 state = build_summary.BuildSummary()
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600169
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600170 try:
171 state_raw = osutils.ReadFile(state_file)
172 state.from_json(state_raw)
173 except IOError as e:
174 logging.warning('Unable to read %s: %s', state_file, e)
175 return state
176 except ValueError as e:
177 logging.warning('Saved state file %s is not valid JSON: %s', state_file, e)
178 return state
179
180 if not state.is_valid():
181 logging.warning('Previous build state is not valid. Ignoring.')
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600182 state = build_summary.BuildSummary()
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600183
184 return state
185
186
187def SetLastBuildState(root, new_state):
188 """Save the state of the last build under |root|.
189
190 Args:
191 root: Root of the working directory tree as a string.
192 new_state: BuildSummary object containing the state to be saved.
193 """
194 state_file = os.path.join(root, BUILDER_STATE_FILENAME)
195 osutils.WriteFile(state_file, new_state.to_json())
196
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600197 # Remove old state file. Its contents have been migrated into the new file.
198 old_state_file = os.path.join(root, '.cbuildbot_launch_state')
199 osutils.SafeUnlink(old_state_file)
200
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600201
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600202def _MaybeCleanDistfiles(repo, distfiles_ts):
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700203 """Cleans the distfiles directory if too old.
204
205 Args:
206 repo: repository.RepoRepository instance.
207 distfiles_ts: A timestamp str for the last time distfiles was cleaned. May
208 be None.
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700209
210 Returns:
211 The new distfiles_ts to persist in state.
212 """
Don Garrette3f1a472018-06-19 13:03:21 -0700213 # distfiles_ts can be None for a fresh environment, which means clean.
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700214 if distfiles_ts is None:
Don Garrette3f1a472018-06-19 13:03:21 -0700215 return time.time()
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700216
217 distfiles_age = (time.time() - distfiles_ts) / 3600.0
218 if distfiles_age < _DISTFILES_CACHE_EXPIRY_HOURS:
219 return distfiles_ts
220
221 logging.info('Remove old distfiles cache (cache expiry %d hours)',
222 _DISTFILES_CACHE_EXPIRY_HOURS)
223 osutils.RmDir(os.path.join(repo.directory, '.cache', 'distfiles'),
224 ignore_missing=True, sudo=True)
225 metrics.Counter(METRIC_DISTFILES_CLEANUP).increment(
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600226 field({}, reason='cache_expired'))
Don Garrette3f1a472018-06-19 13:03:21 -0700227
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700228 # Cleaned cache, so reset distfiles_ts
Don Garrette3f1a472018-06-19 13:03:21 -0700229 return time.time()
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700230
231
Don Garrett125d4dc2017-04-25 16:26:03 -0700232@StageDecorator
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600233def CleanBuildRoot(root, repo, build_state):
Don Garrett7ade05a2017-02-17 13:31:47 -0800234 """Some kinds of branch transitions break builds.
235
Don Garrettbf90cdf2017-05-19 15:54:02 -0700236 This method ensures that cbuildbot's buildroot is a clean checkout on the
237 given branch when it starts. If necessary (a branch transition) it will wipe
238 assorted state that cannot be safely reused from the previous build.
Don Garrett7ade05a2017-02-17 13:31:47 -0800239
Don Garrett7ade05a2017-02-17 13:31:47 -0800240 Args:
Don Garrettbf90cdf2017-05-19 15:54:02 -0700241 root: Root directory owned by cbuildbot_launch.
Don Garrettf324bc32017-05-23 14:00:53 -0700242 repo: repository.RepoRepository instance.
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600243 build_state: BuildSummary object containing the current build state that
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600244 will be saved into the cleaned root. The distfiles_ts property will
245 be updated if the distfiles cache is cleaned.
Don Garrett7ade05a2017-02-17 13:31:47 -0800246 """
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600247 previous_state = GetLastBuildState(root)
Don Garrett4166d182018-12-17 12:52:02 -0800248 SetLastBuildState(root, build_state)
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600249 build_state.distfiles_ts = _MaybeCleanDistfiles(
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600250 repo, previous_state.distfiles_ts)
Don Garrette17e1d92017-04-12 15:28:19 -0700251
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600252 if previous_state.buildroot_layout != BUILDROOT_BUILDROOT_LAYOUT:
Don Garrett125d4dc2017-04-25 16:26:03 -0700253 logging.PrintBuildbotStepText('Unknown layout: Wiping buildroot.')
Don Garrettb5fc08b2017-11-20 21:51:16 +0000254 metrics.Counter(METRIC_CLOBBER).increment(
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600255 field({}, reason='layout_change'))
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700256 chroot_dir = os.path.join(root, constants.DEFAULT_CHROOT_DIR)
Don Garrettb5fc08b2017-11-20 21:51:16 +0000257 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
Don Garrett36650112018-06-28 15:54:34 -0700258 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete=True)
Don Garrettb5fc08b2017-11-20 21:51:16 +0000259 osutils.RmDir(root, ignore_missing=True, sudo=True)
Don Garrettf324bc32017-05-23 14:00:53 -0700260 else:
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600261 if previous_state.branch != repo.branch:
Don Garrettf324bc32017-05-23 14:00:53 -0700262 logging.PrintBuildbotStepText('Branch change: Cleaning buildroot.')
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600263 logging.info('Unmatched branch: %s -> %s', previous_state.branch,
264 repo.branch)
Don Garrettacbb2392017-05-11 18:27:41 -0700265 metrics.Counter(METRIC_BRANCH_CLEANUP).increment(
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600266 field({}, old_branch=previous_state.branch))
Don Garrett39963602017-02-27 14:41:58 -0800267
Don Garrettf324bc32017-05-23 14:00:53 -0700268 logging.info('Remove Chroot.')
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700269 chroot_dir = os.path.join(repo.directory, constants.DEFAULT_CHROOT_DIR)
Benjamin Gordon59ba2f82017-08-28 15:31:06 -0600270 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
Don Garrett36650112018-06-28 15:54:34 -0700271 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete=True)
Don Garrett7ade05a2017-02-17 13:31:47 -0800272
Don Garrettf324bc32017-05-23 14:00:53 -0700273 logging.info('Remove Chrome checkout.')
Don Garrettbf90cdf2017-05-19 15:54:02 -0700274 osutils.RmDir(os.path.join(repo.directory, '.cache', 'distfiles'),
Don Garrettf324bc32017-05-23 14:00:53 -0700275 ignore_missing=True, sudo=True)
276
Don Garrett1241c4e2018-08-06 17:33:41 -0700277 try:
278 # If there is any failure doing the cleanup, wipe everything.
279 # The previous run might have been killed in the middle leaving stale git
280 # locks. Clean those up, first.
281 repo.PreLoad()
Don Garrett4166d182018-12-17 12:52:02 -0800282
283 # If the previous build didn't exit normally, run an expensive step to
284 # cleanup abandoned git locks.
285 if previous_state.status not in (constants.BUILDER_STATUS_FAILED,
286 constants.BUILDER_STATUS_PASSED):
287 repo.CleanStaleLocks()
288
Don Garrett1241c4e2018-08-06 17:33:41 -0700289 repo.BuildRootGitCleanup(prune_all=True)
290 except Exception:
291 logging.info('Checkout cleanup failed, wiping buildroot:', exc_info=True)
292 metrics.Counter(METRIC_CLOBBER).increment(
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600293 field({}, reason='repo_cleanup_failure'))
Don Garrett1241c4e2018-08-06 17:33:41 -0700294 repository.ClearBuildRoot(repo.directory)
Don Garrett39963602017-02-27 14:41:58 -0800295
Don Garrettbf90cdf2017-05-19 15:54:02 -0700296 # Ensure buildroot exists. Save the state we are prepped for.
297 osutils.SafeMakedirs(repo.directory)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600298 SetLastBuildState(root, build_state)
Don Garrett7ade05a2017-02-17 13:31:47 -0800299
300
Don Garrett125d4dc2017-04-25 16:26:03 -0700301@StageDecorator
Don Garrettf324bc32017-05-23 14:00:53 -0700302def InitialCheckout(repo):
Don Garrett86881cb2017-02-15 15:41:55 -0800303 """Preliminary ChromeOS checkout.
304
305 Perform a complete checkout of ChromeOS on the specified branch. This does NOT
306 match what the build needs, but ensures the buildroot both has a 'hot'
307 checkout, and is close enough that the branched cbuildbot can successfully get
308 the right checkout.
309
310 This checks out full ChromeOS, even if a ChromiumOS build is going to be
311 performed. This is because we have no knowledge of the build config to be
312 used.
Don Garrettc4114cc2016-11-01 20:04:06 -0700313
314 Args:
Don Garrettf324bc32017-05-23 14:00:53 -0700315 repo: repository.RepoRepository instance.
Don Garrettc4114cc2016-11-01 20:04:06 -0700316 """
Don Garrettf324bc32017-05-23 14:00:53 -0700317 logging.PrintBuildbotStepText('Branch: %s' % repo.branch)
Don Garrett7ade05a2017-02-17 13:31:47 -0800318 logging.info('Bootstrap script starting initial sync on branch: %s',
Don Garrettf324bc32017-05-23 14:00:53 -0700319 repo.branch)
Don Garrett5516acb2018-11-15 16:02:59 -0800320 repo.PreLoad('/preload/chromeos')
Don Garrett76496912017-05-11 16:59:11 -0700321 repo.Sync(detach=True)
Don Garrettc4114cc2016-11-01 20:04:06 -0700322
323
Lann Martin8c4c6802018-05-23 11:09:46 -0600324def ShouldFixBotoCerts(options):
325 """Decide if FixBotoCerts should be applied for this branch."""
326 try:
327 # Only apply to factory and firmware branches.
328 branch = options.branch or ''
329 prefix = branch.split('-')[0]
330 if prefix not in ('factory', 'firmware'):
331 return False
332
333 # Only apply to "old" branches.
334 if branch.endswith('.B'):
335 version = branch[:-2].split('-')[-1]
336 major = int(version.split('.')[0])
337 return major <= 9667 # This is the newest known to be failing.
338
339 return False
Mike Frysinger9f470262018-08-03 15:09:58 -0400340 except Exception as e:
Lann Martin8c4c6802018-05-23 11:09:46 -0600341 logging.warning(' failed: %s', e)
342 # Conservatively continue without the fix.
343 return False
344
345
Don Garrett066e6f52017-09-28 19:14:01 -0700346@StageDecorator
Don Garrett6e5c6b92018-04-06 17:58:49 -0700347def Cbuildbot(buildroot, depot_tools_path, argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700348 """Start cbuildbot in specified directory with all arguments.
349
350 Args:
Don Garrettbf90cdf2017-05-19 15:54:02 -0700351 buildroot: Directory to be passed to cbuildbot with --buildroot.
Don Garretta50bf492017-09-28 18:33:02 -0700352 depot_tools_path: Directory for depot_tools to be used by cbuildbot.
Don Garrettd1d90dd2017-06-13 17:35:52 -0700353 argv: Command line options passed to cbuildbot_launch.
Don Garrettc4114cc2016-11-01 20:04:06 -0700354
355 Returns:
356 Return code of cbuildbot as an integer.
357 """
Don Garrettbf90cdf2017-05-19 15:54:02 -0700358 logging.info('Bootstrap cbuildbot in: %s', buildroot)
Don Garrettbf90cdf2017-05-19 15:54:02 -0700359
Don Garrettd1d90dd2017-06-13 17:35:52 -0700360 # Fixup buildroot parameter.
361 argv = argv[:]
362 for i in xrange(len(argv)):
363 if argv[i] in ('-r', '--buildroot'):
364 argv[i+1] = buildroot
Don Garrett597ddff2017-02-17 18:29:37 -0800365
Don Garrettd1d90dd2017-06-13 17:35:52 -0700366 # This filters out command line arguments not supported by older versions
367 # of cbuildbot.
368 parser = cbuildbot.CreateParser()
Mike Frysinger80bba8a2017-08-18 15:28:36 -0400369 options = cbuildbot.ParseCommandLine(parser, argv)
Don Garrettd1d90dd2017-06-13 17:35:52 -0700370 cbuildbot_path = os.path.join(buildroot, 'chromite', 'bin', 'cbuildbot')
Don Garrett597ddff2017-02-17 18:29:37 -0800371 cmd = sync_stages.BootstrapStage.FilterArgsForTargetCbuildbot(
Don Garrettbf90cdf2017-05-19 15:54:02 -0700372 buildroot, cbuildbot_path, options)
Don Garrett597ddff2017-02-17 18:29:37 -0800373
Don Garretta50bf492017-09-28 18:33:02 -0700374 # We want cbuildbot to use branched depot_tools scripts from our manifest,
375 # so that depot_tools is branched to match cbuildbot.
376 logging.info('Adding depot_tools into PATH: %s', depot_tools_path)
377 extra_env = {'PATH': PrependPath(depot_tools_path)}
378
Lann Martinebae73d2018-05-21 17:12:00 -0600379 # TODO(crbug.com/845304): Remove once underlying boto issues are resolved.
Lann Martin8c4c6802018-05-23 11:09:46 -0600380 fix_boto = ShouldFixBotoCerts(options)
Lann Martinebae73d2018-05-21 17:12:00 -0600381
382 with boto_compat.FixBotoCerts(activate=fix_boto):
383 result = cros_build_lib.RunCommand(
384 cmd, extra_env=extra_env, error_code_ok=True, cwd=buildroot)
385
Don Garrettacbb2392017-05-11 18:27:41 -0700386 return result.returncode
Don Garrettc4114cc2016-11-01 20:04:06 -0700387
Don Garrett60967922017-04-12 18:51:44 -0700388
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700389@StageDecorator
390def CleanupChroot(buildroot):
391 """Unmount/clean up an image-based chroot without deleting the backing image.
392
393 Args:
394 buildroot: Directory containing the chroot to be cleaned up.
395 """
396 chroot_dir = os.path.join(buildroot, constants.DEFAULT_CHROOT_DIR)
397 logging.info('Cleaning up chroot at %s', chroot_dir)
398 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
Don Garrett36650112018-06-28 15:54:34 -0700399 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete=False)
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700400
401
Don Garrettf15d65b2017-04-12 12:39:55 -0700402def ConfigureGlobalEnvironment():
403 """Setup process wide environmental changes."""
Don Garrettf15d65b2017-04-12 12:39:55 -0700404 # Set umask to 022 so files created by buildbot are readable.
405 os.umask(0o22)
406
Don Garrett86fec482017-05-17 18:13:33 -0700407 # These variables can interfere with LANG / locale behavior.
408 unwanted_local_vars = [
409 'LC_ALL', 'LC_CTYPE', 'LC_COLLATE', 'LC_TIME', 'LC_NUMERIC',
410 'LC_MONETARY', 'LC_MESSAGES', 'LC_PAPER', 'LC_NAME', 'LC_ADDRESS',
411 'LC_TELEPHONE', 'LC_MEASUREMENT', 'LC_IDENTIFICATION',
412 ]
413 for v in unwanted_local_vars:
414 os.environ.pop(v, None)
415
416 # This variable is required for repo sync's to work in all cases.
417 os.environ['LANG'] = 'en_US.UTF-8'
418
Aviv Keshetc38eebe2018-09-27 23:19:58 +0000419
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600420def _main(options, argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700421 """main method of script.
422
423 Args:
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600424 options: preparsed options object for the build.
Don Garrettc4114cc2016-11-01 20:04:06 -0700425 argv: All command line arguments to pass as list of strings.
426
427 Returns:
428 Return code of cbuildbot as an integer.
429 """
Don Garrettd1d90dd2017-06-13 17:35:52 -0700430 branchname = options.branch or 'master'
431 root = options.buildroot
432 buildroot = os.path.join(root, 'repository')
Don Garrettb497f552018-07-09 16:01:13 -0700433 workspace = os.path.join(root, 'workspace')
Don Garretta50bf492017-09-28 18:33:02 -0700434 depot_tools_path = os.path.join(buildroot, constants.DEPOT_TOOLS_SUBPATH)
Don Garrettd1d90dd2017-06-13 17:35:52 -0700435
Ben Pastenec8e4e792018-05-14 20:20:25 +0000436 # Does the entire build pass or fail.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600437 with metrics.Presence(METRIC_ACTIVE), \
438 metrics.SuccessCounter(METRIC_COMPLETED) as s_fields:
Don Garrettc4114cc2016-11-01 20:04:06 -0700439
Ben Pastenec8e4e792018-05-14 20:20:25 +0000440 # Preliminary set, mostly command line parsing.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600441 with metrics.SuccessCounter(METRIC_INVOKED):
Ben Pastenec8e4e792018-05-14 20:20:25 +0000442 if options.enable_buildbot_tags:
443 logging.EnableBuildbotMarkers()
444 ConfigureGlobalEnvironment()
Don Garrett86881cb2017-02-15 15:41:55 -0800445
Ben Pastenec8e4e792018-05-14 20:20:25 +0000446 # Prepare the buildroot with source for the build.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600447 with metrics.SuccessCounter(METRIC_PREP):
Alex Klein2ab29cc2018-07-19 12:01:00 -0600448 manifest_url = config_lib.GetSiteParams().MANIFEST_INT_URL
Ben Pastenec8e4e792018-05-14 20:20:25 +0000449 repo = repository.RepoRepository(manifest_url, buildroot,
450 branch=branchname,
Don Garrett33872502018-08-03 22:30:40 +0000451 git_cache_dir=options.git_cache_dir)
Ben Pastenec8e4e792018-05-14 20:20:25 +0000452 previous_build_state = GetLastBuildState(root)
Don Garrett86881cb2017-02-15 15:41:55 -0800453
Ben Pastenec8e4e792018-05-14 20:20:25 +0000454 # Clean up the buildroot to a safe state.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600455 with metrics.SecondsTimer(METRIC_CLEAN):
Ben Pastenec8e4e792018-05-14 20:20:25 +0000456 build_state = GetCurrentBuildState(options, branchname)
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600457 CleanBuildRoot(root, repo, build_state)
Don Garrettacbb2392017-05-11 18:27:41 -0700458
Ben Pastenec8e4e792018-05-14 20:20:25 +0000459 # Get a checkout close enough to the branch that cbuildbot can handle it.
460 if options.sync:
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600461 with metrics.SecondsTimer(METRIC_INITIAL):
Ben Pastenec8e4e792018-05-14 20:20:25 +0000462 InitialCheckout(repo)
Don Garrettacbb2392017-05-11 18:27:41 -0700463
Ben Pastenec8e4e792018-05-14 20:20:25 +0000464 # Run cbuildbot inside the full ChromeOS checkout, on the specified branch.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600465 with metrics.SecondsTimer(METRIC_CBUILDBOT), \
466 metrics.SecondsInstanceTimer(METRIC_CBUILDBOT_INSTANCE):
Ben Pastenec8e4e792018-05-14 20:20:25 +0000467 if previous_build_state.is_valid():
468 argv.append('--previous-build-state')
469 argv.append(base64.b64encode(previous_build_state.to_json()))
Don Garrettb497f552018-07-09 16:01:13 -0700470 argv.extend(['--workspace', workspace])
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600471
Ben Pastenec8e4e792018-05-14 20:20:25 +0000472 result = Cbuildbot(buildroot, depot_tools_path, argv)
473 s_fields['success'] = (result == 0)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600474
Ben Pastenec8e4e792018-05-14 20:20:25 +0000475 build_state.status = (
476 constants.BUILDER_STATUS_PASSED
477 if result == 0 else constants.BUILDER_STATUS_FAILED)
478 SetLastBuildState(root, build_state)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600479
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600480 with metrics.SecondsTimer(METRIC_CHROOT_CLEANUP):
Don Garrett91a8cfc2018-06-22 15:39:36 -0700481 CleanupChroot(buildroot)
482
Ben Pastenec8e4e792018-05-14 20:20:25 +0000483 return result
484
Don Garrettacbb2392017-05-11 18:27:41 -0700485
486def main(argv):
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600487 options = PreParseArguments(argv)
488 metric_fields = {
489 'branch_name': options.branch or 'master',
490 'build_config': options.build_config_name,
491 'tryjob': options.remote_trybot,
492 }
493
Ben Pastenec8e4e792018-05-14 20:20:25 +0000494 # Enable Monarch metrics gathering.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600495 with ts_mon_config.SetupTsMonGlobalState('cbuildbot_launch',
496 common_metric_fields=metric_fields,
497 indirect=True):
498 return _main(options, argv)