diff --git a/internal/buildinfo/parse.go b/internal/buildinfo/parse.go index 31c4933..b5b2bd3 100644 --- a/internal/buildinfo/parse.go +++ b/internal/buildinfo/parse.go @@ -2,11 +2,14 @@ package buildinfo import ( "fmt" + "regexp" "strings" "github.com/uw-labs/lichen/internal/model" ) +var goVersionRgx = regexp.MustCompile(`^(.*?): (?:(?:devel )?go[0-9]+|devel \+[0-9a-f]+)`) + // Parse parses build info details as returned by `go version -m [bin ...]` func Parse(info string) ([]model.BuildInfo, error) { var ( @@ -23,28 +26,14 @@ func Parse(info string) ([]model.BuildInfo, error) { // start of new build info output if !strings.HasPrefix(l, "\t") { - parts := strings.Split(l, ":") - if len(parts) < 2 { - return nil, fmt.Errorf("invalid version line: %s", l) - } - version := strings.TrimSpace(parts[len(parts)-1]) - path := strings.Join(parts[:len(parts)-1], ":") - switch { - case version == "not executable file": - return nil, fmt.Errorf("%s is not an executable", parts[0]) - case version == "unrecognized executable format": - return nil, fmt.Errorf("%s has an unrecognized executable format", parts[0]) - case version == "go version not found": - return nil, fmt.Errorf("%s does not appear to be a Go compiled binary", parts[0]) - case strings.HasPrefix(version, "go"): - // sensible looking - default: + matches := goVersionRgx.FindStringSubmatch(l) + if len(matches) != 2 { return nil, fmt.Errorf("unrecognised version line: %s", l) } if current.Path != "" { results = append(results, current) } - current = model.BuildInfo{Path: path} + current = model.BuildInfo{Path: matches[1]} continue } diff --git a/internal/buildinfo/parse_test.go b/internal/buildinfo/parse_test.go index b90f5a8..5bad8b8 100644 --- a/internal/buildinfo/parse_test.go +++ b/internal/buildinfo/parse_test.go @@ -117,22 +117,43 @@ func TestParse(t *testing.T) { }, }, { - name: "not executable file", - input: `/tmp/lichen: not executable file`, - expectedErr: "/tmp/lichen is not an executable", + name: "development version (pre-go1.17)", + input: `/tmp/lichen: devel +01821137c2 Sat Apr 3 01:45:17 2021 +0000`, + expected: []model.BuildInfo{ + { + Path: "/tmp/lichen", + }, + }, + }, + { + name: "development version (current)", + input: `/tmp/lichen: devel go1.18-0c83e01e0c Wed Aug 18 15:11:52 2021 +0000`, + expected: []model.BuildInfo{ + { + Path: "/tmp/lichen", + }, + }, }, { - name: "unrecognised exe file", - input: `/tmp/lichen: unrecognized executable format`, - expectedErr: "/tmp/lichen has an unrecognized executable format", + name: "development version (old)", + input: `/tmp/lichen: devel +b7a85e0003 linux/amd64`, + expected: []model.BuildInfo{ + { + Path: "/tmp/lichen", + }, + }, }, { - name: "go version not found", - input: `/tmp/lichen: go version not found`, - expectedErr: "/tmp/lichen does not appear to be a Go compiled binary", + name: "windows development version", + input: `C:\lichen.exe: devel go1.18-0c83e01e0c Wed Aug 18 15:11:52 2021 +0000`, + expected: []model.BuildInfo{ + { + Path: `C:\lichen.exe`, + }, + }, }, { - name: "invalid", + name: "unrecognised line", input: `/tmp/lichen: invalid`, expectedErr: "unrecognised version line: /tmp/lichen: invalid", }, diff --git a/internal/module/extract.go b/internal/module/extract.go index 615f9e0..7c332bc 100644 --- a/internal/module/extract.go +++ b/internal/module/extract.go @@ -24,7 +24,7 @@ func Extract(ctx context.Context, paths ...string) ([]model.BuildInfo, error) { return nil, err } if err := verifyExtracted(parsed, paths); err != nil { - return nil, fmt.Errorf("could not extract module information from binaries: %v", paths) + return nil, fmt.Errorf("could not extract module information: %w", err) } return parsed, nil } @@ -37,7 +37,7 @@ func verifyExtracted(extracted []model.BuildInfo, requested []string) (err error } for _, path := range requested { if _, found := buildInfos[path]; !found { - err = multierror.Append(err, fmt.Errorf("modules could not be obtained from %s", path)) + err = multierror.Append(err, fmt.Errorf("modules could not be obtained from %[1]s (hint: run `go version -m %[1]q`)", path)) } } return