From 0730f56cc7f330a8d8e9eecb49215916e5f0275b Mon Sep 17 00:00:00 2001 From: "Nathan J. Mehl" Date: Tue, 9 Jul 2024 16:42:47 -0400 Subject: [PATCH] attempt to handle --version even in untagged builds If we are installed via `go-install` rather than downloading a tagged binary from github, the `Tag` etc variables will all be empty, and therefore the `--version` flag is diked out. But there is a way around this! If the version info is not filled in via build-time flags, use `debug.BuildInfo` to introspect as much information as we can out of the module metadata: ``` $ go install github.com/odenio/goimports-reviser/v3@v3.6.6-pre5 go: downloading github.com/odenio/goimports-reviser/v3 v3.6.6-pre5 $ ~/go/bin/goimports-reviser --version version: 3.6.6-pre5 built with: go1.22.4 tag: v3.6.6-pre5 commit: n/a source: github.com/odenio/goimports-reviser/v3 ``` Additionally, add a `--version-only` flag that prints only the version string itself, handy for use in shell pipelines. Can be used on its own or in combination with the `--version` flag: ``` $ ./go/bin/goimports-reviser --version-only 3.6.6-pre5 ``` --- main.go | 100 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 86 insertions(+), 14 deletions(-) diff --git a/main.go b/main.go index b376175..3233923 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,7 @@ package main import ( "crypto/md5" "encoding/hex" + "errors" "flag" "fmt" "log" @@ -10,6 +11,8 @@ import ( "os/user" "path" "path/filepath" + "regexp" + "runtime/debug" "strings" "github.com/incu6us/goimports-reviser/v3/helper" @@ -19,6 +22,7 @@ import ( const ( projectNameArg = "project-name" versionArg = "version" + versionOnlyArg = "version-only" removeUnusedImportsArg = "rm-unused" setAliasArg = "set-alias" companyPkgPrefixesArg = "company-prefixes" @@ -31,6 +35,7 @@ const ( useCacheArg = "use-cache" applyToGeneratedFiles = "apply-to-generated-files" excludesArg = "excludes" + modulePathRegex = `^github.com/\w+/goimports-reviser(/v\d+)?@?` // Deprecated options localArg = "local" @@ -45,6 +50,7 @@ var ( GoVersion string shouldShowVersion *bool + shouldShowVersionOnly *bool shouldRemoveUnusedImports *bool shouldSetAlias *bool shouldFormat *bool @@ -53,6 +59,7 @@ var ( setExitStatus *bool isRecursive *bool isUseCache *bool + modulePathMatcher = regexp.MustCompile(modulePathRegex) ) var ( @@ -168,13 +175,18 @@ Optional parameter.`, "Apply imports sorting and formatting(if the option is set) to generated files. Generated file is a file with first comment which starts with comment '// Code generated'. Optional parameter.", ) - if Tag != "" { - shouldShowVersion = flag.Bool( - versionArg, - false, - "Show version.", - ) - } + shouldShowVersion = flag.Bool( + versionArg, + false, + "Show version information", + ) + + shouldShowVersionOnly = flag.Bool( + versionOnlyArg, + false, + "Show only the version string", + ) + } func printUsage() { @@ -185,27 +197,87 @@ func printUsage() { flag.PrintDefaults() } +func getBuildInfo() *debug.BuildInfo { + bi, ok := debug.ReadBuildInfo() + if !ok { + return nil + } + return bi +} + +func getMyModuleInfo(bi *debug.BuildInfo) (*debug.Module, error) { + if bi == nil { + return nil, errors.New("no build info available") + } + // depending on the context in which we are called, the main module may not be set + if bi.Main.Path != "" { + return &bi.Main, nil + } + // if the main module is not set, we need to find the dep that contains our module + for _, m := range bi.Deps { + if modulePathMatcher.MatchString(m.Path) { + return m, nil + } + } + return nil, errors.New("no matching module found in build info") +} + func printVersion() { + if Tag != "" { + fmt.Printf( + "version: %s\nbuilt with: %s\ntag: %s\ncommit: %s\nsource: %s\n", + strings.TrimPrefix(Tag, "v"), + GoVersion, + Tag, + Commit, + SourceURL, + ) + return + } + bi := getBuildInfo() + myModule, err := getMyModuleInfo(bi) + if err != nil { + log.Fatalf("failed to get my module info: %s", err) + } fmt.Printf( - "version: %s\nbuild with: %s\ntag: %s\ncommit: %s\nsource: %s\n", - strings.TrimPrefix(Tag, "v"), - GoVersion, - Tag, - Commit, - SourceURL, + "version: %s\nbuilt with: %s\ntag: %s\ncommit: %s\nsource: %s\n", + strings.TrimPrefix(myModule.Version, "v"), + bi.GoVersion, + myModule.Version, + "n/a", + myModule.Path, ) } +func printVersionOnly() { + if Tag != "" { + fmt.Println(strings.TrimPrefix(Tag, "v")) + return + } + bi := getBuildInfo() + myModule, err := getMyModuleInfo(bi) + if err != nil { + log.Fatalf("failed to get my module info: %s", err) + } + fmt.Println(strings.TrimPrefix(myModule.Version, "v")) +} + func main() { deprecatedMessagesCh := make(chan string, 10) flag.Parse() + if shouldShowVersionOnly != nil && *shouldShowVersionOnly { + printVersionOnly() + return + } + if shouldShowVersion != nil && *shouldShowVersion { printVersion() return } originPath := flag.Arg(0) + if filePath != "" { deprecatedMessagesCh <- fmt.Sprintf("-%s is deprecated. Put file name as last argument to the command(Example: goimports-reviser -rm-unused -set-alias -format goimports-reviser/main.go)", filePathArg) originPath = filePath @@ -391,7 +463,7 @@ func validateRequiredParam(filePath string) error { stat, _ := os.Stdin.Stat() if stat.Mode()&os.ModeNamedPipe == 0 { // no data on stdin - return fmt.Errorf("no data on stdin") + return errors.New("no data on stdin") } } return nil