blob: d0bf75550a6e0d173a05763f986e5a73f8f51e78 [file] [log] [blame] [edit]
# Copyright 2019 The LUCI Authors. All rights reserved.
# Use of this source code is governed under the Apache License, Version 2.0
# that can be found in the LICENSE file.
import logging
import os
import sys
from google.protobuf import json_format as jsonpb
from PB.go.chromium.org.luci.buildbucket.proto.build import Build
from ....third_party import luci_context
from ...engine import RecipeEngine
from ...step_runner.subproc import SubprocessStepRunner
from ...stream.invariants import StreamEngineInvariants
from ...stream.luci import LUCIStreamEngine
from . import RunBuildContractViolation
LOG = logging.getLogger(__name__)
def _contract_in_env(key):
if key not in os.environ:
raise RunBuildContractViolation('Expected $%s in environment.' % key)
return os.environ[key]
def _contract_in_luci_context(section, key):
section = luci_context.read(section)
if section is None or key not in section:
raise RunBuildContractViolation('Expected %r in $LUCI_CONTEXT[%r].' % (
key, section))
return section[key]
def _synth_properties(build):
# TODO(iannucci): expose this data natively as a Build message.
synth_props = {
'$recipe_engine/runtime': {
'is_experimental': build.input.experimental,
'is_luci': True,
},
'$recipe_engine/buildbucket': {
'build': jsonpb.MessageToDict(build),
'hostname': _contract_in_luci_context('buildbucket', 'hostname'),
},
'$recipe_engine/path': {
'temp_dir': _contract_in_env('TMP'),
'cache_dir': _contract_in_luci_context('run_build', 'cache_dir'),
},
}
LOG.info('Synthesized properties: %r', synth_props)
return synth_props
def _tweak_env():
# These tweaks are recipe-engine-specific tweaks to be compatible with the
# behavior of `recipes.py run`.
os.environ['PYTHONUNBUFFERED'] = '1'
os.environ['PYTHONIOENCODING'] = 'UTF-8'
def main(args):
LOG.info('run_build started, parsing Build message from stdin.')
build = Build()
build.ParseFromString(sys.stdin.read())
LOG.info('finished parsing Build message')
LOG.debug('build proto: %s', jsonpb.MessageToJson(build))
properties = jsonpb.MessageToDict(build.input.properties)
properties.update(_synth_properties(build))
_tweak_env()
run_build_engine = LUCIStreamEngine(args.build_proto_jsonpb)
with StreamEngineInvariants.wrap(run_build_engine) as stream_engine:
result, _ = RecipeEngine.run_steps(
args.recipe_deps, properties, stream_engine,
SubprocessStepRunner(), os.environ, os.getcwd())
return 1 if result.HasField("failure") else 0