本文使用 Zhihu On VSCode 創作併發布
前言
在大型的開源專案或者軟體開發過程中, 很多開發者都會去提交
PR
或者進行程式碼的
push
操作。如果對於每次程式碼合併都需要專案的核心維護者進行
code review
,這項工作是及其困難而且耗時的。因此許多團隊都會指定一套程式碼規範, 然後編寫測試用例嚴格的檢查每次程式碼修改, 這樣能夠非常有效的減少後期程式碼維護的成本。
現在,基於
, 我們可以自動化的完成程式碼的
CI/CD
工作流。
是 GitHub 推出的持續整合 (Continuous integration,簡稱 CI) 服務,它提供了配置非常不錯的虛擬伺服器環境,基於它可以進行構建、測試、打包、部署專案。
Github Actions 的最大優勢就是它是與 GitHub 高度整合的,只需一個配置檔案即可自動開啟服務。甚至你不需要購買伺服器 —— GitHub Actions 自帶雲環境執行,包括私有倉庫也可以享用,而且雲環境效能也非常不錯。
本篇文章將介紹 GitHub Actions 的基本使用方法。
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 Actions 為每個任務 (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/分鐘,公共倉庫則無限制。
作業系統方面可選擇 Windows server、Linux、macOS,並預裝了大量軟體包和工具。
TIPS: 雖然名稱叫持續整合,但當所有任務終止和完成時,虛擬環境內的資料會隨之清空,並不會持續。即每個新任務都是一個全>新的虛擬環境。
workflow 檔案
GitHub Actions 的配置檔案叫做 workflow 檔案,存放在程式碼倉庫的
。github/workflows
目錄中。
workflow 檔案採用 YAML 格式,檔名可以任意取,但是字尾名統一為。yml,比如
build。yml
。一個庫可以有多個 workflow 檔案,GitHub 只要發現
。github/workflows
目錄裡面有
。yml
檔案,就會按照檔案中所指定的觸發條件在符合條件時自動執行該檔案中的工作流程。
在 Actions 頁面可以看到很多種語言的 workflow 檔案的模版,可以用於簡單的構建與測試。下面是一個簡單的 workflow 檔案示例:
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 欄位是 workflow 的名稱。若忽略此欄位,則預設會設定為 workflow 檔名。
name: GitHub Actions Demo
(2) on
on 欄位指定 workflow 的觸發條件,通常是某些事件,比如示例中的觸發事件是 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 的完成才能執行。因此,這個 workflow 的執行順序依次為: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 Actions 中的重要組成部分,這點從名稱中就可以看出,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