func (builder *STI) Build(config *api.Config) (*api.Result, error) {
	builder.result = &api.Result{}
	if len(builder.config.CallbackURL) > 0 {
		defer func() {
			builder.result.Messages = builder.callbackInvoker.ExecuteCallback(
				builder.config.CallbackURL,
				builder.result.Success,
				builder.postExecutorStepsContext.labels,
				builder.result.Messages,
			)
		}()
	}
	defer builder.garbage.Cleanup(config)
	log.V(1).Infof("Preparing to build %s", config.Tag)
	if err := builder.preparer.Prepare(config); err != nil {
		return builder.result, err
	}
	if builder.incremental = builder.artifacts.Exists(config); builder.incremental {
		tag := util.FirstNonEmpty(config.IncrementalFromTag, config.Tag)
		log.V(1).Infof("Existing image for tag %s detected for incremental build", tag)
	} else {
		log.V(1).Info("Clean build will be performed")
	}
	log.V(2).Infof("Performing source build from %s", config.Source)
	if builder.incremental {
		if err := builder.artifacts.Save(config); err != nil {
			log.Warning("Clean build will be performed because of error saving previous build artifacts")
			log.V(2).Infof("error: %v", err)
		}
	}
	if len(config.AssembleUser) > 0 {
		log.V(1).Infof("Running %q in %q as %q user", constants.Assemble, config.Tag, config.AssembleUser)
	} else {
		log.V(1).Infof("Running %q in %q", constants.Assemble, config.Tag)
	}
	startTime := time.Now()
	if err := builder.scripts.Execute(constants.Assemble, config.AssembleUser, config); err != nil {
		if err == errMissingRequirements {
			log.V(1).Info("Image is missing basic requirements (sh or tar), layered build will be performed")
			return builder.layered.Build(config)
		}
		if e, ok := err.(s2ierr.ContainerError); ok {
			if !isMissingRequirements(e.Output) {
				builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason(
					utilstatus.ReasonAssembleFailed,
					utilstatus.ReasonMessageAssembleFailed,
				)
				return builder.result, err
			}
			log.V(1).Info("Image is missing basic requirements (sh or tar), layered build will be performed")
			buildResult, err := builder.layered.Build(config)
			return buildResult, err
		}
		return builder.result, err
	}
	builder.result.BuildInfo.Stages = api.RecordStageAndStepInfo(builder.result.BuildInfo.Stages, api.StageAssemble, api.StepAssembleBuildScripts, startTime, time.Now())
	builder.result.Success = true
	return builder.result, nil
}
Build processes a Request and returns a *api.Result and an error.
An error represents a failure performing the build rather than a failure
of the build itself.  Callers should check the Success field of the result
to determine whether a build succeeded or not.
package sti
import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strings"
	"time"
	"github.com/openshift/source-to-image/pkg/api"
	"github.com/openshift/source-to-image/pkg/api/constants"
	"github.com/openshift/source-to-image/pkg/build"
	"github.com/openshift/source-to-image/pkg/build/strategies/layered"
	dockerpkg "github.com/openshift/source-to-image/pkg/docker"
	s2ierr "github.com/openshift/source-to-image/pkg/errors"
	"github.com/openshift/source-to-image/pkg/ignore"
	"github.com/openshift/source-to-image/pkg/scm"
	"github.com/openshift/source-to-image/pkg/scm/git"
	"github.com/openshift/source-to-image/pkg/scripts"
	"github.com/openshift/source-to-image/pkg/tar"
	"github.com/openshift/source-to-image/pkg/util"
	"github.com/openshift/source-to-image/pkg/util/cmd"
	"github.com/openshift/source-to-image/pkg/util/fs"
	utillog "github.com/openshift/source-to-image/pkg/util/log"
	utilstatus "github.com/openshift/source-to-image/pkg/util/status"
)
const (
	injectionResultFile = "/tmp/injection-result"
	rmInjectionsScript  = "/tmp/rm-injections"
)
var (
	log = utillog.StderrLog
	workingDirs = []string{
		constants.UploadScripts,
		constants.Source,
		constants.DefaultScripts,
		constants.UserScripts,
	}
	errMissingRequirements = errors.New("missing requirements")
)
type STI struct {
	config                 *api.Config
	result                 *api.Result
	postExecutor           dockerpkg.PostExecutor
	installer              scripts.Installer
	runtimeInstaller       scripts.Installer
	git                    git.Git
	fs                     fs.FileSystem
	tar                    tar.Tar
	docker                 dockerpkg.Docker
	incrementalDocker      dockerpkg.Docker
	runtimeDocker          dockerpkg.Docker
	callbackInvoker        util.CallbackInvoker
	requiredScripts        []string
	optionalScripts        []string
	optionalRuntimeScripts []string
	externalScripts        map[string]bool
	installedScripts       map[string]bool
	scriptsURL             map[string]string
	incremental            bool
	sourceInfo             *git.SourceInfo
	env                    []string
	newLabels              map[string]string
	preparer  build.Preparer
	ignorer   build.Ignorer
	artifacts build.IncrementalBuilder
	scripts   build.ScriptsHandler
	source    build.Downloader
	garbage   build.Cleaner
	layered   build.Builder
	postExecutorStage            int
	postExecutorFirstStageSteps  []postExecutorStep
	postExecutorSecondStageSteps []postExecutorStep
	postExecutorStepsContext     *postExecutorStepContext
}
func New(client dockerpkg.Client, config *api.Config, fs fs.FileSystem, overrides build.Overrides) (*STI, error) {
	excludePattern, err := regexp.Compile(config.ExcludeRegExp)
	if err != nil {
		return nil, err
	}
	docker := dockerpkg.New(client, config.PullAuthentication)
	var incrementalDocker dockerpkg.Docker
	if config.Incremental {
		incrementalDocker = dockerpkg.New(client, config.IncrementalAuthentication)
	}
	inst := scripts.NewInstaller(
		config.BuilderImage,
		config.ScriptsURL,
		config.ScriptDownloadProxyConfig,
		docker,
		config.PullAuthentication,
		fs,
		config,
	)
	tarHandler := tar.NewParanoid(fs)
	tarHandler.SetExclusionPattern(excludePattern)
	builder := &STI{
		installer:              inst,
		config:                 config,
		docker:                 docker,
		incrementalDocker:      incrementalDocker,
		git:                    git.New(fs, cmd.NewCommandRunner()),
		fs:                     fs,
		tar:                    tarHandler,
		callbackInvoker:        util.NewCallbackInvoker(),
		requiredScripts:        scripts.RequiredScripts,
		optionalScripts:        scripts.OptionalScripts,
		optionalRuntimeScripts: []string{constants.AssembleRuntime},
		externalScripts:        map[string]bool{},
		installedScripts:       map[string]bool{},
		scriptsURL:             map[string]string{},
		newLabels:              map[string]string{},
	}
	if len(config.RuntimeImage) > 0 {
		builder.runtimeDocker = dockerpkg.New(client, config.RuntimeAuthentication)
		builder.runtimeInstaller = scripts.NewInstaller(
			config.RuntimeImage,
			config.ScriptsURL,
			config.ScriptDownloadProxyConfig,
			builder.runtimeDocker,
			config.RuntimeAuthentication,
			builder.fs,
			config,
		)
	}
	builder.source = overrides.Downloader
	if builder.source == nil && !config.Usage {
		downloader, err := scm.DownloaderForSource(builder.fs, config.Source, config.ForceCopy)
		if err != nil {
			return nil, err
		}
		builder.source = downloader
	}
	builder.garbage = build.NewDefaultCleaner(builder.fs, builder.docker)
	builder.layered, err = layered.New(client, config, builder.fs, builder, overrides)
	if err != nil {
		return nil, err
	}
	builder.preparer = builder
	builder.ignorer = &ignore.DockerIgnorer{}
	builder.artifacts = builder
	builder.scripts = builder
	builder.postExecutor = builder
	builder.initPostExecutorSteps()
	return builder, nil
}