二丫讲梵 二丫讲梵
首页
  • 最佳实践
  • 迎刃而解
  • Nginx
  • Php
  • Zabbix
  • AWS
  • Prometheus
  • Grafana
  • CentOS
  • Systemd
  • Docker
  • Rancher
  • Ansible
  • Ldap
  • Gitlab
  • GitHub
  • Etcd
  • Consul
  • RabbitMQ
  • Kafka
  • MySql
  • MongoDB
  • OpenVPN
  • KVM
  • VMware
  • Other
  • ELK
  • K8S
  • LLM
  • Nexus
  • Jenkins
  • 随写编年
  • 家人物语
  • 追忆青春
  • 父亲的朋友圈
  • 电影音乐
  • 效率工具
  • 博客相关
  • Shell
  • 前端实践
  • Vue学习笔记
  • Golang学习笔记
  • Golang编程技巧
  • 学习周刊
  • Obsidian插件周刊
关于
友链
  • 本站索引

    • 分类
    • 标签
    • 归档
  • 本站页面

    • 导航
    • 打赏
  • 我的工具

    • 备忘录清单 (opens new window)
    • json2go (opens new window)
    • gopher (opens new window)
    • 微信MD编辑 (opens new window)
    • 国内镜像 (opens new window)
    • 出口IP查询 (opens new window)
    • 代码高亮工具 (opens new window)
  • 外站页面

    • 开往 (opens new window)
    • ldapdoc (opens new window)
    • HowToStartOpenSource (opens new window)
    • vdoing-template (opens new window)
GitHub (opens new window)

二丫讲梵

行者常至,为者常成
首页
  • 最佳实践
  • 迎刃而解
  • Nginx
  • Php
  • Zabbix
  • AWS
  • Prometheus
  • Grafana
  • CentOS
  • Systemd
  • Docker
  • Rancher
  • Ansible
  • Ldap
  • Gitlab
  • GitHub
  • Etcd
  • Consul
  • RabbitMQ
  • Kafka
  • MySql
  • MongoDB
  • OpenVPN
  • KVM
  • VMware
  • Other
  • ELK
  • K8S
  • LLM
  • Nexus
  • Jenkins
  • 随写编年
  • 家人物语
  • 追忆青春
  • 父亲的朋友圈
  • 电影音乐
  • 效率工具
  • 博客相关
  • Shell
  • 前端实践
  • Vue学习笔记
  • Golang学习笔记
  • Golang编程技巧
  • 学习周刊
  • Obsidian插件周刊
关于
友链
  • 本站索引

    • 分类
    • 标签
    • 归档
  • 本站页面

    • 导航
    • 打赏
  • 我的工具

    • 备忘录清单 (opens new window)
    • json2go (opens new window)
    • gopher (opens new window)
    • 微信MD编辑 (opens new window)
    • 国内镜像 (opens new window)
    • 出口IP查询 (opens new window)
    • 代码高亮工具 (opens new window)
  • 外站页面

    • 开往 (opens new window)
    • ldapdoc (opens new window)
    • HowToStartOpenSource (opens new window)
    • vdoing-template (opens new window)
GitHub (opens new window)
  • Shell编程

  • Go编程笔记

    • 开发技巧

    • 库包研究

      • 使用gorm进行联合查询的整理总结
      • 一个ftp客户端的封装
      • 使用go-bindata将文件编译进二进制
      • go-gitlab包源码探寻与心得
        • 1,物料准备
        • 2,初始化连接
        • 3,项目的交互
          • 1,熟悉结构体
          • 2,查项目
          • 3,创建项目
        • 4,感受
      • 利用cobra库快速开发类似kubectl一样的命令行工具
      • 使用MongoDB官方go库操作MongoDB
      • 再探-利用gorm自身提供的方法实现MySQL中数据关联的能力
      • 使用retry-go给项目添加重试机制
      • go-cache包的使用简析
      • 利用gorm自身提供的方法实现存在更新不存在则创建的能力
      • 近期关于cobra库的一些实践心得总结
    • 个人项目

  • 前端编程笔记

  • Go学习笔记

  • Vue-21年学习笔记

  • Vue-22年重学笔记

  • 编程世界
  • Go编程笔记
  • 库包研究
二丫讲梵
2022-01-15
目录

go-gitlab包源码探寻与心得

文章发布较早,内容可能过时,阅读注意甄别。

gitlab 基本上是每个公司内部代码仓库的首选,那么与之进行一些交互就是一个常见的需求了,在写了一些脚本与之交互之后,越发感受到此项目设计的成熟,因此特来记录一下相关文档。

# 1,物料准备

  • gitlab 环境,怎么部署这里就不详述了,可参考:Gitlab 简单部署 (opens new window)最好是 12.x 版本之后的版本,有一些接口在 11 版本中还不支持,比如项目移动分支,获取 token 的接口。
  • gitlab 官方文档 (opens new window)
  • gitlab-api 接口文档 (opens new window)
  • gitlab-api 中文接口文档 (opens new window)
  • go-gitlab 包 (opens new window)
  • go-gitlab 包接口说明文档 (opens new window)

# 2,初始化连接

一般情况下,我们最好直接在管理员账号中创建一个 access_token,初始化中的认证工作都基于这个 token 来进行。

var (
  git       *gitlab.Client
	token     string = "ceg46SwaL7yy"
	url       string = "https://gitlab.test.com/api/v4"
)

func init() {
	var err error
	git, err = gitlab.NewClient(token, gitlab.WithBaseURL(url))
	if err != nil {
		fmt.Printf("initclienterr:%v\n", err)
		panic(err)
	} else {
		fmt.Println("初始化完成")
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

注意我们在开头声明一个 *gitlab.Client类型的变量,初始化的时候直接将这个客户端对象赋值给这个变量,从而在全局都可以通过此变量直接与 gitlab 进行交互。

在源码当中,我们能看到这个对象拥有的属性如下:

// A Client manages communication with the GitLab API.
type Client struct {
	// HTTP client used to communicate with the API.
	client *retryablehttp.Client

	// Base URL for API requests. Defaults to the public GitLab API, but can be
	// set to a domain endpoint to use with a self hosted GitLab server. baseURL
	// should always be specified with a trailing slash.
	baseURL *url.URL

  //== 中间部分省略 ==//

	Projects              *ProjectsService
	Releases              *ReleasesService
	Users                 *UsersService
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

这些对象都是此包给我们提供的能力,我们可以操作这些对象附带的属性,从而实现我们的需求,接下来我将通过项目这个对象来作为例子进行讲解,从而深入地认识并理解这个包的用法。

# 3,项目的交互

# 1,熟悉结构体

go-gitlab 的包采用了非常优雅的面向对象编程思想,当然这其实也基源于 gitlab 官方接口文档设计的优秀。在进行一些项目相关的操作时,该包会将接口对应的返回值赋给项目这个对象,那么这个对象所拥有的一些方法就都可以直接使用了。

这里针对这一个示例稍微详细点讲解一下,通过编辑器代码追踪我们可以看到项目这个结构体的定义:

// Project represents a GitLab project.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html
type Project struct {
	ID                                        int                        `json:"id"`
	Description                               string                     `json:"description"`
	DefaultBranch                             string                     `json:"default_branch"`
	Public                                    bool                       `json:"public"`
	Visibility                                VisibilityValue            `json:"visibility"`
	SSHURLToRepo                              string                     `json:"ssh_url_to_repo"`
	HTTPURLToRepo                             string                     `json:"http_url_to_repo"`

  //== 中间部分省略 ==//

	BuildCoverageRegex    string             `json:"build_coverage_regex"`
	IssuesTemplate        string             `json:"issues_template"`
	MergeRequestsTemplate string             `json:"merge_requests_template"`
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

go-gitlab 包非常优秀的一点在于,每一个结构体或者接口,作者都将对应的官方文档地址标注在了注释当中,从而便于我们能够直接查阅官方文档。

我们可以看到Project (opens new window)这个结构体拥有众多属性,当我们通过接口获取或者操作的时候,都可以借助于这些属性,进行非常方便的操作。

# 2,查项目

查询接口是最常见最常用的,我们就先来看看这个接口怎么使用,通常,我们可能并不知道某个接口对应的方法名字叫什么,这个时候要么是去官方接口说明文档查看,要么是凭借经验,在编辑器里善用补全来进行。

当我们了解了 go-gitlab 这种面向对象的编程思路之后,就可以拿着开头初始化的 client 对象,来操作它里边的内容,这个时候可以输入一个git.Project.就能看到与项目相关的所有方法了:

通常,查询接口的函数命名无非就是这么几个关键字:List,Find,Select,Cat….于是如果一开始没什么头绪的话,就可以通过关键字来进行模糊补全,这里我们往下翻可以看到 go-gitlab 使用的是 List 关键字:

git.Projects.ListProjects()
1

这个时候可以点击方法跳转到源码当中,看到如下内容:

// ListProjects gets a list of projects accessible by the authenticated user.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#list-projects
func (s *ProjectsService) ListProjects(opt *ListProjectsOptions, options ...RequestOptionFunc) ([]*Project, *Response, error) {
	req, err := s.client.NewRequest(http.MethodGet, "projects", opt, options)
	if err != nil {
		return nil, nil, err
	}

	var p []*Project
	resp, err := s.client.Do(req, &p)
	if err != nil {
		return nil, resp, err
	}

	return p, resp, err
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

首先还是官方接口文档的地址,以及接口的具体定义,函数定义中的三段内容简单说明如下:

  • (s *ProjectsService):表示 ProjectsService 这个对象实例。
  • ListProjects():表示ProjectsService这个对象拥有的ListProjects()方法。其中的两个参数都是在调用此方法时的一些附加属性。
    • opt *ListProjectsOptions:查询时的一些参数,通常我们会用到里边的 ListOptions参数。
    • options ...RequestOptionFunc:自定义请求参数,一般情况下,这个参数都保持默认。
  • ([]*Project, *Response, error):返回值有三个,一个指针类型的项目切片,一个状态码,一个错误。

了解了如上内容之后,我们可以简单定义如下代码:

// GetAllProject 获取所有项目
func GetAllProject() ([]*gitlab.Project, error) {
	lbo := &gitlab.ListProjectsOptions{ListOptions: gitlab.ListOptions{Page: 1, PerPage: 50}}
	var pro []*gitlab.Project
	for {
		p, _, err := git.Projects.ListProjects(lbo)
		if err != nil {
			fmt.Printf("list projects failed:%v\n", err)
			return nil, err
		}
		for _, v := range p {
			pro = append(pro, v)
		}
		if len(p) < 50 {
			break
		}
		lbo.ListOptions.Page++
	}
	return pro, nil
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

通过官方 client 包,我们可以做一个获取全部项目的方法,该方法不接收参数,然后返回所有项目的切片。

这里需要注意的是查询的参数,这种用法是一种比较常见的查询接口的用法,一般接口都不会直接将所有数据返回,而会设计出分页的装置,gitlab 亦是如此,一开始我在使用 git.Projects.ListProjects()方法想要获取所有项目时,发现总是只拿到了固定的五十个项目信息,就是因为这个方法默认也是使用了分页的机制。

当我们用如上方法拿到所有项目之后,一般情况可以使用遍历的方法将项目遍历出来,然后利用项目的结构体对象,来获取我们关心的信息。

# 3,创建项目

其实有了如上的思路之后,一般情况下,我们就可以同样借助于编辑器自动补全对应的功能。首先我们可以看到创建方法的定义:

func (s *ProjectsService) CreateProject(opt *CreateProjectOptions, options ...RequestOptionFunc) (*Project, *Response, error) {
1

核心在于 opt *CreateProjectOptions,再往深处追踪,我们可以看到这个参数项,就是对应着上边 get 出来的项目的一个个属性,现在创建项目也是一样,可以通过定义这些属性,来创建一个符合预期的项目:

func CreateProject(group, name, desc string) {
		gid, err := GetGroupID(group)
		if err != nil {
			fmt.Printf("get group id err:%v\n", err)
			return
		}
		p := &gitlab.CreateProjectOptions{
			Name:                 gitlab.String(name),
			NamespaceID:          gitlab.Int(gid),
			Description:          gitlab.String(desc),
			MergeRequestsEnabled: gitlab.Bool(true),
			JobsEnabled:          gitlab.Bool(true),
			WikiEnabled:          gitlab.Bool(true),
			SnippetsEnabled:      gitlab.Bool(true),
			Visibility:           gitlab.Visibility(gitlab.PrivateVisibility),
		}
		project, _, err := git.Projects.CreateProject(p)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(project.Name)
		fmt.Println(project.WebURL)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

这里只是一个示例代码,并不代表能够直接运行,如果你是封装在自己的平台,或者仅仅是做一个脚本,那么可能还需要添加一些前置检测的方法来辅助,比如:

  • 先判断传递的分组是否存在。
  • 然后判断此项目是否已存在。
  • 以及其他的一些你想要注入的参数。

好了,关于与项目的交互就说到这里,其他的需求,只要经过合理的涉及,参考官方 api 以及 client 包的方法,都能够很方便高效地开发出来。

# 4,感受

事实上我大概是为了想要写一些感受,才写了这篇文章的,什么感受呢,那就是一个项目如果有好的成熟的设计,会时时处处造福后来人!

真正接触了解过 gitlab-api 的同学肯定能体会到,此项目 api 设计的统一性以及优雅度,让我们无论是通过 curl 命令行与之交互,还是基于一些客户端包的交互,都感到优雅与丝滑。

但其实能够设计出如此成熟统一的架构,是非常困难的,可以想见 gitlab 项目的开发者一定基于很多实际开发经验,下了很多功夫对之进行设计与实现,从这个角度来说,应该致敬。

我想,让人感到丝滑与优雅的原因,大概是合理,统一的接口规范,是的,就是规范,当一个项目,有了统一的接口入参规范,统一的返回规范,对于使用者而言,就是一种优雅的感受。有人可能会说规范应该是一个项目要求的基础,的确是基础,但是,我们实际生产中开发维护的项目,真的能有多少是站在统一的规范之上运行的呢,就我目前接触维护的而言,实际操作起来其实真正能够在公司订立统一的规范,每个开发者又能对齐认识,并在开发中能够严格遵守的,实在少之又少。

很多应该在基本上就做好的,其实早都丢得一干二净,很多应该严格遵照的红线,其实无形中就在跨越。这些都是一个项目,一个团队应该时常拿出来审视自省的。

微信 支付宝
上次更新: 2024/07/04, 22:40:37
使用go-bindata将文件编译进二进制
利用cobra库快速开发类似kubectl一样的命令行工具

← 使用go-bindata将文件编译进二进制 利用cobra库快速开发类似kubectl一样的命令行工具→

最近更新
01
记录二五年五一之短暂回归家庭
05-09
02
学习周刊-总第210期-2025年第19周
05-09
03
学习周刊-总第209期-2025年第18周
05-03
更多文章>
Theme by Vdoing | Copyright © 2017-2025 | 点击查看十年之约 | 浙ICP备18057030号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式