From 636547436a9f585d5e80fa364e57268a6795e9a6 Mon Sep 17 00:00:00 2001 From: zoncoen Date: Tue, 28 May 2024 12:40:05 +0900 Subject: [PATCH] feat(plugin): always update go directive to the go version used to build scenarigo --- CREDITS | 25 - cmd/scenarigo/cmd/plugin/build.go | 146 +++-- cmd/scenarigo/cmd/plugin/build_test.go | 847 +++++++++++++------------ cmd/scenarigo/cmd/version.go | 3 +- cmd/scenarigo/cmd/version_test.go | 3 +- go.mod | 3 +- go.sum | 2 - 7 files changed, 549 insertions(+), 480 deletions(-) diff --git a/CREDITS b/CREDITS index 6771fb07..a5d51c32 100644 --- a/CREDITS +++ b/CREDITS @@ -240,31 +240,6 @@ THE SOFTWARE. ================================================================ -github.com/Masterminds/semver -https://github.com/Masterminds/semver ----------------------------------------------------------------- -Copyright (C) 2014-2019, Matt Butcher and Matt Farina - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -================================================================ - github.com/cenkalti/backoff/v4 https://github.com/cenkalti/backoff/v4 ---------------------------------------------------------------- diff --git a/cmd/scenarigo/cmd/plugin/build.go b/cmd/scenarigo/cmd/plugin/build.go index 3e9c7be0..3a383432 100644 --- a/cmd/scenarigo/cmd/plugin/build.go +++ b/cmd/scenarigo/cmd/plugin/build.go @@ -10,6 +10,7 @@ import ( "os" "os/exec" "path/filepath" + "regexp" "runtime" "sort" "strings" @@ -26,11 +27,15 @@ import ( "github.com/zoncoen/scenarigo/version" ) +const ( + versionTooHighErrorPattern = `^go: go.mod requires go >= ([\d\.]+) .+$` +) + var ( - goVer string - tip bool - goMajorMinor string - gomodVer string + goVer string + tip bool + goMinVer string + versionTooHighErrorRegexp *regexp.Regexp ) func init() { @@ -44,12 +49,8 @@ func init() { if len(e) < 2 { panic(fmt.Sprintf("%q is invalid Go version", goVer)) } - goMajorMinor = strings.Join(e[:2], ".") - gomod, err := modfile.Parse("go.mod", scenarigo.GoModBytes, nil) - if err != nil { - panic(fmt.Errorf("failed to parse go.mod of scenarigo: %w", err)) - } - gomodVer = gomod.Go.Version + goMinVer = "1.21.2" + versionTooHighErrorRegexp = regexp.MustCompile(versionTooHighErrorPattern) } var buildCmd = &cobra.Command{ @@ -98,7 +99,7 @@ func buildRun(cmd *cobra.Command, args []string) error { return errors.New("config file not found") } - goCmd, err := findGoCmd(ctx(cmd), tip) + goCmd, err := findGoCmd(ctx(cmd)) if err != nil { return err } @@ -170,26 +171,15 @@ func buildRun(cmd *cobra.Command, args []string) error { return nil } -func findGoCmd(ctx context.Context, tip bool) (string, error) { +func findGoCmd(ctx context.Context) (string, error) { goCmd, err := exec.LookPath("go") - var verr error - if err == nil { - verr = checkGoVersion(ctx, goCmd, gomodVer) - if verr == nil { - return goCmd, nil - } - } - if tip { - if goCmd, err := exec.LookPath("gotip"); err == nil { - if err := checkGoVersion(ctx, goCmd, goVer); err == nil { - return goCmd, nil - } - } + if err != nil { + return "", fmt.Errorf("go command required: %w", err) } - if err == nil { - return "", verr + if err := checkGoVersion(ctx, goCmd, goMinVer); err != nil { + return "", fmt.Errorf("failed to check go version: %w", err) } - return "", fmt.Errorf("go command required: %w", err) + return goCmd, nil } func selectUnifiedVersions(pbs []*pluginBuilder) (map[string]*overrideModule, error) { @@ -205,7 +195,7 @@ func selectUnifiedVersions(pbs []*pluginBuilder) (map[string]*overrideModule, er } continue } - if semver.Compare(o.require.Mod.Version, r.Mod.Version) < 0 { + if compareVers(o.require.Mod.Version, r.Mod.Version) < 0 { overrides[r.Mod.Path].require = r overrides[r.Mod.Path].requiredBy = pb.name } @@ -272,7 +262,7 @@ func checkGoVersion(ctx context.Context, goCmd, minVer string) error { } } ver := strings.TrimPrefix(items[2], "go") - if semver.Compare("v"+ver, "v"+minVer) == -1 { + if compareVers(ver, minVer) == -1 { return fmt.Errorf(`required go %s or later but installed %s`, minVer, ver) } return nil @@ -288,10 +278,10 @@ func downloadModule(ctx context.Context, goCmd, p string) (string, string, *modf os.RemoveAll(tempDir) } - if err := execute(ctx, tempDir, goCmd, "mod", "init", "download_module"); err != nil { + if _, err := execute(ctx, tempDir, goCmd, "mod", "init", "download_module"); err != nil { return "", "", nil, clean, fmt.Errorf("failed to initialize go.mod: %w", err) } - if err := execute(ctx, tempDir, goCmd, downloadCmd(p)...); err != nil { + if _, err := execute(ctx, tempDir, goCmd, downloadCmd(p)...); err != nil { return "", "", nil, clean, fmt.Errorf("failed to download %s: %w", p, err) } mod, src, req, err := modSrcPath(tempDir, p) @@ -383,10 +373,10 @@ func newPluginBuilder(cmd *cobra.Command, goCmd, name, mod, src, out, defaultMod gomodPath := filepath.Join(dir, "go.mod") if _, err := os.Stat(gomodPath); err != nil { - if err := execute(ctx, dir, goCmd, "mod", "init"); err != nil { + if _, err := execute(ctx, dir, goCmd, "mod", "init"); err != nil { // ref. https://github.com/golang/go/wiki/Modules#why-does-go-mod-init-give-the-error-cannot-determine-module-path-for-source-directory if strings.Contains(err.Error(), "cannot determine module path") { - if err := execute(ctx, dir, goCmd, "mod", "init", defaultModName); err != nil { + if _, err := execute(ctx, dir, goCmd, "mod", "init", defaultModName); err != nil { return nil, fmt.Errorf("failed to initialize go.mod: %w", err) } } else { @@ -396,6 +386,9 @@ func newPluginBuilder(cmd *cobra.Command, goCmd, name, mod, src, out, defaultMod } if err := modTidy(ctx, dir, goCmd); err != nil { + if ok, verr := asVersionTooHighError(err); ok { + err = fmt.Errorf("re-install scenarigo command with go%s: %w", verr.requiredVersion, err) + } return nil, err } @@ -420,12 +413,12 @@ func newPluginBuilder(cmd *cobra.Command, goCmd, name, mod, src, out, defaultMod func modTidy(ctx context.Context, dir, goCmd string) error { if cmd := os.Getenv("GO_MOD_TIDY"); cmd != "" { - if err := execute(ctx, dir, goCmd, strings.Split(cmd, " ")...); err != nil { + if _, err := execute(ctx, dir, goCmd, strings.Split(cmd, " ")...); err != nil { return err } return nil } - if err := execute(ctx, dir, goCmd, "mod", "tidy", fmt.Sprintf("-compat=%s", goMajorMinor)); err != nil { + if _, err := execute(ctx, dir, goCmd, "mod", "tidy"); err != nil { return fmt.Errorf(`"go mod tidy" failed: %w`, err) } return nil @@ -439,17 +432,18 @@ func (pb *pluginBuilder) build(cmd *cobra.Command, goCmd string, overrideKeys [] if err := os.RemoveAll(pb.out); err != nil { return fmt.Errorf("failed to delete the old plugin %s: %w", pb.out, err) } - if err := execute(ctx, pb.dir, goCmd, "build", "-buildmode=plugin", "-o", pb.out, pb.src); err != nil { + if _, err := execute(ctx, pb.dir, goCmd, "build", "-buildmode=plugin", "-o", pb.out, pb.src); err != nil { return fmt.Errorf(`"go build -buildmode=plugin -o %s %s" failed: %w`, pb.out, pb.src, err) } return nil } -func execute(ctx context.Context, wd, name string, args ...string) error { +func execute(ctx context.Context, wd, name string, args ...string) (string, error) { return executeWithEnvs(ctx, nil, wd, name, args...) } -func executeWithEnvs(ctx context.Context, envs []string, wd, name string, args ...string) error { +func executeWithEnvs(ctx context.Context, envs []string, wd, name string, args ...string) (string, error) { + var stdout bytes.Buffer var stderr bytes.Buffer cmd := exec.CommandContext(ctx, name, args...) if tip { @@ -461,15 +455,24 @@ func executeWithEnvs(ctx context.Context, envs []string, wd, name string, args . if wd != "" { cmd.Dir = wd } + cmd.Stdout = &stdout cmd.Stderr = &stderr if err := cmd.Run(); err != nil { - return errors.New(strings.TrimSuffix(stderr.String(), "\n")) + return "", wrapVersionTooHighError(errors.New(strings.TrimSuffix(stderr.String(), "\n"))) } - return nil + return stdout.String(), nil } func updateGoMod(cmd *cobra.Command, goCmd, name, gomodPath string, overrideKeys []string, overrides map[string]*overrideModule) error { if err := editGoMod(cmd, goCmd, gomodPath, func(gomod *modfile.File) error { + switch compareVers(gomod.Go.Version, goVer) { + case -1: // go.mod < scenarigo go version + if err := gomod.AddGoStmt(strings.TrimPrefix(goVer, "go")); err != nil { + return fmt.Errorf("%s: %w", gomodPath, err) + } + case 1: // go.mod > scenarigo go version + return fmt.Errorf("%s: go: go.mod requires go >= %s (running go %s)", gomodPath, gomod.Go.Version, goVer) + } gomod.DropToolchainStmt() return nil }); err != nil { @@ -499,11 +502,6 @@ func updateRequireDirectives(cmd *cobra.Command, goCmd, gomodPath string, overri for _, r := range gomod.Replace { initialReplaces[r.Old.Path] = *r } - if semver.Compare(gomod.Go.Version, gomodVer) < 0 { - if err := gomod.AddGoStmt(gomodVer); err != nil { - return fmt.Errorf("%s: %w", gomodPath, err) - } - } for _, o := range overrides { require, _, _, _ := o.requireReplace() if err := gomod.AddRequire(require.Mod.Path, require.Mod.Version); err != nil { @@ -701,7 +699,7 @@ func printUpdatedReplaces(cmd *cobra.Command, name string, overrides map[string] func editGoMod(cmd *cobra.Command, goCmd, gomodPath string, edit func(*modfile.File) error) error { gomod, err := parseGoMod(cmd, goCmd, gomodPath) if err != nil { - return err + return fmt.Errorf("failed to parse %s: %w", gomodPath, err) } if err := edit(gomod); err != nil { @@ -755,3 +753,57 @@ func requiredModulesByScenarigo() ([]*modfile.Require, error) { } return gomod.Require, nil } + +func compareVers(v, w string) int { + v = strings.TrimPrefix(v, "go") + w = strings.TrimPrefix(w, "go") + if !strings.HasPrefix(v, "v") { + v = "v" + v + } + if !strings.HasPrefix(w, "v") { + w = "v" + w + } + return semver.Compare(v, w) +} + +type versionTooHighError struct { + err error + requiredVersion string +} + +func (e *versionTooHighError) Error() string { + return e.err.Error() +} + +func (e *versionTooHighError) Unwrap() error { + return e.err +} + +func wrapVersionTooHighError(err error) error { + if err == nil { + return nil + } + if strings.HasPrefix(err.Error(), "go: go.mod requires go >= ") { + var requiredVersion string + result := versionTooHighErrorRegexp.FindAllStringSubmatch(err.Error(), 1) + if len(result) > 0 && len(result[0]) > 1 { + requiredVersion = result[0][1] + } + err = &versionTooHighError{ + err: err, + requiredVersion: requiredVersion, + } + } + return err +} + +func asVersionTooHighError(err error) (bool, *versionTooHighError) { + if err == nil { + return false, nil + } + var e *versionTooHighError + if errors.As(err, &e) { + return true, e + } + return false, nil +} diff --git a/cmd/scenarigo/cmd/plugin/build_test.go b/cmd/scenarigo/cmd/plugin/build_test.go index b54ef30b..eff74d41 100644 --- a/cmd/scenarigo/cmd/plugin/build_test.go +++ b/cmd/scenarigo/cmd/plugin/build_test.go @@ -19,7 +19,6 @@ import ( "text/template" "time" - "github.com/Masterminds/semver" "github.com/sergi/go-diff/diffmatchpatch" "github.com/sosedoff/gitkit" "github.com/spf13/cobra" @@ -49,22 +48,6 @@ func init() { func TestBuild(t *testing.T) { goVersion := strings.TrimPrefix(goVer, "go") - - // Old Go versions need to trim patch versions. - v, err := semver.NewVersion(goVersion) - if err != nil { - t.Fatal(err) - } - go121, err := semver.NewVersion("1.21.0") - if err != nil { - t.Fatal(err) - } - if v.LessThan(go121) { - if parts := strings.Split(goVersion, "."); len(parts) > 2 { - goVersion = fmt.Sprintf("%s.%s", parts[0], parts[1]) - } - } - pluginCode := `package main func Greet() string { @@ -92,7 +75,7 @@ go %s } gomodWithRequire := b.String() - goCmd, err := findGoCmd(context.Background(), false) + goCmd, err := findGoCmd(context.Background()) if err != nil { t.Fatalf("failed to find go command: %s", err) } @@ -314,12 +297,12 @@ import ( var Dependency = fmt.Sprintf("plugin => %s", gomodule.Dependency) `, - "src/plugin1/go.mod": `module plugin1 + "src/plugin1/go.mod": fmt.Sprintf(`module plugin1 -go 1.21 +go %s require 127.0.0.1/gomodule.git v1.0.0 -`, +`, goVersion), "src/plugin2/main.go": `package main import ( @@ -330,12 +313,12 @@ import ( var Dependency = fmt.Sprintf("plugin => %s", gomodule.Dependency) `, - "src/plugin2/go.mod": `module plugin2 + "src/plugin2/go.mod": fmt.Sprintf(`module plugin2 -go 1.21 +go %s require 127.0.0.1/gomodule.git v1.1.0 -`, +`, goVersion), "src/plugin3/main.go": `package main import ( @@ -346,12 +329,12 @@ import ( var Dependency = fmt.Sprintf("plugin => %s", gomodule.Dependency) `, - "src/plugin3/go.mod": `module plugin3 + "src/plugin3/go.mod": fmt.Sprintf(`module plugin3 -go 1.21 +go %s require 127.0.0.1/gomodule.git/v2 v2.0.0 -`, +`, goVersion), }, expectPluginPaths: []string{ "gen/plugin1.so", @@ -359,30 +342,30 @@ require 127.0.0.1/gomodule.git/v2 v2.0.0 "gen/plugin3.so", }, expectGoMod: map[string]string{ - "src/plugin1/go.mod": `module plugin1 + "src/plugin1/go.mod": fmt.Sprintf(`module plugin1 -go 1.21 +go %s require 127.0.0.1/gomodule.git v1.1.0 require 127.0.0.1/dependent-gomodule.git v1.1.0 // indirect -`, - "src/plugin2/go.mod": `module plugin2 +`, goVersion), + "src/plugin2/go.mod": fmt.Sprintf(`module plugin2 -go 1.21 +go %s require 127.0.0.1/gomodule.git v1.1.0 require 127.0.0.1/dependent-gomodule.git v1.1.0 // indirect -`, - "src/plugin3/go.mod": `module plugin3 +`, goVersion), + "src/plugin3/go.mod": fmt.Sprintf(`module plugin3 -go 1.21 +go %s require 127.0.0.1/gomodule.git/v2 v2.0.0 require 127.0.0.1/dependent-gomodule.git v1.1.0 // indirect -`, +`, goVersion), }, }, "multi plugins with incompatible module": { @@ -406,12 +389,12 @@ import ( var Dependency = fmt.Sprintf("plugin => %s", gomodule.Dependency) `, - "src/plugin1/go.mod": `module plugin1 + "src/plugin1/go.mod": fmt.Sprintf(`module plugin1 -go 1.21 +go %s require 127.0.0.1/gomodule.git v1.0.0 -`, +`, goVersion), "src/plugin2/main.go": `package main import ( @@ -422,34 +405,34 @@ import ( var Dependency = fmt.Sprintf("plugin => %s", gomodule.Dependency) `, - "src/plugin2/go.mod": `module plugin2 + "src/plugin2/go.mod": fmt.Sprintf(`module plugin2 -go 1.21 +go %s require 127.0.0.1/gomodule.git v2.0.0+incompatible -`, +`, goVersion), }, expectPluginPaths: []string{ "gen/plugin1.so", "gen/plugin2.so", }, expectGoMod: map[string]string{ - "src/plugin1/go.mod": `module plugin1 + "src/plugin1/go.mod": fmt.Sprintf(`module plugin1 -go 1.21 +go %s require 127.0.0.1/gomodule.git v2.0.0+incompatible require 127.0.0.1/dependent-gomodule.git v1.0.0 // indirect -`, - "src/plugin2/go.mod": `module plugin2 +`, goVersion), + "src/plugin2/go.mod": fmt.Sprintf(`module plugin2 -go 1.21 +go %s require 127.0.0.1/gomodule.git v2.0.0+incompatible require 127.0.0.1/dependent-gomodule.git v1.0.0 // indirect -`, +`, goVersion), }, skipOpen: true, }, @@ -472,24 +455,24 @@ import ( var Src = src.Src `, - "src/plugin1/go.mod": `module plugin1 + "src/plugin1/go.mod": fmt.Sprintf(`module plugin1 -go 1.21 +go %s require 127.0.0.1/sub.git v1.1.0 -`, +`, goVersion), }, expectPluginPaths: []string{ "gen/plugin1.so", "gen/plugin2.so", }, expectGoMod: map[string]string{ - "src/plugin1/go.mod": `module plugin1 + "src/plugin1/go.mod": fmt.Sprintf(`module plugin1 -go 1.21 +go %s require 127.0.0.1/sub.git v1.0.0 -`, +`, goVersion), }, skipOpen: true, }, @@ -516,16 +499,16 @@ import ( var Dependency = fmt.Sprintf("plugin => %s", gomodule.Dependency) `, - "src/plugin1/go.mod": `module plugin1 + "src/plugin1/go.mod": fmt.Sprintf(`module plugin1 -go 1.21 +go %s require 127.0.0.1/gomodule.git v1.0.0 require 127.0.0.1/dependent-gomodule.git v1.0.0 // indirect replace 127.0.0.1/dependent-gomodule.git v1.0.0 => 127.0.0.1/dependent-gomodule.git v1.1.0 -`, +`, goVersion), "src/plugin2/main.go": `package main import ( @@ -536,26 +519,26 @@ import ( var Dependency = fmt.Sprintf("plugin => %s", gomodule.Dependency) `, - "src/plugin2/go.mod": `module plugin2 + "src/plugin2/go.mod": fmt.Sprintf(`module plugin2 -go 1.21 +go %s require 127.0.0.1/gomodule.git v1.1.0 require 127.0.0.1/dependent-gomodule.git v1.0.0 // indirect -`, +`, goVersion), "src/plugin3/main.go": `package main import ( _ "127.0.0.1/dependent-gomodule.git" ) `, - "src/plugin3/go.mod": `module plugin3 + "src/plugin3/go.mod": fmt.Sprintf(`module plugin3 -go 1.21 +go %s require 127.0.0.1/dependent-gomodule.git v1.0.0 -`, +`, goVersion), }, expectPluginPaths: []string{ "gen/plugin1.so", @@ -563,28 +546,28 @@ require 127.0.0.1/dependent-gomodule.git v1.0.0 "gen/plugin3.so", }, expectGoMod: map[string]string{ - "src/plugin1/go.mod": `module plugin1 + "src/plugin1/go.mod": fmt.Sprintf(`module plugin1 -go 1.21 +go %s require 127.0.0.1/gomodule.git v1.1.0 require 127.0.0.1/dependent-gomodule.git v1.1.0 // indirect -`, - "src/plugin2/go.mod": `module plugin2 +`, goVersion), + "src/plugin2/go.mod": fmt.Sprintf(`module plugin2 -go 1.21 +go %s require 127.0.0.1/gomodule.git v1.1.0 require 127.0.0.1/dependent-gomodule.git v1.1.0 // indirect -`, - "src/plugin3/go.mod": `module plugin3 +`, goVersion), + "src/plugin3/go.mod": fmt.Sprintf(`module plugin3 -go 1.21 +go %s require 127.0.0.1/dependent-gomodule.git v1.1.0 -`, +`, goVersion), }, skipOpen: true, }, @@ -601,10 +584,10 @@ plugins: var Dependency = "local-dependent" `, - "src/go.mod": `module 127.0.0.1/dependent-gomodule.git + "src/go.mod": fmt.Sprintf(`module 127.0.0.1/dependent-gomodule.git -go 1.21 -`, +go %s +`, goVersion), "src/plugin1/main.go": `package main import ( @@ -615,31 +598,31 @@ import ( var Dependency = fmt.Sprintf("plugin => %s", gomodule.Dependency) `, - "src/plugin1/go.mod": `module plugin1 + "src/plugin1/go.mod": fmt.Sprintf(`module plugin1 -go 1.21 +go %s require 127.0.0.1/gomodule.git v1.0.0 require 127.0.0.1/dependent-gomodule.git v1.0.0 // indirect replace 127.0.0.1/dependent-gomodule.git => ../ -`, +`, goVersion), }, expectPluginPaths: []string{ "gen/plugin1.so", }, expectGoMod: map[string]string{ - "src/plugin1/go.mod": `module plugin1 + "src/plugin1/go.mod": fmt.Sprintf(`module plugin1 -go 1.21 +go %s require 127.0.0.1/gomodule.git v1.0.0 require 127.0.0.1/dependent-gomodule.git v1.0.0 // indirect replace 127.0.0.1/dependent-gomodule.git v1.0.0 => ../ -`, +`, goVersion), }, skipOpen: true, }, @@ -660,10 +643,10 @@ plugins: var Dependency = "local-dependent" `, - "src/local/go.mod": `module 127.0.0.1/dependent-gomodule.git + "src/local/go.mod": fmt.Sprintf(`module 127.0.0.1/dependent-gomodule.git -go 1.21 -`, +go %s +`, goVersion), "src/plugin1/main.go": `package main import ( @@ -674,16 +657,16 @@ import ( var Dependency = fmt.Sprintf("plugin => %s", gomodule.Dependency) `, - "src/plugin1/go.mod": `module plugin1 + "src/plugin1/go.mod": fmt.Sprintf(`module plugin1 -go 1.21 +go %s require 127.0.0.1/gomodule.git v1.0.0 require 127.0.0.1/dependent-gomodule.git v1.0.0 // indirect replace 127.0.0.1/dependent-gomodule.git => ./../local -`, +`, goVersion), "src/plugin2/sub/main.go": `package main import ( @@ -694,28 +677,28 @@ import ( var Dependency = fmt.Sprintf("plugin => %s", gomodule.Dependency) `, - "src/plugin2/sub/go.mod": `module plugin2 + "src/plugin2/sub/go.mod": fmt.Sprintf(`module plugin2 -go 1.21 +go %s require 127.0.0.1/gomodule.git v1.1.0 require 127.0.0.1/dependent-gomodule.git v1.0.0 // indirect replace 127.0.0.1/dependent-gomodule.git v1.0.0 => ../../local -`, +`, goVersion), "src/plugin3/main.go": `package main import ( _ "127.0.0.1/dependent-gomodule.git" ) `, - "src/plugin3/go.mod": `module plugin3 + "src/plugin3/go.mod": fmt.Sprintf(`module plugin3 -go 1.21 +go %s require 127.0.0.1/dependent-gomodule.git v1.0.0 -`, +`, goVersion), }, expectPluginPaths: []string{ "gen/plugin1.so", @@ -723,34 +706,34 @@ require 127.0.0.1/dependent-gomodule.git v1.0.0 "gen/plugin3.so", }, expectGoMod: map[string]string{ - "src/plugin1/go.mod": `module plugin1 + "src/plugin1/go.mod": fmt.Sprintf(`module plugin1 -go 1.21 +go %s require 127.0.0.1/gomodule.git v1.1.0 require 127.0.0.1/dependent-gomodule.git v1.0.0 // indirect replace 127.0.0.1/dependent-gomodule.git v1.0.0 => ../local -`, - "src/plugin2/sub/go.mod": `module plugin2 +`, goVersion), + "src/plugin2/sub/go.mod": fmt.Sprintf(`module plugin2 -go 1.21 +go %s require 127.0.0.1/gomodule.git v1.1.0 require 127.0.0.1/dependent-gomodule.git v1.0.0 // indirect replace 127.0.0.1/dependent-gomodule.git v1.0.0 => ../../local -`, - "src/plugin3/go.mod": `module plugin3 +`, goVersion), + "src/plugin3/go.mod": fmt.Sprintf(`module plugin3 -go 1.21 +go %s require 127.0.0.1/dependent-gomodule.git v1.0.0 replace 127.0.0.1/dependent-gomodule.git v1.0.0 => ../local -`, +`, goVersion), }, skipOpen: true, }, @@ -855,12 +838,12 @@ import ( var Dependency = fmt.Sprintf("plugin => %s", gomodule.Dependency) `, - "src/plugin/go.mod": `module plugin1 + "src/plugin/go.mod": fmt.Sprintf(`module plugin1 -go 1.21 +go %s require 127.0.0.1/gomodule.git v2.0.0 -`, +`, goVersion), }, expect: `require 127.0.0.1/gomodule.git: version "v2.0.0" invalid: should be v0 or v1, not v2`, }, @@ -891,16 +874,16 @@ import ( var Dependency = fmt.Sprintf("plugin => %s", gomodule.Dependency) `, - "src/plugin1/go.mod": `module plugin1 + "src/plugin1/go.mod": fmt.Sprintf(`module plugin1 -go 1.21 +go %s require 127.0.0.1/gomodule.git v1.0.0 require 127.0.0.1/dependent-gomodule.git v1.0.0 // indirect replace 127.0.0.1/dependent-gomodule.git v1.0.0 => ../local1 -`, +`, goVersion), }, expect: "replacement directory ../local1 does not exist", }, @@ -924,16 +907,16 @@ import ( var Dependency = fmt.Sprintf("plugin => %s", gomodule.Dependency) `, - "src/plugin1/go.mod": `module plugin1 + "src/plugin1/go.mod": fmt.Sprintf(`module plugin1 -go 1.21 +go %s require 127.0.0.1/gomodule.git v1.0.0 require 127.0.0.1/dependent-gomodule.git v1.0.0 // indirect replace 127.0.0.1/dependent-gomodule.git v1.0.0 => 127.0.0.1/dependent-gomodule.git v1.1.0 -`, +`, goVersion), "src/plugin2/main.go": `package main import ( @@ -944,16 +927,16 @@ import ( var Dependency = fmt.Sprintf("plugin => %s", gomodule.Dependency) `, - "src/plugin2/go.mod": `module plugin2 + "src/plugin2/go.mod": fmt.Sprintf(`module plugin2 -go 1.21 +go %s require 127.0.0.1/gomodule.git v1.1.0 require 127.0.0.1/dependent-gomodule.git v1.0.0 // indirect replace 127.0.0.1/dependent-gomodule.git v1.0.0 => 127.0.0.1/dependent-gomodule.git/v2 v2.0.0 -`, +`, goVersion), }, expect: "replace 127.0.0.1/dependent-gomodule.git directive conflicts: plugin1.so => 127.0.0.1/dependent-gomodule.git v1.1.0, plugin2.so => 127.0.0.1/dependent-gomodule.git/v2 v2.0.0", }, @@ -971,10 +954,10 @@ plugins: var Dependency = "local-dependent" `, - "src/local/go.mod": `module 127.0.0.1/dependent-gomodule.git + "src/local/go.mod": fmt.Sprintf(`module 127.0.0.1/dependent-gomodule.git -go 1.21 -`, +go %s +`, goVersion), "src/plugin1/main.go": `package main import ( @@ -985,16 +968,16 @@ import ( var Dependency = fmt.Sprintf("plugin => %s", gomodule.Dependency) `, - "src/plugin1/go.mod": `module plugin1 + "src/plugin1/go.mod": fmt.Sprintf(`module plugin1 -go 1.21 +go %s require 127.0.0.1/gomodule.git v1.0.0 require 127.0.0.1/dependent-gomodule.git v1.0.0 // indirect replace 127.0.0.1/dependent-gomodule.git v1.0.0 => 127.0.0.1/dependent-gomodule.git v1.1.0 -`, +`, goVersion), "src/plugin2/main.go": `package main import ( @@ -1005,16 +988,16 @@ import ( var Dependency = fmt.Sprintf("plugin => %s", gomodule.Dependency) `, - "src/plugin2/go.mod": `module plugin2 + "src/plugin2/go.mod": fmt.Sprintf(`module plugin2 -go 1.21 +go %s require 127.0.0.1/gomodule.git v1.1.0 require 127.0.0.1/dependent-gomodule.git v1.0.0 // indirect replace 127.0.0.1/dependent-gomodule.git v1.0.0 => ../local -`, +`, goVersion), }, expect: "replace 127.0.0.1/dependent-gomodule.git directive conflicts: plugin1.so => 127.0.0.1/dependent-gomodule.git v1.1.0, plugin2.so => ../local", }, @@ -1032,18 +1015,18 @@ plugins: var Dependency = "local-dependent" `, - "src/local1/go.mod": `module 127.0.0.1/dependent-gomodule.git + "src/local1/go.mod": fmt.Sprintf(`module 127.0.0.1/dependent-gomodule.git -go 1.21 -`, +go %s +`, goVersion), "src/local2/main.go": `package dependent var Dependency = "local-dependent" `, - "src/local2/go.mod": `module 127.0.0.1/dependent-gomodule.git + "src/local2/go.mod": fmt.Sprintf(`module 127.0.0.1/dependent-gomodule.git -go 1.21 -`, +go %s +`, goVersion), "src/plugin1/main.go": `package main import ( @@ -1054,16 +1037,16 @@ import ( var Dependency = fmt.Sprintf("plugin => %s", gomodule.Dependency) `, - "src/plugin1/go.mod": `module plugin1 + "src/plugin1/go.mod": fmt.Sprintf(`module plugin1 -go 1.21 +go %s require 127.0.0.1/gomodule.git v1.0.0 require 127.0.0.1/dependent-gomodule.git v1.0.0 // indirect replace 127.0.0.1/dependent-gomodule.git v1.0.0 => ../local1 -`, +`, goVersion), "src/plugin2/main.go": `package main import ( @@ -1074,16 +1057,16 @@ import ( var Dependency = fmt.Sprintf("plugin => %s", gomodule.Dependency) `, - "src/plugin2/go.mod": `module plugin2 + "src/plugin2/go.mod": fmt.Sprintf(`module plugin2 -go 1.21 +go %s require 127.0.0.1/gomodule.git v1.1.0 require 127.0.0.1/dependent-gomodule.git v1.0.0 // indirect replace 127.0.0.1/dependent-gomodule.git v1.0.0 => ../local2 -`, +`, goVersion), }, expect: "replace 127.0.0.1/dependent-gomodule.git directive conflicts: plugin1.so => ../local1, plugin2.so => ../local2", }, @@ -1142,17 +1125,10 @@ func TestFindGoCmd(t *testing.T) { }, "minimum go version": { cmds: map[string]string{ - "go": fmt.Sprintf("go version %s linux/amd64", gomodVer), + "go": fmt.Sprintf("go version %s linux/amd64", goMinVer), }, expect: "go", }, - "found gotip command": { - cmds: map[string]string{ - "gotip": fmt.Sprintf("go version %s linux/amd64", goVer), - }, - expect: "gotip", - tip: true, - }, } for name, test := range tests { test := test @@ -1162,7 +1138,7 @@ func TestFindGoCmd(t *testing.T) { createExecutable(t, filepath.Join(tmpDir, cmd), stdout) } t.Setenv("PATH", tmpDir) - goCmd, err := findGoCmd(context.Background(), test.tip) + goCmd, err := findGoCmd(context.Background()) if err != nil { t.Fatal(err) } @@ -1184,7 +1160,7 @@ func TestFindGoCmd(t *testing.T) { cmds: map[string]string{ "go": "go version go1.20 linux/amd64", }, - expect: fmt.Sprintf("required go %s or later but installed 1.20", gomodVer), + expect: fmt.Sprintf("required go %s or later but installed 1.20", goMinVer), }, } for name, test := range tests { @@ -1195,7 +1171,7 @@ func TestFindGoCmd(t *testing.T) { createExecutable(t, filepath.Join(tmpDir, cmd), stdout) } t.Setenv("PATH", tmpDir) - _, err := findGoCmd(context.Background(), false) + _, err := findGoCmd(context.Background()) if err == nil { t.Fatal("no error") } @@ -1208,28 +1184,41 @@ func TestFindGoCmd(t *testing.T) { } func TestUpdateGoMod(t *testing.T) { + goVersion := strings.TrimPrefix(goVer, "go") modVers := getModuleVersions(t) - tests := map[string]struct { - gomod string - src string - overrides map[string]*overrideModule - expect string - expectStdout string - }{ - "do nothing": { - gomod: `module plugin_module -go 1.21 -`, - expect: `module plugin_module + t.Run("success", func(t *testing.T) { + tests := map[string]struct { + gomod string + src string + overrides map[string]*overrideModule + expect string + expectStdout string + }{ + "do nothing": { + gomod: fmt.Sprintf(`module plugin_module + +go %s +`, goVersion), + expect: fmt.Sprintf(`module plugin_module + +go %s +`, goVersion), + }, + "update go directive": { + gomod: `module plugin_module go 1.21 `, - }, - "do nothing (no requires)": { - gomod: `module plugin_module + expect: fmt.Sprintf(`module plugin_module -go 1.21 +go %s +`, goVersion), + }, + "do nothing (no requires)": { + gomod: fmt.Sprintf(`module plugin_module + +go %s require google.golang.org/grpc v1.37.1 @@ -1241,16 +1230,16 @@ require ( google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect google.golang.org/protobuf v1.25.0 // indirect ) -`, - src: `package main +`, goVersion), + src: `package main import ( _ "google.golang.org/grpc" ) `, - expect: `module plugin_module + expect: fmt.Sprintf(`module plugin_module -go 1.21 +go %s require google.golang.org/grpc v1.37.1 @@ -1262,60 +1251,60 @@ require ( google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect google.golang.org/protobuf v1.25.0 // indirect ) -`, - }, - "do nothing (not used)": { - gomod: `module plugin_module +`, goVersion), + }, + "do nothing (not used)": { + gomod: fmt.Sprintf(`module plugin_module -go 1.21 +go %s require github.com/zoncoen/scenarigo v0.11.2 replace github.com/zoncoen/scenarigo v0.11.2 => github.com/zoncoen/scenarigo v0.11.0 -`, - overrides: map[string]*overrideModule{ - "google.golang.org/grpc": { - require: &modfile.Require{ - Mod: module.Version{ - Path: "google.golang.org/grpc", - Version: "v1.37.1", +`, goVersion), + overrides: map[string]*overrideModule{ + "google.golang.org/grpc": { + require: &modfile.Require{ + Mod: module.Version{ + Path: "google.golang.org/grpc", + Version: "v1.37.1", + }, }, + requiredBy: "test", }, - requiredBy: "test", }, - }, - expect: `module plugin_module + expect: fmt.Sprintf(`module plugin_module -go 1.21 -`, - expectStdout: `WARN: test.so: remove replace github.com/zoncoen/scenarigo v0.11.2 => github.com/zoncoen/scenarigo v0.11.0 +go %s +`, goVersion), + expectStdout: `WARN: test.so: remove replace github.com/zoncoen/scenarigo v0.11.2 => github.com/zoncoen/scenarigo v0.11.0 `, - }, - "add require": { - gomod: `module plugin_module + }, + "add require": { + gomod: fmt.Sprintf(`module plugin_module -go 1.21 -`, - src: `package main +go %s +`, goVersion), + src: `package main import ( _ "google.golang.org/grpc" ) `, - overrides: map[string]*overrideModule{ - "google.golang.org/grpc": { - require: &modfile.Require{ - Mod: module.Version{ - Path: "google.golang.org/grpc", - Version: "v1.37.1", + overrides: map[string]*overrideModule{ + "google.golang.org/grpc": { + require: &modfile.Require{ + Mod: module.Version{ + Path: "google.golang.org/grpc", + Version: "v1.37.1", + }, }, + requiredBy: "test", }, - requiredBy: "test", }, - }, - expect: `module plugin_module + expect: fmt.Sprintf(`module plugin_module -go 1.21 +go %s require google.golang.org/grpc v1.37.1 @@ -1327,14 +1316,14 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect google.golang.org/protobuf v1.33.0 // indirect ) -`, - expectStdout: fmt.Sprintf(`WARN: test.so: change require google.golang.org/grpc %s ==> v1.37.1 by test +`, goVersion), + expectStdout: fmt.Sprintf(`WARN: test.so: change require google.golang.org/grpc %s ==> v1.37.1 by test `, modVers["google.golang.org/grpc"]), - }, - "overwrite require by require": { - gomod: `module plugin_module + }, + "overwrite require by require": { + gomod: fmt.Sprintf(`module plugin_module -go 1.21 +go %s require google.golang.org/grpc v1.37.1 @@ -1346,27 +1335,27 @@ require ( google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect google.golang.org/protobuf v1.25.0 // indirect ) -`, - src: `package main +`, goVersion), + src: `package main import ( _ "google.golang.org/grpc" ) `, - overrides: map[string]*overrideModule{ - "google.golang.org/grpc": { - require: &modfile.Require{ - Mod: module.Version{ - Path: "google.golang.org/grpc", - Version: "v1.40.0", + overrides: map[string]*overrideModule{ + "google.golang.org/grpc": { + require: &modfile.Require{ + Mod: module.Version{ + Path: "google.golang.org/grpc", + Version: "v1.40.0", + }, }, + requiredBy: "test", }, - requiredBy: "test", }, - }, - expect: `module plugin_module + expect: fmt.Sprintf(`module plugin_module -go 1.21 +go %s require google.golang.org/grpc v1.40.0 @@ -1378,14 +1367,14 @@ require ( google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect google.golang.org/protobuf v1.25.0 // indirect ) +`, goVersion), + expectStdout: `WARN: test.so: change require google.golang.org/grpc v1.37.1 ==> v1.40.0 by test `, - expectStdout: `WARN: test.so: change require google.golang.org/grpc v1.37.1 ==> v1.40.0 by test -`, - }, - "overwrite require by replace": { - gomod: `module plugin_module + }, + "overwrite require by replace": { + gomod: fmt.Sprintf(`module plugin_module -go 1.21 +go %s require google.golang.org/grpc v1.37.1 @@ -1397,38 +1386,38 @@ require ( google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect google.golang.org/protobuf v1.25.0 // indirect ) -`, - src: `package main +`, goVersion), + src: `package main import ( _ "google.golang.org/grpc" ) `, - overrides: map[string]*overrideModule{ - "google.golang.org/grpc": { - require: &modfile.Require{ - Mod: module.Version{ - Path: "google.golang.org/grpc", - Version: "v1.46.0", + overrides: map[string]*overrideModule{ + "google.golang.org/grpc": { + require: &modfile.Require{ + Mod: module.Version{ + Path: "google.golang.org/grpc", + Version: "v1.46.0", + }, }, - }, - requiredBy: "test", - replace: &modfile.Replace{ - Old: module.Version{ - Path: "google.golang.org/grpc", - Version: "v1.46.0", - }, - New: module.Version{ - Path: "google.golang.org/grpc", - Version: "v1.40.0", + requiredBy: "test", + replace: &modfile.Replace{ + Old: module.Version{ + Path: "google.golang.org/grpc", + Version: "v1.46.0", + }, + New: module.Version{ + Path: "google.golang.org/grpc", + Version: "v1.40.0", + }, }, + replacedBy: "test", }, - replacedBy: "test", }, - }, - expect: `module plugin_module + expect: fmt.Sprintf(`module plugin_module -go 1.21 +go %s require google.golang.org/grpc v1.40.0 @@ -1440,14 +1429,14 @@ require ( google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect google.golang.org/protobuf v1.25.0 // indirect ) +`, goVersion), + expectStdout: `WARN: test.so: change require google.golang.org/grpc v1.37.1 ==> v1.40.0 by test `, - expectStdout: `WARN: test.so: change require google.golang.org/grpc v1.37.1 ==> v1.40.0 by test -`, - }, - "do nothing (same version)": { - gomod: `module plugin_module + }, + "do nothing (same version)": { + gomod: fmt.Sprintf(`module plugin_module -go 1.21 +go %s require google.golang.org/grpc v1.37.1 @@ -1459,27 +1448,27 @@ require ( google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect google.golang.org/protobuf v1.25.0 // indirect ) -`, - src: `package main +`, goVersion), + src: `package main import ( _ "google.golang.org/grpc" ) `, - overrides: map[string]*overrideModule{ - "google.golang.org/grpc": { - require: &modfile.Require{ - Mod: module.Version{ - Path: "google.golang.org/grpc", - Version: "v1.37.1", + overrides: map[string]*overrideModule{ + "google.golang.org/grpc": { + require: &modfile.Require{ + Mod: module.Version{ + Path: "google.golang.org/grpc", + Version: "v1.37.1", + }, }, + requiredBy: "test", }, - requiredBy: "test", }, - }, - expect: `module plugin_module + expect: fmt.Sprintf(`module plugin_module -go 1.21 +go %s require google.golang.org/grpc v1.37.1 @@ -1491,12 +1480,12 @@ require ( google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect google.golang.org/protobuf v1.25.0 // indirect ) -`, - }, - "add replace": { - gomod: `module plugin_module +`, goVersion), + }, + "add replace": { + gomod: fmt.Sprintf(`module plugin_module -go 1.21 +go %s require github.com/zoncoen/scenarigo v0.11.2 @@ -1518,27 +1507,27 @@ require ( google.golang.org/grpc v1.46.0 // indirect google.golang.org/protobuf v1.28.0 // indirect ) -`, - src: `package main +`, goVersion), + src: `package main import ( _ "github.com/zoncoen/scenarigo/protocol/grpc" ) `, - overrides: map[string]*overrideModule{ - "google.golang.org/grpc": { - require: &modfile.Require{ - Mod: module.Version{ - Path: "google.golang.org/grpc", - Version: "v1.40.0", + overrides: map[string]*overrideModule{ + "google.golang.org/grpc": { + require: &modfile.Require{ + Mod: module.Version{ + Path: "google.golang.org/grpc", + Version: "v1.40.0", + }, }, + requiredBy: "test", }, - requiredBy: "test", }, - }, - expect: `module plugin_module + expect: fmt.Sprintf(`module plugin_module -go 1.21 +go %s require github.com/zoncoen/scenarigo v0.11.2 @@ -1562,14 +1551,14 @@ require ( ) replace google.golang.org/grpc v1.46.0 => google.golang.org/grpc v1.40.0 +`, goVersion), + expectStdout: `WARN: test.so: add replace google.golang.org/grpc v1.46.0 => google.golang.org/grpc v1.40.0 by test `, - expectStdout: `WARN: test.so: add replace google.golang.org/grpc v1.46.0 => google.golang.org/grpc v1.40.0 by test -`, - }, - "overwrite replace by require": { - gomod: `module plugin_module + }, + "overwrite replace by require": { + gomod: fmt.Sprintf(`module plugin_module -go 1.21 +go %s require github.com/zoncoen/scenarigo v0.11.2 @@ -1593,27 +1582,27 @@ require ( ) replace google.golang.org/grpc v1.46.0 => google.golang.org/grpc v1.40.0 -`, - src: `package main +`, goVersion), + src: `package main import ( _ "github.com/zoncoen/scenarigo/protocol/grpc" ) `, - overrides: map[string]*overrideModule{ - "google.golang.org/grpc": { - require: &modfile.Require{ - Mod: module.Version{ - Path: "google.golang.org/grpc", - Version: "v1.40.1", + overrides: map[string]*overrideModule{ + "google.golang.org/grpc": { + require: &modfile.Require{ + Mod: module.Version{ + Path: "google.golang.org/grpc", + Version: "v1.40.1", + }, }, + requiredBy: "test", }, - requiredBy: "test", }, - }, - expect: `module plugin_module + expect: fmt.Sprintf(`module plugin_module -go 1.21 +go %s require github.com/zoncoen/scenarigo v0.11.2 @@ -1637,14 +1626,14 @@ require ( ) replace google.golang.org/grpc v1.46.0 => google.golang.org/grpc v1.40.1 +`, goVersion), + expectStdout: `WARN: test.so: change replace google.golang.org/grpc v1.46.0 => google.golang.org/grpc v1.40.0 ==> google.golang.org/grpc v1.46.0 => google.golang.org/grpc v1.40.1 by test `, - expectStdout: `WARN: test.so: change replace google.golang.org/grpc v1.46.0 => google.golang.org/grpc v1.40.0 ==> google.golang.org/grpc v1.46.0 => google.golang.org/grpc v1.40.1 by test -`, - }, - "override replace by replace": { - gomod: `module plugin_module + }, + "override replace by replace": { + gomod: fmt.Sprintf(`module plugin_module -go 1.21 +go %s require github.com/zoncoen/scenarigo v0.11.2 @@ -1668,38 +1657,38 @@ require ( ) replace google.golang.org/grpc v1.46.0 => google.golang.org/grpc v1.40.0 -`, - src: `package main +`, goVersion), + src: `package main import ( _ "github.com/zoncoen/scenarigo/protocol/grpc" ) `, - overrides: map[string]*overrideModule{ - "google.golang.org/grpc": { - require: &modfile.Require{ - Mod: module.Version{ - Path: "google.golang.org/grpc", - Version: "v1.46.0", - }, - }, - requiredBy: "test", - replace: &modfile.Replace{ - Old: module.Version{ - Path: "google.golang.org/grpc", - Version: "v1.46.0", + overrides: map[string]*overrideModule{ + "google.golang.org/grpc": { + require: &modfile.Require{ + Mod: module.Version{ + Path: "google.golang.org/grpc", + Version: "v1.46.0", + }, }, - New: module.Version{ - Path: "google.golang.org/grpc", - Version: "v1.40.1", + requiredBy: "test", + replace: &modfile.Replace{ + Old: module.Version{ + Path: "google.golang.org/grpc", + Version: "v1.46.0", + }, + New: module.Version{ + Path: "google.golang.org/grpc", + Version: "v1.40.1", + }, }, + replacedBy: "test", }, - replacedBy: "test", }, - }, - expect: `module plugin_module + expect: fmt.Sprintf(`module plugin_module -go 1.21 +go %s require github.com/zoncoen/scenarigo v0.11.2 @@ -1723,14 +1712,14 @@ require ( ) replace google.golang.org/grpc v1.46.0 => google.golang.org/grpc v1.40.1 +`, goVersion), + expectStdout: `WARN: test.so: change replace google.golang.org/grpc v1.46.0 => google.golang.org/grpc v1.40.0 ==> google.golang.org/grpc v1.46.0 => google.golang.org/grpc v1.40.1 by test `, - expectStdout: `WARN: test.so: change replace google.golang.org/grpc v1.46.0 => google.golang.org/grpc v1.40.0 ==> google.golang.org/grpc v1.46.0 => google.golang.org/grpc v1.40.1 by test -`, - }, - "do nothing (alredy replaced)": { - gomod: `module plugin_module + }, + "do nothing (alredy replaced)": { + gomod: fmt.Sprintf(`module plugin_module -go 1.21 +go %s require github.com/zoncoen/scenarigo v0.11.2 @@ -1754,27 +1743,27 @@ require ( ) replace google.golang.org/grpc v1.46.0 => google.golang.org/grpc v1.40.0 -`, - src: `package main +`, goVersion), + src: `package main import ( _ "github.com/zoncoen/scenarigo/protocol/grpc" ) `, - overrides: map[string]*overrideModule{ - "google.golang.org/grpc": { - require: &modfile.Require{ - Mod: module.Version{ - Path: "google.golang.org/grpc", - Version: "v1.40.0", + overrides: map[string]*overrideModule{ + "google.golang.org/grpc": { + require: &modfile.Require{ + Mod: module.Version{ + Path: "google.golang.org/grpc", + Version: "v1.40.0", + }, }, + requiredBy: "test", }, - requiredBy: "test", }, - }, - expect: `module plugin_module + expect: fmt.Sprintf(`module plugin_module -go 1.21 +go %s require github.com/zoncoen/scenarigo v0.11.2 @@ -1798,57 +1787,111 @@ require ( ) replace google.golang.org/grpc v1.46.0 => google.golang.org/grpc v1.40.0 -`, - expectStdout: "", // don't print the warn log if already replaced - }, - } - for name, test := range tests { - test := test - t.Run(name, func(t *testing.T) { - tmpDir := t.TempDir() - gomod := filepath.Join(tmpDir, "go.mod") - create(t, gomod, test.gomod) - if test.src != "" { - create(t, filepath.Join(tmpDir, "main.go"), test.src) - } +`, goVersion), + expectStdout: "", // don't print the warn log if already replaced + }, + } + for name, test := range tests { + test := test + t.Run(name, func(t *testing.T) { + tmpDir := t.TempDir() + gomod := filepath.Join(tmpDir, "go.mod") + create(t, gomod, test.gomod) + if test.src != "" { + create(t, filepath.Join(tmpDir, "main.go"), test.src) + } - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - goCmd, err := findGoCmd(ctx, false) - if err != nil { - t.Fatalf("failed to find go command: %s", err) - } + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + goCmd, err := findGoCmd(ctx) + if err != nil { + t.Fatalf("failed to find go command: %s", err) + } - overrideKeys := make([]string, 0, len(test.overrides)) - for k := range test.overrides { - overrideKeys = append(overrideKeys, k) - } - sort.Strings(overrideKeys) + overrideKeys := make([]string, 0, len(test.overrides)) + for k := range test.overrides { + overrideKeys = append(overrideKeys, k) + } + sort.Strings(overrideKeys) - cmd := &cobra.Command{} - var stdout bytes.Buffer - cmd.SetOutput(&stdout) - if err := updateGoMod(cmd, goCmd, "test.so", gomod, overrideKeys, test.overrides); err != nil { - t.Fatalf("failed to update go.mod: %s", err) - } + cmd := &cobra.Command{} + var stdout bytes.Buffer + cmd.SetOutput(&stdout) + if err := updateGoMod(cmd, goCmd, "test.so", gomod, overrideKeys, test.overrides); err != nil { + t.Fatalf("failed to update go.mod: %s", err) + } - b, err := os.ReadFile(gomod) - if err != nil { - t.Fatalf("failed read go.mod: %s", err) - } - if got := string(b); got != test.expect { - dmp := diffmatchpatch.New() - diffs := dmp.DiffMain(test.expect, got, false) - t.Errorf("go.mod differs:\n%s", dmp.DiffPrettyText(diffs)) - } + b, err := os.ReadFile(gomod) + if err != nil { + t.Fatalf("failed read go.mod: %s", err) + } + if got := string(b); got != test.expect { + dmp := diffmatchpatch.New() + diffs := dmp.DiffMain(test.expect, got, false) + t.Errorf("go.mod differs:\n%s", dmp.DiffPrettyText(diffs)) + } - if got := strings.ReplaceAll(stdout.String(), gomod, "/path/to/go.mod"); got != test.expectStdout { - dmp := diffmatchpatch.New() - diffs := dmp.DiffMain(test.expectStdout, got, false) - t.Errorf("stdout differs:\n%s", dmp.DiffPrettyText(diffs)) - } - }) - } + if got := strings.ReplaceAll(stdout.String(), gomod, "/path/to/go.mod"); got != test.expectStdout { + dmp := diffmatchpatch.New() + diffs := dmp.DiffMain(test.expectStdout, got, false) + t.Errorf("stdout differs:\n%s", dmp.DiffPrettyText(diffs)) + } + }) + } + }) + + t.Run("failure", func(t *testing.T) { + tests := map[string]struct { + gomod string + src string + overrides map[string]*overrideModule + expect string + }{ + "too high": { + gomod: `module plugin_module + +go 100.0.0 +`, + expect: fmt.Sprintf("go.mod requires go >= 100.0.0 (running go %s)", goVer), + }, + } + for name, test := range tests { + test := test + t.Run(name, func(t *testing.T) { + tmpDir := t.TempDir() + gomod := filepath.Join(tmpDir, "go.mod") + create(t, gomod, test.gomod) + if test.src != "" { + create(t, filepath.Join(tmpDir, "main.go"), test.src) + } + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + goCmd, err := findGoCmd(ctx) + if err != nil { + t.Fatalf("failed to find go command: %s", err) + } + + overrideKeys := make([]string, 0, len(test.overrides)) + for k := range test.overrides { + overrideKeys = append(overrideKeys, k) + } + sort.Strings(overrideKeys) + + cmd := &cobra.Command{} + var stdout bytes.Buffer + cmd.SetOutput(&stdout) + + err = updateGoMod(cmd, goCmd, "test.so", gomod, overrideKeys, test.overrides) + if err == nil { + t.Fatal("no error") + } + if !strings.Contains(err.Error(), test.expect) { + t.Fatalf("expected %q but got %q", test.expect, err) + } + }) + } + }) } func setupGitServer(t *testing.T, goCmd string) { @@ -1886,16 +1929,16 @@ func setupGitServer(t *testing.T, goCmd string) { fmt.Sprintf("GOMODCACHE=%s", filepath.Join(tempDir, ".cache")), } t.Cleanup(func() { - if err := executeWithEnvs(ctx, envs, tempDir, goCmd, "clean", "-modcache"); err != nil { + if _, err := executeWithEnvs(ctx, envs, tempDir, goCmd, "clean", "-modcache"); err != nil { t.Errorf("go clean -modcache failed: %s", err) } }) // create git objects for test repositories - if err := executeWithEnvs(ctx, envs, tempDir, "git", "config", "--global", "user.name", "scenarigo-test"); err != nil { + if _, err := executeWithEnvs(ctx, envs, tempDir, "git", "config", "--global", "user.name", "scenarigo-test"); err != nil { t.Fatalf("git config failed: %s", err) } - if err := executeWithEnvs(ctx, envs, tempDir, "git", "config", "--global", "user.email", "scenarigo-test@example.com"); err != nil { + if _, err := executeWithEnvs(ctx, envs, tempDir, "git", "config", "--global", "user.email", "scenarigo-test@example.com"); err != nil { t.Fatalf("git config failed: %s", err) } repoDir := filepath.Join("testdata", "git") @@ -1909,7 +1952,7 @@ func setupGitServer(t *testing.T, goCmd string) { } wd := filepath.Join(repoDir, e.Name()) if _, err := os.Stat(filepath.Join(wd, "go.mod")); err == nil { - if err := executeWithEnvs(ctx, envs, wd, goCmd, "mod", "tidy"); err != nil { + if _, err := executeWithEnvs(ctx, envs, wd, goCmd, "mod", "tidy"); err != nil { t.Fatalf("go mod tidy failed: %s", err) } t.Cleanup(func() { @@ -1917,33 +1960,33 @@ func setupGitServer(t *testing.T, goCmd string) { }) } if _, err := os.Stat(filepath.Join(wd, "v2", "go.mod")); err == nil { - if err := executeWithEnvs(ctx, envs, filepath.Join(wd, "v2"), goCmd, "mod", "tidy"); err != nil { + if _, err := executeWithEnvs(ctx, envs, filepath.Join(wd, "v2"), goCmd, "mod", "tidy"); err != nil { t.Fatalf("go mod tidy failed: %s", err) } t.Cleanup(func() { os.RemoveAll(filepath.Join(wd, "v2", "go.sum")) }) } - if err := executeWithEnvs(ctx, envs, wd, "git", "init"); err != nil { + if _, err := executeWithEnvs(ctx, envs, wd, "git", "init"); err != nil { t.Fatalf("git init failed: %s", err) } t.Cleanup(func() { os.RemoveAll(filepath.Join(wd, ".git")) }) - if err := executeWithEnvs(ctx, envs, wd, "git", "add", "-A"); err != nil { + if _, err := executeWithEnvs(ctx, envs, wd, "git", "add", "-A"); err != nil { t.Fatalf("git add failed: %s", err) } - if err := executeWithEnvs(ctx, envs, wd, "git", "commit", "-m", "commit"); err != nil { + if _, err := executeWithEnvs(ctx, envs, wd, "git", "commit", "-m", "commit"); err != nil { t.Fatalf("git commit failed: %s", err) } - if err := executeWithEnvs(ctx, envs, wd, "git", "tag", "v1.0.0"); err != nil { + if _, err := executeWithEnvs(ctx, envs, wd, "git", "tag", "v1.0.0"); err != nil { t.Fatalf("git tag failed: %s", err) } - if err := executeWithEnvs(ctx, envs, wd, "git", "tag", "v1.1.0"); err != nil { + if _, err := executeWithEnvs(ctx, envs, wd, "git", "tag", "v1.1.0"); err != nil { t.Fatalf("git tag failed: %s", err) } if _, err := os.Stat(filepath.Join(wd, "v2")); err == nil { - if err := executeWithEnvs(ctx, envs, wd, "git", "tag", "v2.0.0"); err != nil { + if _, err := executeWithEnvs(ctx, envs, wd, "git", "tag", "v2.0.0"); err != nil { t.Fatalf("git tag failed: %s", err) } } diff --git a/cmd/scenarigo/cmd/version.go b/cmd/scenarigo/cmd/version.go index 3b41460f..b3bcbc83 100644 --- a/cmd/scenarigo/cmd/version.go +++ b/cmd/scenarigo/cmd/version.go @@ -2,6 +2,7 @@ package cmd import ( "fmt" + "runtime" "github.com/spf13/cobra" "github.com/zoncoen/scenarigo/version" @@ -21,5 +22,5 @@ var versionCmd = &cobra.Command{ } func printVersion(cmd *cobra.Command, args []string) { - fmt.Fprintf(cmd.OutOrStdout(), "%s version %s\n", appName, version.String()) + fmt.Fprintf(cmd.OutOrStdout(), "%s version %s %s %s/%s\n", appName, version.String(), runtime.Version(), runtime.GOOS, runtime.GOARCH) } diff --git a/cmd/scenarigo/cmd/version_test.go b/cmd/scenarigo/cmd/version_test.go index 18b91d11..c13f77e8 100644 --- a/cmd/scenarigo/cmd/version_test.go +++ b/cmd/scenarigo/cmd/version_test.go @@ -3,6 +3,7 @@ package cmd import ( "bytes" "fmt" + "runtime" "testing" "github.com/sergi/go-diff/diffmatchpatch" @@ -15,7 +16,7 @@ func TestVersion(t *testing.T) { cmd := &cobra.Command{} cmd.SetOut(&b) printVersion(cmd, nil) - if got, expect := b.String(), fmt.Sprintf("%s version %s\n", appName, version.String()); got != expect { + if got, expect := b.String(), fmt.Sprintf("%s version %s %s %s/%s\n", appName, version.String(), runtime.Version(), runtime.GOOS, runtime.GOARCH); got != expect { dmp := diffmatchpatch.New() diffs := dmp.DiffMain(expect, got, false) t.Errorf("output differs:\n%s", dmp.DiffPrettyText(diffs)) diff --git a/go.mod b/go.mod index 35db6db3..d6b3f3c8 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,9 @@ module github.com/zoncoen/scenarigo -go 1.21.2 +go 1.22.3 require ( carvel.dev/ytt v0.48.0 - github.com/Masterminds/semver v1.5.0 github.com/cenkalti/backoff/v4 v4.3.0 github.com/fatih/color v1.17.0 github.com/goccy/go-yaml v1.11.3 diff --git a/go.sum b/go.sum index bcd54e3e..c0ec7d23 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,6 @@ carvel.dev/ytt v0.48.0 h1:NNLW6mHBxYFhz8tRlaUW4xjtDmq8u8unmC0dcsCKPxU= carvel.dev/ytt v0.48.0/go.mod h1:ss9N6IKCUxg5yH5xomguFBQmuLnEwQr8slbAjTToCPQ= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= -github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=