248 lines
9.5 KiB
Go
248 lines
9.5 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io/fs"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestMultiModuleSelectorMapsSelectorsAndTags(t *testing.T) {
|
|
example := filepath.Join(cliRepositoryRoot(t), "examples", "multi-module")
|
|
helper := filepath.Join(example, "select-module.sh")
|
|
for _, test := range []struct {
|
|
mode string
|
|
value string
|
|
want string
|
|
}{
|
|
{mode: "selector", value: "root", want: "root\tv[01].*\t.\treleasekit-root.json"},
|
|
{mode: "selector", value: "ckconfig", want: "ckconfig\tintegrations/ckconfig/v*\tintegrations/ckconfig\treleasekit-ckconfig.json"},
|
|
{mode: "tag", value: "integrations/ckconfig/v1.2.3", want: "ckconfig\tintegrations/ckconfig/v*\tintegrations/ckconfig\treleasekit-ckconfig.json"},
|
|
{mode: "tag", value: "integrations/core/v2.3.4", want: "core-v2\tintegrations/core/v2.*\tintegrations/core/v2\treleasekit-core-v2.json"},
|
|
{mode: "tag", value: "v1.2.3", want: "root\tv[01].*\t.\treleasekit-root.json"},
|
|
} {
|
|
t.Run(test.mode+" "+test.value, func(t *testing.T) {
|
|
got := runModuleSelector(t, helper, test.mode, test.value, "")
|
|
if strings.TrimSpace(got) != test.want {
|
|
t.Fatalf("selector output = %q, want %q", got, test.want)
|
|
}
|
|
})
|
|
}
|
|
|
|
mapPath := filepath.Join(t.TempDir(), "release modules.tsv")
|
|
if err := os.WriteFile(mapPath, []byte("one\tfeature/*\tmodules/one\tprofiles/one.json\ntwo\tfeature/v1.*\tmodules/two\tprofiles/two.json\n"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if output := runModuleSelectorError(t, helper, "selector", "absent", mapPath); !strings.Contains(output, "no module map row") {
|
|
t.Fatalf("unknown selector error = %q", output)
|
|
}
|
|
if output := runModuleSelectorError(t, helper, "tag", "feature/v1.2.3", mapPath); !strings.Contains(output, "ambiguous") {
|
|
t.Fatalf("ambiguous event error = %q", output)
|
|
}
|
|
|
|
if err := os.WriteFile(mapPath, []byte("space\tfeature/*\tintegrations/ck config\tprofiles/core release.json\n"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got := runModuleSelector(t, helper, "tag", "feature/v1.2.3", mapPath)
|
|
want := "space\tfeature/*\tintegrations/ck config\tprofiles/core release.json"
|
|
if strings.TrimSpace(got) != want {
|
|
t.Fatalf("paths with spaces = %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestMultiModuleLauncherUsesSelectedModuleAndProfile(t *testing.T) {
|
|
example := filepath.Join(cliRepositoryRoot(t), "examples", "multi-module")
|
|
repository := newCLIRepository(t)
|
|
copyExampleTree(t, example, repository)
|
|
|
|
rootModule := "module example.com/multi-module-consumer\n\ngo 1.25.0\n\nrequire gitea.maximumdirect.net/eric/ReleaseKit v0.2.197\n"
|
|
selectedModule := "module example.com/multi-module-consumer/integrations/ckconfig\n\ngo 1.25.0\n\nrequire gitea.maximumdirect.net/eric/ReleaseKit v0.2.198\n"
|
|
writeRepositoryFile(t, repository, "go.mod", rootModule)
|
|
writeRepositoryFile(t, repository, "integrations/ckconfig/go.mod", selectedModule)
|
|
spaceModule := "module example.com/multi-module-consumer/integrations/ckconfig\n\ngo 1.25.0\n\nrequire gitea.maximumdirect.net/eric/ReleaseKit v0.2.299\n"
|
|
writeRepositoryFile(t, repository, "integrations/ck config/go.mod", spaceModule)
|
|
writeRepositoryFile(t, repository, "profiles/core release.json", `{"product":"Example Consumer Integration","module_dir":"integrations/ck config"}`)
|
|
cliGitRun(t, repository, "add", ".")
|
|
cliGitRun(t, repository, "commit", "-m", "prepare multi-module launcher fixture")
|
|
|
|
mapPath := filepath.Join(t.TempDir(), "release modules.tsv")
|
|
if err := os.WriteFile(mapPath, []byte("with space\tfeature/*\tintegrations/ck config\tprofiles/core release.json\n"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
shimDir := t.TempDir()
|
|
goLog := filepath.Join(t.TempDir(), "go commands")
|
|
argsLog := filepath.Join(t.TempDir(), "tool arguments")
|
|
shim := `#!/bin/sh
|
|
set -eu
|
|
printf 'cwd=%s args=%s\n' "$PWD" "$*" >> "$RELEASEKIT_TEST_GO_LOG"
|
|
modfile=
|
|
output=
|
|
previous=
|
|
for argument in "$@"; do
|
|
case "$argument" in -modfile=*) modfile=${argument#-modfile=} ;; esac
|
|
if [ "$previous" = -o ]; then output=$argument; fi
|
|
previous=$argument
|
|
done
|
|
if [ -n "$modfile" ]; then cat "$modfile" >> "$RELEASEKIT_TEST_GO_LOG"; fi
|
|
if [ "$1" = build ]; then
|
|
cat > "$output" <<'TOOL'
|
|
#!/bin/sh
|
|
printf '<%s>\n' "$@" > "$RELEASEKIT_TEST_TOOL_ARGS"
|
|
TOOL
|
|
chmod +x "$output"
|
|
fi
|
|
`
|
|
if err := os.WriteFile(filepath.Join(shimDir, "go"), []byte(shim), 0o700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Setenv("PATH", shimDir+string(os.PathListSeparator)+os.Getenv("PATH"))
|
|
t.Setenv("RELEASEKIT_MODULE_MAP", mapPath)
|
|
t.Setenv("RELEASEKIT_TEST_GO_LOG", goLog)
|
|
t.Setenv("RELEASEKIT_TEST_TOOL_ARGS", argsLog)
|
|
launcher := filepath.Join(repository, "release.sh")
|
|
command := exec.Command(launcher, "with space", "v1.2.4")
|
|
var stdout, stderr bytes.Buffer
|
|
command.Stdout = &stdout
|
|
command.Stderr = &stderr
|
|
if err := command.Run(); err != nil {
|
|
t.Fatalf("launcher error: %v stdout=%q stderr=%q", err, stdout.String(), stderr.String())
|
|
}
|
|
|
|
goCommands, err := os.ReadFile(goLog)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
wantModule := filepath.Join(repository, "integrations", "ck config")
|
|
if !strings.Contains(string(goCommands), "cwd="+wantModule) || !strings.Contains(string(goCommands), "v0.2.299") || strings.Contains(string(goCommands), "v0.2.197") {
|
|
t.Fatalf("tool bootstrap did not use selected module metadata: %s", goCommands)
|
|
}
|
|
toolArgs, err := os.ReadFile(argsLog)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, expected := range []string{"<publish>", "<--repo>", "<" + repository + ">", "<--config>", "<profiles/core release.json>", "<--expected-module>", "<integrations/ck config>", "<v1.2.4>"} {
|
|
if !strings.Contains(string(toolArgs), expected) {
|
|
t.Errorf("publication arguments %q omit %q", toolArgs, expected)
|
|
}
|
|
}
|
|
if strings.Contains(string(goCommands), repository+"/.releasekit-bootstrap.") {
|
|
t.Fatalf("temporary tool metadata was placed inside the repository: %s", goCommands)
|
|
}
|
|
}
|
|
|
|
func TestMultiModuleLauncherBuildsToolFromSelectedDependency(t *testing.T) {
|
|
example := filepath.Join(cliRepositoryRoot(t), "examples", "multi-module")
|
|
repository := newCLIRepository(t)
|
|
copyExampleTree(t, example, repository)
|
|
writeRepositoryFile(t, repository, "go.mod", "module example.com/multi-module-consumer\n\ngo 1.25.0\n\nrequire gitea.maximumdirect.net/eric/ReleaseKit v0.2.197\n")
|
|
writeRepositoryFile(t, repository, "integrations/ckconfig/go.mod", "module example.com/multi-module-consumer/integrations/ckconfig\n\ngo 1.25.0\n\nrequire gitea.maximumdirect.net/eric/ReleaseKit v0.2.198\n")
|
|
cliGitRun(t, repository, "add", ".")
|
|
cliGitRun(t, repository, "commit", "-m", "prepare selected tool module")
|
|
|
|
proxy := writeReleaseKitModuleProxy(t)
|
|
moduleCache := t.TempDir()
|
|
t.Setenv("GOMODCACHE", moduleCache)
|
|
t.Cleanup(func() {
|
|
_ = filepath.WalkDir(moduleCache, func(path string, entry fs.DirEntry, err error) error {
|
|
if err == nil && entry.IsDir() {
|
|
_ = os.Chmod(path, 0o700)
|
|
}
|
|
return nil
|
|
})
|
|
})
|
|
t.Setenv("GOPROXY", "file://"+filepath.ToSlash(proxy))
|
|
t.Setenv("GOSUMDB", "off")
|
|
t.Setenv("GOTOOLCHAIN", "local")
|
|
moduleBefore, err := os.ReadFile(filepath.Join(repository, "integrations", "ckconfig", "go.mod"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
command := exec.Command(filepath.Join(repository, "release.sh"), "ckconfig", "v1.2.3")
|
|
output, err := command.CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("launcher error: %v output=%q", err, output)
|
|
}
|
|
for _, expected := range []string{
|
|
"tool-version=v0.2.198 helper=tool-only dependency",
|
|
`"publish"`, `"--repo"`, `"` + repository + `"`,
|
|
`"--config"`, `"releasekit-ckconfig.json"`,
|
|
`"--expected-module"`, `"integrations/ckconfig"`, `"v1.2.3"`,
|
|
} {
|
|
if !strings.Contains(string(output), expected) {
|
|
t.Errorf("launcher output %q omits %q", output, expected)
|
|
}
|
|
}
|
|
if strings.Contains(string(output), "tool-version=v0.2.197") {
|
|
t.Fatalf("launcher built the root-selected ReleaseKit version: %s", output)
|
|
}
|
|
moduleAfter, err := os.ReadFile(filepath.Join(repository, "integrations", "ckconfig", "go.mod"))
|
|
if err != nil || !bytes.Equal(moduleAfter, moduleBefore) {
|
|
t.Fatalf("selected module go.mod changed: got %q want %q error=%v", moduleAfter, moduleBefore, err)
|
|
}
|
|
}
|
|
|
|
func runModuleSelector(t *testing.T, helper, mode, value, mapPath string) string {
|
|
t.Helper()
|
|
command := exec.Command("sh", helper, mode, value)
|
|
command.Env = os.Environ()
|
|
if mapPath != "" {
|
|
command.Env = append(command.Env, "RELEASEKIT_MODULE_MAP="+mapPath)
|
|
}
|
|
output, err := command.CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("selector %s %q: %v: %s", mode, value, err, output)
|
|
}
|
|
return string(output)
|
|
}
|
|
|
|
func runModuleSelectorError(t *testing.T, helper, mode, value, mapPath string) string {
|
|
t.Helper()
|
|
command := exec.Command("sh", helper, mode, value)
|
|
command.Env = append(os.Environ(), "RELEASEKIT_MODULE_MAP="+mapPath)
|
|
output, err := command.CombinedOutput()
|
|
if err == nil {
|
|
t.Fatalf("selector %s %q unexpectedly succeeded: %s", mode, value, output)
|
|
}
|
|
return string(output)
|
|
}
|
|
|
|
func copyExampleTree(t *testing.T, source, destination string) {
|
|
t.Helper()
|
|
err := filepath.WalkDir(source, func(path string, entry fs.DirEntry, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
relative, err := filepath.Rel(source, path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if relative == "." {
|
|
return nil
|
|
}
|
|
target := filepath.Join(destination, relative)
|
|
if entry.IsDir() {
|
|
return os.MkdirAll(target, 0o700)
|
|
}
|
|
contents, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
info, err := entry.Info()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
mode := info.Mode().Perm()
|
|
if err := os.WriteFile(target, contents, mode); err != nil {
|
|
return err
|
|
}
|
|
return os.Chmod(target, mode)
|
|
})
|
|
if err != nil {
|
|
t.Fatal(fmt.Errorf("copy multi-module example: %w", err))
|
|
}
|
|
}
|