跳到主要内容

问题描述

在使用 Gitlab 作为 Go 模块仓库时,遇到一个问题:当仓库中地址含有多级path时(这里说的多级path是指仓库地址中包含多个 / 符号,而不是指仓库本身有多级子目录),例如:https://gitlab.com/go-pkg/go-pkg-repo/go-pkg-repo-sub-repo,那么 Go 模块解析时会出现如下错误:

~ go get gitlab.ssgeek.com/ssgeek/gopkg/nas-client-go
go: gitlab.ssgeek.com/ssgeek/gopkg/nas-client-go@v0.0.0-20251011113640-bdc14a56fc68: invalid version: git ls-remote -q origin in /Users/ssgeek/.gvm/pkgsets/go1.24.4/global/pkg/mod/cache/vcs/06e0842a0d4fe210ff56b260b6ef7af3321e2aaa3c6dd76ced2609027b411b5a: exit status 128:
remote:
remote: ========================================================================
remote:
remote: The project you were looking for could not be found or you don't have permission to view it.
remote:
remote: ========================================================================
remote:
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

问题原因分析

通过 go get -x 命令可以查看详细信息,可以看到 Go 模块在拉取该模块时,执行了如下命令:

~ go get -x gitlab.ssgeek.com/ssgeek/gopkg/nas-client-go
# get https://gitlab.ssgeek.com/ssgeek/gopkg/nas-client-go?go-get=1
# get https://gitlab.ssgeek.com/ssgeek/gopkg/nas-client-go?go-get=1: 200 OK (0.135s)
# get https://gitlab.ssgeek.com/ssgeek/gopkg?go-get=1
# get https://gitlab.ssgeek.com/ssgeek/gopkg?go-get=1: 200 OK (0.025s)
mkdir -p /Users/ssgeek/.gvm/pkgsets/go1.24.4/global/pkg/mod/cache/vcs # git3 http://gitlab.ssgeek.com/ssgeek/gopkg.git
# lock /Users/ssgeek/.gvm/pkgsets/go1.24.4/global/pkg/mod/cache/vcs/06e0842a0d4fe210ff56b260b6ef7af3321e2aaa3c6dd76ced2609027b411b5a.lock
# /Users/ssgeek/.gvm/pkgsets/go1.24.4/global/pkg/mod/cache/vcs/06e0842a0d4fe210ff56b260b6ef7af3321e2aaa3c6dd76ced2609027b411b5a for git3 http://gitlab.ssgeek.com/ssgeek/gopkg.git
cd /Users/ssgeek/.gvm/pkgsets/go1.24.4/global/pkg/mod/cache/vcs/06e0842a0d4fe210ff56b260b6ef7af3321e2aaa3c6dd76ced2609027b411b5a; git -c log.showsignature=false log --no-decorate -n1 '--format=format:%H %ct %D' bdc14a56fc68 --
0.017s # cd /Users/ssgeek/.gvm/pkgsets/go1.24.4/global/pkg/mod/cache/vcs/06e0842a0d4fe210ff56b260b6ef7af3321e2aaa3c6dd76ced2609027b411b5a; git -c log.showsignature=false log --no-decorate -n1 '--format=format:%H %ct %D' bdc14a56fc68 --
cd /Users/ssgeek/.gvm/pkgsets/go1.24.4/global/pkg/mod/cache/vcs/06e0842a0d4fe210ff56b260b6ef7af3321e2aaa3c6dd76ced2609027b411b5a; git ls-remote -q origin
0.099s # cd /Users/ssgeek/.gvm/pkgsets/go1.24.4/global/pkg/mod/cache/vcs/06e0842a0d4fe210ff56b260b6ef7af3321e2aaa3c6dd76ced2609027b411b5a; git ls-remote -q origin
# get http://gitlab.ssgeek.com/ssgeek/gopkg.git
# get http://gitlab.ssgeek.com/ssgeek/gopkg.git: 200 OK (0.124s)
cd /Users/ssgeek/.gvm/pkgsets/go1.24.4/global/pkg/mod/cache/vcs/06e0842a0d4fe210ff56b260b6ef7af3321e2aaa3c6dd76ced2609027b411b5a; git tag -l
0.016s # cd /Users/ssgeek/.gvm/pkgsets/go1.24.4/global/pkg/mod/cache/vcs/06e0842a0d4fe210ff56b260b6ef7af3321e2aaa3c6dd76ced2609027b411b5a; git tag -l
go: gitlab.ssgeek.com/ssgeek/gopkg/nas-client-go@v0.0.0-20251011113640-bdc14a56fc68: invalid version: git ls-remote -q origin in /Users/ssgeek/.gvm/pkgsets/go1.24.4/global/pkg/mod/cache/vcs/06e0842a0d4fe210ff56b260b6ef7af3321e2aaa3c6dd76ced2609027b411b5a: exit status 128:
remote:
remote: ========================================================================
remote:
remote: The project you were looking for could not be found or you don't have permission to view it.
remote:
remote: ========================================================================
remote:
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

可以看到 Go 模块在拉取该模块时,实际上是将仓库地址 gitlab.ssgeek.com/ssgeek/gopkg/nas-client-go 截断成了 gitlab.ssgeek.com/ssgeek/gopkg,然后去拉取这个地址的仓库,而这个地址并不是实际的仓库地址,所以就会出现上述错误

这个问题的原因是gitlabGo 模块支持有问题,Go 模块在解析模块地址时,会去请求 https://gitlab.ssgeek.com/ssgeek/gopkg/nas-client-go?go-get=1

然后从返回的内容中解析出实际的仓库地址,但是 Gitlab 返回的内容中,并没有包含正确的仓库地址,而是返回了错误的地址 https://gitlab.ssgeek.com/ssgeek/gopkg,所以导致 Go 模块解析错误

通过查找资料,发现 Gitlab 官方文档中也提到了这个问题:Unable to go get go-packages in repositories in nested sub groups deeper than 1

解决方案

配置.netrc文件

Go拉取私有仓库的问题文档中提到了一种解决方案:配置 .netrc 文件,这个文件用于存储访问私有仓库的认证信息,Go 模块在拉取私有仓库时,会去读取这个文件中的认证信息,然后使用这些信息去访问私有仓库,获取真实的 git 路径

在用户主目录下创建.netrc文件

~ vim ~/.netrc
1machine gitlab.com login 账号 password 密码或者访问令牌

这种方式的缺点是需要将账号和密码明文存储在文件中,存在安全隐患,对于自动化打包编译等场景,不太适用

使用go replace指令

另一种方式是使用 go replace 指令,在 go.mod 文件中添加如下内容:

replace gitlab.ssgeek.com/ssgeek/gopkg/nas-client-go => gitlab.ssgeek.com/ssgeek/gopkg/nas-client-go.git v0.0.0-20251011113640-bdc14a56fc68

具体来说,replace 指令用于替换模块路径和版本号,第一个参数是需要替换的模块路径,第二个参数是替换后的模块路径和版本号,这里仅仅在替换后的模块路径后面加上了 .git 后缀,版本号保持不变,这样 Go 模块在拉取该模块时,就会去访问 https://gitlab.ssgeek.com/ssgeek/gopkg/nas-client-go.git,而不是 https://gitlab.ssgeek.com/ssgeek/gopkg,这样就能正确拉取到该模块

后面的 v0.0.0-20251011113640-bdc14a56fc68, 这是在没有打 tag 的情况下,Go 模块会根据提交时间和提交哈希值生成一个伪版本号,这个版本号的格式为 v0.0.0-yyyymmddhhmmss-commit,其中 yyyymmddhhmmss 是提交时间,commit 是提交哈希值

我们更多的场景是直接打 tag 发布版本,那么这个版本号就可以直接使用打的 tag,例如 v1.0.0,这样版本号也更清晰

这种方式的缺点是 go.mod 文件中需要添加额外的内容,并且需要手动维护并更新版本号,即使使用 tag 也需要手动维护 replace 指令后面的版本号

小结

Gitlab 允许创建多级目录的仓库,而我们更多使用的 GitHub 并不太能支持这种多级目录的仓库,所有仓库地址一般都是一级目录的形式,用户或者组织创建的仓库地址一般都是 https://github.com/用户名或组织名/仓库名 的格式

因此在使用 Gitlab 作为 Go 模块仓库时,建议尽量避免使用多级目录的仓库地址,避免出现上述问题