diff --git a/bake/bake.go b/bake/bake.go index a319c59ee62e..b39c0ac93db9 100644 --- a/bake/bake.go +++ b/bake/bake.go @@ -32,6 +32,7 @@ import ( "github.com/moby/buildkit/client/llb" "github.com/moby/buildkit/frontend/dockerfile/dfgitutil" "github.com/pkg/errors" + "github.com/sirupsen/logrus" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/convert" ) @@ -1281,7 +1282,25 @@ func (t *Target) GetName(ectx *hcl.EvalContext, block *hcl.Block, loadDeps func( return value.AsString(), nil } +func warnDuplicateCacheExports(m map[string]*Target) { + seen := map[string][]string{} + for _, name := range slices.Sorted(maps.Keys(m)) { + t := m[name] + for _, entry := range t.CacheTo { + key := entry.String() + seen[key] = append(seen[key], name) + } + } + for _, key := range slices.Sorted(maps.Keys(seen)) { + names := seen[key] + if len(names) > 1 { + logrus.Warnf("multiple targets write to the same cache export %q: %s", key, strings.Join(names, ", ")) + } + } +} + func TargetsToBuildOpt(m map[string]*Target, inp *Input) (map[string]build.Options, error) { + warnDuplicateCacheExports(m) m2 := make(map[string]build.Options, len(m)) for k, v := range m { bo, err := toBuildOpt(v, inp) diff --git a/bake/bake_test.go b/bake/bake_test.go index 466c411d8a04..4eb7c96416bf 100644 --- a/bake/bake_test.go +++ b/bake/bake_test.go @@ -11,6 +11,7 @@ import ( "github.com/docker/buildx/util/buildflags" "github.com/moby/buildkit/util/entitlements" + "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -2491,3 +2492,46 @@ func stringify[V fmt.Stringer](values []V) []string { sort.Strings(s) return s } + +func TestWarnDuplicateCacheExports(t *testing.T) { + var buf strings.Builder + logrus.SetOutput(&buf) + logrus.SetLevel(logrus.WarnLevel) + t.Cleanup(func() { logrus.SetOutput(os.Stderr) }) + + t.Run("duplicate cache-to", func(t *testing.T) { + buf.Reset() + m := map[string]*Target{ + "app": { + CacheTo: buildflags.CacheOptions{ + {Type: "registry", Attrs: map[string]string{"ref": "registry.example.com/cache/shared"}}, + }, + }, + "worker": { + CacheTo: buildflags.CacheOptions{ + {Type: "registry", Attrs: map[string]string{"ref": "registry.example.com/cache/shared"}}, + }, + }, + } + warnDuplicateCacheExports(m) + require.Contains(t, buf.String(), "registry.example.com/cache/shared") + }) + + t.Run("no duplicate", func(t *testing.T) { + buf.Reset() + m := map[string]*Target{ + "app": { + CacheTo: buildflags.CacheOptions{ + {Type: "registry", Attrs: map[string]string{"ref": "registry.example.com/cache/app"}}, + }, + }, + "worker": { + CacheTo: buildflags.CacheOptions{ + {Type: "registry", Attrs: map[string]string{"ref": "registry.example.com/cache/worker"}}, + }, + }, + } + warnDuplicateCacheExports(m) + require.Empty(t, buf.String()) + }) +}