-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #256 from depot/feat/pull-all-bake-targets
feat: pull all bake targets by default
- Loading branch information
Showing
7 changed files
with
434 additions
and
240 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
package pull | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"os" | ||
"strings" | ||
|
||
"github.com/depot/cli/pkg/load" | ||
cliv1 "github.com/depot/cli/pkg/proto/depot/cli/v1" | ||
prog "github.com/docker/buildx/util/progress" | ||
"golang.org/x/exp/slices" | ||
) | ||
|
||
func isSavedBuild(options []*cliv1.BuildOptions) bool { | ||
for _, opt := range options { | ||
if opt.Save { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
func isBake(options []*cliv1.BuildOptions) bool { | ||
for _, opt := range options { | ||
if opt.Command == cliv1.Command_COMMAND_BAKE { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
type pull struct { | ||
imageName string | ||
pullOptions load.PullOptions | ||
} | ||
|
||
// Collect the build pull option. | ||
// Preconditions: !isBake(msg.Options) && isSavedBuild(msg.Options) | ||
func buildPullOpt(msg *cliv1.GetPullInfoResponse, userTags []string, platform, progress string) *pull { | ||
// If the user does not specify pull tag names, we use the tags in the build file. | ||
tags := userTags | ||
if len(tags) == 0 && len(msg.Options) > 0 && len(msg.Options[0].Tags) > 0 { | ||
tags = msg.Options[0].Tags | ||
} | ||
|
||
opts := load.PullOptions{ | ||
UserTags: tags, | ||
Quiet: progress == prog.PrinterModeQuiet, | ||
KeepImage: true, | ||
Username: &msg.Username, | ||
Password: &msg.Password, | ||
} | ||
if platform != "" { | ||
opts.Platform = &platform | ||
} | ||
|
||
return &pull{ | ||
imageName: msg.Reference, | ||
pullOptions: opts, | ||
} | ||
} | ||
|
||
func validateTargets(targets []string, msg *cliv1.GetPullInfoResponse) error { | ||
var validTargets []string | ||
for _, opt := range msg.Options { | ||
validTargets = append(validTargets, *opt.TargetName) | ||
} | ||
for _, target := range targets { | ||
if !slices.Contains(validTargets, target) { | ||
return fmt.Errorf("target %s not found. The available targets are %s", target, strings.Join(validTargets, ", ")) | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// Collect all the bake targets to pull. | ||
// Preconditions: isBake(msg.Options) && isSavedBuild(msg.Options) && validateTargets(targets, msg) == nil | ||
func bakePullOpts(msg *cliv1.GetPullInfoResponse, targets, userTags []string, platform, progress string) []*pull { | ||
pulls := []*pull{} | ||
for _, opt := range msg.Options { | ||
// Bake builds always have a target name. | ||
targetName := *opt.TargetName | ||
if len(targets) > 0 && !slices.Contains(targets, targetName) { | ||
continue | ||
} | ||
|
||
imageName := fmt.Sprintf("%s-%s", msg.Reference, targetName) | ||
|
||
// If a user specified tags, we override the tags in the bake file | ||
// with <TAG>-<TARGET_NAME>. | ||
tags := opt.Tags | ||
if len(userTags) > 0 { | ||
tags = make([]string, len(userTags)) | ||
for i, tag := range userTags { | ||
tags[i] = fmt.Sprintf("%s-%s", tag, targetName) | ||
} | ||
} | ||
|
||
opts := load.PullOptions{ | ||
UserTags: tags, | ||
Quiet: progress == prog.PrinterModeQuiet, | ||
KeepImage: true, | ||
Username: &msg.Username, | ||
Password: &msg.Password, | ||
} | ||
if platform != "" { | ||
opts.Platform = &platform | ||
} | ||
|
||
pulls = append(pulls, &pull{ | ||
imageName: imageName, | ||
pullOptions: opts, | ||
}) | ||
} | ||
|
||
return pulls | ||
} | ||
|
||
func buildPrinter(ctx context.Context, p *pull, progress string) (printer *Printer, cancel context.CancelFunc, err error) { | ||
displayPhrase := fmt.Sprintf("Pulling image %s", p.imageName) | ||
printerCtx, cancel := context.WithCancel(ctx) | ||
printer, err = NewPrinter(printerCtx, displayPhrase, os.Stderr, os.Stderr, progress) | ||
if err != nil { | ||
cancel() | ||
return nil, nil, err | ||
} | ||
return printer, cancel, nil | ||
} | ||
|
||
func bakePrinter(ctx context.Context, ps []*pull, progress string) (printer *Printer, cancel context.CancelFunc, err error) { | ||
images := []string{} | ||
for _, p := range ps { | ||
images = append(images, p.imageName) | ||
} | ||
|
||
var displayPhrase string | ||
if len(images) == 1 { | ||
displayPhrase = fmt.Sprintf("Pulling image %s", images[0]) | ||
} else { | ||
displayPhrase = fmt.Sprintf("Pulling images %s", strings.Join(images, ", ")) | ||
} | ||
printerCtx, cancel := context.WithCancel(ctx) | ||
printer, err = NewPrinter(printerCtx, displayPhrase, os.Stderr, os.Stderr, progress) | ||
if err != nil { | ||
cancel() | ||
return nil, nil, err | ||
} | ||
return printer, cancel, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package pull | ||
|
||
import ( | ||
"context" | ||
"io" | ||
"os" | ||
"sync" | ||
|
||
"github.com/containerd/console" | ||
"github.com/docker/buildx/util/logutil" | ||
prog "github.com/docker/buildx/util/progress" | ||
"github.com/moby/buildkit/client" | ||
"github.com/moby/buildkit/util/progress/progressui" | ||
"github.com/opencontainers/go-digest" | ||
"github.com/pkg/errors" | ||
"github.com/sirupsen/logrus" | ||
) | ||
|
||
// Specialized printer as the default buildkit one has a hard-coded display phrase, "Building."" | ||
type Printer struct { | ||
status chan *client.SolveStatus | ||
done <-chan struct{} | ||
err error | ||
warnings []client.VertexWarning | ||
logMu sync.Mutex | ||
logSourceMap map[digest.Digest]interface{} | ||
} | ||
|
||
func (p *Printer) Wait() error { close(p.status); <-p.done; return p.err } | ||
func (p *Printer) Write(s *client.SolveStatus) { p.status <- s } | ||
func (p *Printer) Warnings() []client.VertexWarning { return p.warnings } | ||
|
||
func (p *Printer) ValidateLogSource(dgst digest.Digest, v interface{}) bool { | ||
p.logMu.Lock() | ||
defer p.logMu.Unlock() | ||
src, ok := p.logSourceMap[dgst] | ||
if ok { | ||
if src == v { | ||
return true | ||
} | ||
} else { | ||
p.logSourceMap[dgst] = v | ||
return true | ||
} | ||
return false | ||
} | ||
|
||
func (p *Printer) ClearLogSource(v interface{}) { | ||
p.logMu.Lock() | ||
defer p.logMu.Unlock() | ||
for d := range p.logSourceMap { | ||
if p.logSourceMap[d] == v { | ||
delete(p.logSourceMap, d) | ||
} | ||
} | ||
} | ||
|
||
func NewPrinter(ctx context.Context, displayPhrase string, w io.Writer, out console.File, mode string) (*Printer, error) { | ||
statusCh := make(chan *client.SolveStatus) | ||
doneCh := make(chan struct{}) | ||
|
||
pw := &Printer{ | ||
status: statusCh, | ||
done: doneCh, | ||
logSourceMap: map[digest.Digest]interface{}{}, | ||
} | ||
|
||
if v := os.Getenv("BUILDKIT_PROGRESS"); v != "" && mode == prog.PrinterModeAuto { | ||
mode = v | ||
} | ||
|
||
var c console.Console | ||
switch mode { | ||
case prog.PrinterModeQuiet: | ||
w = io.Discard | ||
case prog.PrinterModeAuto, prog.PrinterModeTty: | ||
if cons, err := console.ConsoleFromFile(out); err == nil { | ||
c = cons | ||
} else { | ||
if mode == prog.PrinterModeTty { | ||
return nil, errors.Wrap(err, "failed to get console") | ||
} | ||
} | ||
} | ||
|
||
go func() { | ||
resumeLogs := logutil.Pause(logrus.StandardLogger()) | ||
// not using shared context to not disrupt display but let is finish reporting errors | ||
// DEPOT: allowed displayPhrase to be overridden. | ||
pw.warnings, pw.err = progressui.DisplaySolveStatus(ctx, displayPhrase, c, w, statusCh) | ||
resumeLogs() | ||
close(doneCh) | ||
}() | ||
|
||
return pw, nil | ||
} |
Oops, something went wrong.