在敏捷开发的时代, 快速的编码, code review, 测试, 部署, 是提升程序员效率的关键。 同时在基础工具完备的如今, 我们甚至无需过多的操作就可以轻松实现上述步骤, 本文就以gitlab为例, 分享一下golang项目结合gitlab如何实现自动化CI。

在gitlab中执行CI, 需要在项目根目录下增加.gitlab-ci.yml文件, 定义执行CI任务的步骤及方式, 例如典型的操作:执行代码检测, 编译, 测试, 发布。 gitlab会在每次commit或push的时候调用runner来执行该文件中所定义的操作。runner是一个执行具体CI任务的工具, 可以运行在任意实体机, docker或k8s集群, 在项目中配置好后, gitlab就会自动调用runner并将结果返回。

配置gitlab runner

runner可以为某个项目单独配置, 也可以由gitlab管理员预先定义一些公共使用的runner, 后者无需开发人员过多操作, 主要讲解一下前者, 如何单独配置一个runner。首先安装gitlab runner, 参照官方安装教程, 安装好之后需要注册该runner到项目中, 本文使用的是docker作为executer, 也就是用docker执行pipeline, 在setting->pipeline下获取到项目的token和url, 通过gitlab-runner register来注册该runner,

其中DOCKER_IMAGE 是如果未在.gitlab-ci.yml文件中指定image时默认的image。

sudo gitlab-runner register -n -u $CI_SERVER_URL \
-r $REGISTRATION_TOKEN \
--description "test-docker-runner" \
--executor docker \
--docker-image $DOCKER_IMAGE \
--docker-privileged

编写ci文件

上述如此就安装并关联好了gitlab-runner, 接下来就是编写.gitlab-ci.yml文件了:

image: cr.registry.name/groupname/project-name-builder

stages:
- test
- build
- release cache:
paths:
- bin before_script:
- echo "Git Branch is ${CI_COMMIT_REF_NAME}" testcase:
stage: test
script:
- echo "begin run test"
- make test build_bin:
stage: build
script:
- make release_bin:
stage: release
script:
- echo "begin release rpm pkg"
- make push_rpm

image运行pipeline的镜像, 可以直接使用golang这些基础镜像, 也可以是自己写的一个Builder镜像, 本例中笔者使用的是一个自己写好的builder镜像, 因为在后面的ci操作中, 笔者会打包rpm包并上传到文件服务器上以便后续的发布, 这些client都需要在镜像中准备好, 基础镜像无法满足条件, 所有笔者就自己打包了一个镜像。

接下来是stage的定义, stage就是定义ci任务的各个阶段, 本例中是执行测试, 编译, 发布三个阶段(因为go test的实现并不是分析编译后binary文件, 所以可以看到测试在编译之前), 各个stage串行执行, 只有前一阶段执行完毕才会执行后面的阶段, 如果哪个stage失败则整个pipeline失败。下面的testcase, build_bin, release_bin分别是各个stage的具体定义, 称为job, 各个job的script中可以执行任意的命令, 需要注意script中的命令必须在上面指定的image中存在, 笔者的script只是调用了项目中的makefile, makefile中写好了具体执行的操作, 如果一个stage中定义了多个job的话, 他们会并行执行。before_script定义了在各个job的script执行之前执行的操作, 笔者是打印出执行pipeline的branch, 其中CI_COMMIT_REF_NAME是gitlab内置变量, 表示当前的branch, 如果需要使用自己的变量, 可以通过variables来定义, 但是程序中使用到的敏感的token, key等信息, 不要直接定义在variables中, 可以通过setting->pipeline->Secret variables中设置, 使用的时候会自动作为环境变量注入进去。cache定义了各个stage直接可以保存下来供其他stage使用的数据, 笔者这里是项目下的bin文件, 因为在build阶段生成了binary可执行文件之后, 在release阶段就可以直接使用。需要注意的是, cache并不能保证每次都会存在, 所以在release中需要有其他的逻辑保证没找到cache也可以正常的工作。

笔者上述只是三个stage, 在一个成熟的项目中应该包括很多这样的stage, 依次进行lint, unit tests, data race, memory sanitizer, code coverage, build, release。幸运的是对于这些步骤中的大部分go都直接提供了相应的工具来使用, 例如goline, go test等, 其中代码测试覆盖率这块有个小问题需要单独拿出来讲一下。

golang 项目的 test coverage

golang项目的测试覆盖率可以直接用go test -cover ${package}输出

也可以通过go test -coverprofile生成coverage profile, 该文件中保存了一些收集到的测试信息, 然后将这些信息提供给go tool cover使用来输出测试覆盖率。

go test -coverprofile "profile.cov" ${package_name}
go tool cover -func="profile.cov"

但是上面的测试覆盖率只是针对一个package, 对于一个项目中有多个package, 需要依次调用go test -coverprofile产生每个package的profile, 然后将其合并在一起, 再调用go tool cover产生整个项目的coverage例如:

#!/bin/bash
#
# Code coverage generation
COVERAGE_DIR="${COVERAGE_DIR:-coverage}"
PKG_LIST=$(go list ./... | grep -v /vendor/)
# Create the coverage files directory
mkdir -p "$COVERAGE_DIR";
# Create a coverage file for each package
for package in ${PKG_LIST}; do
go test -covermode=count -coverprofile "${COVERAGE_DIR}/${package##*/}.cov" "$package" ;
done ;
# Merge the coverage profile files
echo 'mode: count' > "${COVERAGE_DIR}"/coverage.cov ;
tail -q -n +2 "${COVERAGE_DIR}"/*.cov >> "${COVERAGE_DIR}"/coverage.cov ;
# Display the global code coverage
go tool cover -func="${COVERAGE_DIR}"/coverage.cov ;
# Remove the coverage files directory
rm -rf "$COVERAGE_DIR";

在golang 1.10中已经支持了go test后面的多个package共同生成一个profile, 无需手动合并多个profile, go test $(go list ./... | grep -v vendor) -v -coverprofile .testCoverage.txt

所以现在整个项目的coverage的输出方式为:go test $(go list ./...) -v -coverprofile .testCoverage.txt && go tool cover --func=.testCoverage.txt 如果你需要一步生成coverage的话记得升级你的golang版本。

如果项目中某个package不含有测试文件, 则这个package的coverage应该为0%, 但是目前go tes对于不含有测试文件的package就不会在profile中产生对应的记录, 在算项目的coverage的时候也就不会包括该package。如此整个项目的coverage其实比实际值大, 因为少算了coverage为0的package, 这个目前还没有一种简单的解决方式, 只能是在每个package中都增加测试文件, 其实这也是我们应该做到的。

徽章(Badges)

有了测试覆盖率的数据就可以在项目的首页展示这些数据了, 如下图所示:

在项目的Setting-> pipeline 最下面可以找到对应的Markdown格式的输出, 在首页的ReadMe中添加上即可, 形如

[![Build Status](https://gitlab.com/pantomath-io/demo-tools/badges/master/build.svg)](https://gitlab.com/pantomath-io/demo-tools/commits/master)

对于测试覆盖率还需要在项目中配置正则表达式, 来匹配pipeline的输出中的测试覆盖率, 如果用上面的命令则正则表达式为: total:\s+\(statements\)\s+(\d+.\d+\%)

如果想要添加其他的badege, 查阅相关资料即可。如果想要使用go report card badges 而有不想公开公司内网的代码则需要自己搭建go report 服务器了。

gitlab与golang 的其他要点

  1. 项目中每次改代码都应该在一个新的branch上进行编码操作, 每次push代码都会触发提前设置好的pipeline自动执行一些编译测试等操作, pipeline结束后会生成代码的测试覆盖率, code small等数据, 如果数据不符合预先的规范, 需要继续修改直到数据达标。开发完成之后然后提交merge request, 然后指派给同事进行code review, 如何需要多人review的话就在description里cc @somebody 艾特其他人就可以了。 gitlab本身的code review效果还不错, 可以选择在任意两个commit 之间进行diff。 如果本次提交是为了解决issue的话, 在commit message中直接通过#issueID来链接或关闭对应的issue。最后merge request通过之后代码合并至master, master中的pipeline与dev分支的有些不同, 可以在master中执行CD进行自动发布。

    对于所有出现的bug都应该反应在一次issue里, 后续的进展都应该及时更新在issue里。 issue可以通过设置tag, milestone 来管理, 这样做更加清晰明了, 方便管理, 回溯。
  2. 项目中必须包含makefile, 任何编译, 测试, 发布, 都通过make subcommand 一条命令解决, 无需额外设置GOPATH的位置, git submodule, go package的下载等, 这样才足够敏捷, .gitlab-ci.yml文件中也无需写冗长的script, 将来如果编译方式变化也可以只修改makefile就可以了, 同时对于新人刚接触项目也更友好。
  3. 项目中推荐写一个builder镜像, 取名为Dockerfile.build以区分真正的业务镜像, 这样的好处是对开发环境依赖较少, 只需要有docker就行, golang, rpm制作工具等都打包在builder镜像之中, 使用的时候直接docker run -v ``pwd``:/project cr.registry.name/groupname/project-name-builder make即可。尤其是在gitlab ci的时候, 可能ci过程中需要执行代码质量检查等, 需要用到golint等工具, 如果只使用基础镜像, 则每次ci任务都需要在去下载, 这样费时费力, 不如将这些工具打包在builder镜像中, 然后在yaml文件的image中指定该builder镜像即可。

reference

Go tools and GitLab: How to do continuous integration like a boss

golang在gitlab中的工作流的更多相关文章

  1. golang 结构体中的匿名接口

    golang 结构体中的匿名接口 代码示例 golang 中,可以给结构体增加匿名field,可参考 unknwon 大神的书. 匿名字段和内嵌结构体 但,golang同时也可以给结构体定义一个匿名i ...

  2. 将gitlab中的postgresql数据库开通远程访问

    postgresql数据库是gitlab的一个配置数据库,记录gitlab的一些配置信息. 我们访问gitlab中的postgresql数据有本地命令行访问和远程可视化软件访问2种方式. (一)本地命 ...

  3. Eclipse导入GitLab中指定分支的项目

    一.如果主分支丢失,是否可以恢复其他分支? 答案是可以的,下面我们就拿恢复分支publish-2018-6-5来说明问题,最终实现把分支publish-2018-6-5还原成项目放到Eclipse中 ...

  4. 在本地生成ssh-key 免密码远程clone GitLab中的项目到本地

    每次项目push.pull都需要输入账号和密码,很烦,方便免密pull与push代码,在本地需要用git bash 创建一个公钥,然后在gitlab中把公钥保存下来. 步骤如下: 1.打开 git b ...

  5. Vue&Element开发框架中增加工作流处理,查看申请单中整合多个处理类型的处理

    关于我在Winform框架.混合框架.Bootstrap开发框架中的简易审批性工作流模块,我写过不少文章,有兴趣可以参考<工作流模块>的随笔进行了解,本篇随笔在完成了Vue&Ele ...

  6. 微软Azure Services Bus中的工作流

    在Azure Services Platform上对于工作流服务的支持,一直是我很感兴趣的内容.当然也是疑问 比较多的领域.鉴于这方面的资料太少,所以今天就从AzureServicesKit中的一个D ...

  7. Golang网络库中socket阻塞调度源码剖析

    本文分析了Golang的socket文件描述符和goroutine阻塞调度的原理.代码中大部分是Go代码,小部分是汇编代码.完整理解本文需要Go语言知识,并且用Golang写过网络程序.更重要的是,需 ...

  8. Dynamics 365中自定义工作流活动更新了输入输出参数后获取的方法

    关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复245或者20170309可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong. ...

  9. Dynamics 365中自定义工作流活动获取的上下文分析及注意事项

    关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复244或者20170306可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong. ...

随机推荐

  1. CSS中float和Clear的使用

    CSS中float和Clear的使用 本文和大家重点讨论一下CSS中Float和Clear属性的使用,一个float对象可以居左或居右,一个设置为float的对象,将根据设置的方向,左移或右移到其父容 ...

  2. UVa 818Cutting Chains (暴力dfs+位运算+二进制法)

    题意:有 n 个圆环,其中有一些已经扣在一起了,现在要打开尽量少的环,使所有的环可以组成一条链. 析:刚开始看的时候,确实是不会啊....现在有点思路,但是还是差一点,方法也不够好,最后还是参考了网上 ...

  3. 排序:快速排序Quick Sort

    原理,通过一趟扫描将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序 ...

  4. Google Tango service outdated谷歌Tango的服务过时了

    If you device showed "tango service outdated." It means that your Tango Core need to be up ...

  5. Ubuntu 14.04 install emacs 24.5

    1.前期准备工作 2.安装基础构件工具 3.下载emacs编译需要的依赖库 4.下载emacs24.5编译安装 5.下载并安装我的emacs配置文件 6.配置tmux和zsh 1. 前期准备工作 在阿 ...

  6. Java ActiveMQ 讲解(一)理解JMS 和 ActiveMQ基本使用

    最近的项目中用到了mq,之前自己一直在码农一样的照葫芦画瓢.最近几天研究了下,把自己所有看下来的文档和了解总结一下. 一. 认识JMS 1.概述 对于JMS,百度百科,是这样介绍的:JMS即Java消 ...

  7. raiserror 的用法

    if exists(select top 1 UserName from [dbo].[LJS_Test_User] where UserName=@UserName) begin raiserror ...

  8. easyui datagrid fit 属性

    fit:true - 自适应大小,填充容器 fitColumns:true - 自动使列适应表格宽度以防止出现水平滚动.

  9. cesium编程入门(八)设置材质

    cesium编程入门(八)设置材质 Cesium中为几何形状设置材质有多种方法 第一种方法 Material 直接构建Cesium.Material对象,通过设置Material的属性来进行控制,官方 ...

  10. indows 2008 r2/做了SPS2007---2013后,发现添加原来域中的域组添加不上

    根据上次的网络包的分析, 我们在AD中找到了wtc-beijing-it的组, 不过在SharePoint日志中我们没有发现搜索成功的记录. - SearchResultEntry: CN=WTC-B ...