Skip to content

Commit

Permalink
Merge pull request #256 from depot/feat/pull-all-bake-targets
Browse files Browse the repository at this point in the history
feat: pull all bake targets by default
  • Loading branch information
goller authored Mar 7, 2024
2 parents acff7e9 + ee9ded6 commit 9d3911c
Show file tree
Hide file tree
Showing 7 changed files with 434 additions and 240 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,13 @@ Pull an image from the Depot ephemeral registry to your local Docker daemon.
depot pull --tag repo:tag <BUILD_ID>
```

Pull all bake images from the Depot ephemeral registry to your local Docker daemon.
By default images will be tagged with the bake target names.

```shell
depot pull <BUILD_ID>
```

### `depot push`

Push an image from the Depot ephemeral registry to a destination registry.
Expand Down
2 changes: 1 addition & 1 deletion pkg/buildx/commands/bake.go
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ func printSaveUsage(project, buildID, progressMode string, requestedTargets []st

targets := strings.Join(requestedTargets, ",")
fmt.Fprintf(os.Stderr, "Saved %s: %s\n", saved, targets)
fmt.Fprintf(os.Stderr, "\tTo pull: depot pull %s--project %s %s\n", targetUsage, project, buildID)
fmt.Fprintf(os.Stderr, "\tTo pull: depot pull --project %s %s\n", project, buildID)
fmt.Fprintf(os.Stderr, "\tTo push: depot push %s--project %s --tag <REPOSITORY:TAG> %s\n", targetUsage, project, buildID)
}
}
150 changes: 150 additions & 0 deletions pkg/cmd/pull/options.go
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
}
96 changes: 96 additions & 0 deletions pkg/cmd/pull/printer.go
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
}
Loading

0 comments on commit 9d3911c

Please sign in to comment.