1. 程式人生 > >Go工具& GitLab-讓你如何輕鬆進行持續整合

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個徽章:

構建狀態:主分支上最後一個管道的狀態:

Build Status

覆蓋報告:測試程式碼覆蓋百分比

Coverage Report

go報告卡:

Go Report Card

許可證:
License MIT

其中覆蓋報告需要特殊配置。因為考慮到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是一個現代化的開源監控解決方案,專為效能而設計,彌補了公司各個層面的差距。