Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add verbose flag to "plugin" sub-command #454

Merged
merged 3 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ jobs:
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
disable_search: true
file: ./coverage-cmd.out
flags: cmd
fail_ci_if_error: true
Expand All @@ -36,6 +37,7 @@ jobs:
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
disable_search: true
file: ./coverage-module.out
flags: module
fail_ci_if_error: true
127 changes: 84 additions & 43 deletions cmd/scenarigo/cmd/plugin/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"errors"
"fmt"
"go/build"
goversion "go/version"
"io"
"io/fs"
"os"
"os/exec"
Expand Down Expand Up @@ -34,43 +36,59 @@ const (
var (
goVer string
toolchain string
tip bool
goMinVer string
versionTooHighErrorRegexp *regexp.Regexp
)

func init() {
goVer = runtime.Version()
toolchain = goVer
if strings.HasPrefix(goVer, "devel ") {
// gotip
goVer = strings.Split(strings.TrimPrefix(goVer, "devel "), "-")[0]
toolchain = "local"
tip = true
}
e := strings.Split(strings.TrimPrefix(goVer, "go"), ".")
if len(e) < 2 {
panic(fmt.Sprintf("%q is invalid Go version", goVer))
}
goVer, toolchain = parseGoVersion(runtime.Version())
goMinVer = "1.21.2"
versionTooHighErrorRegexp = regexp.MustCompile(versionTooHighErrorPattern)
}

var buildCmd = &cobra.Command{
Use: "build",
Short: "build plugins",
Long: strings.Trim(`
func parseGoVersion(ver string) (string, string) {
tc := ver
// gotip
if strings.HasPrefix(ver, "devel ") {
ver = strings.Split(strings.TrimPrefix(ver, "devel "), "-")[0]
tc = "local"
}
// go installed with homebrew (e.g., go1.23.2 X:rangefunc)
if !goversion.IsValid(ver) {
if v := strings.Split(ver, " ")[0]; goversion.IsValid(v) {
ver = v
tc = v
} else {
tc = "local"
}
}
return ver, tc
}

var verbose bool

func newBuildCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "build",
Short: "build plugins",
Long: strings.Trim(`
Builds plugins based on the configuration file.
This command requires go command in $PATH.
`, "\n"),
Args: cobra.ExactArgs(0),
RunE: buildRun,
SilenceErrors: true,
SilenceUsage: true,
Args: cobra.ExactArgs(0),
RunE: buildRun,
SilenceErrors: true,
SilenceUsage: true,
}
cmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "print verbose log")
return cmd
}

var warnColor = color.New(color.Bold, color.FgYellow)
var (
warnColor = color.New(color.Bold, color.FgYellow)
debugColor = color.New(color.Bold)
)

type retriableError struct {
reason string
Expand Down Expand Up @@ -103,6 +121,12 @@ func (o *overrideModule) requireReplace() (*modfile.Require, string, *modfile.Re
}

func buildRun(cmd *cobra.Command, args []string) error {
runtimeVersion := runtime.Version()
debugLog(cmd.OutOrStderr(), "scenarigo was built with %s", runtimeVersion)
if !goversion.IsValid(goVer) {
warnLog(cmd.OutOrStderr(), "failed to parse the Go version that built scenarigo: %s", runtimeVersion)
}

cfg, err := config.Load()
if err != nil {
return fmt.Errorf("failed to load config: %w", err)
Expand All @@ -115,6 +139,8 @@ func buildRun(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
debugLog(cmd.OutOrStderr(), "found go command: %s", goCmd)
debugLog(cmd.OutOrStderr(), "set GOTOOLCHAIN=%s", toolchain)

pbs := make([]*pluginBuilder, 0, cfg.Plugins.Len())
pluginModules := map[string]*overrideModule{}
Expand Down Expand Up @@ -506,11 +532,7 @@ func executeWithEnvs(ctx context.Context, envs []string, wd, name string, args .
var stdout bytes.Buffer
var stderr bytes.Buffer
cmd := exec.CommandContext(ctx, name, args...)
if tip {
envs = append(envs, "GOTOOLCHAIN=local")
} else {
envs = append(envs, fmt.Sprintf("GOTOOLCHAIN=%s", goVer))
}
envs = append(envs, fmt.Sprintf("GOTOOLCHAIN=%s", toolchain))
cmd.Env = append(os.Environ(), envs...)
if wd != "" {
cmd.Dir = wd
Expand All @@ -525,18 +547,26 @@ func executeWithEnvs(ctx context.Context, envs []string, wd, name string, args .

func (pb *pluginBuilder) updateGoMod(cmd *cobra.Command, goCmd string, overrideKeys []string, overrides map[string]*overrideModule) error {
if err := pb.editGoMod(cmd, goCmd, func(gomod *modfile.File) error {
switch compareVers(gomod.Go.Version, goVer) {
if toolchain == "local" {
if gomod.Toolchain != nil {
warnLog(cmd.OutOrStdout(), "%s: remove toolchain by scenarigo", pb.name)
gomod.DropToolchainStmt()
}
return nil
}

switch compareVers(gomod.Go.Version, toolchain) {
case -1: // go.mod < scenarigo go version
if gomod.Toolchain == nil {
fmt.Fprintf(cmd.OutOrStdout(), "%s: %s: add toolchain %s by scenarigo\n", warnColor.Sprint("WARN"), pb.name, goVer)
} else if gomod.Toolchain.Name != goVer {
fmt.Fprintf(cmd.OutOrStdout(), "%s: %s: change toolchain %s ==> %s by scenarigo\n", warnColor.Sprint("WARN"), pb.name, gomod.Toolchain.Name, goVer)
warnLog(cmd.OutOrStdout(), "%s: add toolchain %s by scenarigo", pb.name, toolchain)
} else if gomod.Toolchain.Name != toolchain {
warnLog(cmd.OutOrStdout(), "%s: change toolchain %s ==> %s by scenarigo", pb.name, gomod.Toolchain.Name, toolchain)
}
if err := gomod.AddToolchainStmt(goVer); err != nil {
if err := gomod.AddToolchainStmt(toolchain); err != nil {
return fmt.Errorf("%s: %w", pb.gomodPath, err)
}
case 1: // go.mod > scenarigo go version
return fmt.Errorf("%s: go: go.mod requires go >= %s (running go %s)", pb.gomodPath, gomod.Go.Version, goVer)
return fmt.Errorf("%s: go: go.mod requires go >= %s (scenarigo was built with %s)", pb.gomodPath, gomod.Go.Version, toolchain)
}
return nil
}); err != nil {
Expand Down Expand Up @@ -692,22 +722,22 @@ func printUpdatedRequires(cmd *cobra.Command, name string, overrides map[string]
if !diff.new.Indirect {
if o := overrides[k]; o != nil {
_, requiredBy, _, _ := o.requireReplace()
fmt.Fprintf(cmd.OutOrStdout(), "%s: %s: add require %s %s by %s\n", warnColor.Sprint("WARN"), name, k, diff.new.Mod.Version, requiredBy)
warnLog(cmd.OutOrStdout(), "%s: add require %s %s by %s", name, k, diff.new.Mod.Version, requiredBy)
} else {
fmt.Fprintf(cmd.OutOrStdout(), "%s: %s: add require %s %s\n", warnColor.Sprint("WARN"), name, k, diff.new.Mod.Version)
warnLog(cmd.OutOrStdout(), "%s: add require %s %s", name, k, diff.new.Mod.Version)
}
}
case diff.new.Mod.Path == "":
if !diff.old.Indirect {
fmt.Fprintf(cmd.OutOrStdout(), "%s: %s: remove require %s %s\n", warnColor.Sprint("WARN"), name, k, diff.old.Mod.Version)
warnLog(cmd.OutOrStdout(), "%s: remove require %s %s", name, k, diff.old.Mod.Version)
}
case diff.old.Mod.Version != diff.new.Mod.Version:
if !diff.old.Indirect || !diff.new.Indirect {
if o := overrides[k]; o != nil {
_, requiredBy, _, _ := o.requireReplace()
fmt.Fprintf(cmd.OutOrStdout(), "%s: %s: change require %s %s ==> %s by %s\n", warnColor.Sprint("WARN"), name, k, diff.old.Mod.Version, diff.new.Mod.Version, requiredBy)
warnLog(cmd.OutOrStdout(), "%s: change require %s %s ==> %s by %s", name, k, diff.old.Mod.Version, diff.new.Mod.Version, requiredBy)
} else {
fmt.Fprintf(cmd.OutOrStdout(), "%s: %s: change require %s %s ==> %s\n", warnColor.Sprint("WARN"), name, k, diff.old.Mod.Version, diff.new.Mod.Version)
warnLog(cmd.OutOrStdout(), "%s: change require %s %s ==> %s", name, k, diff.old.Mod.Version, diff.new.Mod.Version)
}
}
}
Expand Down Expand Up @@ -745,21 +775,21 @@ func printUpdatedReplaces(cmd *cobra.Command, name string, overrides map[string]
if replace != nil {
by = replaceBy
}
fmt.Fprintf(cmd.OutOrStdout(), "%s: %s: add replace %s => %s by %s\n", warnColor.Sprint("WARN"), name, replacePathVersion(k, diff.new.Old.Version), replacePathVersion(diff.new.New.Path, diff.new.New.Version), by)
warnLog(cmd.OutOrStdout(), "%s: add replace %s => %s by %s", name, replacePathVersion(k, diff.new.Old.Version), replacePathVersion(diff.new.New.Path, diff.new.New.Version), by)
} else {
fmt.Fprintf(cmd.OutOrStdout(), "%s: %s: add replace %s => %s\n", warnColor.Sprint("WARN"), name, replacePathVersion(k, diff.new.Old.Version), replacePathVersion(diff.new.New.Path, diff.new.New.Version))
warnLog(cmd.OutOrStdout(), "%s: add replace %s => %s", name, replacePathVersion(k, diff.new.Old.Version), replacePathVersion(diff.new.New.Path, diff.new.New.Version))
}
case diff.new.Old.Path == "":
fmt.Fprintf(cmd.OutOrStdout(), "%s: %s: remove replace %s => %s\n", warnColor.Sprint("WARN"), name, replacePathVersion(k, diff.old.Old.Version), replacePathVersion(diff.old.New.Path, diff.old.New.Version))
warnLog(cmd.OutOrStdout(), "%s: remove replace %s => %s", name, replacePathVersion(k, diff.old.Old.Version), replacePathVersion(diff.old.New.Path, diff.old.New.Version))
case diff.old.New.Path != diff.new.New.Path || diff.old.New.Version != diff.new.New.Version:
if o := overrides[k]; o != nil {
_, by, replace, replaceBy := o.requireReplace()
if replace != nil {
by = replaceBy
}
fmt.Fprintf(cmd.OutOrStdout(), "%s: %s: change replace %s => %s ==> %s => %s by %s\n", warnColor.Sprint("WARN"), name, replacePathVersion(k, diff.old.Old.Version), replacePathVersion(diff.old.New.Path, diff.old.New.Version), replacePathVersion(k, diff.new.Old.Version), replacePathVersion(diff.new.New.Path, diff.new.New.Version), by)
warnLog(cmd.OutOrStdout(), "%s: change replace %s => %s ==> %s => %s by %s", name, replacePathVersion(k, diff.old.Old.Version), replacePathVersion(diff.old.New.Path, diff.old.New.Version), replacePathVersion(k, diff.new.Old.Version), replacePathVersion(diff.new.New.Path, diff.new.New.Version), by)
} else {
fmt.Fprintf(cmd.OutOrStdout(), "%s: %s: change replace %s => %s ==> %s => %s\n", warnColor.Sprint("WARN"), name, replacePathVersion(k, diff.old.Old.Version), replacePathVersion(diff.old.New.Path, diff.old.New.Version), replacePathVersion(k, diff.new.Old.Version), replacePathVersion(diff.new.New.Path, diff.new.New.Version))
warnLog(cmd.OutOrStdout(), "%s: change replace %s => %s ==> %s => %s", name, replacePathVersion(k, diff.old.Old.Version), replacePathVersion(diff.old.New.Path, diff.old.New.Version), replacePathVersion(k, diff.new.Old.Version), replacePathVersion(diff.new.New.Path, diff.new.New.Version))
}
}
}
Expand Down Expand Up @@ -889,3 +919,14 @@ func asVersionTooHighError(err error) (bool, *versionTooHighError) {
}
return false, nil
}

func warnLog(w io.Writer, format string, a ...any) {
fmt.Fprintf(w, fmt.Sprintf("%s: %s\n", warnColor.Sprint("WARN"), format), a...)
}

func debugLog(w io.Writer, format string, a ...any) {
if !verbose {
return
}
fmt.Fprintf(w, fmt.Sprintf("%s: %s\n", debugColor.Sprint("DEBUG"), format), a...)
}
82 changes: 75 additions & 7 deletions cmd/scenarigo/cmd/plugin/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
var (
bash string
echo string
_ = newBuildCmd()
)

func init() {
Expand All @@ -46,6 +47,47 @@ func init() {
}
}

func TestParseGoVersion(t *testing.T) {
tests := map[string]struct {
version string
expect string
expectToolchain string
}{
"go1.2.3": {
version: "go1.2.3",
expect: "go1.2.3",
expectToolchain: "go1.2.3",
},
"devel go1.24-76f320836": {
version: "devel go1.24-76f320836",
expect: "go1.24",
expectToolchain: "local",
},
"go1.23.2 X:rangefunc": {
version: "go1.23.2 X:rangefunc",
expect: "go1.23.2",
expectToolchain: "go1.23.2",
},
"invalid": {
version: "invalid",
expect: "invalid",
expectToolchain: "local",
},
}
for name, test := range tests {
test := test
t.Run(name, func(t *testing.T) {
got, gotToolchain := parseGoVersion(test.version)
if got != test.expect {
t.Errorf("expect %s but got %s", test.expect, got)
}
if gotToolchain != test.expectToolchain {
t.Errorf("expect toolchain %s but got %s", test.expectToolchain, gotToolchain)
}
})
}
}

func TestBuild(t *testing.T) {
goVersion := strings.TrimPrefix(goVer, "go")
pluginCode := `package main
Expand Down Expand Up @@ -1200,6 +1242,11 @@ func TestFindGoCmd(t *testing.T) {
func TestUpdateGoMod(t *testing.T) {
goVersion := strings.TrimPrefix(goVer, "go")

gomodToolchain := toolchain
if toolchain == "local" {
gomodToolchain = "default"
}

t.Run("success", func(t *testing.T) {
tests := map[string]struct {
gomod string
Expand Down Expand Up @@ -1227,9 +1274,12 @@ go 1.21
go 1.21
toolchain go%s
`, goVersion),
expectStdout: fmt.Sprintf("WARN: test.so: add toolchain go%s by scenarigo\n", goVersion),
toolchain %s
`, toolchain),
expectStdout: tipOut(
"",
fmt.Sprintf("WARN: test.so: add toolchain %s by scenarigo\n", gomodToolchain),
),
},
"change toolchain directive": {
gomod: `module plugin_module
Expand All @@ -1242,9 +1292,12 @@ toolchain go1.21.1
go 1.21
toolchain go%s
`, goVersion),
expectStdout: fmt.Sprintf("WARN: test.so: change toolchain go1.21.1 ==> go%s by scenarigo\n", goVersion),
toolchain %s
`, toolchain),
expectStdout: tipOut(
"WARN: test.so: remove toolchain by scenarigo\n",
fmt.Sprintf("WARN: test.so: change toolchain go1.21.1 ==> %s by scenarigo\n", gomodToolchain),
),
},
"do nothing (no requires)": {
gomod: fmt.Sprintf(`module plugin_module
Expand Down Expand Up @@ -1815,6 +1868,9 @@ replace google.golang.org/grpc v1.46.0 => google.golang.org/grpc v1.40.0
if err != nil {
t.Fatalf("failed read go.mod: %s", err)
}
if toolchain == "local" {
test.expect = strings.ReplaceAll(test.expect, "\ntoolchain local\n", "")
}
if got := string(b); got != test.expect {
dmp := diffmatchpatch.New()
diffs := dmp.DiffMain(test.expect, got, false)
Expand Down Expand Up @@ -1986,6 +2042,11 @@ import (
})

t.Run("failure", func(t *testing.T) {
tooHighError := fmt.Sprintf("go.mod requires go >= 100.0.0 (scenarigo was built with %s)", goVer)
if toolchain == "local" {
tooHighError = fmt.Sprintf(`failed to edit toolchain directive: "go mod tidy" failed: go: go.mod requires go >= 100.0.0 (running go %s; GOTOOLCHAIN=local)`, strings.TrimPrefix(goVer, "go"))
}

tests := map[string]struct {
gomod string
src string
Expand All @@ -1997,7 +2058,7 @@ import (
go 100.0.0
`,
expect: fmt.Sprintf("go.mod requires go >= 100.0.0 (running go %s)", goVer),
expect: tooHighError,
},
}
for name, test := range tests {
Expand Down Expand Up @@ -2179,3 +2240,10 @@ func createExecutable(t *testing.T, path, stdout string) {
t.Fatalf("failed to write %s: %s", path, err)
}
}

func tipOut(tip, s string) string {
if toolchain == "local" {
return tip
}
return s
}
2 changes: 1 addition & 1 deletion cmd/scenarigo/cmd/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ package plugin
import "github.com/spf13/cobra"

func Commands() []*cobra.Command {
return []*cobra.Command{buildCmd, listCmd}
return []*cobra.Command{newBuildCmd(), listCmd}
}
Loading
Loading