|  | @@ -18,6 +18,8 @@ package compose
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import (
 | 
	
		
			
				|  |  |  	"context"
 | 
	
		
			
				|  |  | +	"crypto/sha1"
 | 
	
		
			
				|  |  | +	"fmt"
 | 
	
		
			
				|  |  |  	"os"
 | 
	
		
			
				|  |  |  	"path/filepath"
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -25,11 +27,13 @@ import (
 | 
	
		
			
				|  |  |  	_ "github.com/docker/buildx/driver/docker-container" //nolint:blank-imports
 | 
	
		
			
				|  |  |  	_ "github.com/docker/buildx/driver/kubernetes"       //nolint:blank-imports
 | 
	
		
			
				|  |  |  	_ "github.com/docker/buildx/driver/remote"           //nolint:blank-imports
 | 
	
		
			
				|  |  | +	"github.com/moby/buildkit/client"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	"github.com/docker/buildx/build"
 | 
	
		
			
				|  |  |  	"github.com/docker/buildx/builder"
 | 
	
		
			
				|  |  |  	"github.com/docker/buildx/util/dockerutil"
 | 
	
		
			
				|  |  |  	xprogress "github.com/docker/buildx/util/progress"
 | 
	
		
			
				|  |  | +	"github.com/docker/compose/v2/pkg/progress"
 | 
	
		
			
				|  |  |  )
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  func (s *composeService) doBuildBuildkit(ctx context.Context, opts map[string]build.Options, mode string) (map[string]string, error) {
 | 
	
	
		
			
				|  | @@ -43,23 +47,27 @@ func (s *composeService) doBuildBuildkit(ctx context.Context, opts map[string]bu
 | 
	
		
			
				|  |  |  		return nil, err
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	// Progress needs its own context that lives longer than the
 | 
	
		
			
				|  |  | -	// build one otherwise it won't read all the messages from
 | 
	
		
			
				|  |  | -	// build and will lock
 | 
	
		
			
				|  |  | -	progressCtx, cancel := context.WithCancel(context.Background())
 | 
	
		
			
				|  |  | -	defer cancel()
 | 
	
		
			
				|  |  | -	w, err := xprogress.NewPrinter(progressCtx, s.stdout(), os.Stdout, mode)
 | 
	
		
			
				|  |  | -	if err != nil {
 | 
	
		
			
				|  |  | -		return nil, err
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -	response, err := build.Build(ctx, nodes, opts, dockerutil.NewClient(s.dockerCli), filepath.Dir(s.configFile().Filename), w)
 | 
	
		
			
				|  |  | -	errW := w.Wait()
 | 
	
		
			
				|  |  | -	if err == nil {
 | 
	
		
			
				|  |  | -		err = errW
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -	if err != nil {
 | 
	
		
			
				|  |  | -		return nil, WrapCategorisedComposeError(err, BuildFailure)
 | 
	
		
			
				|  |  | +	var response map[string]*client.SolveResponse
 | 
	
		
			
				|  |  | +	if s.dryRun {
 | 
	
		
			
				|  |  | +		response = s.dryRunBuildResponse(ctx, opts)
 | 
	
		
			
				|  |  | +	} else {
 | 
	
		
			
				|  |  | +		// Progress needs its own context that lives longer than the
 | 
	
		
			
				|  |  | +		// build one otherwise it won't read all the messages from
 | 
	
		
			
				|  |  | +		// build and will lock
 | 
	
		
			
				|  |  | +		progressCtx, cancel := context.WithCancel(context.Background())
 | 
	
		
			
				|  |  | +		defer cancel()
 | 
	
		
			
				|  |  | +		w, err := xprogress.NewPrinter(progressCtx, s.stdout(), os.Stdout, mode)
 | 
	
		
			
				|  |  | +		if err != nil {
 | 
	
		
			
				|  |  | +			return nil, err
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		response, err = build.Build(ctx, nodes, opts, dockerutil.NewClient(s.dockerCli), filepath.Dir(s.configFile().Filename), w)
 | 
	
		
			
				|  |  | +		errW := w.Wait()
 | 
	
		
			
				|  |  | +		if err == nil {
 | 
	
		
			
				|  |  | +			err = errW
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		if err != nil {
 | 
	
		
			
				|  |  | +			return nil, WrapCategorisedComposeError(err, BuildFailure)
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	imagesBuilt := map[string]string{}
 | 
	
	
		
			
				|  | @@ -76,3 +84,30 @@ func (s *composeService) doBuildBuildkit(ctx context.Context, opts map[string]bu
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	return imagesBuilt, err
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +func (s composeService) dryRunBuildResponse(ctx context.Context, options map[string]build.Options) map[string]*client.SolveResponse {
 | 
	
		
			
				|  |  | +	w := progress.ContextWriter(ctx)
 | 
	
		
			
				|  |  | +	buildResponse := map[string]*client.SolveResponse{}
 | 
	
		
			
				|  |  | +	for name, option := range options {
 | 
	
		
			
				|  |  | +		dryRunUUID := fmt.Sprintf("dryRun-%x", sha1.Sum([]byte(name)))
 | 
	
		
			
				|  |  | +		w.Event(progress.Event{
 | 
	
		
			
				|  |  | +			ID:     " ",
 | 
	
		
			
				|  |  | +			Status: progress.Done,
 | 
	
		
			
				|  |  | +			Text:   fmt.Sprintf("build service %s", name),
 | 
	
		
			
				|  |  | +		})
 | 
	
		
			
				|  |  | +		w.Event(progress.Event{
 | 
	
		
			
				|  |  | +			ID:     "==>",
 | 
	
		
			
				|  |  | +			Status: progress.Done,
 | 
	
		
			
				|  |  | +			Text:   fmt.Sprintf("==> writing image %s", dryRunUUID),
 | 
	
		
			
				|  |  | +		})
 | 
	
		
			
				|  |  | +		w.Event(progress.Event{
 | 
	
		
			
				|  |  | +			ID:     "==> ==>",
 | 
	
		
			
				|  |  | +			Status: progress.Done,
 | 
	
		
			
				|  |  | +			Text:   fmt.Sprintf(`naming to %s`, option.Tags[0]),
 | 
	
		
			
				|  |  | +		})
 | 
	
		
			
				|  |  | +		buildResponse[name] = &client.SolveResponse{ExporterResponse: map[string]string{
 | 
	
		
			
				|  |  | +			"containerimage.digest": dryRunUUID,
 | 
	
		
			
				|  |  | +		}}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	return buildResponse
 | 
	
		
			
				|  |  | +}
 |