Skip to content

Commit

Permalink
Merge pull request #454 from zoncoen/debug
Browse files Browse the repository at this point in the history
feat(plugin): add verbose flag
  • Loading branch information
zoncoen authored Oct 24, 2024
2 parents 4434685 + 0d0acbc commit b0eb572
Show file tree
Hide file tree
Showing 11 changed files with 201 additions and 59 deletions.
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

0 comments on commit b0eb572

Please sign in to comment.