mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 10:03:42 +08:00 
			
		
		
		
	Merge pull request #2965 from jsternberg/handle-unknown-values
buildflags: handle unknown values from cty
This commit is contained in:
		@@ -2,8 +2,10 @@ package bake
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hcl "github.com/hashicorp/hcl/v2"
 | 
				
			||||||
	"github.com/stretchr/testify/require"
 | 
						"github.com/stretchr/testify/require"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -647,7 +649,7 @@ func TestHCLAttrsCapsuleType(t *testing.T) {
 | 
				
			|||||||
	require.Equal(t, []string{"default", "key=path/to/key"}, stringify(c.Targets[0].SSH))
 | 
						require.Equal(t, []string{"default", "key=path/to/key"}, stringify(c.Targets[0].SSH))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestHCLAttrsCapsuleTypeVars(t *testing.T) {
 | 
					func TestHCLAttrsCapsuleType_ObjectVars(t *testing.T) {
 | 
				
			||||||
	dt := []byte(`
 | 
						dt := []byte(`
 | 
				
			||||||
	variable "foo" {
 | 
						variable "foo" {
 | 
				
			||||||
		default = "bar"
 | 
							default = "bar"
 | 
				
			||||||
@@ -716,6 +718,52 @@ func TestHCLAttrsCapsuleTypeVars(t *testing.T) {
 | 
				
			|||||||
	require.Equal(t, []string{"id=oci,src=/local/secret"}, stringify(web.Secrets))
 | 
						require.Equal(t, []string{"id=oci,src=/local/secret"}, stringify(web.Secrets))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestHCLAttrsCapsuleType_MissingVars(t *testing.T) {
 | 
				
			||||||
 | 
						dt := []byte(`
 | 
				
			||||||
 | 
						target "app" {
 | 
				
			||||||
 | 
							attest = [
 | 
				
			||||||
 | 
								"type=sbom,disabled=${SBOM}",
 | 
				
			||||||
 | 
							]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							cache-from = [
 | 
				
			||||||
 | 
								{ type = "registry", ref = "user/app:${FOO1}" },
 | 
				
			||||||
 | 
					      "type=local,src=path/to/cache:${FOO2}",
 | 
				
			||||||
 | 
							]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							cache-to = [
 | 
				
			||||||
 | 
								{ type = "local", dest = "path/to/${BAR}" },
 | 
				
			||||||
 | 
							]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							output = [
 | 
				
			||||||
 | 
								{ type = "oci", dest = "../${OUTPUT}.tar" },
 | 
				
			||||||
 | 
							]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							secret = [
 | 
				
			||||||
 | 
								{ id = "mysecret", src = "/local/${SECRET}" },
 | 
				
			||||||
 | 
							]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ssh = [
 | 
				
			||||||
 | 
								{ id = "key", paths = ["path/to/${SSH_KEY}"] },
 | 
				
			||||||
 | 
							]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var diags hcl.Diagnostics
 | 
				
			||||||
 | 
						_, err := ParseFile(dt, "docker-bake.hcl")
 | 
				
			||||||
 | 
						require.ErrorAs(t, err, &diags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						re := regexp.MustCompile(`There is no variable named "([\w\d_]+)"`)
 | 
				
			||||||
 | 
						var actual []string
 | 
				
			||||||
 | 
						for _, diag := range diags {
 | 
				
			||||||
 | 
							if m := re.FindStringSubmatch(diag.Error()); m != nil {
 | 
				
			||||||
 | 
								actual = append(actual, m[1])
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						require.ElementsMatch(t,
 | 
				
			||||||
 | 
							[]string{"SBOM", "FOO1", "FOO2", "BAR", "OUTPUT", "SECRET", "SSH_KEY"},
 | 
				
			||||||
 | 
							actual)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestHCLMultiFileAttrs(t *testing.T) {
 | 
					func TestHCLMultiFileAttrs(t *testing.T) {
 | 
				
			||||||
	dt := []byte(`
 | 
						dt := []byte(`
 | 
				
			||||||
		variable "FOO" {
 | 
							variable "FOO" {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
				
			|||||||
module github.com/docker/buildx
 | 
					module github.com/docker/buildx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
go 1.22.0
 | 
					go 1.23.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/Masterminds/semver/v3 v3.2.1
 | 
						github.com/Masterminds/semver/v3 v3.2.1
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ ARG XX_VERSION=1.6.1
 | 
				
			|||||||
ARG GOLANGCI_LINT_VERSION=1.62.0
 | 
					ARG GOLANGCI_LINT_VERSION=1.62.0
 | 
				
			||||||
ARG GOPLS_VERSION=v0.26.0
 | 
					ARG GOPLS_VERSION=v0.26.0
 | 
				
			||||||
# disabled: deprecated unusedvariable simplifyrange
 | 
					# disabled: deprecated unusedvariable simplifyrange
 | 
				
			||||||
ARG GOPLS_ANALYZERS="embeddirective fillreturns infertypeargs nonewvars norangeoverfunc noresultvalues simplifycompositelit simplifyslice undeclaredname unusedparams useany"
 | 
					ARG GOPLS_ANALYZERS="embeddirective fillreturns infertypeargs nonewvars noresultvalues simplifycompositelit simplifyslice undeclaredname unusedparams useany"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
 | 
					FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,9 +24,7 @@ func (e *Attests) FromCtyValue(in cty.Value, p cty.Path) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (e *Attests) fromCtyValue(in cty.Value, p cty.Path) error {
 | 
					func (e *Attests) fromCtyValue(in cty.Value, p cty.Path) error {
 | 
				
			||||||
	*e = make([]*Attest, 0, in.LengthInt())
 | 
						*e = make([]*Attest, 0, in.LengthInt())
 | 
				
			||||||
	for elem := in.ElementIterator(); elem.Next(); {
 | 
						for value := range eachElement(in) {
 | 
				
			||||||
		_, value := elem.Element()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		entry := &Attest{}
 | 
							entry := &Attest{}
 | 
				
			||||||
		if err := entry.FromCtyValue(value, p); err != nil {
 | 
							if err := entry.FromCtyValue(value, p); err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
@@ -64,6 +62,10 @@ func (e *Attest) FromCtyValue(in cty.Value, p cty.Path) error {
 | 
				
			|||||||
	e.Attrs = map[string]string{}
 | 
						e.Attrs = map[string]string{}
 | 
				
			||||||
	for it := conv.ElementIterator(); it.Next(); {
 | 
						for it := conv.ElementIterator(); it.Next(); {
 | 
				
			||||||
		k, v := it.Element()
 | 
							k, v := it.Element()
 | 
				
			||||||
 | 
							if !v.IsKnown() {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		switch key := k.AsString(); key {
 | 
							switch key := k.AsString(); key {
 | 
				
			||||||
		case "type":
 | 
							case "type":
 | 
				
			||||||
			e.Type = v.AsString()
 | 
								e.Type = v.AsString()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,13 +23,7 @@ func (o *CacheOptions) FromCtyValue(in cty.Value, p cty.Path) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (o *CacheOptions) fromCtyValue(in cty.Value, p cty.Path) error {
 | 
					func (o *CacheOptions) fromCtyValue(in cty.Value, p cty.Path) error {
 | 
				
			||||||
	*o = make([]*CacheOptionsEntry, 0, in.LengthInt())
 | 
						*o = make([]*CacheOptionsEntry, 0, in.LengthInt())
 | 
				
			||||||
	for elem := in.ElementIterator(); elem.Next(); {
 | 
						for value := range eachElement(in) {
 | 
				
			||||||
		_, value := elem.Element()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if isEmpty(value) {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Special handling for a string type to handle ref only format.
 | 
							// Special handling for a string type to handle ref only format.
 | 
				
			||||||
		if value.Type() == cty.String {
 | 
							if value.Type() == cty.String {
 | 
				
			||||||
			entries, err := ParseCacheEntry([]string{value.AsString()})
 | 
								entries, err := ParseCacheEntry([]string{value.AsString()})
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,13 +23,7 @@ func (e *Exports) FromCtyValue(in cty.Value, p cty.Path) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (e *Exports) fromCtyValue(in cty.Value, p cty.Path) error {
 | 
					func (e *Exports) fromCtyValue(in cty.Value, p cty.Path) error {
 | 
				
			||||||
	*e = make([]*ExportEntry, 0, in.LengthInt())
 | 
						*e = make([]*ExportEntry, 0, in.LengthInt())
 | 
				
			||||||
	for elem := in.ElementIterator(); elem.Next(); {
 | 
						for value := range eachElement(in) {
 | 
				
			||||||
		_, value := elem.Element()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if isEmpty(value) {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		entry := &ExportEntry{}
 | 
							entry := &ExportEntry{}
 | 
				
			||||||
		if err := entry.FromCtyValue(value, p); err != nil {
 | 
							if err := entry.FromCtyValue(value, p); err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,13 +30,7 @@ func (s *Secrets) FromCtyValue(in cty.Value, p cty.Path) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (s *Secrets) fromCtyValue(in cty.Value, p cty.Path) error {
 | 
					func (s *Secrets) fromCtyValue(in cty.Value, p cty.Path) error {
 | 
				
			||||||
	*s = make([]*Secret, 0, in.LengthInt())
 | 
						*s = make([]*Secret, 0, in.LengthInt())
 | 
				
			||||||
	for elem := in.ElementIterator(); elem.Next(); {
 | 
						for value := range eachElement(in) {
 | 
				
			||||||
		_, value := elem.Element()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if isEmpty(value) {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		entry := &Secret{}
 | 
							entry := &Secret{}
 | 
				
			||||||
		if err := entry.FromCtyValue(value, p); err != nil {
 | 
							if err := entry.FromCtyValue(value, p); err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
@@ -71,13 +65,13 @@ func (e *Secret) FromCtyValue(in cty.Value, p cty.Path) error {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if id := conv.GetAttr("id"); !id.IsNull() {
 | 
						if id := conv.GetAttr("id"); !id.IsNull() && id.IsKnown() {
 | 
				
			||||||
		e.ID = id.AsString()
 | 
							e.ID = id.AsString()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if src := conv.GetAttr("src"); !src.IsNull() {
 | 
						if src := conv.GetAttr("src"); !src.IsNull() && src.IsKnown() {
 | 
				
			||||||
		e.FilePath = src.AsString()
 | 
							e.FilePath = src.AsString()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if env := conv.GetAttr("env"); !env.IsNull() {
 | 
						if env := conv.GetAttr("env"); !env.IsNull() && env.IsKnown() {
 | 
				
			||||||
		e.Env = env.AsString()
 | 
							e.Env = env.AsString()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,13 +30,7 @@ func (s *SSHKeys) FromCtyValue(in cty.Value, p cty.Path) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (s *SSHKeys) fromCtyValue(in cty.Value, p cty.Path) error {
 | 
					func (s *SSHKeys) fromCtyValue(in cty.Value, p cty.Path) error {
 | 
				
			||||||
	*s = make([]*SSH, 0, in.LengthInt())
 | 
						*s = make([]*SSH, 0, in.LengthInt())
 | 
				
			||||||
	for elem := in.ElementIterator(); elem.Next(); {
 | 
						for value := range eachElement(in) {
 | 
				
			||||||
		_, value := elem.Element()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if isEmpty(value) {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		entry := &SSH{}
 | 
							entry := &SSH{}
 | 
				
			||||||
		if err := entry.FromCtyValue(value, p); err != nil {
 | 
							if err := entry.FromCtyValue(value, p); err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
@@ -71,10 +65,10 @@ func (e *SSH) FromCtyValue(in cty.Value, p cty.Path) error {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if id := conv.GetAttr("id"); !id.IsNull() {
 | 
						if id := conv.GetAttr("id"); !id.IsNull() && id.IsKnown() {
 | 
				
			||||||
		e.ID = id.AsString()
 | 
							e.ID = id.AsString()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if paths := conv.GetAttr("paths"); !paths.IsNull() {
 | 
						if paths := conv.GetAttr("paths"); !paths.IsNull() && paths.IsKnown() {
 | 
				
			||||||
		if err := gocty.FromCtyValue(paths, &e.Paths); err != nil {
 | 
							if err := gocty.FromCtyValue(paths, &e.Paths); err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,8 @@
 | 
				
			|||||||
package buildflags
 | 
					package buildflags
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"iter"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/zclconf/go-cty/cty"
 | 
						"github.com/zclconf/go-cty/cty"
 | 
				
			||||||
	"github.com/zclconf/go-cty/cty/gocty"
 | 
						"github.com/zclconf/go-cty/cty/gocty"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -34,7 +36,7 @@ func removeDupes[E comparable[E]](s []E) []E {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getAndDelete(m map[string]cty.Value, attr string, gv interface{}) error {
 | 
					func getAndDelete(m map[string]cty.Value, attr string, gv interface{}) error {
 | 
				
			||||||
	if v, ok := m[attr]; ok {
 | 
						if v, ok := m[attr]; ok && v.IsKnown() {
 | 
				
			||||||
		delete(m, attr)
 | 
							delete(m, attr)
 | 
				
			||||||
		return gocty.FromCtyValue(v, gv)
 | 
							return gocty.FromCtyValue(v, gv)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -44,11 +46,28 @@ func getAndDelete(m map[string]cty.Value, attr string, gv interface{}) error {
 | 
				
			|||||||
func asMap(m map[string]cty.Value) map[string]string {
 | 
					func asMap(m map[string]cty.Value) map[string]string {
 | 
				
			||||||
	out := make(map[string]string, len(m))
 | 
						out := make(map[string]string, len(m))
 | 
				
			||||||
	for k, v := range m {
 | 
						for k, v := range m {
 | 
				
			||||||
 | 
							if v.IsKnown() {
 | 
				
			||||||
			out[k] = v.AsString()
 | 
								out[k] = v.AsString()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return out
 | 
						return out
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func isEmpty(v cty.Value) bool {
 | 
					func isEmptyOrUnknown(v cty.Value) bool {
 | 
				
			||||||
	return v.Type() == cty.String && v.AsString() == ""
 | 
						return !v.IsKnown() || (v.Type() == cty.String && v.AsString() == "")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func eachElement(in cty.Value) iter.Seq[cty.Value] {
 | 
				
			||||||
 | 
						return func(yield func(v cty.Value) bool) {
 | 
				
			||||||
 | 
							for elem := in.ElementIterator(); elem.Next(); {
 | 
				
			||||||
 | 
								_, value := elem.Element()
 | 
				
			||||||
 | 
								if isEmptyOrUnknown(value) {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if !yield(value) {
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user