diff --git a/plan/task/push.go b/plan/task/push.go new file mode 100644 index 00000000..17d0834d --- /dev/null +++ b/plan/task/push.go @@ -0,0 +1,87 @@ +package task + +import ( + "context" + "fmt" + + "github.com/docker/distribution/reference" + bk "github.com/moby/buildkit/client" + "github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb" + "github.com/rs/zerolog/log" + "go.dagger.io/dagger/compiler" + "go.dagger.io/dagger/plancontext" + "go.dagger.io/dagger/solver" +) + +func init() { + Register("Push", func() Task { return &pushTask{} }) +} + +type pushTask struct { +} + +func (c *pushTask) Run(ctx context.Context, pctx *plancontext.Context, s solver.Solver, v *compiler.Value) (*compiler.Value, error) { + lg := log.Ctx(ctx) + + rawDest, err := v.Lookup("dest").String() + if err != nil { + return nil, err + } + + dest, err := reference.ParseNormalizedNamed(rawDest) + if err != nil { + return nil, fmt.Errorf("failed to parse ref %s: %w", rawDest, err) + } + // Add the default tag "latest" to a reference if it only has a repo name. + dest = reference.TagNameOnly(dest) + + // Read auth info + auth, err := decodeAuthValue(pctx, v.Lookup("auth")) + if err != nil { + return nil, err + } + for _, a := range auth { + s.AddCredentials(a.Target, a.Username, a.Secret.PlainText()) + lg.Debug().Str("target", a.Target).Msg("add target credentials") + } + + // Get input state + input, err := pctx.FS.FromValue(v.Lookup("input")) + if err != nil { + return nil, err + } + st, err := input.Result().ToState() + if err != nil { + return nil, err + } + + // Decode the image config + imageConfig := dockerfile2llb.ImageConfig{} + if err := v.Lookup("config").Decode(&imageConfig); err != nil { + return nil, err + } + + // Export image + lg.Debug().Str("dest", dest.String()).Msg("export image") + resp, err := s.Export(ctx, st, &dockerfile2llb.Image{Config: imageConfig}, bk.ExportEntry{ + Type: bk.ExporterImage, + Attrs: map[string]string{ + "name": dest.String(), + "push": "true", + }, + }, pctx.Platform.Get()) + if err != nil { + return nil, err + } + + digest, hasImageDigest := resp.ExporterResponse["containerimage.digest"] + if !hasImageDigest { + return nil, fmt.Errorf("image push target %q did not return an image digest", dest.String()) + } + imageRef := fmt.Sprintf("%s@%s", resp.ExporterResponse["image.name"], digest) + + // Fill result + return compiler.NewValue().FillFields(map[string]interface{}{ + "result": imageRef, + }) +} diff --git a/stdlib/europa/dagger/engine/image.cue b/stdlib/europa/dagger/engine/image.cue index 554b4cc9..13e0fbd6 100644 --- a/stdlib/europa/dagger/engine/image.cue +++ b/stdlib/europa/dagger/engine/image.cue @@ -2,7 +2,6 @@ package engine // Upload a container image to a remote repository #Push: { - @dagger(notimplemented) $dagger: task: _name: "Push" // Target repository address