我正在尝试调试 CI 中的以下构建错误,其中“A 依赖于 B,而 B 无法构建,因为它依赖于 C。”我正在构建不直接依赖于 kafkaAvailMonitor.go 的数据服务,这使得此错误难以追踪。换句话说:
数据(我正在构建的)取决于(?),这取决于 kafkaAvailMonitor.go
对于开发人员来说,修复问题似乎微不足道,他们只是“去获取任何东西”,但我不能在发布过程中这样做 - 我必须找到添加依赖项的人并要求他们修复它。
我知道有一些工具可以可视化依赖关系树和其他更复杂的构建系统,但这似乎是一个非常基本的问题:有什么方法可以查看完整的依赖关系树以了解导致构建问题的原因?
go build -a -v
../../../msgq/kafkaAvailMonitor.go:8:2: cannot find package
"github.com/Shopify/sarama/tz/breaker" in any of:
/usr/lib/go-1.6/src/github.com/Shopify/sarama/tz/breaker (from $GOROOT)
/home/jenkins/go/src/github.com/Shopify/sarama/tz/breaker (from $GOPATH)
/home/jenkins/vendor-library/src/github.com/Shopify/sarama/tz/breaker
/home/jenkins/go/src/github.com/Shopify/sarama/tz/breaker
/home/jenkins/vendor-library/src/github.com/Shopify/sarama/tz/breaker
使用模块时,您也许可以从
go mod graph
获得您需要的东西。
用法:go mod graph Graph 打印模块需求图(应用了替换) 以文本形式。输出中的每一行都有两个以空格分隔的字段:一个模块 及其要求之一。每个模块都被标识为以下形式的字符串 路径@version,但主模块除外,主模块没有@version后缀。
即,对于原始问题,运行
go mod graph | grep github.com/Shopify/sarama
,然后仔细查看左侧的每个条目。
如果以下不是堆栈跟踪,那是什么?
这是 Go 正在寻找丢失的包的路径列表。
我不知道谁在导入 kafkaAvailMonitor.go
它不是“导入的”,只是您的源代码的一部分并进行编译。
但它无法编译,因为它需要
github.com/Shopify/sarama/tz/breaker
,而它不在 GOROOT
或 GOPATH
中。
go list
,看看是否提到了kafkaAvailMonitor
。
go list
可以显示您的包直接依赖的包,或其完整的传递依赖项集。
% go list -f '{{ .Imports }}' github.com/davecheney/profile
[io/ioutil log os os/signal path/filepath runtime runtime/pprof]
% go list -f '{{ .Deps }}' github.com/davecheney/profile
[bufio bytes errors fmt io io/ioutil log math os os/signal path/filepath reflect run
然后您可以编写 go list 脚本来列出
所有依赖项。
例如,请参阅
这个 bash 脚本,作者:Noel Cower (nilium
)
#!/usr/bin/env bash
# Usage: lsdep [PACKAGE...]
#
# Example (list github.com/foo/bar and package dir deps [the . argument])
# $ lsdep github.com/foo/bar .
#
# By default, this will list dependencies (imports), test imports, and test
# dependencies (imports made by test imports). You can recurse further by
# setting TESTIMPORTS to an integer greater than one, or to skip test
# dependencies, set TESTIMPORTS to 0 or a negative integer.
: "${TESTIMPORTS:=1}"
lsdep_impl__ () {
local txtestimps='{{range $v := .TestImports}}{{print . "\n"}}{{end}}'
local txdeps='{{range $v := .Deps}}{{print . "\n"}}{{end}}'
{
go list -f "${txtestimps}${txdeps}" "$@"
if [[ -n "${TESTIMPORTS}" ]] && [[ "${TESTIMPORTS:-1}" -gt 0 ]]
then
go list -f "${txtestimps}" "$@" |
sort | uniq |
comm -23 - <(go list std | sort) |
TESTIMPORTS=$((TESTIMPORTS - 1)) xargs bash -c 'lsdep_impl__ "$@"' "$0"
fi
} |
sort | uniq |
comm -23 - <(go list std | sort)
}
export -f lsdep_impl__
lsdep_impl__ "$@"
go mod why
也可以提供帮助。无论如何,你无法获取并显示整棵树。但是您可以追溯到子依赖项的一个分支,直到其父根。示例:
$ go mod why github.com/childdep
# github.com/childdep
github.com/arepo.git/service
github.com/arepo.git/service.test
github.com/anotherrepo.git/mocks
github.com/childdep
这意味着,您终于在“anotherrepo.git/mocks”中导入了“childdep”。
https://github.com/vc60er/deptree
redis git:(master) go mod graph | deptree -d 3
package: github.com/go-redis/redis/v9
dependence tree:
┌── github.com/cespare/xxhash/[email protected]
├── github.com/dgryski/[email protected]
├── github.com/fsnotify/[email protected]
│ └── golang.org/x/[email protected]
├── github.com/nxadm/[email protected]
│ ├── github.com/fsnotify/[email protected]
│ │ └── golang.org/x/[email protected]
│ └── gopkg.in/[email protected]
├── github.com/onsi/[email protected]
│ ├── github.com/go-task/[email protected]
│ │ ├── github.com/davecgh/[email protected]
│ │ └── github.com/stretchr/[email protected]
│ │ └── ...
上述解决方案(其他建议如 go list)的问题是它只告诉我顶层。他们不会“遍历树”。这是我得到的输出 - 它没有比 go build 给我的更多帮助。
.../npd/auth/
.../mon/mlog
.../auth/service
这就是我想要得到的 - 我知道 auth 被破坏(顶部)并且断路器在 go build 中被破坏(底部),但我不知道中间是什么 - 我下面的脚本给了我这个输出。
.../npd/auth/
.../npd/auth/service
.../npd/auth/resource
.../npd/auth/storage
.../npd/middleware
.../npd/metrics/persist
.../npd/kafka
.../vendor-library/src/github.com/Shopify/sarama
.../vendor-library/src/github.com/Shopify/sarama/vz/breaker
我的Python脚本:
import subprocess
import os
folder_locations=['.../go/src','.../vendor-library/src']
def getImports(_cwd):
#When the commands were combined they overflowed the bugger and I couldn't find a workaround
cmd1 = ["go", "list", "-f", " {{.ImportPath}}","./..."]
cmd2 = ["go", "list", "-f", " {{.Imports}}","./..."]
process = subprocess.Popen(' '.join(cmd1), cwd=_cwd,shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out1, err = process.communicate()
process = subprocess.Popen(' '.join(cmd2), cwd=_cwd,shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out2, err = process.communicate()
out2clean=str(out2).replace("b'",'').replace('[','').replace(']','').replace("'",'')
return str(out1).split('\\n'),out2clean.split('\\n')
def getFullPath(rel_path):
for i in folder_locations:
if os.path.exists(i+'/'+rel_path):
return i+'/'+rel_path
return None
def getNextImports(start,depth):
depth=depth+1
indent = '\t'*(depth+1)
for i,val in enumerate(start.keys()):
if depth==1:
print (val)
out1,out2=getImports(val)
noDeps=True
for j in out2[i].split(' '):
noDeps=False
_cwd2=getFullPath(j)
new_tree = {_cwd2:[]}
not_exists = (not _cwd2 in alltmp)
if not_exists:
print(indent+_cwd2)
start[val].append(new_tree)
getNextImports(new_tree,depth)
alltmp.append(_cwd2)
if noDeps:
print(indent+'No deps')
_cwd = '/Users/.../npd/auth'
alltmp=[]
start_root={_cwd:[]}
getNextImports(start_root,0)