本文使用 Zhihu On VSCode 創作併發布

前言

在大型的開源專案或者軟體開發過程中, 很多開發者都會去提交

PR

或者進行程式碼的

push

操作。如果對於每次程式碼合併都需要專案的核心維護者進行

code review

,這項工作是及其困難而且耗時的。因此許多團隊都會指定一套程式碼規範, 然後編寫測試用例嚴格的檢查每次程式碼修改, 這樣能夠非常有效的減少後期程式碼維護的成本。

現在,基於

Github  Action

, 我們可以自動化的完成程式碼的

CI/CD

工作流。

Github Ac­tions

是 GitHub 推出的持續整合 (Con­tin­u­ous in­te­gra­tion,簡稱 CI) 服務,它提供了配置非常不錯的虛擬伺服器環境,基於它可以進行構建、測試、打包、部署專案。

Github Actions 的最大優勢就是它是與 GitHub 高度整合的,只需一個配置檔案即可自動開啟服務。甚至你不需要購買伺服器 —— GitHub Actions 自帶雲環境執行,包括私有倉庫也可以享用,而且雲環境效能也非常不錯。

本篇文章將介紹 GitHub Ac­tions 的基本使用方法。

CI (Continuous integration)

網際網路軟體的開發和釋出,已經形成了一套標準流程,最重要的組成部分就是持續整合(Continuous integration,簡稱CI)

概念

持續整合指的是,頻繁地(一天多次)將程式碼整合到主幹。

它的好處主要有兩個

快速發現錯誤。每完成一點更新,就整合到主幹,可以快速發現錯誤,定位錯誤也比較容易。

防止分支大幅偏離主幹。如果不是經常整合,主幹又在不斷更新,會導致以後整合的難度變大,甚至難以整合。

持續整合的目的,就是讓產品可以快速迭代,同時還能保持高質量。它的核心措施是,程式碼整合到主幹之前,必須透過自動化測試。只要有一個測試用例失敗,就不能整合。

持續交付

持續交付(Continuous delivery)指的是,頻繁地將軟體的新版本,交付給質量團隊或者使用者,以供評審。如果評審透過,程式碼就進入生產階段。

持續交付可以看作持續整合的下一步。它強調的是,不管怎麼更新,軟體是隨時隨地可以交付的。

持續部署

持續部署(continuous deployment)是持續交付的下一步,指的是程式碼透過評審以後,自動部署到生產環境。

持續部署的目標是,程式碼在任何時刻都是可部署的,可以進入生產階段。

流程

根據持續整合的設計,程式碼從提交到生產,整個過程有以下幾步。

(1)提交

流程的第一步,是開發者向程式碼倉庫提交程式碼。所有後面的步驟都始於原生代碼的一次提交(commit)。

(2)測試(第一輪)

程式碼倉庫對commit操作配置了鉤子(hook),只要提交程式碼或者合併進主幹,就會跑自動化測試。

測試有好幾種。

單元測試:針對函式或模組的測試

整合測試:針對整體產品的某個功能的測試,又稱功能測試

端對端測試:從使用者介面直達資料庫的全鏈路測試

(3) 構建

透過第一輪測試,程式碼就可以合併進主幹,就算可以交付了。

交付後,就先進行構建(build),再進入第二輪測試。所謂構建,指的是將原始碼轉換為可以執行的實際程式碼,比如安裝依賴,配置各種資源(樣式表、JS指令碼、圖片)等等。

常用的構建工具如下。

Jenkins

Travis

Codeship

Strider

(4) 測試(第二輪)

構建完成,就要進行第二輪測試。如果第一輪已經涵蓋了所有測試內容,第二輪可以省略,當然,這時構建步驟也要移到第一輪測試前面。

第二輪是全面測試,單元測試和整合測試都會跑,有條件的話,也要做端對端測試。所有測試以自動化為主,少數無法自動化的測試用例,就要人工跑。

需要強調的是,新版本的每一個更新點都必須測試到。如果測試的覆蓋率不高,進入後面的部署階段後,很可能會出現嚴重的問題。

(5) 部署

通過了第二輪測試,當前程式碼就是一個可以直接部署的版本(artifact)。將這個版本的所有檔案打包( tar filename。tar * )存檔,發到生產伺服器。

(6) 回滾

一旦當前版本發生問題,就要回滾到上一個版本的構建結果。最簡單的做法就是修改一下符號連結,指向上一個版本的目錄。

Github Action

GitHub Actions 是什麼?

Github Actions是由Github建立的 CI/CD服務。 它的目的是使所有軟體開發工作流程的自動化變得容易。 直接從GitHub構建,測試和部署程式碼。CI(持續整合)由很多操作組成,比如程式碼合併、執行測試、登入遠端伺服器,釋出到第三方服務等等。GitHub 把這些操作就稱為 actions。

很多操作在不同專案裡面是類似的,完全可以共享。GitHub 允許開發者把每個操作寫成獨立的指令碼檔案,存放到程式碼倉庫,使得其他開發者可以引用。

如果你需要某個 action,不必自己寫複雜的指令碼,直接引用他人寫好的 action 即可,整個持續整合過程,就變成了一個 actions 的組合。這就是 GitHub Actions 最特別的地方。

GitHub 做了一個GitHub Marketplace ,可以搜尋到他人提交的 actions。另外,還有一個Awesome Actions的倉庫,也可以找到不少 action。

基礎概念

GitHub Actions 有一些自己的術語。

workflow (工作流程):持續整合一次執行的過程。

job (任務):一個 workflow 由一個或多個 job 構成,含義是一次持續整合的執行,可以完成多個任務。

step(步驟):每個 job 由多個 step 構成,一步步完成。

action (動作):每個 step 可以依次執行一個或多個命令(action)。

虛擬環境

GitHub Ac­tions 為每個任務 (job) 都提供了一個虛擬機器來執行,每臺虛擬機器都有相同的硬體資源:

2-core CPU, 7 GB RAM 記憶體, 14 GB SSD 硬碟空間

硬碟總容量為90G左右,可用空間為30G左右,評測詳見:《GitHub Actions 虛擬伺服器環境簡單評測》

使用限制:

每個倉庫只能同時支援20個 workflow 並行。

每小時可以呼叫1000次 GitHub API 。

每個 job 最多可以執行6個小時。

免費版的使用者最大支援20個 job 併發執行,macOS 最大隻支援5個。

私有倉庫每月累計使用時間為2000分鐘,超過後$ 0。008/分鐘,公共倉庫則無限制。

作業系統方面可選擇 Win­dows server、Linux、ma­cOS,並預裝了大量軟體包和工具。

TIPS: 雖然名稱叫持續整合,但當所有任務終止和完成時,虛擬環境內的資料會隨之清空,並不會持續。即每個新任務都是一個全>新的虛擬環境。

workflow 檔案

GitHub Ac­tions 的配置檔案叫做 work­flow 檔案,存放在程式碼倉庫的

。github/workflows

目錄中。

work­flow 檔案採用 YAML 格式,檔名可以任意取,但是字尾名統一為。yml,比如

build。yml

。一個庫可以有多個 work­flow 檔案,GitHub 只要發現

。github/workflows

目錄裡面有

。yml

檔案,就會按照檔案中所指定的觸發條件在符合條件時自動執行該檔案中的工作流程。

在 Ac­tions 頁面可以看到很多種語言的 work­flow 檔案的模版,可以用於簡單的構建與測試。下面是一個簡單的 work­flow 檔案示例:

name: Hello World

on: push

jobs:

my_first_job:

name: My first job

runs-on: ubuntu-latest

steps:

- name: checkout

uses: actions/checkout@master

- name: Run a single-line script

run:

echo

“Hello World!”

my_second_job:

name: My second job

runs-on: macos-latest

steps:

- name: Run a multi-line script

env:

MY_VAR: Hello World!

MY_NAME: P3TERX

run:

|

echo

$MY_VAR

echo

My name is

$MY_NAME

workflow 語法

(1) name

name 欄位是 work­flow 的名稱。若忽略此欄位,則預設會設定為 work­flow 檔名。

name: GitHub Actions Demo

(2) on

on 欄位指定 work­flow 的觸發條件,通常是某些事件,比如示例中的觸發事件是 push,即在程式碼 push 到倉庫後被觸發。on 欄位也可以是事件的陣列,多種事件觸發,比如在 push 或 pull_request 時觸發:

on:

push, pull_request

push 指定分支觸發

on:

push:

branches:

- master

push tag 時觸發

on:

push:

tags:

-

‘v*’

完整的事件列表,請檢視官方文件。除了程式碼庫事件,GitHub Actions 也支援外部事件觸發,或者定時執行。

(3) jobs

jobs 表示要執行的一項或多項任務。每一項任務必須關聯一個 ID (job_id),比如示例中的 my_first_job 和 my_second_job。job_id 裡面的 name 欄位是任務的名稱。job_id 不能有空格,只能使用數字、英文字母和 - 或_符號,而 name 可以隨意,若忽略 name 欄位,則預設會設定為 job_id。

當有多個任務時,可以指定任務的依賴關係,即執行順序,否則是同時執行。

jobs:

job1:

job2:

needs: job1

job3:

needs:

job1, job2

上面程式碼中,job1 必須先於 job2 完成,而 job3 等待 job1 和 job2 的完成才能執行。因此,這個 work­flow 的執行順序依次為:job1、job2、job3。

(4) runs-on

runs-on: ubuntu-18。04

runs-on 欄位指定任務執行所需要的虛擬伺服器環境,是必填欄位,目前可用的虛擬機器如下:

(5)steps

steps 欄位指定每個任務的執行步驟,可以包含一個或多個步驟。步驟開頭使用 - 符號。每個步驟可以指定以下欄位:

name:步驟名稱。

uses:該步驟引用的action或 Docker 映象。

run:該步驟執行的 bash 命令。

env:該步驟所需的環境變數。

其中 uses 和 run 是必填欄位,每個步驟只能有其一。同樣名稱也是可以忽略的。

action

action 是 GitHub Ac­tions 中的重要組成部分,這點從名稱中就可以看出,actions 是 action 的複數形式。它是已經編寫好的步驟指令碼,存放在 GitHub 倉庫中。

對於初學者來說可以直接引用其它開發者已經寫好的 action,可以在官方 action 倉庫或者 GitHub Marketplace 去獲取。此外 Awesome Actions 這個專案收集了很多非常不錯的 action。

既然 action 是程式碼倉庫,當然就有版本的概念。引用某個具體版本的 action:

steps:

- uses: actions/setup-node@74bc508

# 指定一個 commit

- uses: actions/setup-node@v1。2

# 指定一個 tag

- uses: actions/setup-node@master

# 指定一個分支

Git hooks

1。 什麼是 Git hooks

Git hooks 是 Git 在事件之前或之後執行的指令碼, 用於控制 git 工作的流程。

Git hooks

指令碼對於我們提交

code review

之前識別一些簡單的問題很有用。 我們在每次提交程式碼時都會觸發這些 hooks,以自動指出程式碼中的問題,例如缺少分號,尾隨空白和除錯語句。透過在

code review

之前指出這些問題,程式碼審閱者可以專注於程式碼結構和功能的更改,而不需要浪費時間來審查這些格式問題。

Git hooks 分為客戶端鉤子和服務端鉤子。客戶端鉤子由諸如提交和合並這樣的操作所呼叫,而伺服器端鉤子作用於諸如接收被推送的提交這樣的聯網操作。

客戶端鉤子:

pre-commit

prepare-commit-msg

commit-msg

post-commit

等,主要用於控制客戶端 git 的提交和合並這樣的操作。

服務端鉤子:pre-receive、post-receive、update,主要在服務端接收提交物件時、推送到伺服器之前呼叫。

pre-commit: Check the commit message for spelling errors。

pre-receive: Enforce project coding standards。

post-commit: Email/SMS team members of a new commit。

post-receive: Push the code to production。

2。 Git hooks 如何工作?

每個Git儲存庫都有一個

。git/hooks

資料夾,其中包含可以繫結到的每個鉤子的指令碼。您可以根據需要隨意更改或更新這些指令碼,Git將在這些事件發生時執行它們。進去

。git/hooks

後會看到一些

hooks

的官方示例,他們都是以

。sample

結尾的檔名。

注意這些以

。sample

結尾的示例指令碼是不會執行的,如果你想啟用它們,得先移除這個字尾。

例如下面的檔案列表是

git init

。git/hooks

資料夾下自動建立的

hooks

方法。

-

rwxrwxr

-

x

1

robin

robin

478

Jun

1

17

54

applypatch

-

msg

sample

*

-

rwxrwxr

-

x

1

robin

robin

896

Jun

1

17

54

commit

-

msg

sample

*

-

rwxrwxr

-

x

1

robin

robin

189

Jun

1

17

54

post

-

update

sample

*

-

rwxrwxr

-

x

1

robin

robin

424

Jun

1

17

54

pre

-

applypatch

sample

*

-

rwxrwxr

-

x

1

robin

robin

1642

Jun

1

17

54

pre

-

commit

sample

*

-

rwxrwxr

-

x

1

robin

robin

1239

Jun

1

17

54

prepare

-

commit

-

msg

sample

*

-

rwxrwxr

-

x

1

robin

robin

1348

Jun

1

17

54

pre

-

push

sample

*

-

rwxrwxr

-

x

1

robin

robin

4898

Jun

1

17

54

pre

-

rebase

sample

*

-

rwxrwxr

-

x

1

robin

robin

3610

Jun

1

17

54

update

sample

*

例如下面的檔案列表中多了一個

pre-commit

的檔案, 是我新增的用於程式碼檢查的

pre-commit hook

方法。

-

rwxrwxr

-

x

1

robin

robin

478

Sep

7

10

25

applypatch

-

msg

sample

*

-

rwxrwxr

-

x

1

robin

robin

896

Sep

7

10

25

commit

-

msg

sample

*

-

rwxrwxr

-

x

1

robin

robin

189

Sep

7

10

25

post

-

update

sample

*

-

rwxrwxr

-

x

1

robin

robin

424

Sep

7

10

25

pre

-

applypatch

sample

*

-

rwxrwxr

-

x

1

robin

robin

1475

Sep

8

20

13

pre

-

commit

*

-

rwxrwxr

-

x

1

robin

robin

1642

Sep

7

10

25

pre

-

commit

sample

*

-

rwxrwxr

-

x

1

robin

robin

1239

Sep

7

10

25

prepare

-

commit

-

msg

sample

*

-

rwxrwxr

-

x

1

robin

robin

1348

Sep

7

10

25

pre

-

push

sample

*

-

rwxrwxr

-

x

1

robin

robin

4898

Sep

7

10

25

pre

-

rebase

sample

*

-

rwxrwxr

-

x

1

robin

robin

3610

Sep

7

10

25

update

sample

*

3。pre-commit 簡介

pre-commit

是客戶端hooks之一,

pre-commit

git add

提交之後,然後執行

git commit

時執行, 實現對程式碼的審查。 指令碼執行沒報錯就繼續提交,反之就駁回提交的操作。

這個鉤子指令碼可用於處理簡單問題:對將要提交的程式碼進行檢查、最佳化程式碼格式, 例如檢查是字元數是否超出限制,尾隨空格和除錯語句等。

Git鉤子。我們在每次提交時執行我們的鉤子,以。本文以python 專案為例。

(1)。安裝

## 使用 pip 安裝:

pip

install

pre

-

commit

(2)。 配置

在專案根目錄填加

。pre-commit-config。yaml

檔案, 這裡以

mmdetection

的配置檔案為例來做說明:

repos

-

repo

https

//

gitlab

com

/

pycqa

/

flake8

git

rev

3。8

3

hooks

-

id

flake8

-

repo

https

//

github

com

/

asottile

/

seed

-

isort

-

config

rev

v2

2。0

hooks

-

id

seed

-

isort

-

config

-

repo

https

//

github

com

/

timothycrosley

/

isort

rev

4。3

21

hooks

-

id

isort

-

repo

https

//

github

com

/

pre

-

commit

/

mirrors

-

yapf

rev

v0

30。0

hooks

-

id

yapf

-

repo

https

//

github

com

/

pre

-

commit

/

pre

-

commit

-

hooks

rev

v3

1。0

hooks

-

id

trailing

-

whitespace

-

id

check

-

yaml

-

id

end

-

of

-

file

-

fixer

-

id

requirements

-

txt

-

fixer

-

id

double

-

quote

-

string

-

fixer

-

id

check

-

merge

-

conflict

-

id

fix

-

encoding

-

pragma

args

“——remove”

-

id

mixed

-

line

-

ending

args

“——fix=lf”

-

repo

https

//

github

com

/

myint

/

docformatter

rev

v1

3。1

hooks

-

id

docformatter

args

“——in-place”

“——wrap-descriptions”

“79”

其中 flake8 根據 flake8 給出的程式碼規則檢查程式碼。

(3)。 安裝 git hook scripts

# pre-commit install

pre

-

commit

install

# pre-commit installed at 。git/hooks/pre-commit

第一次 pre-commit 執行時,將會自動下載、安裝並且執行 hook。安裝完成之後,

pre-commit

將會在每次執行

git commit

命令時自動執行。 注意: 每次 clone 程式碼後,都需要執行

pre-commit install

(4)。 手動觸發

第一次,需要觸發全部:

pytorch

robin

@robin

-

Z390

-

UD

~/

jianzh

/

ApulisVision

$

pre

-

commit

run

——

all

-

files

flake8

…………………………………………………………。

Passed

seed

isort

known_third_party

……………………………………。。。

Passed

isort

…………………………………………………………。。

Passed

yapf

…………………………………………………………。。。

Passed

Trim

Trailing

Whitespace

…………………………………………。

Failed

-

hook

id

trailing

-

whitespace

-

exit

code

1

-

files

were

modified

by

this

hook

Fixing

docs

/

GithubAction

md

Check

Yaml

……………………………………………………。。。

Passed

Fix

End

of

Files

………………………………………………。。。

Passed

Fix

requirements

txt

…………………………………………。。。。。

Passed

Fix

double

quoted

strings

…………………………………………

Passed

Check

for

merge

conflicts

…………………………………………

Passed

Fix

python

encoding

pragma

……………………………………。。。。。

Passed

Mixed

line

ending

………………………………………………。。

Passed

docformatter

……………………………………………………。

Passed

可以看到, 我的文件中, 存在一些

trailing whitespace

(5)。 支援語言

docker

docker_image

fail

golang

node

python

python_venv

ruby

rust

swift

pcre

pygrep

script

system