Go工具& GitLab-讓你如何輕鬆進行持續整合
譯者注:在Pantomath,我們使用GitLab進行所有開發工作。本文介紹如何使用這些工具來減輕開發壓力。
在Pantomath,我們使用GitLab進行所有開發工作。 本文的目的不是介紹GitLab及其所有功能,而是介紹如何使用這些工具來減輕我們的壓力。
為了讓你開發專案中的所有內容自動化,並能讓你專注於程式碼編寫。 我們將介紹lint,單元測試,資料競爭,記憶體消毒,程式碼覆蓋和構建。
在這篇文章中顯示的所有程式碼都可以在https://gitlab.com/pantomath-io/demo-tools中找到。你可以免費獲取使用程式碼,還可以做標記連結。程式碼存放在$ GOPATH的src資料夾中:
$ go get -v -d gitlab.com/pantomath-io/demo-tools
$ cd $GOPATH/src/gitlab.com/pantomath-io/demo-tools
go工具
幸運的是,Go有很多有用的工具,可以構建、測試和檢查程式碼。事實上,一切都在那裡。我們只需要利用額外的工具來拼接組合它們。但前提是我們需要一個一個地瞭解他們的功能。
包列表
正如在官方文件中所描述的那樣,go專案是包的集合。下面介紹的大多數工具都將使用這些包,因此我們需要的第一個命令是列出包的方法。我們可以用go list子命令來完成(閱讀優秀的手冊和來自Dave Cheney的優秀文章):
$ go list ./...
請注意,如果我們要避免將我們的工具應用於外部資源,並將其限制在我們的程式碼中。 那麼我們需要去除vendor 目錄,命令如下:
$ go list ./... | grep -v /vendor/
linter
這是我們在程式碼中使用的第一個工具:linter。它的作用是檢查程式碼風格/錯誤。這聽起來像是一個可選的工具,或者至少是一個“不錯”的工具,但它確實有助於在專案上保持一致的程式碼風格。
linter並不是go本身的一部分,所以如果要使用,你需要手動安裝它(見官方文件)。
使用方法相當簡單:只需在程式碼包上執行它(也可以指向. go檔案):
$ golint -set_exit_status $(go list ./... | grep -v /vendor/)
注意-set_exit_status選項。 預設情況下,golint僅輸出樣式問題,並帶有返回值(帶有0返回碼),所以CI不認為是出錯。 如果指定了-set_exit_status,則在遇到任何樣式問題時,golint的返回碼將不為0。
單元測試
這些是您可以在程式碼中執行的最常見的測試。每個.go檔案需要一個能支援單元測試的_test.go檔案。可以使用以下命令執行所有包的測試:
$ go test -short $(go list ./... | grep -v /vendor/)
資料競爭
這通常是一個難以逃避解決的問題,go工具預設具有(但只能在linux / amd64、freebsd / amd64、darwin / amd64和windows / amd64上使用)。有關資料競爭的更多資訊,請參閱本文。與此同時,下面是如何執行它的命令:
$ go test -race -short $(go list . /…| grep - v /vendor/)
記憶體檢錯
Clang有一個很好的用讀未初始化的記憶體檢測器,稱為MemorySanitizer。go測試工具能很好地與Clang模組進行互動(只要在linux / amd64主機上,並使用最新版本的Clang / LLVM(> = 3.8.0)。執行命令如下:
$ go test -msan -short $(go list . /…| grep - v /vendor/)
程式碼覆蓋
這是評估程式碼的質量的必備工具,並能顯示哪部分程式碼進行了單元測試,哪部分沒有。 Rob Pike關於這個主題上寫了一篇完整的文章。
要計算程式碼覆蓋率,需要執行以下指令碼:
$ PKG_LIST=$(go list ./... | grep -v /vendor/)
$ for package in ${PKG_LIST}; do
go test -covermode=count -coverprofile "cover/${package##*/}.cov" "$package" ;
done
$ tail -q -n +2 cover/*.cov >> cover/coverage.cov
$ go tool cover -func=cover/coverage.cov
如果我們想要獲得HTML格式的覆蓋率報告,我們需要新增以下命令:
$ go tool cover -html=cover/coverage.cov -o coverage.html
構建
最後一旦程式碼經過了完全測試,我們要對程式碼進行編譯,從而構建可以執行的二進位制檔案。
$ go build -i -v gitlab.com/pantomath-io/demo-tools
Makefile
git標籤:init-makefile
現在我們可以把持續整合環境中使用的所有工具,全部打包在Makefile中,並用統一的方式呼叫它們。
這個文件的目的不是要make,你可以參考官方文件瞭解更多關於Makefile的資訊。
PROJECT_NAME := "demo-tools"
PKG := "gitlab.com/pantomath-io/$(PROJECT_NAME)"
PKG_LIST := $(shell go list ${PKG}/... | grep -v /vendor/)
GO_FILES := $(shell find . -name '*.go' | grep -v /vendor/ | grep -v _test.go)
.PHONY: all dep build clean test coverage coverhtml lint
all: build
lint: ## Lint the files
@golint -set_exit_status ${PKG_LIST}
test: ## Run unittests
@go test -short ${PKG_LIST}
race: dep ## Run data race detector
@go test -race -short ${PKG_LIST}
msan: dep ## Run memory sanitizer
@go test -msan -short ${PKG_LIST}
coverage: ## Generate global code coverage report
./tools/coverage.sh;
coverhtml: ## Generate global code coverage report in HTML
./tools/coverage.sh html;
dep: ## Get the dependencies
@go get -v -d ./...
build: dep ## Build the binary file
@go build -i -v $(PKG)
clean: ## Remove previous build
@rm -f $(PROJECT_NAME)
help: ## Display this help screen
@grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
我們現在有什麼? 除了之前提供任何工具之外,還有另外3點:
- 依賴的安裝(dep);
- 專案的內部管理(清潔);
- 有效的幫助(幫助)。
注意還必須為計算程式碼覆蓋率建立一個指令碼。 這是因為在Makefile檔案中實現迴圈是很困難的。所以通過編寫bash指令碼,Makefile觸發指令碼來完成程式碼覆蓋率工作。
使用以下命令來建立Makefile:
$ make help
$ make lint
$ make coverage
持續整合
git標籤:init-ci
現在這些工具已經到位了,我們可以對程式碼執行各種測試,我們希望在程式碼庫中實現測試自動化。 幸運的是,GitLab為此提供CI管道。 它的設定非常簡單:所有你要建立的僅僅是在儲存庫根目錄下的.gitlab-ci.yml檔案。
其中Yaml檔案的完整文件提供了所有的選項,首先從這個.gitlab-ci.yml開始:
image: golang:1.9
cache:
paths:
- /apt-cache
- /go/src/github.com
- /go/src/golang.org
- /go/src/google.golang.org
- /go/src/gopkg.in
stages:
- test
- build
before_script:
- mkdir -p /go/src/gitlab.com/pantomath-io /go/src/_/builds
- cp -r $CI_PROJECT_DIR /go/src/gitlab.com/pantomath-io/pantomath
- ln -s /go/src/gitlab.com/pantomath-io /go/src/_/builds/pantomath-io
- make dep
unit_tests:
stage: test
script:
- make test
race_detector:
stage: test
script:
- make race
memory_sanitizer:
stage: test
script:
- make msan
code_coverage:
stage: test
script:
- make coverage
code_coverage_report:
stage: test
script:
- make coverhtml
only:
- master
lint_code:
stage: test
script:
- make lint
build:
stage: build
script:
- make
我們對上面的檔案進行分解,下面是對它的內容的一些解釋:
- 首先要選擇Docker映象用於執行CI。到Docker Hub為你的專案選擇正確的映象。
然後,指定要快取映象的資料夾。目的是避免多次下載相同的內容。一旦工作完成,列出的路徑將被存檔,下一次將使用相同的存檔。
其次將工作拆分成不同階段。在我們的例子中,我們有兩個階段(按照這個順序來處理):測試和構建。也可以有其他的階段,比如部署。
before_script定義在作業實際完成之前在Docker容器中執行的命令。在我們的環境中,這些命令只是複製或連結在$ GOPATH中部署的儲存庫,並安裝依賴關係。
然後使用Makefile來執行作業。請注意code_coverage_report的特殊情況,其中執行被限制在masterbranch中。
但當我們提交/推送儲存庫中的.gitlab-ci.yml檔案時,CI會自動觸發管道錯誤。 為什麼?
其中lint_code作業錯誤,是因為它找不到golint二進位制檔案:
$ make lint
make: golint: Command not found
Makefile:11: recipe for target 'lint' failed
make: *** [lint] Error 127
所以,需要更新你的Makefile來安裝golint作為依賴的一部分。
memory_sanitizer作業錯誤,是因為gcc:
$ make msan
# runtime/cgo
gcc: error: unrecognized argument to -fsanitize= option: 'memory'
Makefile:20: recipe for target 'msan' failed
make: *** [msan] Error 2
但是請記住,我們需要使用Clang / LLVM> = 3.8.0來使用go測試命令中的-msan選項。
這裡有兩個選擇:
- 我們要麼在作業中設定Clang(使用before_script);
- 或者我們使用預設安裝了Clang的Docker映象。
第一個選擇很好,但這意味著要為每個獨立的作業進行設定。 這將耗費時間,但我們想一勞永逸。 所以我們更喜歡第二個選擇,使用GitLab登錄檔。
git標籤:use-own-docker
我們需要為容器建立Dockerfile(像往常一樣:閱讀官方文件以獲取更多關於它的選項):
# Base image: https://hub.docker.com/_/golang/
FROM golang:1.9
MAINTAINER Julien Andrieux <[email protected]>
# Install golint
ENV GOPATH /go
ENV PATH ${GOPATH}/bin:$PATH
RUN go get -u github.com/golang/lint/golint
# Add apt key for LLVM repository
RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
# Add LLVM apt repository
RUN echo "deb http://apt.llvm.org/stretch/ llvm-toolchain-stretch-5.0 main" | tee -a /etc/apt/sources.list
# Install clang from LLVM repository
RUN apt-get update && apt-get install -y --no-install-recommends \
clang-5.0 \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# Set Clang as default CC
ENV set_clang /etc/profile.d/set-clang-cc.sh
RUN echo "export CC=clang-5.0" | tee -a ${set_clang} && chmod a+x ${set_clang}
Dockerfile構建的容器將基於golang:1.9映象(是在.gitlab-ci.yml檔案中引用的映象)。
以前我們在容器中首先安裝golint, 然後我們按照規範的方式在LLVM倉庫安裝Clang 5.0。
現在我們有了Dockerfile,我們則需要構建容器映象,並將其提供給GitLab:
$ docker login registry.gitlab.com
$ docker build -t registry.gitlab.com/pantomath-io/demo-tools .
$ docker push registry.gitlab.com/pantomath-io/demo-tools
上面第一條命令代表連線到GitLab登錄檔。第二條代表構建Dockerfile中描述的容器映像。第三條代表把它放到GitLab登錄檔。
如果檢視儲存庫的登錄檔,你將看到你準備使用的映象。為了讓CI使用你的映象,你需要更新.gitlab- ci.yml檔案:
image: golang:1.9
變
image:registry.gitlab.com/pantomath-io/demo-tools:latest
最後一個細節:需要讓CI使用正確的編譯器(即CCenvironment變數),因此我們在.gitlab-ci.yml檔案中添加了變數初始化:
export CC=clang-5.0
一旦修改完成,下一次提交將觸發管道:
https://gitlab.com/pantomath-io/demo-tools/pipelines/13497136
徽章
git標籤:init-badges
現在工具已經就緒,每個提交都將啟動一個測試套件,您可能想要顯示它,這是合理的:)最好的方法就是在README檔案中使用徽章。
編輯README檔案,並新增以下4個徽章:
構建狀態:主分支上最後一個管道的狀態:
覆蓋報告:測試程式碼覆蓋百分比
go報告卡:
其中覆蓋報告需要特殊配置。因為考慮到CI中有作業要在執行時顯示,所以需要告訴GitLab如何獲取這些資訊。
在作業的輸出中,要為GitLab配置正則表示式。 如果正則表示式匹配到,GitLab則把匹配結果作為程式碼覆蓋率。
這需要在儲存庫中的設定> CI / CD,向下滾動到“常規管道設定”部分中的“測試覆蓋率分析”設定,然後使用以下正則表示式:
total:\s+\(statements\)\s+(\d+.\d+\%)
結論
接下來是什麼?可能在CI中進行更多測試。檢視CD(持續部署),自動部署構建。文件可以使用GoDoc完成。其中使用code_coverage_report生成一個覆蓋率報告,但是不要在CI中使用它。你可以使用scp將該HTML檔案複製到web伺服器(參見關於如何使用SSH金鑰的文件)。
我們目前正在研究Pantomath。 Pantomath是一個現代化的開源監控解決方案,專為效能而設計,彌補了公司各個層面的差距。