Go 模块使用 GitLab subgroups 的问题

大家好,我是煎鱼。

最近帮忙小伙伴处理了一个小问题,感觉五六年前就有人问过我,当年觉得没啥大问题记录。没想到。。。2024 年了,还是有同学表示他的姿势搜不到相关的解决办法。

今天主打一个分享和记载,看看有没有也踩过坑的朋友。(结果我发文前两天就有社区的朋友问到了我😅)

我感觉 2027 年这问题都不会解决。

功能介绍

在 GitLab 中,提供了一种叫做子组(subgroups)的功能特性。

它允许对项目仓库进行进一步的分组,而无需创建多个组织去实现不同内容的存放。

创建子组的截图和地方:

71be7937735eb03fef3ac85c8df409a9.jpeg
新建子组

在项目列表下子组的展示:

1797400c317eb361ec7c0c018419ccea.jpeg
列表展示

注:这个功能,我翻了下 GitHub 是没有的。并且有人在 Community Discussions#4837 提出也想要,但官方没有人回应此项提议。

问题背景

虽然这个功能有一定的特色。但是子组(subgroups)和 Go 模块管理有一定的水土不服。我们直接使用 go get 命令试图拉取子组时,就会出现问题。

在最常用的 group/project 格式下,模拟 go get 命令直接拉取:

curl -X GET "https://gitlab.xxx.com/libraries/example?go-get=1"
<html><head><meta name="go-import" content="gitlab.xxx.com/libraries/example git https://gitlab.xxx.com/libraries/example.git" /></head></html>

可以看到是正常返回:gitlab.xxx.com/libraries/example.git

那如果是子组的模式呢?

我们按照 group/subgroup/project 再获取试试:

curl -X GET "https://gitlab.xxx.com/libraries/subgroup1/example?go-get=1"
<html><head><meta name="go-import" content="gitlab.xxx.com/libraries/subgroup1 git https://gitlab.xxx.com/libraries/subgroup1.git" /></head></html>

可以看到返回的是:gitlab.xxx.com/libraries/subgroup1.git,这显然不正确。我们要获取的是 gitlab.xxx.com/libraries/subgroup1/example。获取到的层级都错了!

不支持的原因

现阶段来看,根本原因是 Go 的 go get 实现和 GitLab 的原因共同导致。互相不完全适配。Go 觉得 GitLab 这个太定制化,GitLab 觉得是 Go 实现不完整。

Go 为了使用 SSH 身份验证,go get 需要知道项目位于什么地方,然后才能去获取他。

以下是梳理的流程和步骤:

1、对于 group/subgroup/project 中的项目,go get 将发送以下请求:

  • GET https://gitlab.com/group/subgroup/project?go-get=1

  • GET https://gitlab.com/group/subgroup?go-get=1

  • GET https://gitlab.com/group?go-get=1

2、这些请求未经授权,因此 GitLab 无法提供指向项目的正确链接 (也就是预期的结果:https://gitlab.com/group/subgroup/project.git)。

3、相反,GitLab 会返回 https://gitlab.com/group/subgroup.git 作为响应结果。(如此返回的目的是:防止未经身份验证的用户访问,避免出现暴露项目存在的安全风险)

4、然后 Go 会尝试使用 SSH 身份验证 ssh://gitlab.com/group/subgroup.gitgit clone 仓库。想也知道,拉取的结果失败了,因为这个仓库并不存在。

因此对于 GitLab 子组中的 Go 私有项目,用户必须通过 HTTPS 认证,这样 Go 的 go get 命令才能通过 GitLab 的访问校验,再拉取到正确的响应结果。

解决方案

现阶段的解决方案大致有两种,都是一些绕过校验或逻辑的操作。

使用 .netrc 鉴权

GitLab 文档 Authenticate Go requests to private projects[1] 和 Go 文档 Passing credentials to private proxies[2],推荐的是通过定义 .netrc 文件来实现该鉴权访问。

但有前置条件的要求:

  • GitLab 实例必须可通过 HTTPS 访问。

  • 用到的账号必须拥有 read_api 权限的个人访问令牌。

根据你的 OS 环境不同,创建一个 .netrc 文件,并填入相关信息:

machine gitlab.example.com
login <gitlab_user_name>
password <personal_access_token>

需要注意:在 Windows 上,Go 读取 ~/_netrc 而不是 Linux 的 ~/.netrc 文件。

或者可以直接在 GOPROXY URL 鉴权凭证:

GOPROXY=https://jrgopher:hunter2@proxy.corp.example.com

采用此方法时请务必小心:环境变量可能会出现在 shell 历史记录和日志中。(不推荐使用)

但总的来说,netrc 这个方案,有部分社区反馈不是很认可,因为有安全隐患,存在一定的风险。还要配这配那的。

使用 replace .git 绕过逻辑

有一种方法更简单的方法,可以跳过 go get 请求并强制 Go 直接使用 Git 身份验证,但需要修改模块名称。

直接修改 go.mod 文件,采用 replace 的方式给实际引用的模块名的项目名增加 .git。当然,如果你不想 replace,直接引用模块后缀是 .git 也是可以的。 

如下所示:

require(
    gitlab.xxx.com/group/subgroup/project v1.7.0
)

replace(
    gitlab.xxx.com/group/subgroup/project => gitlab.xxx.com/group/subgroup/project.git v1.7.0
)

Go 模块能用这个骚操作的原因是:如果模块路径的某个组件末尾有一个 VCS 标识符符(例如:.bzr.fossil.git.hg.svn)。go get 命令就会使用该路径限定符之前的所有内容作为仓库 URL。

例如,对于 example.com/foo.git/bar 模块,go 命令会使用 git 下载 example.com/foo.git 的版本库,并在 bar 子目录中找到该模块。

总结和吐槽

比较可惜的是,这个问题,至少 7 年前(2017 年)就有人提出了,可惜到现在 2024 年,都没有解决。

追根溯源上,Go 团队并不想完整实现一套完整的单独的程序去支持这个。Go 团队负责人 rsc(Go 模块他独立开发、应用和推广的)只想做一个符合标准的,因此 Go 最终支持了读取 $HOME/.netrc。后续至此再无更多的动作。社区反馈也不会说特别激烈。

而对于 GitLab 而已,他是乙方。客户各种吐槽。因此他们最有动力去推进解决这个事情。

近期由于 Go 团队多年对此的冷漠。GitLab 开始也有提出 Allow to set a go-modules folder for private Go projects[3]

希望允许用户在 group 或者子组为私有 Go 模块设置 go-modules 的专属标识再通过第二点解决方案的 .git 的方式绕过逻辑:

a7c4d36460793abae728cbe7507bf483.jpeg
预想的定制

不过这个还在讨论中,反对意见也不小。暂且苟住。

大家在实际使用上,可以先看看 .netrcreplace .git 的方式。可能前者更标准,后者更易用。这样团队成员就不用每次都配置一遍了。

推荐阅读

参考资料

[1]

Authenticate Go requests to private projects: https://docs.gitlab.com/ee/user/project/use_project_as_go_package.html#authenticate-go-requests-to-private-projects

[2]

Passing credentials to private proxies: https://go.dev/ref/mod#private-module-proxy-auth

[3]

Allow to set a go-modules folder for private Go projects: https://gitlab.com/gitlab-org/gitlab/-/issues/437005

关注和加煎鱼微信,

一手消息和知识,拉你进技术交流群👇

730fcb1cd811b773c21ab98de9c90d07.jpeg

4e05124d8a6f5aaf254ffee4a044596e.png

你好,我是煎鱼,出版过 Go 畅销书《Go 语言编程之旅》,再到获得 GOP(Go 领域最有观点专家)荣誉,点击蓝字查看我的出书之路

日常分享高质量文章,输出 Go 面试、工作经验、架构设计,加微信拉读者交流群,和大家交流!

原创不易 点赞支持

  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值