diff --git a/dagger/compiler/compiler.go b/dagger/compiler/compiler.go index 26ea2156..eb4eaec1 100644 --- a/dagger/compiler/compiler.go +++ b/dagger/compiler/compiler.go @@ -2,6 +2,7 @@ package compiler import ( "errors" + "fmt" "sync" "cuelang.org/go/cue" @@ -28,6 +29,10 @@ func Wrap(v cue.Value, inst *cue.Instance) *Value { return DefaultCompiler.Wrap(v, inst) } +func InstanceMerge(src ...interface{}) (*Value, error) { + return DefaultCompiler.InstanceMerge(src...) +} + func Cue() *cue.Runtime { return DefaultCompiler.Cue() } @@ -74,12 +79,7 @@ func (c *Compiler) Cue() *cue.Runtime { // Compile an empty value func (c *Compiler) NewValue() *Value { - empty, err := c.Compile("", ` - { - ... - _ - } - `) + empty, err := c.Compile("", "_") if err != nil { panic(err) } @@ -98,6 +98,40 @@ func (c *Compiler) Compile(name string, src interface{}) (*Value, error) { return c.Wrap(inst.Value(), inst), nil } +// InstanceMerge merges multiple values and mirrors the value in the cue.Instance. +// FIXME: AVOID THIS AT ALL COST +// Special case: we must return an instance with the same +// contents as v, for the purposes of cueflow. +func (c *Compiler) InstanceMerge(src ...interface{}) (*Value, error) { + var ( + v = c.NewValue() + inst = v.CueInst() + err error + ) + + c.lock() + defer c.unlock() + + for _, s := range src { + // If calling Fill() with a Value, we want to use the underlying + // cue.Value to fill. + if val, ok := s.(*Value); ok { + inst, err = inst.Fill(val.val) + if err != nil { + return nil, fmt.Errorf("merge failed: %w", err) + } + } else { + inst, err = inst.Fill(s) + if err != nil { + return nil, fmt.Errorf("merge failed: %w", err) + } + } + } + + v = c.Wrap(inst.Value(), inst) + return v, nil +} + func (c *Compiler) DecodeJSON(path string, data []byte) (*Value, error) { inst, err := cuejson.Decode(c.Cue(), path, data) if err != nil { diff --git a/dagger/result.go b/dagger/result.go index 0918b854..25e6d87d 100644 --- a/dagger/result.go +++ b/dagger/result.go @@ -52,32 +52,11 @@ func (r *DeploymentResult) Computed() *compiler.Value { } func (r *DeploymentResult) Merge() (*compiler.Value, error) { - // FIXME: v.CueInst() must return an instance with the same - // contents as v, for the purposes of cueflow. - // That is not currently how *compiler.Value works, so we prepare the cue - // instance manually. - // --> refactor the compiler.Value API to do this for us. - var ( - v = compiler.NewValue() - inst = v.CueInst() - err error + return compiler.InstanceMerge( + r.plan, + r.input, + r.computed, ) - - inst, err = inst.Fill(r.plan.Cue()) - if err != nil { - return nil, fmt.Errorf("merge plan: %w", err) - } - inst, err = inst.Fill(r.input.Cue()) - if err != nil { - return nil, fmt.Errorf("merge input: %w", err) - } - inst, err = inst.Fill(r.computed.Cue()) - if err != nil { - return nil, fmt.Errorf("merge computed: %w", err) - } - - v = compiler.Wrap(inst.Value(), inst) - return v, nil } func (r *DeploymentResult) ToLLB() (llb.State, error) {