blob: c7e8e14dabed12c00713da1ba4ffd8aa95ddad5d [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
Benjamin Gordon90b2dd92018-04-12 14:04:21 -060026from chromite.lib import build_summary
Don Garrett86881cb2017-02-15 15:41:55 -080027from chromite.lib import config_lib
Don Garretta50bf492017-09-28 18:33:02 -070028from chromite.lib import constants
Don Garrettc4114cc2016-11-01 20:04:06 -070029from chromite.lib import cros_build_lib
30from chromite.lib import cros_logging as logging
Don Garrettacbb2392017-05-11 18:27:41 -070031from chromite.lib import metrics
Don Garrettc4114cc2016-11-01 20:04:06 -070032from chromite.lib import osutils
Don Garrettacbb2392017-05-11 18:27:41 -070033from chromite.lib import ts_mon_config
Don Garrett86881cb2017-02-15 15:41:55 -080034from chromite.scripts import cbuildbot
Don Garrettc4114cc2016-11-01 20:04:06 -070035
Don Garrett60967922017-04-12 18:51:44 -070036# This number should be incremented when we change the layout of the buildroot
37# in a non-backwards compatible way. This wipes all buildroots.
Don Garrettbf90cdf2017-05-19 15:54:02 -070038BUILDROOT_BUILDROOT_LAYOUT = 2
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -070039_DISTFILES_CACHE_EXPIRY_HOURS = 8 * 24
Don Garrett60967922017-04-12 18:51:44 -070040
Don Garrettacbb2392017-05-11 18:27:41 -070041# Metrics reported to Monarch.
Don Garrett45e77412017-06-14 16:57:55 -070042METRIC_ACTIVE = 'chromeos/chromite/cbuildbot_launch/active'
Don Garrettacbb2392017-05-11 18:27:41 -070043METRIC_INVOKED = 'chromeos/chromite/cbuildbot_launch/invoked'
44METRIC_COMPLETED = 'chromeos/chromite/cbuildbot_launch/completed'
45METRIC_PREP = 'chromeos/chromite/cbuildbot_launch/prep_completed'
46METRIC_CLEAN = 'chromeos/chromite/cbuildbot_launch/clean_buildroot_durations'
47METRIC_INITIAL = 'chromeos/chromite/cbuildbot_launch/initial_checkout_durations'
48METRIC_CBUILDBOT = 'chromeos/chromite/cbuildbot_launch/cbuildbot_durations'
49METRIC_CLOBBER = 'chromeos/chromite/cbuildbot_launch/clobber'
50METRIC_BRANCH_CLEANUP = 'chromeos/chromite/cbuildbot_launch/branch_cleanup'
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -070051METRIC_DISTFILES_CLEANUP = (
52 'chromeos/chromite/cbuildbot_launch/distfiles_cleanup')
Don Garrett066e6f52017-09-28 19:14:01 -070053METRIC_DEPOT_TOOLS = 'chromeos/chromite/cbuildbot_launch/depot_tools_prep'
Don Garrettacbb2392017-05-11 18:27:41 -070054
Benjamin Gordon90b2dd92018-04-12 14:04:21 -060055# Builder state
56BUILDER_STATE_FILENAME = '.cbuildbot_build_state.json'
57
Don Garrett60967922017-04-12 18:51:44 -070058
Don Garrett125d4dc2017-04-25 16:26:03 -070059def StageDecorator(functor):
60 """A Decorator that adds buildbot stage tags around a method.
61
Don Garrettacbb2392017-05-11 18:27:41 -070062 It uses the method name as the stage name, and assumes failure on a true
63 return value, or an exception.
Don Garrett125d4dc2017-04-25 16:26:03 -070064 """
65 @functools.wraps(functor)
66 def wrapped_functor(*args, **kwargs):
67 try:
68 logging.PrintBuildbotStepName(functor.__name__)
Don Garrettacbb2392017-05-11 18:27:41 -070069 result = functor(*args, **kwargs)
Don Garrett125d4dc2017-04-25 16:26:03 -070070 except Exception:
71 logging.PrintBuildbotStepFailure()
72 raise
73
Don Garrettacbb2392017-05-11 18:27:41 -070074 if result:
75 logging.PrintBuildbotStepFailure()
76 return result
77
Don Garrett125d4dc2017-04-25 16:26:03 -070078 return wrapped_functor
79
80
Don Garrettb5fc08b2017-11-20 21:51:16 +000081def field(fields, **kwargs):
Don Garrettacbb2392017-05-11 18:27:41 -070082 """Helper for inserting more fields into a metrics fields dictionary.
83
84 Args:
85 fields: Dictionary of metrics fields.
86 kwargs: Each argument is a key/value pair to insert into dict.
87
88 Returns:
89 Copy of original dictionary with kwargs set as fields.
90 """
91 f = fields.copy()
92 f.update(kwargs)
93 return f
94
Don Garretta50bf492017-09-28 18:33:02 -070095
96def PrependPath(prepend):
97 """Generate path with new directory at the beginning.
98
99 Args:
100 prepend: Directory to add at the beginning of the path.
101
102 Returns:
103 Extended path as a string.
104 """
105 return os.pathsep.join([prepend, os.environ.get('PATH', os.defpath)])
106
107
Don Garrett86881cb2017-02-15 15:41:55 -0800108def PreParseArguments(argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700109 """Extract the branch name from cbuildbot command line arguments.
110
Don Garrettc4114cc2016-11-01 20:04:06 -0700111 Args:
112 argv: The command line arguments to parse.
113
114 Returns:
115 Branch as a string ('master' if nothing is specified).
116 """
Don Garrett86881cb2017-02-15 15:41:55 -0800117 parser = cbuildbot.CreateParser()
Mike Frysinger80bba8a2017-08-18 15:28:36 -0400118 options = cbuildbot.ParseCommandLine(parser, argv)
Don Garrettd1d90dd2017-06-13 17:35:52 -0700119 options.Freeze()
Don Garrett86881cb2017-02-15 15:41:55 -0800120
121 # This option isn't required for cbuildbot, but is for us.
122 if not options.buildroot:
123 cros_build_lib.Die('--buildroot is a required option.')
124
125 return options
Don Garrettc4114cc2016-11-01 20:04:06 -0700126
127
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600128def GetCurrentBuildState(options, branch):
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600129 """Extract information about the current build state from command-line args.
130
131 Args:
132 options: A parsed options object from a cbuildbot parser.
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600133 branch: The name of the branch this builder was called with.
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600134
135 Returns:
136 A BuildSummary object describing the current build.
137 """
138 build_state = build_summary.BuildSummary(
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600139 status=constants.BUILDER_STATUS_INFLIGHT,
140 buildroot_layout=BUILDROOT_BUILDROOT_LAYOUT,
141 branch=branch)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600142 if options.buildnumber:
143 build_state.build_number = options.buildnumber
144 if options.buildbucket_id:
145 build_state.buildbucket_id = options.buildbucket_id
146 if options.master_build_id:
147 build_state.master_build_id = options.master_build_id
148 return build_state
149
150
151def GetLastBuildState(root):
152 """Fetch the state of the last build run from |root|.
153
154 If the saved state file can't be read or doesn't contain valid JSON, a default
155 state will be returned.
156
157 Args:
158 root: Root of the working directory tree as a string.
159
160 Returns:
161 A BuildSummary object representing the previous build.
162 """
163 state_file = os.path.join(root, BUILDER_STATE_FILENAME)
164
165 state = build_summary.BuildSummary()
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600166
167 # Merge the old state file into the state. This will be overwritten by the
168 # new state file if it contains the same fields, which is what we want.
169 # TODO(bmgordon): Remove this once all builders have had time to migrate to
170 # the new file.
171 state.buildroot_layout, state.branch, state.distfiles_ts = GetState(root)
172
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600173 try:
174 state_raw = osutils.ReadFile(state_file)
175 state.from_json(state_raw)
176 except IOError as e:
177 logging.warning('Unable to read %s: %s', state_file, e)
178 return state
179 except ValueError as e:
180 logging.warning('Saved state file %s is not valid JSON: %s', state_file, e)
181 return state
182
183 if not state.is_valid():
184 logging.warning('Previous build state is not valid. Ignoring.')
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600185 state = build_summary.BuildSummary()
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600186
187 return state
188
189
190def SetLastBuildState(root, new_state):
191 """Save the state of the last build under |root|.
192
193 Args:
194 root: Root of the working directory tree as a string.
195 new_state: BuildSummary object containing the state to be saved.
196 """
197 state_file = os.path.join(root, BUILDER_STATE_FILENAME)
198 osutils.WriteFile(state_file, new_state.to_json())
199
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600200 # Remove old state file. Its contents have been migrated into the new file.
201 old_state_file = os.path.join(root, '.cbuildbot_launch_state')
202 osutils.SafeUnlink(old_state_file)
203
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600204
Don Garrettbf90cdf2017-05-19 15:54:02 -0700205def GetState(root):
206 """Fetch the current state of our working directory.
207
208 Will return with a default result if there is no known state.
209
210 Args:
211 root: Root of the working directory tree as a string.
212
213 Returns:
214 Layout version as an integer (0 for unknown).
215 Previous branch as a string ('' for unknown).
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700216 Last distfiles clearance time in POSIX time (None for unknown).
Don Garrettbf90cdf2017-05-19 15:54:02 -0700217 """
218 state_file = os.path.join(root, '.cbuildbot_launch_state')
Don Garrett60967922017-04-12 18:51:44 -0700219
220 try:
221 state = osutils.ReadFile(state_file)
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700222 parts = state.split()
223 if len(parts) >= 3:
224 return int(parts[0]), parts[1], float(parts[2])
225 else:
226 # TODO(pprabhu) delete this branch once most buildslaves have migrated to
227 # newer state with three parts.
228 return int(parts[0]), parts[1], None
229 except (IOError, ValueError, IndexError):
Don Garrett60967922017-04-12 18:51:44 -0700230 # If we are unable to either read or parse the state file, we get here.
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700231 return 0, '', None
Don Garrett60967922017-04-12 18:51:44 -0700232
233
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700234def _MaybeCleanDistfiles(repo, distfiles_ts, metrics_fields):
235 """Cleans the distfiles directory if too old.
236
237 Args:
238 repo: repository.RepoRepository instance.
239 distfiles_ts: A timestamp str for the last time distfiles was cleaned. May
240 be None.
241 metrics_fields: Dictionary of fields to include in metrics.
242
243 Returns:
244 The new distfiles_ts to persist in state.
245 """
246
247 if distfiles_ts is None:
248 return None
249
250 distfiles_age = (time.time() - distfiles_ts) / 3600.0
251 if distfiles_age < _DISTFILES_CACHE_EXPIRY_HOURS:
252 return distfiles_ts
253
254 logging.info('Remove old distfiles cache (cache expiry %d hours)',
255 _DISTFILES_CACHE_EXPIRY_HOURS)
256 osutils.RmDir(os.path.join(repo.directory, '.cache', 'distfiles'),
257 ignore_missing=True, sudo=True)
258 metrics.Counter(METRIC_DISTFILES_CLEANUP).increment(
259 field(metrics_fields, reason='cache_expired'))
260 # Cleaned cache, so reset distfiles_ts
261 return None
262
263
Don Garrett125d4dc2017-04-25 16:26:03 -0700264@StageDecorator
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600265def CleanBuildRoot(root, repo, metrics_fields, build_state):
Don Garrett7ade05a2017-02-17 13:31:47 -0800266 """Some kinds of branch transitions break builds.
267
Don Garrettbf90cdf2017-05-19 15:54:02 -0700268 This method ensures that cbuildbot's buildroot is a clean checkout on the
269 given branch when it starts. If necessary (a branch transition) it will wipe
270 assorted state that cannot be safely reused from the previous build.
Don Garrett7ade05a2017-02-17 13:31:47 -0800271
Don Garrett7ade05a2017-02-17 13:31:47 -0800272 Args:
Don Garrettbf90cdf2017-05-19 15:54:02 -0700273 root: Root directory owned by cbuildbot_launch.
Don Garrettf324bc32017-05-23 14:00:53 -0700274 repo: repository.RepoRepository instance.
Don Garrettacbb2392017-05-11 18:27:41 -0700275 metrics_fields: Dictionary of fields to include in metrics.
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600276 build_state: BuildSummary object containing the current build state that
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600277 will be saved into the cleaned root. The distfiles_ts property will
278 be updated if the distfiles cache is cleaned.
Don Garrett7ade05a2017-02-17 13:31:47 -0800279 """
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600280 previous_state = GetLastBuildState(root)
281 build_state.distfiles_ts = _MaybeCleanDistfiles(
282 repo, previous_state.distfiles_ts, metrics_fields)
Don Garrette17e1d92017-04-12 15:28:19 -0700283
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600284 if previous_state.buildroot_layout != BUILDROOT_BUILDROOT_LAYOUT:
Don Garrett125d4dc2017-04-25 16:26:03 -0700285 logging.PrintBuildbotStepText('Unknown layout: Wiping buildroot.')
Don Garrettb5fc08b2017-11-20 21:51:16 +0000286 metrics.Counter(METRIC_CLOBBER).increment(
287 field(metrics_fields, reason='layout_change'))
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700288 chroot_dir = os.path.join(root, constants.DEFAULT_CHROOT_DIR)
Don Garrettb5fc08b2017-11-20 21:51:16 +0000289 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
290 cros_build_lib.CleanupChrootMount(chroot_dir, delete_image=True)
291 osutils.RmDir(root, ignore_missing=True, sudo=True)
Don Garrettf324bc32017-05-23 14:00:53 -0700292 else:
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600293 if previous_state.branch != repo.branch:
Don Garrettf324bc32017-05-23 14:00:53 -0700294 logging.PrintBuildbotStepText('Branch change: Cleaning buildroot.')
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600295 logging.info('Unmatched branch: %s -> %s', previous_state.branch,
296 repo.branch)
Don Garrettacbb2392017-05-11 18:27:41 -0700297 metrics.Counter(METRIC_BRANCH_CLEANUP).increment(
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600298 field(metrics_fields, old_branch=previous_state.branch))
Don Garrett39963602017-02-27 14:41:58 -0800299
Don Garrettf324bc32017-05-23 14:00:53 -0700300 logging.info('Remove Chroot.')
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700301 chroot_dir = os.path.join(repo.directory, constants.DEFAULT_CHROOT_DIR)
Benjamin Gordon59ba2f82017-08-28 15:31:06 -0600302 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
303 cros_build_lib.CleanupChrootMount(chroot_dir, delete_image=True)
304 osutils.RmDir(chroot_dir, ignore_missing=True, sudo=True)
Don Garrett7ade05a2017-02-17 13:31:47 -0800305
Don Garrettf324bc32017-05-23 14:00:53 -0700306 logging.info('Remove Chrome checkout.')
Don Garrettbf90cdf2017-05-19 15:54:02 -0700307 osutils.RmDir(os.path.join(repo.directory, '.cache', 'distfiles'),
Don Garrettf324bc32017-05-23 14:00:53 -0700308 ignore_missing=True, sudo=True)
309
310 try:
311 # If there is any failure doing the cleanup, wipe everything.
312 repo.BuildRootGitCleanup(prune_all=True)
313 except Exception:
314 logging.info('Checkout cleanup failed, wiping buildroot:', exc_info=True)
Don Garrettacbb2392017-05-11 18:27:41 -0700315 metrics.Counter(METRIC_CLOBBER).increment(
Don Garrettb5fc08b2017-11-20 21:51:16 +0000316 field(metrics_fields, reason='repo_cleanup_failure'))
Don Garrettbf90cdf2017-05-19 15:54:02 -0700317 repository.ClearBuildRoot(repo.directory)
Don Garrett39963602017-02-27 14:41:58 -0800318
Don Garrettbf90cdf2017-05-19 15:54:02 -0700319 # Ensure buildroot exists. Save the state we are prepped for.
320 osutils.SafeMakedirs(repo.directory)
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600321 if not build_state.distfiles_ts:
322 build_state.distfiles_ts = time.time()
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600323 SetLastBuildState(root, build_state)
Don Garrett7ade05a2017-02-17 13:31:47 -0800324
325
Don Garrett125d4dc2017-04-25 16:26:03 -0700326@StageDecorator
Don Garrettf324bc32017-05-23 14:00:53 -0700327def InitialCheckout(repo):
Don Garrett86881cb2017-02-15 15:41:55 -0800328 """Preliminary ChromeOS checkout.
329
330 Perform a complete checkout of ChromeOS on the specified branch. This does NOT
331 match what the build needs, but ensures the buildroot both has a 'hot'
332 checkout, and is close enough that the branched cbuildbot can successfully get
333 the right checkout.
334
335 This checks out full ChromeOS, even if a ChromiumOS build is going to be
336 performed. This is because we have no knowledge of the build config to be
337 used.
Don Garrettc4114cc2016-11-01 20:04:06 -0700338
339 Args:
Don Garrettf324bc32017-05-23 14:00:53 -0700340 repo: repository.RepoRepository instance.
Don Garrettc4114cc2016-11-01 20:04:06 -0700341 """
Don Garrettf324bc32017-05-23 14:00:53 -0700342 logging.PrintBuildbotStepText('Branch: %s' % repo.branch)
Don Garrett7ade05a2017-02-17 13:31:47 -0800343 logging.info('Bootstrap script starting initial sync on branch: %s',
Don Garrettf324bc32017-05-23 14:00:53 -0700344 repo.branch)
Don Garrett76496912017-05-11 16:59:11 -0700345 repo.Sync(detach=True)
Don Garrettc4114cc2016-11-01 20:04:06 -0700346
347
Don Garrett125d4dc2017-04-25 16:26:03 -0700348@StageDecorator
Don Garrett066e6f52017-09-28 19:14:01 -0700349def DepotToolsEnsureBootstrap(depot_tools_path):
350 """Start cbuildbot in specified directory with all arguments.
351
352 Args:
353 buildroot: Directory to be passed to cbuildbot with --buildroot.
354 depot_tools_path: Directory for depot_tools to be used by cbuildbot.
355 argv: Command line options passed to cbuildbot_launch.
356
357 Returns:
358 Return code of cbuildbot as an integer.
359 """
360 ensure_bootstrap_script = os.path.join(depot_tools_path, 'ensure_bootstrap')
361 if os.path.exists(ensure_bootstrap_script):
362 extra_env = {'PATH': PrependPath(depot_tools_path)}
363 cros_build_lib.RunCommand(
364 [ensure_bootstrap_script], extra_env=extra_env, cwd=depot_tools_path)
365 else:
366 # This is normal when checking out branches older than this script.
367 logging.warn('ensure_bootstrap not found, skipping: %s',
368 ensure_bootstrap_script)
369
370
371@StageDecorator
Don Garrett6e5c6b92018-04-06 17:58:49 -0700372def Cbuildbot(buildroot, depot_tools_path, argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700373 """Start cbuildbot in specified directory with all arguments.
374
375 Args:
Don Garrettbf90cdf2017-05-19 15:54:02 -0700376 buildroot: Directory to be passed to cbuildbot with --buildroot.
Don Garretta50bf492017-09-28 18:33:02 -0700377 depot_tools_path: Directory for depot_tools to be used by cbuildbot.
Don Garrettd1d90dd2017-06-13 17:35:52 -0700378 argv: Command line options passed to cbuildbot_launch.
Don Garrettc4114cc2016-11-01 20:04:06 -0700379
380 Returns:
381 Return code of cbuildbot as an integer.
382 """
Don Garrettbf90cdf2017-05-19 15:54:02 -0700383 logging.info('Bootstrap cbuildbot in: %s', buildroot)
Don Garrettbf90cdf2017-05-19 15:54:02 -0700384
Don Garrettd1d90dd2017-06-13 17:35:52 -0700385 # Fixup buildroot parameter.
386 argv = argv[:]
387 for i in xrange(len(argv)):
388 if argv[i] in ('-r', '--buildroot'):
389 argv[i+1] = buildroot
Don Garrett597ddff2017-02-17 18:29:37 -0800390
Don Garrettd1d90dd2017-06-13 17:35:52 -0700391 # This filters out command line arguments not supported by older versions
392 # of cbuildbot.
393 parser = cbuildbot.CreateParser()
Mike Frysinger80bba8a2017-08-18 15:28:36 -0400394 options = cbuildbot.ParseCommandLine(parser, argv)
Don Garrettd1d90dd2017-06-13 17:35:52 -0700395 cbuildbot_path = os.path.join(buildroot, 'chromite', 'bin', 'cbuildbot')
Don Garrett597ddff2017-02-17 18:29:37 -0800396 cmd = sync_stages.BootstrapStage.FilterArgsForTargetCbuildbot(
Don Garrettbf90cdf2017-05-19 15:54:02 -0700397 buildroot, cbuildbot_path, options)
Don Garrett597ddff2017-02-17 18:29:37 -0800398
Don Garretta50bf492017-09-28 18:33:02 -0700399 # We want cbuildbot to use branched depot_tools scripts from our manifest,
400 # so that depot_tools is branched to match cbuildbot.
401 logging.info('Adding depot_tools into PATH: %s', depot_tools_path)
402 extra_env = {'PATH': PrependPath(depot_tools_path)}
403
404 result = cros_build_lib.RunCommand(
405 cmd, extra_env=extra_env, error_code_ok=True, cwd=buildroot)
Don Garrettacbb2392017-05-11 18:27:41 -0700406 return result.returncode
Don Garrettc4114cc2016-11-01 20:04:06 -0700407
Don Garrett60967922017-04-12 18:51:44 -0700408
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700409@StageDecorator
410def CleanupChroot(buildroot):
411 """Unmount/clean up an image-based chroot without deleting the backing image.
412
413 Args:
414 buildroot: Directory containing the chroot to be cleaned up.
415 """
416 chroot_dir = os.path.join(buildroot, constants.DEFAULT_CHROOT_DIR)
417 logging.info('Cleaning up chroot at %s', chroot_dir)
418 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
419 cros_build_lib.CleanupChrootMount(chroot_dir, delete_image=False)
420
421
Don Garrettf15d65b2017-04-12 12:39:55 -0700422def ConfigureGlobalEnvironment():
423 """Setup process wide environmental changes."""
Don Garrettf15d65b2017-04-12 12:39:55 -0700424 # Set umask to 022 so files created by buildbot are readable.
425 os.umask(0o22)
426
Don Garrett86fec482017-05-17 18:13:33 -0700427 # These variables can interfere with LANG / locale behavior.
428 unwanted_local_vars = [
429 'LC_ALL', 'LC_CTYPE', 'LC_COLLATE', 'LC_TIME', 'LC_NUMERIC',
430 'LC_MONETARY', 'LC_MESSAGES', 'LC_PAPER', 'LC_NAME', 'LC_ADDRESS',
431 'LC_TELEPHONE', 'LC_MEASUREMENT', 'LC_IDENTIFICATION',
432 ]
433 for v in unwanted_local_vars:
434 os.environ.pop(v, None)
435
436 # This variable is required for repo sync's to work in all cases.
437 os.environ['LANG'] = 'en_US.UTF-8'
438
Don Garrettc4114cc2016-11-01 20:04:06 -0700439
Don Garrettacbb2392017-05-11 18:27:41 -0700440def _main(argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700441 """main method of script.
442
443 Args:
444 argv: All command line arguments to pass as list of strings.
445
446 Returns:
447 Return code of cbuildbot as an integer.
448 """
Don Garrettd1d90dd2017-06-13 17:35:52 -0700449 options = PreParseArguments(argv)
450
451 branchname = options.branch or 'master'
452 root = options.buildroot
453 buildroot = os.path.join(root, 'repository')
Don Garretta50bf492017-09-28 18:33:02 -0700454 depot_tools_path = os.path.join(buildroot, constants.DEPOT_TOOLS_SUBPATH)
Don Garrettd1d90dd2017-06-13 17:35:52 -0700455
456 metrics_fields = {
457 'branch_name': branchname,
Don Garrettf0761152017-10-19 19:38:27 -0700458 'build_config': options.build_config_name,
Don Garrettd1d90dd2017-06-13 17:35:52 -0700459 'tryjob': options.remote_trybot,
460 }
Don Garrettf15d65b2017-04-12 12:39:55 -0700461
Don Garrettacbb2392017-05-11 18:27:41 -0700462 # Does the entire build pass or fail.
Don Garrett45e77412017-06-14 16:57:55 -0700463 with metrics.Presence(METRIC_ACTIVE, metrics_fields), \
464 metrics.SuccessCounter(METRIC_COMPLETED, metrics_fields) as s_fields:
Don Garrettc4114cc2016-11-01 20:04:06 -0700465
Don Garrettacbb2392017-05-11 18:27:41 -0700466 # Preliminary set, mostly command line parsing.
Don Garrettd1d90dd2017-06-13 17:35:52 -0700467 with metrics.SuccessCounter(METRIC_INVOKED, metrics_fields):
Don Garrettfbbccec2017-09-20 14:04:48 -0700468 if options.enable_buildbot_tags:
469 logging.EnableBuildbotMarkers()
Don Garrettacbb2392017-05-11 18:27:41 -0700470 ConfigureGlobalEnvironment()
Don Garrett86881cb2017-02-15 15:41:55 -0800471
Don Garrettacbb2392017-05-11 18:27:41 -0700472 # Prepare the buildroot with source for the build.
473 with metrics.SuccessCounter(METRIC_PREP, metrics_fields):
474 site_config = config_lib.GetConfig()
475 manifest_url = site_config.params['MANIFEST_INT_URL']
476 repo = repository.RepoRepository(manifest_url, buildroot,
477 branch=branchname,
Don Garrettd1d90dd2017-06-13 17:35:52 -0700478 git_cache_dir=options.git_cache_dir)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600479 previous_build_state = GetLastBuildState(root)
Don Garrett86881cb2017-02-15 15:41:55 -0800480
Don Garrettacbb2392017-05-11 18:27:41 -0700481 # Clean up the buildroot to a safe state.
482 with metrics.SecondsTimer(METRIC_CLEAN, fields=metrics_fields):
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600483 build_state = GetCurrentBuildState(options, branchname)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600484 CleanBuildRoot(root, repo, metrics_fields, build_state)
Don Garrettacbb2392017-05-11 18:27:41 -0700485
Don Garretta50bf492017-09-28 18:33:02 -0700486 # Get a checkout close enough to the branch that cbuildbot can handle it.
Mike Frysingerdb117c82017-12-15 18:10:33 -0500487 if options.sync:
488 with metrics.SecondsTimer(METRIC_INITIAL, fields=metrics_fields):
489 InitialCheckout(repo)
Don Garrettacbb2392017-05-11 18:27:41 -0700490
Don Garrett066e6f52017-09-28 19:14:01 -0700491 # Get a checkout close enough to the branch that cbuildbot can handle it.
492 with metrics.SecondsTimer(METRIC_DEPOT_TOOLS, fields=metrics_fields):
493 DepotToolsEnsureBootstrap(depot_tools_path)
494
Don Garrettacbb2392017-05-11 18:27:41 -0700495 # Run cbuildbot inside the full ChromeOS checkout, on the specified branch.
496 with metrics.SecondsTimer(METRIC_CBUILDBOT, fields=metrics_fields):
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600497 if previous_build_state.is_valid():
498 argv.append('--previous-build-state')
499 argv.append(base64.b64encode(previous_build_state.to_json()))
500
Don Garrett6e5c6b92018-04-06 17:58:49 -0700501 result = Cbuildbot(buildroot, depot_tools_path, argv)
Don Garrettd1d90dd2017-06-13 17:35:52 -0700502 s_fields['success'] = (result == 0)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600503
504 build_state.status = (
505 constants.BUILDER_STATUS_PASSED
506 if result == 0 else constants.BUILDER_STATUS_FAILED)
507 SetLastBuildState(root, build_state)
508
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700509 CleanupChroot(buildroot)
Don Garrettacbb2392017-05-11 18:27:41 -0700510 return result
511
512
513def main(argv):
514 # Enable Monarch metrics gathering.
515 with ts_mon_config.SetupTsMonGlobalState('cbuildbot_launch', indirect=True):
516 return _main(argv)