我们一起聊聊 Go 模块使用 GitLab subgroups 的问题

开发 前端
追根溯源上,Go 团队并不想完整实现一套完整的单独的程序去支持这个。Go 团队负责人 rsc(Go 模块他独立开发、应用和推广的)只想做一个符合标准的,因此 Go 最终支持了读取 $HOME/.netrc。

大家好,我是煎鱼。

最近帮忙小伙伴处理了一个小问题,感觉五六年前就有人问过我,当年觉得没啥大问题记录。没想到。。。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" cnotallow="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" cnotallow="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.git 来 git 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 的方式绕过逻辑:

预想的定制预想的定制

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

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

责任编辑:武晓燕 来源: 脑子进煎鱼了
相关推荐

2023-03-26 23:47:32

Go内存模型

2024-02-26 00:00:00

Go性能工具

2022-10-28 07:27:17

Netty异步Future

2023-01-04 18:10:26

服务模块化jre

2024-02-28 08:41:51

Maven冲突版本

2023-10-26 08:38:43

SQL排名平分分区

2023-05-29 09:07:10

SQLpageSize主键

2024-05-17 08:47:33

数组切片元素

2022-10-08 00:00:05

SQL机制结构

2022-05-24 08:21:16

数据安全API

2023-08-10 08:28:46

网络编程通信

2023-08-04 08:20:56

DockerfileDocker工具

2023-09-10 21:42:31

2023-06-30 08:18:51

敏捷开发模式

2023-04-26 07:30:00

promptUI非结构化

2021-08-27 07:06:10

IOJava抽象

2024-02-20 21:34:16

循环GolangGo

2021-07-12 11:35:13

Go协程Goroutine

2022-12-06 08:12:11

Java关键字

2023-08-02 08:35:54

文件操作数据源
点赞
收藏

51CTO技术栈公众号