Go 模块使用 GitLab subgroups 的问题

AlwaysBeta

共 4990字,需浏览 10分钟

 ·

2024-07-03 17:41

大家好,我是煎鱼。

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

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

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

功能介绍

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

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

创建子组的截图和地方:

新建子组

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

列表展示

注:这个功能,我翻了下 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 的方式绕过逻辑:

预想的定制

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

大家在实际使用上,可以先看看 .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


关注和加煎鱼微信,

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



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

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


原创不易 点赞支持

浏览 15
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报