github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/engine/build_and_deployer.go (about) 1 package engine 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 8 "go.opentelemetry.io/otel/attribute" 9 "go.opentelemetry.io/otel/trace" 10 11 "github.com/tilt-dev/tilt/internal/engine/buildcontrol" 12 "github.com/tilt-dev/tilt/internal/store" 13 "github.com/tilt-dev/tilt/internal/store/liveupdates" 14 "github.com/tilt-dev/tilt/pkg/logger" 15 "github.com/tilt-dev/tilt/pkg/model" 16 ) 17 18 type BuildOrder []buildcontrol.BuildAndDeployer 19 20 func (bo BuildOrder) String() string { 21 var output strings.Builder 22 output.WriteString("BuildOrder{") 23 24 for _, b := range bo { 25 output.WriteString(fmt.Sprintf(" %T", b)) 26 } 27 28 output.WriteString(" }") 29 30 return output.String() 31 } 32 33 type FallbackTester func(error) bool 34 35 // CompositeBuildAndDeployer tries to run each builder in order. If a builder 36 // emits an error, it uses the FallbackTester to determine whether the error is 37 // critical enough to stop the whole pipeline, or to fallback to the next 38 // builder. 39 type CompositeBuildAndDeployer struct { 40 builders BuildOrder 41 tracer trace.Tracer 42 } 43 44 var _ buildcontrol.BuildAndDeployer = &CompositeBuildAndDeployer{} 45 46 func NewCompositeBuildAndDeployer(builders BuildOrder, tracer trace.Tracer) *CompositeBuildAndDeployer { 47 return &CompositeBuildAndDeployer{builders: builders, tracer: tracer} 48 } 49 50 func (composite *CompositeBuildAndDeployer) BuildAndDeploy(ctx context.Context, st store.RStore, specs []model.TargetSpec, currentState store.BuildStateSet) (store.BuildResultSet, error) { 51 ctx, span := composite.tracer.Start(ctx, "update") 52 defer span.End() 53 var lastErr, lastUnexpectedErr error 54 55 specNames := []string{} 56 57 for _, s := range specs { 58 specNames = append(specNames, s.ID().String()) 59 } 60 span.SetAttributes(attribute.KeyValue{Key: attribute.Key("targetNames"), Value: attribute.StringValue(strings.Join(specNames, ","))}) 61 62 logger.Get(ctx).Debugf("Building with BuildOrder: %s", composite.builders.String()) 63 for i, builder := range composite.builders { 64 buildType := fmt.Sprintf("%T", builder) 65 logger.Get(ctx).Debugf("Trying to build and deploy with %s", buildType) 66 67 br, err := builder.BuildAndDeploy(ctx, st, specs, currentState) 68 if err == nil { 69 buildTypes := br.BuildTypes() 70 for _, bt := range buildTypes { 71 span.SetAttributes(attribute.KeyValue{Key: attribute.Key(fmt.Sprintf("buildType.%s", bt)), Value: attribute.BoolValue(true)}) 72 } 73 return br, nil 74 } 75 76 if !buildcontrol.ShouldFallBackForErr(err) { 77 return br, err 78 } 79 80 l := logger.Get(ctx).WithFields(logger.Fields{logger.FieldNameBuildEvent: "fallback"}) 81 82 if redirectErr, ok := err.(buildcontrol.RedirectToNextBuilder); ok { 83 s := fmt.Sprintf("Falling back to next update method…\nREASON: %v\n", err) 84 l.Write(redirectErr.Level, []byte(s)) 85 } else { 86 lastUnexpectedErr = err 87 if i+1 < len(composite.builders) { 88 logger.Get(ctx).Infof("got unexpected error during build/deploy: %v", err) 89 } 90 } 91 lastErr = err 92 } 93 94 if lastUnexpectedErr != nil { 95 // The most interesting error is the last UNEXPECTED error we got 96 return store.BuildResultSet{}, lastUnexpectedErr 97 } 98 return store.BuildResultSet{}, lastErr 99 } 100 101 func DefaultBuildOrder(ibad *buildcontrol.ImageBuildAndDeployer, dcbad *buildcontrol.DockerComposeBuildAndDeployer, 102 ltbad *buildcontrol.LocalTargetBuildAndDeployer, updMode liveupdates.UpdateMode) BuildOrder { 103 if updMode == liveupdates.UpdateModeImage { 104 return BuildOrder{dcbad, ibad, ltbad} 105 } 106 107 return BuildOrder{dcbad, ibad, ltbad} 108 }