10

How do we make go vet, gofmt, and other Go linter tools ignore third-party files in vendor/, preferably with an accurate, cumulative exit status?

For example, would find . -name vendor -prune -o -name '*.go' -exec gofmt -s -w {} \; present a meaningful exit status?

mcandre
  • 22,868
  • 20
  • 88
  • 147
  • Note: https://github.com/golang/go/issues/19090 (for `go vet` specifically) follows that issues and reference other issues. – VonC Nov 04 '17 at 11:12
  • With go 1.10 (Q1 2018), go vet won'f fail with `uses unkeyed fields` errors when depending on vendor packages. See [my answer below](https://stackoverflow.com/a/47110039/6309) – VonC Nov 04 '17 at 11:23

9 Answers9

9

I usually do

go fmt $(go list ./... | grep -v /vendor/)
go test $(go list ./... | grep -v /vendor/)

But since I started using govendor discovered I can do the same using govendor with less typing

govendor fmt +l // +l is shorthand for local
Havelock
  • 6,913
  • 4
  • 34
  • 42
4

Usually go list is more helpful than find here:

$ go list ./... 
arp242.net/trackwall
arp242.net/trackwall/cmdline
arp242.net/trackwall/vendor/arp242.net/sconfig
arp242.net/trackwall/vendor/bitbucket.org/pkg/inflect
[..trim..]

As you see, this will list all the package names in the current directory. To filter the vendor directory we can use grep:

$ go list ./... | grep -v /vendor/
arp242.net/trackwall
arp242.net/trackwall/cmdline

If you want to run several linters, report all errors, and return 0 only if there is a success, you could use a loop and a status variable:

#!/bin/sh

st=0
for pkg in $(go list ./... | grep -v /vendor/); do
    echo "==> $pkg"

    go vet "$pkg"
    [ $? -ne 0 ] && st=1

    golint "$pkg"
    [ $? -ne 0 ] && st=1

    # gofmt works on files, not packages
    gofmt -d "${f#arp242.net/trackwall}"*.go
    [ $? -ne 0 ] && st=1
done
exit $st

Which will output something like:

==> arp242.net/trackwall
http.go:71: database/sql.NullString composite literal uses unkeyed fields
exit status 1
/home/martin/gocode/src/arp242.net/trackwall/http.go:70:2: don't use ALL_CAPS in Go names; use CamelCase
/home/martin/gocode/src/arp242.net/trackwall/http.go:75:9: if block ends with a return statement, so drop this else and outdent its block
==> arp242.net/trackwall/cmdline
Exit 1

Of course, you're not the first one with this problem, and there are tools which basically do the same as above, except better. gometalinter is probably the best known and I recommend you use this. It helpfully includes a --vendor switch to ignore the vendor directory:

$ go get -u github.com/alecthomas/gometalinter

$ gometalinter --vendor ./...
helpers.go:25:1:warning: realpath is unused (deadcode)
http.go:32:1:warning: _list is unused (deadcode)
[..trim..]
Martin Tournoij
  • 26,737
  • 24
  • 105
  • 146
2

Thanks for all the helpful suggestions! This works for my purposes:

https://github.com/mcandre/go-ios7crypt/blob/ea8dd957e8f146ea20a57122870008a968875b53/Makefile

mcandre
  • 22,868
  • 20
  • 88
  • 147
2

Here is what I use:

golint $(ls -d1 */ | sed s/\\//\\/.../g | grep -v -E "^vendor/" | tr "\n" " ")
Csongor Halmai
  • 3,239
  • 29
  • 30
1

You can put all your source code into a subdirectory (e.g. internal or lib). Alternatively, you can do this:

go vet ./... | grep -v vendor/ && exit 1 || exit 0
Ainar-G
  • 34,563
  • 13
  • 93
  • 119
1

With Go 1.10 (Q1 2018), the kind of tips mentioned by the OP mcandre's answer will be more robust:

go list ./... | grep -v vendor | xargs go vet -v

As illustrated in carpetsmoker's answer, a go vet on packages depending on (yet non-compiled) vendored packages will fail:

==> arp242.net/trackwall
http.go:71: database/sql.NullString composite literal uses unkeyed fields

Note anymore, since go issue 16086 is resolved!

See this thread:

Go vet

The "go vet" command works best with full type information for the packages it is analyzing. Historically that's been problematic in a variety of situations: packages using cgo, packages using vendoring, and packages that don't have up-to-date installed dependencies have all been things that kept vet from running with full type information.

Not anymore. The "go vet" command now passes to vet full information about all these things, building new .a files if needed (CL 74355 and CL 74750).
Now "go vet" is guaranteed to have up-to-date type information when it analyzes a package, which will improve the accuracy of its analyses.
Having the build cache to amortize the cost of building those .a files was the final piece needed to make this happen.

Only "go vet" has this guarantee. Do not use "go tool vet", which is essentially only useful to people working on vet (just like you don't typically run "go tool compile").
Previously you needed to use "go tool vet" if you wanted control over vet flags, but "go vet" now accepts all the flags that "go tool vet" does. (See "go help vet".)

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
0

In a cross platform fashion i do this philea -s "666 go vet %s" "666 go-fmt-fail %s"

yields,

$ ls -alh | grep ven
drwxr-xr-x  5 mh-cbon mh-cbon 4,0K 14 juin  20:43 vendor
$ philea -s "666 go vet %s" "666 go-fmt-fail %s"
go-fmt-fail ./local/local.go
 ✔ Success
go-fmt-fail ./dl/index.go
 ✔ Success
go vet ./local/local.go
 ✔ Success
go-fmt-fail ./gh/gh.go
 ✔ Success
go vet ./main.go
 ✔ Success
go vet ./gh/gh.go
 ✔ Success
go vet ./dl/index.go
 ✔ Success
go-fmt-fail ./main.go
 ✔ Success
  • 1
    I'm not sure what philae is, and have failed to find out on my own, you should probably link to whatever that is – nothingmuch Nov 10 '16 at 19:19
  • 1
    those stuff here https://github.com/mh-cbon/philea and here https://github.com/mh-cbon/go-fmt-fail and also this https://github.com/mh-cbon/666 –  Nov 10 '16 at 20:14
0

In Go 1.9, a change was introduced for ./... to no longer match packages in the vendor directory (release notes), which was a popular issue.

This means that for most purposes, the easiest way is now

go test ./...  # ignores vendor
go fmt ./...   # ignores vendor
go list ./...  # ignores vendor
# etc.

However, specifically for gofmt (which we might want to use if go fmt doesn't have the option we need), the case is not as clear cut:

  • gofmt takes paths as arguments, not packages
  • it works recursively if given directories, so it seems like just gofmt . should work – but that does not ignore vendor

So either we have to go back to

gofmt -l . | grep -v vendor

but this would match vendor for a directory that is not the actual vendor directory, and it feels wasteful to check everything in vendor before throwing it away.

We also can't use

gofmt -l $(go list -f '{{.Dir}}' ./...)

because ./... matches the package of the current directory, causing gofmt to descend everywhere including vendor.

A more robust solution using templates iterating over the files directly

gofmt -l $(go list -f '{{range .GoFiles}} {{$.Dir}}/{{.}}{{end}}' ./...)

can run into command line length limitations in a large directory tree with longs paths, so that's not perfect either. My inclination is to use go fmt ./... where possible.

Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
0

If under Makefile(as we usually do):

gofmt:
    gofmt -l $(shell find . -type f -name '*.go'| grep -v "/vendor/\|/.git/") # ignore vendor and .git dir

How to add \t before gofmt on StackOverflow web page? -__-

coanor
  • 3,746
  • 4
  • 50
  • 67