blob: 9c741f3a9a07ea2544c97a5c39a08bf195e3b1af [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.
Don Garrett45e77412017-06-14 16:57:55 -070044METRIC_ACTIVE = 'chromeos/chromite/cbuildbot_launch/active'
Don Garrettacbb2392017-05-11 18:27:41 -070045METRIC_INVOKED = 'chromeos/chromite/cbuildbot_launch/invoked'
46METRIC_COMPLETED = 'chromeos/chromite/cbuildbot_launch/completed'
47METRIC_PREP = 'chromeos/chromite/cbuildbot_launch/prep_completed'
48METRIC_CLEAN = 'chromeos/chromite/cbuildbot_launch/clean_buildroot_durations'
49METRIC_INITIAL = 'chromeos/chromite/cbuildbot_launch/initial_checkout_durations'
50METRIC_CBUILDBOT = 'chromeos/chromite/cbuildbot_launch/cbuildbot_durations'
51METRIC_CLOBBER = 'chromeos/chromite/cbuildbot_launch/clobber'
52METRIC_BRANCH_CLEANUP = 'chromeos/chromite/cbuildbot_launch/branch_cleanup'
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -070053METRIC_DISTFILES_CLEANUP = (
54 'chromeos/chromite/cbuildbot_launch/distfiles_cleanup')
Don Garrett066e6f52017-09-28 19:14:01 -070055METRIC_DEPOT_TOOLS = 'chromeos/chromite/cbuildbot_launch/depot_tools_prep'
Don Garrettacbb2392017-05-11 18:27:41 -070056
Benjamin Gordon90b2dd92018-04-12 14:04:21 -060057# Builder state
58BUILDER_STATE_FILENAME = '.cbuildbot_build_state.json'
59
Don Garrett60967922017-04-12 18:51:44 -070060
Don Garrett125d4dc2017-04-25 16:26:03 -070061def StageDecorator(functor):
62 """A Decorator that adds buildbot stage tags around a method.
63
Don Garrettacbb2392017-05-11 18:27:41 -070064 It uses the method name as the stage name, and assumes failure on a true
65 return value, or an exception.
Don Garrett125d4dc2017-04-25 16:26:03 -070066 """
67 @functools.wraps(functor)
68 def wrapped_functor(*args, **kwargs):
69 try:
70 logging.PrintBuildbotStepName(functor.__name__)
Don Garrettacbb2392017-05-11 18:27:41 -070071 result = functor(*args, **kwargs)
Don Garrett125d4dc2017-04-25 16:26:03 -070072 except Exception:
73 logging.PrintBuildbotStepFailure()
74 raise
75
Don Garrettacbb2392017-05-11 18:27:41 -070076 if result:
77 logging.PrintBuildbotStepFailure()
78 return result
79
Don Garrett125d4dc2017-04-25 16:26:03 -070080 return wrapped_functor
81
82
Don Garrettb5fc08b2017-11-20 21:51:16 +000083def field(fields, **kwargs):
Don Garrettacbb2392017-05-11 18:27:41 -070084 """Helper for inserting more fields into a metrics fields dictionary.
85
86 Args:
87 fields: Dictionary of metrics fields.
88 kwargs: Each argument is a key/value pair to insert into dict.
89
90 Returns:
91 Copy of original dictionary with kwargs set as fields.
92 """
93 f = fields.copy()
94 f.update(kwargs)
95 return f
96
Don Garretta50bf492017-09-28 18:33:02 -070097
98def PrependPath(prepend):
99 """Generate path with new directory at the beginning.
100
101 Args:
102 prepend: Directory to add at the beginning of the path.
103
104 Returns:
105 Extended path as a string.
106 """
107 return os.pathsep.join([prepend, os.environ.get('PATH', os.defpath)])
108
109
Don Garrett86881cb2017-02-15 15:41:55 -0800110def PreParseArguments(argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700111 """Extract the branch name from cbuildbot command line arguments.
112
Don Garrettc4114cc2016-11-01 20:04:06 -0700113 Args:
114 argv: The command line arguments to parse.
115
116 Returns:
117 Branch as a string ('master' if nothing is specified).
118 """
Don Garrett86881cb2017-02-15 15:41:55 -0800119 parser = cbuildbot.CreateParser()
Mike Frysinger80bba8a2017-08-18 15:28:36 -0400120 options = cbuildbot.ParseCommandLine(parser, argv)
Don Garrettd1d90dd2017-06-13 17:35:52 -0700121 options.Freeze()
Don Garrett86881cb2017-02-15 15:41:55 -0800122
123 # This option isn't required for cbuildbot, but is for us.
124 if not options.buildroot:
125 cros_build_lib.Die('--buildroot is a required option.')
126
127 return options
Don Garrettc4114cc2016-11-01 20:04:06 -0700128
129
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600130def GetCurrentBuildState(options, branch):
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600131 """Extract information about the current build state from command-line args.
132
133 Args:
134 options: A parsed options object from a cbuildbot parser.
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600135 branch: The name of the branch this builder was called with.
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600136
137 Returns:
138 A BuildSummary object describing the current build.
139 """
140 build_state = build_summary.BuildSummary(
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600141 status=constants.BUILDER_STATUS_INFLIGHT,
142 buildroot_layout=BUILDROOT_BUILDROOT_LAYOUT,
143 branch=branch)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600144 if options.buildnumber:
145 build_state.build_number = options.buildnumber
146 if options.buildbucket_id:
147 build_state.buildbucket_id = options.buildbucket_id
148 if options.master_build_id:
149 build_state.master_build_id = options.master_build_id
150 return build_state
151
152
153def GetLastBuildState(root):
154 """Fetch the state of the last build run from |root|.
155
156 If the saved state file can't be read or doesn't contain valid JSON, a default
157 state will be returned.
158
159 Args:
160 root: Root of the working directory tree as a string.
161
162 Returns:
163 A BuildSummary object representing the previous build.
164 """
165 state_file = os.path.join(root, BUILDER_STATE_FILENAME)
166
167 state = build_summary.BuildSummary()
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600168
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600169 try:
170 state_raw = osutils.ReadFile(state_file)
171 state.from_json(state_raw)
172 except IOError as e:
173 logging.warning('Unable to read %s: %s', state_file, e)
174 return state
175 except ValueError as e:
176 logging.warning('Saved state file %s is not valid JSON: %s', state_file, e)
177 return state
178
179 if not state.is_valid():
180 logging.warning('Previous build state is not valid. Ignoring.')
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600181 state = build_summary.BuildSummary()
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600182
183 return state
184
185
186def SetLastBuildState(root, new_state):
187 """Save the state of the last build under |root|.
188
189 Args:
190 root: Root of the working directory tree as a string.
191 new_state: BuildSummary object containing the state to be saved.
192 """
193 state_file = os.path.join(root, BUILDER_STATE_FILENAME)
194 osutils.WriteFile(state_file, new_state.to_json())
195
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600196 # Remove old state file. Its contents have been migrated into the new file.
197 old_state_file = os.path.join(root, '.cbuildbot_launch_state')
198 osutils.SafeUnlink(old_state_file)
199
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600200
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700201def _MaybeCleanDistfiles(repo, distfiles_ts, metrics_fields):
202 """Cleans the distfiles directory if too old.
203
204 Args:
205 repo: repository.RepoRepository instance.
206 distfiles_ts: A timestamp str for the last time distfiles was cleaned. May
207 be None.
208 metrics_fields: Dictionary of fields to include in metrics.
209
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(
226 field(metrics_fields, 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
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600233def CleanBuildRoot(root, repo, metrics_fields, 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.
Don Garrettacbb2392017-05-11 18:27:41 -0700243 metrics_fields: Dictionary of fields to include in metrics.
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600244 build_state: BuildSummary object containing the current build state that
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600245 will be saved into the cleaned root. The distfiles_ts property will
246 be updated if the distfiles cache is cleaned.
Don Garrett7ade05a2017-02-17 13:31:47 -0800247 """
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600248 previous_state = GetLastBuildState(root)
249 build_state.distfiles_ts = _MaybeCleanDistfiles(
250 repo, previous_state.distfiles_ts, metrics_fields)
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(
255 field(metrics_fields, 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'):
Benjamin Gordon74645232018-05-04 17:40:42 -0600258 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete_image=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(
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600266 field(metrics_fields, 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'):
Benjamin Gordon74645232018-05-04 17:40:42 -0600271 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete_image=True)
Don Garrette3f1a472018-06-19 13:03:21 -0700272 osutils.RmDir(chroot_dir, ignore_missing=True, sudo=True)
Don Garrett7ade05a2017-02-17 13:31:47 -0800273
Don Garrettf324bc32017-05-23 14:00:53 -0700274 logging.info('Remove Chrome checkout.')
Don Garrettbf90cdf2017-05-19 15:54:02 -0700275 osutils.RmDir(os.path.join(repo.directory, '.cache', 'distfiles'),
Don Garrettf324bc32017-05-23 14:00:53 -0700276 ignore_missing=True, sudo=True)
277
278 try:
279 # If there is any failure doing the cleanup, wipe everything.
Jason D. Clinton874afdd2018-06-06 15:21:32 -0600280 # The previous run might have been killed in the middle leaving stale git
281 # locks. Clean those up, first.
282 repo.CleanStaleLocks()
Don Garrettf324bc32017-05-23 14:00:53 -0700283 repo.BuildRootGitCleanup(prune_all=True)
284 except Exception:
285 logging.info('Checkout cleanup failed, wiping buildroot:', exc_info=True)
Don Garrettacbb2392017-05-11 18:27:41 -0700286 metrics.Counter(METRIC_CLOBBER).increment(
Don Garrettb5fc08b2017-11-20 21:51:16 +0000287 field(metrics_fields, reason='repo_cleanup_failure'))
Don Garrettbf90cdf2017-05-19 15:54:02 -0700288 repository.ClearBuildRoot(repo.directory)
Don Garrett39963602017-02-27 14:41:58 -0800289
Don Garrettbf90cdf2017-05-19 15:54:02 -0700290 # Ensure buildroot exists. Save the state we are prepped for.
291 osutils.SafeMakedirs(repo.directory)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600292 SetLastBuildState(root, build_state)
Don Garrett7ade05a2017-02-17 13:31:47 -0800293
294
Don Garrett125d4dc2017-04-25 16:26:03 -0700295@StageDecorator
Don Garrettf324bc32017-05-23 14:00:53 -0700296def InitialCheckout(repo):
Don Garrett86881cb2017-02-15 15:41:55 -0800297 """Preliminary ChromeOS checkout.
298
299 Perform a complete checkout of ChromeOS on the specified branch. This does NOT
300 match what the build needs, but ensures the buildroot both has a 'hot'
301 checkout, and is close enough that the branched cbuildbot can successfully get
302 the right checkout.
303
304 This checks out full ChromeOS, even if a ChromiumOS build is going to be
305 performed. This is because we have no knowledge of the build config to be
306 used.
Don Garrettc4114cc2016-11-01 20:04:06 -0700307
308 Args:
Don Garrettf324bc32017-05-23 14:00:53 -0700309 repo: repository.RepoRepository instance.
Don Garrettc4114cc2016-11-01 20:04:06 -0700310 """
Don Garrettf324bc32017-05-23 14:00:53 -0700311 logging.PrintBuildbotStepText('Branch: %s' % repo.branch)
Don Garrett7ade05a2017-02-17 13:31:47 -0800312 logging.info('Bootstrap script starting initial sync on branch: %s',
Don Garrettf324bc32017-05-23 14:00:53 -0700313 repo.branch)
Don Garrett76496912017-05-11 16:59:11 -0700314 repo.Sync(detach=True)
Don Garrettc4114cc2016-11-01 20:04:06 -0700315
316
Don Garrett125d4dc2017-04-25 16:26:03 -0700317@StageDecorator
Don Garrett066e6f52017-09-28 19:14:01 -0700318def DepotToolsEnsureBootstrap(depot_tools_path):
319 """Start cbuildbot in specified directory with all arguments.
320
321 Args:
322 buildroot: Directory to be passed to cbuildbot with --buildroot.
323 depot_tools_path: Directory for depot_tools to be used by cbuildbot.
324 argv: Command line options passed to cbuildbot_launch.
325
326 Returns:
327 Return code of cbuildbot as an integer.
328 """
329 ensure_bootstrap_script = os.path.join(depot_tools_path, 'ensure_bootstrap')
330 if os.path.exists(ensure_bootstrap_script):
331 extra_env = {'PATH': PrependPath(depot_tools_path)}
332 cros_build_lib.RunCommand(
333 [ensure_bootstrap_script], extra_env=extra_env, cwd=depot_tools_path)
334 else:
335 # This is normal when checking out branches older than this script.
336 logging.warn('ensure_bootstrap not found, skipping: %s',
337 ensure_bootstrap_script)
338
339
Lann Martin8c4c6802018-05-23 11:09:46 -0600340def ShouldFixBotoCerts(options):
341 """Decide if FixBotoCerts should be applied for this branch."""
342 try:
343 # Only apply to factory and firmware branches.
344 branch = options.branch or ''
345 prefix = branch.split('-')[0]
346 if prefix not in ('factory', 'firmware'):
347 return False
348
349 # Only apply to "old" branches.
350 if branch.endswith('.B'):
351 version = branch[:-2].split('-')[-1]
352 major = int(version.split('.')[0])
353 return major <= 9667 # This is the newest known to be failing.
354
355 return False
356 except Exception, e:
357 logging.warning(' failed: %s', e)
358 # Conservatively continue without the fix.
359 return False
360
361
Don Garrett066e6f52017-09-28 19:14:01 -0700362@StageDecorator
Don Garrett6e5c6b92018-04-06 17:58:49 -0700363def Cbuildbot(buildroot, depot_tools_path, argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700364 """Start cbuildbot in specified directory with all arguments.
365
366 Args:
Don Garrettbf90cdf2017-05-19 15:54:02 -0700367 buildroot: Directory to be passed to cbuildbot with --buildroot.
Don Garretta50bf492017-09-28 18:33:02 -0700368 depot_tools_path: Directory for depot_tools to be used by cbuildbot.
Don Garrettd1d90dd2017-06-13 17:35:52 -0700369 argv: Command line options passed to cbuildbot_launch.
Don Garrettc4114cc2016-11-01 20:04:06 -0700370
371 Returns:
372 Return code of cbuildbot as an integer.
373 """
Don Garrettbf90cdf2017-05-19 15:54:02 -0700374 logging.info('Bootstrap cbuildbot in: %s', buildroot)
Don Garrettbf90cdf2017-05-19 15:54:02 -0700375
Don Garrettd1d90dd2017-06-13 17:35:52 -0700376 # Fixup buildroot parameter.
377 argv = argv[:]
378 for i in xrange(len(argv)):
379 if argv[i] in ('-r', '--buildroot'):
380 argv[i+1] = buildroot
Don Garrett597ddff2017-02-17 18:29:37 -0800381
Don Garrettd1d90dd2017-06-13 17:35:52 -0700382 # This filters out command line arguments not supported by older versions
383 # of cbuildbot.
384 parser = cbuildbot.CreateParser()
Mike Frysinger80bba8a2017-08-18 15:28:36 -0400385 options = cbuildbot.ParseCommandLine(parser, argv)
Don Garrettd1d90dd2017-06-13 17:35:52 -0700386 cbuildbot_path = os.path.join(buildroot, 'chromite', 'bin', 'cbuildbot')
Don Garrett597ddff2017-02-17 18:29:37 -0800387 cmd = sync_stages.BootstrapStage.FilterArgsForTargetCbuildbot(
Don Garrettbf90cdf2017-05-19 15:54:02 -0700388 buildroot, cbuildbot_path, options)
Don Garrett597ddff2017-02-17 18:29:37 -0800389
Don Garretta50bf492017-09-28 18:33:02 -0700390 # We want cbuildbot to use branched depot_tools scripts from our manifest,
391 # so that depot_tools is branched to match cbuildbot.
392 logging.info('Adding depot_tools into PATH: %s', depot_tools_path)
393 extra_env = {'PATH': PrependPath(depot_tools_path)}
394
Lann Martinebae73d2018-05-21 17:12:00 -0600395 # TODO(crbug.com/845304): Remove once underlying boto issues are resolved.
Lann Martin8c4c6802018-05-23 11:09:46 -0600396 fix_boto = ShouldFixBotoCerts(options)
Lann Martinebae73d2018-05-21 17:12:00 -0600397
398 with boto_compat.FixBotoCerts(activate=fix_boto):
399 result = cros_build_lib.RunCommand(
400 cmd, extra_env=extra_env, error_code_ok=True, cwd=buildroot)
401
Don Garrettacbb2392017-05-11 18:27:41 -0700402 return result.returncode
Don Garrettc4114cc2016-11-01 20:04:06 -0700403
Don Garrett60967922017-04-12 18:51:44 -0700404
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700405@StageDecorator
406def CleanupChroot(buildroot):
407 """Unmount/clean up an image-based chroot without deleting the backing image.
408
409 Args:
410 buildroot: Directory containing the chroot to be cleaned up.
411 """
412 chroot_dir = os.path.join(buildroot, constants.DEFAULT_CHROOT_DIR)
413 logging.info('Cleaning up chroot at %s', chroot_dir)
414 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
Benjamin Gordon74645232018-05-04 17:40:42 -0600415 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete_image=False)
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700416
417
Don Garrettf15d65b2017-04-12 12:39:55 -0700418def ConfigureGlobalEnvironment():
419 """Setup process wide environmental changes."""
Don Garrettf15d65b2017-04-12 12:39:55 -0700420 # Set umask to 022 so files created by buildbot are readable.
421 os.umask(0o22)
422
Don Garrett86fec482017-05-17 18:13:33 -0700423 # These variables can interfere with LANG / locale behavior.
424 unwanted_local_vars = [
425 'LC_ALL', 'LC_CTYPE', 'LC_COLLATE', 'LC_TIME', 'LC_NUMERIC',
426 'LC_MONETARY', 'LC_MESSAGES', 'LC_PAPER', 'LC_NAME', 'LC_ADDRESS',
427 'LC_TELEPHONE', 'LC_MEASUREMENT', 'LC_IDENTIFICATION',
428 ]
429 for v in unwanted_local_vars:
430 os.environ.pop(v, None)
431
432 # This variable is required for repo sync's to work in all cases.
433 os.environ['LANG'] = 'en_US.UTF-8'
434
Don Garrettc4114cc2016-11-01 20:04:06 -0700435
Don Garrettacbb2392017-05-11 18:27:41 -0700436def _main(argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700437 """main method of script.
438
439 Args:
440 argv: All command line arguments to pass as list of strings.
441
442 Returns:
443 Return code of cbuildbot as an integer.
444 """
Don Garrettd1d90dd2017-06-13 17:35:52 -0700445 options = PreParseArguments(argv)
446
447 branchname = options.branch or 'master'
448 root = options.buildroot
449 buildroot = os.path.join(root, 'repository')
Don Garretta50bf492017-09-28 18:33:02 -0700450 depot_tools_path = os.path.join(buildroot, constants.DEPOT_TOOLS_SUBPATH)
Don Garrettd1d90dd2017-06-13 17:35:52 -0700451
452 metrics_fields = {
453 'branch_name': branchname,
Don Garrettf0761152017-10-19 19:38:27 -0700454 'build_config': options.build_config_name,
Don Garrettd1d90dd2017-06-13 17:35:52 -0700455 'tryjob': options.remote_trybot,
456 }
Don Garrettf15d65b2017-04-12 12:39:55 -0700457
Ben Pastenec8e4e792018-05-14 20:20:25 +0000458 # Does the entire build pass or fail.
459 with metrics.Presence(METRIC_ACTIVE, metrics_fields), \
460 metrics.SuccessCounter(METRIC_COMPLETED, metrics_fields) as s_fields:
Don Garrettc4114cc2016-11-01 20:04:06 -0700461
Ben Pastenec8e4e792018-05-14 20:20:25 +0000462 # Preliminary set, mostly command line parsing.
463 with metrics.SuccessCounter(METRIC_INVOKED, metrics_fields):
464 if options.enable_buildbot_tags:
465 logging.EnableBuildbotMarkers()
466 ConfigureGlobalEnvironment()
Don Garrett86881cb2017-02-15 15:41:55 -0800467
Ben Pastenec8e4e792018-05-14 20:20:25 +0000468 # Prepare the buildroot with source for the build.
469 with metrics.SuccessCounter(METRIC_PREP, metrics_fields):
470 site_config = config_lib.GetConfig()
471 manifest_url = site_config.params['MANIFEST_INT_URL']
472 repo = repository.RepoRepository(manifest_url, buildroot,
473 branch=branchname,
474 git_cache_dir=options.git_cache_dir)
475 previous_build_state = GetLastBuildState(root)
Don Garrett86881cb2017-02-15 15:41:55 -0800476
Ben Pastenec8e4e792018-05-14 20:20:25 +0000477 # Clean up the buildroot to a safe state.
478 with metrics.SecondsTimer(METRIC_CLEAN, fields=metrics_fields):
479 build_state = GetCurrentBuildState(options, branchname)
480 CleanBuildRoot(root, repo, metrics_fields, build_state)
Don Garrettacbb2392017-05-11 18:27:41 -0700481
Ben Pastenec8e4e792018-05-14 20:20:25 +0000482 # Get a checkout close enough to the branch that cbuildbot can handle it.
483 if options.sync:
484 with metrics.SecondsTimer(METRIC_INITIAL, fields=metrics_fields):
485 InitialCheckout(repo)
Don Garrettacbb2392017-05-11 18:27:41 -0700486
Ben Pastenec8e4e792018-05-14 20:20:25 +0000487 # Get a checkout close enough to the branch that cbuildbot can handle it.
488 with metrics.SecondsTimer(METRIC_DEPOT_TOOLS, fields=metrics_fields):
489 DepotToolsEnsureBootstrap(depot_tools_path)
Don Garrett066e6f52017-09-28 19:14:01 -0700490
Ben Pastenec8e4e792018-05-14 20:20:25 +0000491 # Run cbuildbot inside the full ChromeOS checkout, on the specified branch.
492 with metrics.SecondsTimer(METRIC_CBUILDBOT, fields=metrics_fields):
493 if previous_build_state.is_valid():
494 argv.append('--previous-build-state')
495 argv.append(base64.b64encode(previous_build_state.to_json()))
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600496
Ben Pastenec8e4e792018-05-14 20:20:25 +0000497 result = Cbuildbot(buildroot, depot_tools_path, argv)
498 s_fields['success'] = (result == 0)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600499
Ben Pastenec8e4e792018-05-14 20:20:25 +0000500 build_state.status = (
501 constants.BUILDER_STATUS_PASSED
502 if result == 0 else constants.BUILDER_STATUS_FAILED)
503 SetLastBuildState(root, build_state)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600504
Ben Pastenec8e4e792018-05-14 20:20:25 +0000505 CleanupChroot(buildroot)
506 return result
507
Don Garrettacbb2392017-05-11 18:27:41 -0700508
509def main(argv):
Ben Pastenec8e4e792018-05-14 20:20:25 +0000510 # Enable Monarch metrics gathering.
511 with ts_mon_config.SetupTsMonGlobalState('cbuildbot_launch', indirect=True):
512 return _main(argv)