in common/build.go [783:859]
func (b *Build) run(ctx context.Context, trace JobTrace, executor Executor) (err error) {
b.setCurrentState(BuildRunRuntimeRunning)
buildFinish := make(chan error, 1)
buildPanic := make(chan error, 1)
runContext, runCancel := context.WithCancel(ctx)
defer runCancel()
if term, ok := executor.(terminal.InteractiveTerminal); b.Session != nil && ok {
b.Session.SetInteractiveTerminal(term)
}
if proxyPooler, ok := executor.(proxy.Pooler); b.Session != nil && ok {
b.Session.SetProxyPool(proxyPooler)
}
// Run build script
go func() {
defer func() {
if r := recover(); r != nil {
err := &BuildError{FailureReason: RunnerSystemFailure, Inner: fmt.Errorf("panic: %s", r)}
b.Log().WithError(err).Error(string(debug.Stack()))
buildPanic <- err
}
}()
buildFinish <- b.executeScript(runContext, trace, executor)
}()
// Wait for signals: cancel, timeout, abort or finish
b.Log().Debugln("Waiting for signals...")
select {
case <-ctx.Done():
err = b.handleError(context.Cause(ctx))
case signal := <-b.SystemInterrupt:
err = &BuildError{
Inner: fmt.Errorf("aborted: %v", signal),
FailureReason: RunnerSystemFailure,
}
b.setCurrentState(BuildRunRuntimeTerminated)
case err = <-buildFinish:
// It's possible that the parent context being cancelled will
// terminate the build early, bringing us here, and although we handle
// `ctx.Done()` above, select statements are not ordered.
// We handle this the same as if we received ctx.Done(), but
// return early because we're no longer waiting for the build
// to finish.
if ctx.Err() != nil {
return b.handleError(context.Cause(ctx))
}
if err != nil {
b.setCurrentState(BuildRunRuntimeFailed)
} else {
b.setCurrentState(BuildRunRuntimeSuccess)
}
return err
case err = <-buildPanic:
b.setCurrentState(BuildRunRuntimeTerminated)
return err
}
b.Log().WithError(err).Debugln("Waiting for build to finish...")
// Wait till we receive that build did finish
runCancel()
b.waitForBuildFinish(buildFinish, WaitForBuildFinishTimeout)
b.ensureFinishedAt()
return err
}