From d85f49d8dce151577b410248db6861b4655d6600 Mon Sep 17 00:00:00 2001
From: Jon Johnson <jon.johnson@chainguard.dev>
Date: Tue, 28 May 2024 15:57:43 -0700
Subject: [PATCH] Disallow duplicate subpackage names

Signed-off-by: Jon Johnson <jon.johnson@chainguard.dev>
---
 pkg/config/config.go      |  6 ++++++
 pkg/config/config_test.go | 30 ++++++++++++++++++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/pkg/config/config.go b/pkg/config/config.go
index 5bda3910a..5b0e8cff8 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -1042,7 +1042,13 @@ func (cfg Configuration) validate() error {
 		return ErrInvalidConfiguration{Problem: err}
 	}
 
+	saw := map[string]int{}
 	for i, sp := range cfg.Subpackages {
+		if extant, ok := saw[sp.Name]; ok {
+			return fmt.Errorf("saw duplicate subpackage name %q (subpackages index: %d and %d)", sp.Name, extant, i)
+		}
+		saw[sp.Name] = i
+
 		if !packageNameRegex.MatchString(sp.Name) {
 			return ErrInvalidConfiguration{Problem: fmt.Errorf("subpackage name %q (subpackages index: %d) must match regex %q", sp.Name, i, packageNameRegex)}
 		}
diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go
index 3decdec5d..2c3ba166c 100644
--- a/pkg/config/config_test.go
+++ b/pkg/config/config_test.go
@@ -299,3 +299,33 @@ pipeline:
 	require.Equal(t, "/home/build/baz", cfg.Pipeline[1].Pipeline[0].Pipeline[1].WorkDir)
 	require.Equal(t, "/home/build/baz", cfg.Pipeline[1].Pipeline[0].Pipeline[2].WorkDir)
 }
+
+func TestDuplicateSubpackage(t *testing.T) {
+	ctx := slogtest.TestContextWithLogger(t)
+
+	fp := filepath.Join(os.TempDir(), "melange-test-applySubstitutionsInProvides")
+	if err := os.WriteFile(fp, []byte(`
+package:
+  name: dupe-subpackage
+  version: 0.0.1
+  epoch: 8
+  description: example using a two subpackages with same name
+
+data:
+  - name: I-am-a-range
+    items:
+      a: ""
+      b: ""
+
+subpackages:
+  - name: subpackage
+    range: I-am-a-range
+    pipeline:
+      - runs: echo "I am a subpackage for ${{range.key}"
+`), 0644); err != nil {
+		t.Fatal(err)
+	}
+	if _, err := ParseConfiguration(ctx, fp); err == nil {
+		t.Errorf("configuration should have failed to validate, got: %v", err)
+	}
+}