diff --git a/sdk/buildstep/ask_test.go b/sdk/buildstep/ask_test.go index e75ef4a6..3b409b4e 100644 --- a/sdk/buildstep/ask_test.go +++ b/sdk/buildstep/ask_test.go @@ -215,12 +215,24 @@ func TestPauseCapacityAndComposerKey(t *testing.T) { t.Fatalf("%+v %v", c, err) } tc := ToolchainUsed{Language: "go", Version: "1.26.0", Recipe: "1"} - if k := ComposerKey("novox/r", testCommit, "d", "p", tc); k != ComposerKey("novox/r", testCommit, "d", "p", tc) || !strings.HasPrefix(k, "composer-") || len(k) != len("composer-")+16 { + if k := ComposerKey("novox/r", testCommit, "d", "p", tc, nil); k != ComposerKey("novox/r", testCommit, "d", "p", tc, nil) || !strings.HasPrefix(k, "composer-") || len(k) != len("composer-")+16 { t.Errorf("composer key %q", k) } - if ComposerKey("novox/r", testCommit, "d", "p", tc) == ComposerKey("novox/r", testCommit, "d2", "p", tc) { + if ComposerKey("novox/r", testCommit, "d", "p", tc, nil) == ComposerKey("novox/r", testCommit, "d2", "p", tc, nil) { t.Error("the directory is not in the key") } + // 1921: the asked versions are in the key, in any order of naming; none gives the key without them. + a, b := strings.Repeat("a", 40), strings.Repeat("b", 40) + k := ComposerKey("novox/r", testCommit, "d", "p", tc, PackageVersions{"m/x": a, "m/y": b}) + if k != ComposerKey("novox/r", testCommit, "d", "p", tc, PackageVersions{"m/y": b, "m/x": a}) { + t.Error("the key depends on the order of the versions") + } + if k == ComposerKey("novox/r", testCommit, "d", "p", tc, PackageVersions{"m/x": b, "m/y": a}) || k == ComposerKey("novox/r", testCommit, "d", "p", tc, nil) { + t.Error("other versions, or none, share the key") + } + if ComposerKey("novox/r", testCommit, "d", "p", tc, nil) != ComposerKey("novox/r", testCommit, "d", "p", tc, PackageVersions{}) { + t.Error("an empty ask changed the key") + } } // The part `build-step` is one layout for the three build seats: each seat's fixtures read strictly in the neutral diff --git a/sdk/buildstep/pause.go b/sdk/buildstep/pause.go index 9ce027b9..d9da0b87 100644 --- a/sdk/buildstep/pause.go +++ b/sdk/buildstep/pause.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "sort" "strings" "time" ) @@ -100,8 +101,18 @@ func decodeVersioned(data []byte, into any, version func() int) error { } // ComposerKey is the key a composer build is kept under: the fetched commit, the directory, the package and the -// toolchain it ran with. -func ComposerKey(repository, commit, dir, pkg string, toolchain ToolchainUsed) string { - sum := sha256.Sum256([]byte(strings.Join([]string{repository, commit, dir, pkg, toolchain.Version, toolchain.Recipe}, "\n"))) +// toolchain it ran with. The mesh package versions the ask names are an input of the build, so they are in the key, +// sorted, when there are any; an ask that names none gets the key it always had (issue 1921). +func ComposerKey(repository, commit, dir, pkg string, toolchain ToolchainUsed, versions PackageVersions) string { + parts := []string{repository, commit, dir, pkg, toolchain.Version, toolchain.Recipe} + pkgs := make([]string, 0, len(versions)) + for p := range versions { + pkgs = append(pkgs, p) + } + sort.Strings(pkgs) + for _, p := range pkgs { + parts = append(parts, p+"="+versions[p]) + } + sum := sha256.Sum256([]byte(strings.Join(parts, "\n"))) return "composer-" + hex.EncodeToString(sum[:8]) }