@@ -59,6 +59,8 @@ const (
5959 printLintFallbackImage = "docker/dockerfile:1.8.1@sha256:e87caa74dcb7d46cd820352bfea12591f3dba3ddc4285e19c7dcd13359f7cefd"
6060)
6161
62+ var ErrRestart = errors .New ("build: restart" )
63+
6264type Options struct {
6365 Inputs Inputs
6466
@@ -312,7 +314,7 @@ func toRepoOnly(in string) (string, error) {
312314}
313315
314316type Handler struct {
315- OnResult func (driverIdx int , rCtx * ResultHandle )
317+ Evaluate func (ctx context. Context , c gateway. Client , res * gateway. Result ) error
316318}
317319
318320func Build (ctx context.Context , nodes []builder.Node , opts map [string ]Options , docker * dockerutil.Client , cfg * confutil.Config , w progress.Writer ) (resp map [string ]* client.SolveResponse , err error ) {
@@ -479,9 +481,14 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
479481 ch , done := progress .NewChannel (pw )
480482 defer func () { <- done }()
481483
482- cc := c
483- var callRes map [string ][]byte
484- buildFunc := func (ctx context.Context , c gateway.Client ) (* gateway.Result , error ) {
484+ var (
485+ callRes map [string ][]byte
486+ frontendErr error
487+ )
488+ buildFunc := func (ctx context.Context , c gateway.Client ) (_ * gateway.Result , retErr error ) {
489+ // Capture the error from this build function.
490+ defer catchFrontendError (& retErr , & frontendErr )
491+
485492 if opt .CallFunc != nil {
486493 if _ , ok := req .FrontendOpt ["frontend.caps" ]; ! ok {
487494 req .FrontendOpt ["frontend.caps" ] = "moby.buildkit.frontend.subrequests+forward"
@@ -504,24 +511,25 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
504511 results .Set (rKey , res )
505512
506513 if children := childTargets [rKey ]; len (children ) > 0 {
507- if err := waitForChildren (ctx , res , results , children ); err != nil {
514+ if err := waitForChildren (ctx , bh , c , res , results , children ); err != nil {
515+ return nil , err
516+ }
517+ } else if bh != nil && bh .Evaluate != nil {
518+ if err := bh .Evaluate (ctx , c , res ); err != nil {
508519 return nil , err
509520 }
510521 }
511522 return res , nil
512523 }
524+
513525 buildRef := fmt .Sprintf ("%s/%s/%s" , node .Builder , node .Name , so .Ref )
514526
515- var rr * client.SolveResponse
516- if bh != nil && bh .OnResult != nil {
517- var resultHandle * ResultHandle
518- resultHandle , rr , err = NewResultHandle (ctx , cc , * so , "buildx" , buildFunc , ch )
519- bh .OnResult (dp .driverIndex , resultHandle )
520- } else {
521- span , ctx := tracing .StartSpan (ctx , "build" )
522- rr , err = c .Build (ctx , * so , "buildx" , buildFunc , ch )
523- tracing .FinishWithError (span , err )
527+ span , ctx := tracing .StartSpan (ctx , "build" )
528+ rr , err := c .Build (ctx , * so , "buildx" , buildFunc , ch )
529+ if errors .Is (frontendErr , ErrRestart ) {
530+ err = ErrRestart
524531 }
532+ tracing .FinishWithError (span , err )
525533
526534 if ! so .Internal && desktop .BuildBackendEnabled () && node .Driver .HistoryAPISupported (ctx ) {
527535 if err != nil {
@@ -1191,7 +1199,7 @@ func solve(ctx context.Context, c gateway.Client, req gateway.SolveRequest) (*ga
11911199 return res , nil
11921200}
11931201
1194- func waitForChildren (ctx context.Context , res * gateway.Result , results * waitmap.Map , children []string ) error {
1202+ func waitForChildren (ctx context.Context , bh * Handler , c gateway. Client , res * gateway.Result , results * waitmap.Map , children []string ) error {
11951203 // wait for the child targets to register their LLB before evaluating
11961204 _ , err := results .Get (ctx , children ... )
11971205 if err != nil {
@@ -1200,6 +1208,9 @@ func waitForChildren(ctx context.Context, res *gateway.Result, results *waitmap.
12001208 // we need to wait until the child targets have completed before we can release
12011209 eg , ctx := errgroup .WithContext (ctx )
12021210 eg .Go (func () error {
1211+ if bh != nil && bh .Evaluate != nil {
1212+ return bh .Evaluate (ctx , c , res )
1213+ }
12031214 return res .EachRef (func (ref gateway.Reference ) error {
12041215 return ref .Evaluate (ctx )
12051216 })
@@ -1210,3 +1221,12 @@ func waitForChildren(ctx context.Context, res *gateway.Result, results *waitmap.
12101221 })
12111222 return eg .Wait ()
12121223}
1224+
1225+ func catchFrontendError (retErr , frontendErr * error ) {
1226+ * frontendErr = * retErr
1227+ if errors .Is (* retErr , ErrRestart ) {
1228+ // Overwrite the sentinel error with a more user friendly message.
1229+ // This gets stored only in the return error.
1230+ * retErr = errors .New ("build restarted by client" )
1231+ }
1232+ }
0 commit comments