diff --git a/bake/bake.go b/bake/bake.go index 8322edbb3ae..afba92794cb 100644 --- a/bake/bake.go +++ b/bake/bake.go @@ -1582,6 +1582,9 @@ type arrValue[B any] interface { func parseArrValue[T any, PT arrValue[T]](s []string) ([]*T, error) { outputs := make([]*T, 0, len(s)) for _, text := range s { + if text == "" { + continue + } output := new(T) if err := PT(output).UnmarshalText([]byte(text)); err != nil { return nil, err @@ -1594,7 +1597,9 @@ func parseArrValue[T any, PT arrValue[T]](s []string) ([]*T, error) { func parseCacheArrValues(s []string) ([]*buildflags.CacheOptionsEntry, error) { outs := make([]*buildflags.CacheOptionsEntry, 0, len(s)) for _, in := range s { - if !strings.Contains(in, "=") { + if in == "" { + continue + } else if !strings.Contains(in, "=") { // This is ref only format. Each field in the CSV is its own entry. fields, err := csvvalue.Fields(in, nil) if err != nil { diff --git a/bake/bake_test.go b/bake/bake_test.go index b61e2ecff3e..98715a5cd19 100644 --- a/bake/bake_test.go +++ b/bake/bake_test.go @@ -2019,6 +2019,26 @@ target "app" { }) } +// https://github.com/docker/buildx/pull/428 +// https://github.com/docker/buildx/issues/2822 +func TestEmptyAttribute(t *testing.T) { + fp := File{ + Name: "docker-bake.hcl", + Data: []byte(` +target "app" { + output = [""] +} +`), + } + + ctx := context.TODO() + + m, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil) + require.Equal(t, 1, len(m)) + require.Len(t, m["app"].Outputs, 0) + require.NoError(t, err) +} + func stringify[V fmt.Stringer](values []V) []string { s := make([]string, len(values)) for i, v := range values { diff --git a/util/buildflags/attests.go b/util/buildflags/attests.go index 600b755e0d9..44e9d93e7a3 100644 --- a/util/buildflags/attests.go +++ b/util/buildflags/attests.go @@ -21,9 +21,12 @@ func CanonicalizeAttest(attestType string, in string) string { } func ParseAttests(in []string) ([]*controllerapi.Attest, error) { - out := []*controllerapi.Attest{} + var out []*controllerapi.Attest found := map[string]struct{}{} for _, in := range in { + if in == "" { + continue + } in := in attest, err := ParseAttest(in) if err != nil { diff --git a/util/buildflags/cache.go b/util/buildflags/cache.go index fcd36ff5e04..f771d3293cf 100644 --- a/util/buildflags/cache.go +++ b/util/buildflags/cache.go @@ -140,8 +140,11 @@ func (e *CacheOptionsEntry) validate(gv interface{}) error { } func ParseCacheEntry(in []string) ([]*controllerapi.CacheOptionsEntry, error) { - outs := make([]*controllerapi.CacheOptionsEntry, 0, len(in)) + var outs []*controllerapi.CacheOptionsEntry for _, in := range in { + if in == "" { + continue + } var out CacheOptionsEntry if err := out.UnmarshalText([]byte(in)); err != nil { return nil, err diff --git a/util/buildflags/context.go b/util/buildflags/context.go index ac2e80742c1..3d8021872cd 100644 --- a/util/buildflags/context.go +++ b/util/buildflags/context.go @@ -13,6 +13,9 @@ func ParseContextNames(values []string) (map[string]string, error) { } result := make(map[string]string, len(values)) for _, value := range values { + if value == "" { + continue + } kv := strings.SplitN(value, "=", 2) if len(kv) != 2 { return nil, errors.Errorf("invalid context value: %s, expected key=value", value) diff --git a/util/buildflags/cty.go b/util/buildflags/cty.go index d8b6a19a03e..98c422d7821 100644 --- a/util/buildflags/cty.go +++ b/util/buildflags/cty.go @@ -179,5 +179,9 @@ func unmarshalTextFallback[V encoding.TextUnmarshaler](in cty.Value, v V, inErr // Conversion was successful. Use UnmarshalText on the string and return any // errors associated with that. - return v.UnmarshalText([]byte(conv.AsString())) + convstr := conv.AsString() + if convstr == "" { + return nil + } + return v.UnmarshalText([]byte(convstr)) } diff --git a/util/buildflags/entitlements.go b/util/buildflags/entitlements.go index 31f04304f29..f3c4ebe182d 100644 --- a/util/buildflags/entitlements.go +++ b/util/buildflags/entitlements.go @@ -5,6 +5,9 @@ import "github.com/moby/buildkit/util/entitlements" func ParseEntitlements(in []string) ([]entitlements.Entitlement, error) { out := make([]entitlements.Entitlement, 0, len(in)) for _, v := range in { + if v == "" { + continue + } e, err := entitlements.Parse(v) if err != nil { return nil, err diff --git a/util/buildflags/export.go b/util/buildflags/export.go index 90eaa743d7b..0f157f74169 100644 --- a/util/buildflags/export.go +++ b/util/buildflags/export.go @@ -136,6 +136,9 @@ func ParseExports(inp []string) ([]*controllerapi.ExportEntry, error) { return nil, nil } for _, s := range inp { + if s == "" { + continue + } var out ExportEntry if err := out.UnmarshalText([]byte(s)); err != nil { return nil, err @@ -153,6 +156,9 @@ func ParseAnnotations(inp []string) (map[exptypes.AnnotationKey]string, error) { annotations := make(map[exptypes.AnnotationKey]string) for _, inp := range inp { + if inp == "" { + continue + } k, v, ok := strings.Cut(inp, "=") if !ok { return nil, errors.Errorf("invalid annotation %q, expected key=value", inp) diff --git a/util/buildflags/secrets.go b/util/buildflags/secrets.go index d480e6860a9..23f3a781025 100644 --- a/util/buildflags/secrets.go +++ b/util/buildflags/secrets.go @@ -85,6 +85,9 @@ func (s *Secret) UnmarshalText(text []byte) error { func ParseSecretSpecs(sl []string) ([]*controllerapi.Secret, error) { fs := make([]*controllerapi.Secret, 0, len(sl)) for _, v := range sl { + if v == "" { + continue + } s, err := parseSecret(v) if err != nil { return nil, err diff --git a/util/buildflags/ssh.go b/util/buildflags/ssh.go index e7944db8eb3..d8f552b836a 100644 --- a/util/buildflags/ssh.go +++ b/util/buildflags/ssh.go @@ -62,6 +62,9 @@ func ParseSSHSpecs(sl []string) ([]*controllerapi.SSH, error) { } for _, s := range sl { + if s == "" { + continue + } var out SSH if err := out.UnmarshalText([]byte(s)); err != nil { return nil, err