go 使用 gitlab 搭建私有化模块系统

随笔4个月前发布 北美唐小笙
68 0 0

背景

本教程旨在教大家使用私有化部署的 gitlab 作为 go 的代码共享库,帮助团队分离代码模块,加强质量管控。go 官方在实现过程中就高度结合 VCS 系统,
可以仅通过配置相关的环境变量就实现私有库在 VCS 上的搭建。

代码分离样例

这里直接举出各种开发场景,提供开箱即用的案例参考,内部核心原理最后部分再讲。

案例1: 我会使用 gitlab, 我只想提交代码,不想搞什么 go 的配置

摘要

gitlab 拉下来的代码是一个独立的目录,建立对这个目录的依赖就可以使用这个模块的代码

## 配置项目对 example.com/project2 的包依赖定位到本地代码目录中
go mod edit -replace example.com/project2=../project2

## replace 完还需要手动 go get 来建立依赖关系
go get example.com/project2

  • 1
  • 2
  • 3
  • 4
  • 5
案例讲解

这里从零创建两个独立模块 project1, project2 来讲解。至于 gitlab 拉取的项目也是一个独立的目录,按照下面的方法建立本地依赖即可。

go 使用 gitlab 搭建私有化模块系统

project1, project2 分别初始化

C:Projectcode-allproject1> go mod init example.com/project1
go: creating new go.mod: module example.com/project1

C:Projectcode-allproject2> go mod init example.com/project2
go: creating new go.mod: module example.com/project2

  • 1
  • 2
  • 3
  • 4
  • 5

在项目 project2 中新建自己的模块文件,暴露自己的公有函数

project2/result.go

package myfunc

func HelloProject2() string {
	return "这里是 project2 的工具方法"
}

  • 1
  • 2
  • 3
  • 4
  • 5

在项目 project1 中导入模块 example.com/project2,使用暴露的方法

注意,直接这么写还不能让 go 正确定位模块,IDE 也会缺少语法提示

project1/main.go

package main

import (
	"fmt"

	myfunc "example.com/project2"
)

func main() {
	fmt.Print(myfunc.HelloProject2())
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

对本地分离的代码模块,go 提供 go mod edit -replace 指令来进行依赖管理。
我们在 project1 中使用该指令添加本地代码依赖

## 配置项目对 example.com/project2 的包依赖定位到本地代码目录中
go mod edit -replace example.com/project2=../project2

## replace 完还需要手动 go get 来建立依赖关系
go get example.com/project2

  • 1
  • 2
  • 3
  • 4
  • 5

执行结果

C:Projectcode-allproject1> go mod edit -replace example.com/project2=../project2
C:Projectcode-allproject1> go get example.com/project2
go get: added example.com/project2 v0.0.0-00010101000000-000000000000
C:Projectcode-allproject1> 

  • 1
  • 2
  • 3
  • 4

现在运行 project1/main.go,可以看到 myfunc 包的函数被正常调用了

C:Projectcode-allproject1> go run .main.go
这里是 project2 的工具方法

  • 1
  • 2
拓展

这个案例讲解的是怎么把本地的代码模块作为依赖添加到项目中。
go 官方推荐的用法是用这个 replace 方法做一些工具库的调试。
比如你依赖了一个流行的库,可以用这个方法在不改变主工程代码 import 语句的情况下,将库的代码手动拉下来,然后建立本地依赖进行调试。

官方举例:

replace golang.org/x/net v1.2.3 => example.com/fork/net v1.4.5

replace (
    golang.org/x/net v1.2.3 => example.com/fork/net v1.4.5
    golang.org/x/net => example.com/fork/net v1.4.5
    golang.org/x/net v1.2.3 => ./fork/net
    golang.org/x/net => ./fork/net
)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

更多讲解查阅 https://go.dev/ref/mod#go-mod-file-replace

案例2:我不想把项目代码拉到本地来建立依赖,能不能像使用其他库那样直接从网络拉取

摘要

go 一开始就深度结合了 VCS 系统,提供了非常简便的环境变量配置来完成模块私有化的问题

## 配置私有域名,go 查找依赖时就不会经过 GOPROXY
go env -w GOPRIVATE=gitlab.b.com

## gitlab 没有配置 https 的话, 还需要配置域名让 go 拉包时发 http
go env -w GOINSECURE=gitlab.b.com

## 加一个不做公共校验的配置
go env -w GONOSUMDB=gitlab.b.com

## 创建 gitlab 仓库,将我们需要共享的库代码的 module 改为 gitlab 仓库地址
## 举例
module gitlab.b.com/c/test-helloworld.git

## 新建一个 gitlab 仓库 test-helloworld ,可见性设为 public, 上传自己的模块代码,
## 现在可以直接中 gitlab 拉取模块代码了
## 注意,模块地址后面要用 .git 结尾,不然拉取模块走的是 HTTP GET
go get gitlab.b.com/c/test-helloworld.git

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
案例讲解

案例1 中我们讲解了怎么建立对本地模块的依赖,或者说对独立拉取的 gitlab 项目代码的依赖。replace 方式并不是官方推崇的模块私有化方法。因为这样每次模块代码更新了,你都要手动在本地的目录中同步更新。目前网络上主流的模块升级方法是在项目中更改一下版本号后重新拉取模块即可。

go 的底层已经针对各种 VCS 系统 (.bzr, .fossil, .git, .hg, .svn)做了适配,可以简单的从模块地址是否以 VCS 后缀结尾来选择模块拉取逻辑

首先我们要配置我们的内部仓库域名为私有域名,一般 go 项目配置的代理 GOPROXYhttps://goproxy.cn,direct

所以需要配置私有域名,绕过我们配置的代理

go env -w GOPRIVATE=gitlab.b.com

  • 1

gitlab 没有配置 https 的话, 需要配置域名让 go 拉包时发 http

go env -w GOINSECURE=gitlab.b.com

  • 1

搭配不做公共校验的配置

go env -w GONOSUMDB=gitlab.b.com

  • 1

接下来就是重点了,我们一定要对线上的模块打 git tag 标签,go 才能正常的完成拉取。我们下载 go 依赖时一般也会写上版本号,这个版本号就是 git tag 的对应名字。默认是 latest

下面我们试着将上面创建的 project2 上传到 gitlab 进行共享

cd ..project2
git init .
git add .
git commit -m "init"
git remote add origin ssh://git@gitlab.b.com:11111/c/go-test-project2.git
git push origin master

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

现在 gitlab 线上已经有我们的代码了,但这时还没有打 tag
go 使用 gitlab 搭建私有化模块系统
完成了上面的环境变量设置后,我们在 project1 移除 replace 后再用 go get 来拉取我们的线上模块

不出意外报错了

C:Projectcode-allproject1> go get gitlab.b.com/c/go-test-project2
go get: unrecognized import path "gitlab.b.com/c/go-test-project2": 
parse http://gitlab.b.com/c/go-test-project2?go-get=1: 
no go-import meta tags (meta tag gitlab.b.com:81/c/go-test-project2 did not match 
import path gitlab.b.com/c/go-test-project2)

  • 1
  • 2
  • 3
  • 4
  • 5

这里直接讲分析
当拉去一个模块时,如果模块的 URL 地址 gitlab.b.com/c/go-test-project2 后面没有携带 VCS 后缀(如 .git, .svn),go 将通过 HTTP GET 的方式去请求模块代码,发出的请求如:
http://gitlab.b.com/c/go-test-project2?go-get=1

会携带上 go-get=1 作为标志。一般的解决方法是 nginx 做一个反向代理,伪造正常响应来处理。但是内部配置的 gitlab 运行在非 80 端口上时,对 80 的访问会收到重定向的响应。这也是 go 会去请求 gitlab.b.com:81/c/go-test-project2 的原因。

但是 go 内部的包路径不允许携带端口在 URL 中,所以就中断了拉取流程。

而当包的 URL 携带了 .git 时,go 会进入直连模式,并且使用 git 来拉取代码。我们试一下

C:Projectcode-allproject1> go get gitlab.b.com/c/go-test-project2.git 
go: downloading gitlab.b.com/c/go-test-project2.git v0.0.0-20240726024502-c0914057a375
go get: gitlab.b.com/c/go-test-project2.git@v0.0.0-20240726024502-c0914057a375: parsing go.mod:
        module declares its path as: example.com/project2
                but was required as: gitlab.b.com/c/go-test-project2.git

  • 1
  • 2
  • 3
  • 4
  • 5

可以发现现在 go 可以正常下载仓库代码,但是进行模块分析时报错,显示我们请求的模块路径和模块自己声明的路径不一致。我们模块里面写的 example.com/project2,但是请求是用的 gitlab.b.com/c/go-test-project2.git

所以我们修改 project2 的 module 为 gitlab.b.com/c/go-test-project2.git,然后提交到 gitlab 后重新拉取。

project2/go.mod

module gitlab.b.com/c/go-test-project2.git

go 1.17

  • 1
  • 2
  • 3

现在重新拉取,可以看到模块被成功拉到本地了,而且是 go 去管理

C:Projectcode-allproject1> go get gitlab.b.com/c/go-test-project2.git 
go: downloading gitlab.b.com/c/go-test-project2.git v0.0.0-20240726030655-fa5e48794cf3
go get: added gitlab.b.com/c/go-test-project2.git v0.0.0-20240726030655-fa5e48794cf3

  • 1
  • 2
  • 3

至此,已经完成了模块私有化的全部流程。

拓展

为了更好的对代码进行版本控制,标准的流程是在拉起 go 的模块时,需要携带版本号。这里演示一下怎么建立版本号:

首先在 project2 项目中打一个 git tag

C:Projectcode-allproject2> git tag -a v1.0.0 -m 'my version 1.0.0' 
C:Projectcode-allproject2> git show v1.0.0
tag v1.0.0
Tagger: c<c@b.org>
Date:   Fri Jul 26 11:17:28 2024 +0800

my version 1.0.0
tag v1.0.0
Tagger: c<c@b.org>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

然后将这个 tag 发布到线上

C:Projectcode-allproject2> git push origin v1.0.0
Enumerating objects: 1, done.
Counting objects: 100% (1/1), done.
Writing objects: 100% (1/1), 160 bytes | 160.00 KiB/s, done.
Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
To ssh://gitlab.b.com:10022/c/go-test-project2.git
 * [new tag]         v1.0.0 -> v1.0.0

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

现在我们可以用版本号来拉取代码了,可以看到 go 自动帮我们更新了依赖的版本

C:Projectcode-allproject1> go get gitlab.b.com/c/go-test-project2.git@v1.0.0
go: downloading gitlab.b.com/c/go-test-project2.git v1.0.0
go get: upgraded gitlab.b.com/c/go-test-project2.git v0.0.0-20240726030655-fa5e48794cf3 => v1.0.0

  • 1
  • 2
  • 3

更多 go get 版本控制查阅
https://pkg.go.dev/cmd/go#hdr-Add_dependencies_to_current_module_and_install_them

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...