[package] Make jiri fail when multiple packages have same url and path

This change makes jiri fail if there are multiple packages with the same
CIPD package name and download path. Before this patch, jiri will pass
silently but downloaded revision is nondeterministic. This patch fixes
this issue.

Bug: 83445
Change-Id: Ic40d9aad9fd4ebd5188c403e6ef256af557e1bb8
Reviewed-on: https://fuchsia-review.googlesource.com/c/jiri/+/582792
Reviewed-by: Oliver Newman <olivernewman@google.com>
Commit-Queue: Haowei Wu <haowei@google.com>
diff --git a/project/loader.go b/project/loader.go
index 0b01607..86ee0ef 100644
--- a/project/loader.go
+++ b/project/loader.go
@@ -665,6 +665,11 @@
 		// Record manifest location.
 		pkg.ManifestPath = f
 		key := pkg.Key()
+		if val, ok := ld.Packages[key]; ok {
+			// Package with same remote url and local path already exists in manifest.
+			// Abort loading.
+			return fmt.Errorf("conflicting packages: %v conflicts %v when loading manifest %s", val, pkg, file)
+		}
 		ld.Packages[key] = pkg
 	}
 	return nil
diff --git a/project/project_test.go b/project/project_test.go
index bb439c4..16d1e04 100644
--- a/project/project_test.go
+++ b/project/project_test.go
@@ -2609,6 +2609,33 @@
 	assertExist(filepath.Join(fake.X.Root, pkg1.Path))
 }
 
+func TestPackageConflics(t *testing.T) {
+	fake, cleanup := jiritest.NewFakeJiriRoot(t)
+	defer cleanup()
+
+	pkg0 := project.Package{
+		Name:    "fuchsia/tools/jiri/${platform}",
+		Path:    "path-multi-pkg0",
+		Version: "git_revision:9904764ed228c7fb87bfb252762952b502d1e360",
+	}
+	pkg1 := project.Package{
+		Name:    "fuchsia/tools/jiri/${platform}",
+		Path:    "path-multi-pkg0",
+		Version: "git_revision:05715c8fbbdb952ab38e50533a1b653445e74b40",
+	}
+
+	fake.AddPackage(pkg0)
+	fake.AddPackage(pkg1)
+
+	if err := fake.UpdateUniverse(true); err != nil {
+		if !strings.Contains(err.Error(), "conflicting packages:") {
+			t.Errorf("expecting package conflicting error, but got %v", err)
+		}
+		return
+	}
+	t.Errorf("expect package conflicting error, but it didn't happen")
+}
+
 func TestOverrideImport(t *testing.T) {
 	_, fake, cleanup := setupUniverse(t)
 	defer cleanup()