如何让 Go Linters 忽略 vendor/ 目录?

huangapple go评论156阅读模式
英文:

How to make go linters ignore vendor/?

问题

我们如何让go vetgofmt和其他Go语言的代码检查工具忽略vendor/目录下的第三方文件,并且最好能够准确地返回累积的退出状态?

例如,使用find . -name vendor -prune -o -name '*.go' -exec gofmt -s -w {} \;命令是否能够提供有意义的退出状态?

英文:

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?

答案1

得分: 9

我通常会执行以下操作:

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

但是自从我开始使用govendor后,我发现可以用更少的输入来使用govendor完成相同的操作:

govendor fmt +l // +l是local的简写
英文:

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

答案2

得分: 4

通常情况下,go listfind更有帮助:

$ 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..]

如你所见,这将列出当前目录中的所有包名。要过滤vendor目录,我们可以使用grep

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

如果你想运行多个代码检查工具,报告所有错误,并且只在成功时返回0,你可以使用循环和一个状态变量:

#!/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

这将输出类似以下内容:

==> 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

当然,你并不是第一个遇到这个问题的人,有一些工具基本上做的和上面一样,只不过更好。gometalinter可能是最知名的工具,我建议你使用它。它还提供了一个--vendor开关来忽略vendor目录:

$ 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..]
英文:

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..]

答案3

得分: 2

感谢所有有用的建议!这对我的目的很有效:

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

英文:

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

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

答案4

得分: 2

这是我使用的代码:

golint $(ls -d1 */ | sed s/\\//\\/.../g | grep -v -E "^vendor/" | tr "\n" " ")
英文:

Here is what I use:

golint $(ls -d1 */ | sed s/\\//\\/.../g | grep -v -E "^vendor/" | tr "\n" " ")

答案5

得分: 1

你可以将所有的源代码放入一个子目录中(例如 internallib)。或者,你可以这样做:

go vet ./... | grep -v vendor/ && exit 1 || exit 0
英文:

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

答案6

得分: 1

在Go 1.10(2018年第一季度)中,由OP mcandre答案中提到的提示类型将更加健壮:

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

正如carpetsmoker答案所示,对依赖于(尚未编译的)vendored包的包进行go vet将失败:

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

不再需要注意,因为go issue 16086已解决!

请参阅此线程

Go vet

go vet”命令在分析它所分析的包时,最好具有完整的类型信息。在历史上,这在各种情况下都存在问题:使用cgo的包、使用vendoring的包以及没有安装最新依赖项的包都是阻止vet使用完整类型信息的因素。

不再是问题了。go vet”命令现在会将所有这些信息的完整信息传递给vet,如果需要的话会构建新的.a文件(CL 74355和CL 74750)。
现在,“go vet”在分析包时保证具有最新的类型信息,这将提高其分析的准确性。
使用构建缓存来分摊构建这些.a文件的成本是使这一切成为可能的最后一步。

只有“go vet”有这个保证。不要使用“go tool vet”,它基本上只对vet的开发人员有用(就像你通常不运行“go tool compile”一样)。
以前,如果你想要控制vet标志,你需要使用“go tool vet”,但是“go vet”现在接受“go tool vet”的所有标志(参见“go help vet”)。

英文:

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".)

答案7

得分: 0

以跨平台的方式执行以下命令:

philea -s "666 go vet %s" "666 go-fmt-fail %s"

输出结果如下:

$ 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
英文:

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

答案8

得分: 0

在Go 1.9中,引入了一个更改,使得./...不再匹配vendor目录中的包(发布说明),这是一个常见的问题。

这意味着对于大多数情况来说,最简单的方法现在是:

go test ./...  # 忽略vendor
go fmt ./...   # 忽略vendor
go list ./...  # 忽略vendor
# 等等

然而,对于gofmt(如果go fmt没有我们需要的选项,我们可能会想使用它),情况就不那么明确了:

  • gofmt接受路径作为参数,而不是包
  • 如果给定目录,它会递归地工作,所以似乎只需gofmt .就可以了——但这样不会忽略vendor

因此,我们要么回到以前的方式:

gofmt -l . | grep -v vendor

但这会匹配不是实际vendor目录的目录中的vendor,而且在丢弃之前检查vendor中的所有内容似乎是浪费资源。

我们也不能使用

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

因为./...会匹配当前目录的包,导致gofmt在包括vendor在内的所有地方进行递归。

一个更健壮的解决方案是直接使用模板迭代文件:

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

但在具有长路径的大目录树中,这可能会遇到命令行长度限制,所以这也不是完美的解决方案。我倾向于在可能的情况下使用go fmt ./...

英文:

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.

答案9

得分: 0

如果在Makefile中(通常我们会这样做):

gofmt:
    gofmt -l $(shell find . -type f -name '*.go'| grep -v "/vendor/\|/.git/") # 忽略 vendor 和 .git 目录

如何在 StackOverflow 网页上在 gofmt 前添加 \t?-__-

英文:

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? -__-

huangapple
  • 本文由 发表于 2016年11月10日 23:59:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/40531874.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定