diff --git a/.golangci.yml b/.golangci.yml index fe894532..3799f756 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -24,12 +24,38 @@ linters: - goimports - gofmt - exhaustivestruct + - godox # presets: # - bugs # - unused # bugs|comment|complexity|error|format|import|metalinter|module|performance|sql|style|test|unused # fast: true - +issues: + exclude-rules: + - path: _test\.go + linters: + - goerr113 + - wrapcheck + - funlen # complexity length in tests isn't a big concern + - cyclop # complexity length in tests isn't a big concern + - gocognit # complexity length in tests isn't a big concern + - unparam # unused params in tests isn't as important + - varnamelen # don't worry about variable name length in test code (nice to keep clean, but not important enough to speed time on) + - revive # additional checks like constants and more aren't important in the test code + - linters: + - goerr113 + text: do not define dynamic errors + - path: .*mage.*go + linters: + - goerr113 + - wrapcheck + - linters: + - goerr113 + text: magefiles don't need to worry about wrapping in the same way + - linters: + - govet + - revive + text: 'shadow: declaration of .err. shadows declaration' # err shadowing is a normal practice, don't worry about this run: skip-dirs: - cmd/test-files @@ -38,6 +64,7 @@ run: - _tools - starter/* - _artifacts/ + - _test.go # lint magefile, but ignore tools.go build-tags: diff --git a/.tasks/tasks.go b/.tasks/tasks.go index 26e5fdcb..35f6c461 100644 --- a/.tasks/tasks.go +++ b/.tasks/tasks.go @@ -57,7 +57,7 @@ func Init() error { mg.SerialDeps( Clean, createDirectories, - (gotools.Go{}.Init()), + gotools.Go{}.Init, // tooling.SilentInstallTools(toolList), ) // if err := (gotools.Go{}.Init()); err != nil { diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..b2188363 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,26 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch test function", + "type": "go", + "request": "launch", + "mode": "test", + "program": "${workspaceFolder}/pkg/magetoolsutils", + "args": [ + "-test.run", + "Test_CheckPtermDebug", + ] + }, + { + "name": "Launch file", + "type": "go", + "request": "launch", + "mode": "debug", + "program": "${file}" + } + ] +} diff --git a/README.md b/README.md index 50b9a9c6..0ae0e0a6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # magetools -![Coverage](https://img.shields.io/badge/Coverage-54.0%25-yellow) +![Coverage](https://img.shields.io/badge/Coverage-56.0%25-yellow) General tooling helpers for simplifying cross repository automation using Mage. diff --git a/gotools/gotools.go b/gotools/gotools.go index 740d0fa3..6516f349 100644 --- a/gotools/gotools.go +++ b/gotools/gotools.go @@ -76,6 +76,9 @@ var toolList = []string{ //nolint:gochecknoglobals // ok to be global for toolin "github.com/haya14busa/goplay/cmd/goplay@latest", "github.com/go-delve/delve/cmd/dlv@latest", "github.com/rogpeppe/godef@latest", + + // Self setup mage + "github.com/magefile/mage@latest", } // getModuleName returns the name from the module file. diff --git a/pkg/magetoolsutils/magetoolsutils.go b/pkg/magetoolsutils/magetoolsutils.go index 38438f40..20b6762b 100644 --- a/pkg/magetoolsutils/magetoolsutils.go +++ b/pkg/magetoolsutils/magetoolsutils.go @@ -3,17 +3,79 @@ package magetoolsutils import ( "os" + "strconv" "github.com/magefile/mage/mg" "github.com/pterm/pterm" ) // checkPtermDebug looks for DEBUG=1 and sets debug level output if this is found to help troubleshoot tasks. -func CheckPtermDebug() { - if os.Getenv("DEBUG") == "1" || mg.Verbose() { +func CheckPtermDebug() { //nolint:cyclop,funlen // cyclop,funlen: i'm sure there's a better way, but for now it works, refactor later // TODO: simplify this env var logic check with helpers + // var debug bool + // var err error + + // pterm.Debug.Printfln( + // "\nDEBUG: %v\n"+"SYSTEM_DEBUG: %v\n"+"ACTIONS_STEP_DEBUG: %v\n", + // os.Getenv("DEBUG"), + // os.Getenv("SYSTEM_DEBUG"), + // os.Getenv("ACTIONS_STEP_DEBUG"), + // ) + + // --------------------- GENERAL DEBUG --------------------- + envDebug, isSet := os.LookupEnv("DEBUG") + if isSet { + debug, err := strconv.ParseBool(envDebug) + if err != nil { + pterm.Warning.WithShowLineNumber(true). + WithLineNumberOffset(1). + Printfln("ParseBool(DEBUG): %v\t debug: %v", err, debug) + } + if debug { + pterm.Debug.Println("strconv.ParseBool(\"DEBUG\") true, enabling debug output and exiting") + pterm.Info.Println("DEBUG env var detected, setting tasks to debug level output") + pterm.EnableDebugMessages() + return + } + } + + // --------------------- AZURE DEVOPS DEBUG --------------------- + envSystemDebug, isSet := os.LookupEnv("SYSTEM_DEBUG") + if isSet { + debug, err := strconv.ParseBool(envSystemDebug) // CI: azure devops uses this for diagnostic level output + if err != nil { + pterm.Warning.WithShowLineNumber(true). + WithLineNumberOffset(1). + Printfln("ParseBool(SYSTEM_DEBUG): %v\t debug: %v", err, debug) + } + + if debug { + pterm.Debug.Println("strconv.ParseBool(\"SYSTEM_DEBUG\") true, enabling debug output and exiting") + pterm.Info.Println("SYSTEM_DEBUG env var detected, setting tasks to debug level output") + pterm.EnableDebugMessages() + return + } + } + + // --------------------- GITHUB ACTIONS DEBUG --------------------- + envActionsDebug, isSet := os.LookupEnv("ACTIONS_STEP_DEBUG") + if isSet { + debug, err := strconv.ParseBool(envActionsDebug) // CI: github uses this for diagnostic level output + if err != nil { + pterm.Warning.WithShowLineNumber(true). + WithLineNumberOffset(1). + Printfln("ParseBool(ACTIONS_STEP_DEBUG): %v\t debug: %v", err, debug) + } + if debug { + pterm.Debug.Println("strconv.ParseBool(\"ACTIONS_STEP_DEBUG\") true, enabling debug output and exiting") + pterm.Info.Println("ACTIONS_STEP_DEBUG env var detected, setting tasks to debug level output") + pterm.EnableDebugMessages() + return + } + } + if mg.Verbose() { + pterm.Debug.Printfln("mg.Verbose() true, setting pterm.EnableDebugMessages()") + pterm.Info.Println("mg.Verbose() true (-v or MAGEFILE_VERBOSE env var), setting tasks to debug level output") pterm.EnableDebugMessages() - pterm.Debug.Println("DEBUG enabled per env variable DEBUG = 1 or mage verbose flag") - } else { - pterm.DisableDebugMessages() + return } } diff --git a/pkg/magetoolsutils/magetoolsutils_test.go b/pkg/magetoolsutils/magetoolsutils_test.go index d1f16086..f93fa0f4 100644 --- a/pkg/magetoolsutils/magetoolsutils_test.go +++ b/pkg/magetoolsutils/magetoolsutils_test.go @@ -12,39 +12,126 @@ import ( "github.com/pterm/pterm" ) -func TestFuncName(t *testing.T) { - pterm.DisableStyling() - pterm.DisableOutput() +func Test_CheckPtermDebug(t *testing.T) { + // pterm.EnableDebugMessages() + // pterm.DisableDebugMessages() + // pterm.DisableStyling() + // pterm.DisableOutput() orig := os.Getenv("DEBUG") defer os.Setenv("DEBUG", orig) - t.Run("DEBUG is set to 0", func(t *testing.T) { - is := iz.New(t) - os.Setenv("DEBUG", "0") - u.CheckPtermDebug() - is.Equal(pterm.PrintDebugMessages, false) // DEBUG = 0 should be false - os.Unsetenv("DEBUG") - }) - t.Run("DEBUG is set to 1", func(t *testing.T) { - is := iz.New(t) - os.Setenv("DEBUG", "1") - u.CheckPtermDebug() - is.Equal(pterm.PrintDebugMessages, true) // DEBUG = 1 should be true - os.Unsetenv("DEBUG") - }) - t.Run("DEBUG is set to empty string", func(t *testing.T) { - is := iz.New(t) - os.Setenv("DEBUG", "") - u.CheckPtermDebug() - is.Equal(pterm.PrintDebugMessages, false) // DEBUG = "" should be false - os.Unsetenv("DEBUG") - }) - t.Run("DEBUG is unset", func(t *testing.T) { - is := iz.New(t) - os.Unsetenv("DEBUG") - u.CheckPtermDebug() - is.Equal(pterm.PrintDebugMessages, false) // DEBUG unset should be false - os.Unsetenv("DEBUG") - }) + origSystem := os.Getenv("SYSTEM_DEBUG") + defer os.Setenv("SYSTEM_DEBUG", origSystem) + + origActions := os.Getenv("ACTIONS_STEP_DEBUG") + defer os.Setenv("ACTIONS_STEP_DEBUG", origActions) + testCases := []struct { + desc string + envvar string + }{ + { + desc: "DEBUG general flag", + envvar: "DEBUG", + }, + { + desc: "SYSTEM_DEBUG azure-devops flag", + envvar: "SYSTEM_DEBUG", + }, + { + desc: "ACTIONS_STEP_DEBUG github-actions flag", + envvar: "ACTIONS_STEP_DEBUG", + }, + } + for _, tt := range testCases { + tt := tt + // Cannot be used in parallel tests + t.Run(tt.envvar+" unset should be false", func(t *testing.T) { + is := iz.New(t) + u.CheckPtermDebug() + is.Equal(pterm.PrintDebugMessages, false) // unset should be false + }) + t.Run(tt.envvar+" 0 should be false", func(t *testing.T) { + var err error + is := iz.New(t) + err = os.Setenv(tt.envvar, "0") + is.NoErr(err) // os.SetEnv() should not error + u.CheckPtermDebug() + is.Equal(pterm.PrintDebugMessages, false) // 0 should be false + err = os.Unsetenv(tt.envvar) + is.NoErr(err) // os.Unsetenv should not error for test cleanup + pterm.DisableDebugMessages() + }) + t.Run(tt.envvar+" false should be false", func(t *testing.T) { + var err error + is := iz.New(t) + err = os.Setenv(tt.envvar, "false") + is.NoErr(err) // os.SetEnv() should not error + u.CheckPtermDebug() + is.Equal(pterm.PrintDebugMessages, false) // "false" should be false + err = os.Unsetenv(tt.envvar) + is.NoErr(err) // os.Unsetenv should not error for test cleanup + pterm.DisableDebugMessages() + }) + t.Run(tt.envvar+" False should be false", func(t *testing.T) { + var err error + is := iz.New(t) + os.Setenv(tt.envvar, orig) + err = os.Setenv(tt.envvar, "False") + is.NoErr(err) // os.SetEnv() should not error + u.CheckPtermDebug() + is.Equal(pterm.PrintDebugMessages, false) // "False" should be false + err = os.Unsetenv(tt.envvar) + is.NoErr(err) // os.Unsetenv should not error for test cleanup + pterm.DisableDebugMessages() + }) + t.Run(tt.envvar+" blank should be false", func(t *testing.T) { + var err error + is := iz.New(t) + os.Setenv(tt.envvar, orig) + err = os.Setenv(tt.envvar, "") + is.NoErr(err) // os.SetEnv() should not error + u.CheckPtermDebug() + is.Equal(pterm.PrintDebugMessages, false) // "" should be false + err = os.Unsetenv(tt.envvar) + is.NoErr(err) // os.Unsetenv should not error for test cleanup + pterm.DisableDebugMessages() + }) + t.Run(tt.envvar+" 1 should be true", func(t *testing.T) { + var err error + is := iz.New(t) + os.Setenv(tt.envvar, orig) + err = os.Setenv(tt.envvar, "1") + is.NoErr(err) // os.SetEnv() should not error + u.CheckPtermDebug() + is.Equal(pterm.PrintDebugMessages, true) // "1" should be true + err = os.Unsetenv(tt.envvar) + is.NoErr(err) // os.Unsetenv should not error for test cleanup + pterm.DisableDebugMessages() + }) + t.Run(tt.envvar+" true should be true", func(t *testing.T) { + var err error + is := iz.New(t) + os.Setenv(tt.envvar, orig) + err = os.Setenv(tt.envvar, "true") + is.NoErr(err) // os.SetEnv() should not error + u.CheckPtermDebug() + is.Equal(pterm.PrintDebugMessages, true) // "true" should be true + err = os.Unsetenv(tt.envvar) + is.NoErr(err) // os.Unsetenv should not error for test cleanup + pterm.DisableDebugMessages() + }) + t.Run(tt.envvar+" True should be true", func(t *testing.T) { + var err error + is := iz.New(t) + os.Setenv(tt.envvar, orig) + err = os.Setenv(tt.envvar, "True") + is.NoErr(err) // os.SetEnv() should not error + u.CheckPtermDebug() + is.Equal(pterm.PrintDebugMessages, true) // "True" should be true + err = os.Unsetenv(tt.envvar) + is.NoErr(err) // os.Unsetenv should not error for test cleanup + pterm.DisableDebugMessages() + }) + } } diff --git a/starter/root-imports-with-tasks-in-subdirectory/.tasks/tasks.go b/starter/root-imports-with-tasks-in-subdirectory/.tasks/tasks.go index c970f6fd..17f576eb 100644 --- a/starter/root-imports-with-tasks-in-subdirectory/.tasks/tasks.go +++ b/starter/root-imports-with-tasks-in-subdirectory/.tasks/tasks.go @@ -8,6 +8,7 @@ import ( "github.com/pterm/pterm" "github.com/sheldonhull/magetools/ci" "github.com/sheldonhull/magetools/fancy" + // "github.com/sheldonhull/magetools/tooling" // mage:import @@ -58,7 +59,7 @@ func Init() error { mg.SerialDeps( Clean, createDirectories, - (gotools.Go{}.Init()), + (gotools.Go{}.Init), // tooling.SilentInstallTools(toolList), ) // if err := (gotools.Go{}.Init()); err != nil {