背景
我自己有一個用Hexo搭建的網(wǎng)站——https://4ading.com/倦畅,用來發(fā)布自己寫的文章。文章使用阿里云的代碼托管服務(wù)(以下簡稱CODE呻粹,使用Gitlab搭建)存儲闸翅,并在ECS上進行克隆,通過Nginx進行訪問贵扰。
開始的時候,我發(fā)布文章的流程如下:
- 在電腦上編輯文章流部。
- 要發(fā)布的時候戚绕,執(zhí)行
hexo g -d
,通過Git發(fā)布到CODE上枝冀。 - 使用SSH登錄服務(wù)器舞丛,在服務(wù)器上的網(wǎng)站根目錄下執(zhí)行
git pull
耘子,完成同步。
在此之前球切,我把連接配置名谷誓、密鑰什么的都設(shè)置好了。新版的Windows 10自帶SSH客戶端吨凑,因此基本上不需要下載其他的SSH客戶端捍歪。
不過,上面的流程要輸入好幾行代碼鸵钝,中間等待的時間還是比較長的糙臼。后來,我將第二步和第三步用一行PowerShell命令行搞定了:
hexo g -d; ssh 之前設(shè)置好的連接配置名 "cd 服務(wù)器上的網(wǎng)站根目錄 && git pull"
不過恩商,這行代碼太長变逃,我一般都是在終端里面上翻歷史命令,找到這一條再執(zhí)行的怠堪。結(jié)果揽乱,幾周前重裝系統(tǒng)后,這條記錄就沒了粟矿,我還因為命令中的分隔符糾結(jié)了半天锤窑。
于是,我便想:如果能夠在執(zhí)行hexo g -d
之后自動更新服務(wù)器上的文件嚷炉,就好了渊啰。
WebHooks介紹
我以前聽說,GitHub上面可以通過Travis CI這樣的持續(xù)集成工具申屹,在推送時進行測試绘证、構(gòu)建、部署等工作哗讥。
后來嚷那,我發(fā)現(xiàn),Gitlab中杆煞,有一個叫WebHooks的東西魏宽,如果加以利用,可以自建屬于自己的持續(xù)集成工具(其他的代碼托管平臺也有類似的功能决乎,包括上面提到的GitHub)队询。
簡單來說,WebHooks就是:當(dāng)項目進行了某項操作(如提交)构诚,代碼托管平臺就會調(diào)用給定的URL蚌斩,發(fā)送一條POST請求。至于URL那邊的服務(wù)器范嘱,接收到請求之后送膳,就可以為所欲為了——這篇文章中提到的同步存儲庫什么的员魏,只是冰山一角,如果你會寫一些腳本叠聋,理論上可以做到發(fā)郵件撕阎、在社交平臺/即時通信平臺上發(fā)送消息、甚至如前面提到的那樣碌补,自動進行測試闻书、構(gòu)建、部署脑慧。當(dāng)然魄眉,和一般的請求一樣,服務(wù)器也會給一個返回闷袒。不過這個返回的意義并不是非常大坑律,相比之下,服務(wù)器內(nèi)的處理流程才是最重要的囊骤。
設(shè)置WebHooks的方法
在CODE(其他基于Gitlab的托管平臺的操作流程應(yīng)該也差不多)中晃择,設(shè)置WebHooks的方式很簡單:
在項目的設(shè)置中,點擊“WebHooks”選項也物,填入接收請求的URL宫屠,選擇觸發(fā)事件,點擊下面的“增加WEBHOOKS”滑蚯,即可成功添加一條浪蹂。可以看到告材,在“觸發(fā)”的選項中坤次,有一個“推送事件”,當(dāng)推送內(nèi)容至倉庫后斥赋,即觸發(fā)缰猴。
這樣一來,我們可以寫實現(xiàn)如下操作的代碼:當(dāng)接收到請求后疤剑,即在服務(wù)器中對網(wǎng)站根目錄進行git pull
操作滑绒。
其實,這種要求非常低隘膘。如果你裝了寶塔面板疑故,只要裝一下“寶塔WebHook”的插件,在插件里面設(shè)置好如下的shell腳本就可以了棘幸。
cd 網(wǎng)站根目錄
git pull
點“查看密鑰”焰扳,就可以看到插件給你的URL,用這個URL做WebHook的鏈接就行了误续。
僅在提交特定分支時調(diào)用URL
但是吨悍,我的這個倉庫有兩個分支:master
和hexo
。master
分支存儲生成的網(wǎng)頁文件蹋嵌,hexo
分支存儲網(wǎng)站的源文件和設(shè)置育瓜。我只想讓服務(wù)器在master
分支被提交時更新網(wǎng)站內(nèi)容,而hexo
分支更新與否與網(wǎng)站無關(guān)栽烂。
Gitlab中并沒有關(guān)于“提交某個特定的分支”的觸發(fā)事件躏仇,但是,在調(diào)用URL的時候腺办,會發(fā)送特定的請求頭和請求體焰手。
幫助文檔中給出了比較詳細的事例。比如推送事件的請求頭如下:
X-Gitlab-Event: Push Hook
請求體為JSON格式的內(nèi)容怀喉,如下:
{
"object_kind": "push",
"before": "95790bf891e76fee5e1747ab589903a6a1f80f22",
"after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"ref": "refs/heads/master",
"user_id": 4,
"user_name": "John Smith",
"user_email": "john@example.com",
"project_id": 15,
"repository": {
"name": "Diaspora",
"url": "git@example.com:mike/diasporadiaspora.git",
"description": "",
"homepage": "http://example.com/mike/diaspora",
"git_http_url":"http://example.com/mike/diaspora.git",
"git_ssh_url":"git@example.com:mike/diaspora.git",
"visibility_level":0
},
"commits": [
{
"id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
"message": "Update Catalan translation to e38cb41.",
"timestamp": "2011-12-12T14:27:31+02:00",
"url": "http://example.com/mike/diaspora/commit/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
"author": {
"name": "Jordi Mallach",
"email": "jordi@softcatala.org"
}
"added": ["CHANGELOG"],
"modified": ["app/controller/application.rb"],
"removed": []
},
{
"id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"message": "fixed readme",
"timestamp": "2012-01-03T23:36:29+02:00",
"url": "http://example.com/mike/diaspora/commit/da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"author": {
"name": "GitLab dev user",
"email": "gitlabdev@dv6700.(none)"
},
"added": ["CHANGELOG"],
"modified": ["app/controller/application.rb"],
"removed": []
}
],
"total_commits_count": 4
}
可以看到书妻,請求體里面的"ref"
字段中包含了推送的分支,只要做到這個操作就可以了:在服務(wù)器收到請求后躬拢,讀取請求體躲履,判斷該字段中是否表示master
分支,如果是聊闯,則更新網(wǎng)站根目錄的內(nèi)容工猜。
當(dāng)然,為了安全菱蔬,還要驗證請求頭是否正確篷帅。
但是,“寶塔WebHook”插件太簡單了拴泌,做不到這個犹褒。它的傳值在查詢字符串中,讀不了請求頭和請求體弛针。這樣一來叠骑,我就只能寫服務(wù)器腳本來實現(xiàn)這個功能了。
你可以使用你熟悉的語言和框架寫服務(wù)器腳本削茁,只要能夠收到請求并作出正確的反應(yīng)就行了宙枷。我接觸JS和Python比較多,不過Python中的Flask框架足夠輕量(說實話貌似有更輕量的)茧跋,開發(fā)起來也容易得多慰丛。于是,我便使用了Python和Flask框架來寫這個腳本瘾杭。
使用Python和Flask框架實現(xiàn)功能
安裝好Flask诅病,然后寫入如下的代碼。
基本骨架是Flask提供的Hello World示例,我翻了很多資料贤笆,東拼西湊出來可以運行的代碼蝇棉。不過確實非常簡單。
一些必要的注釋我放在代碼里面了芥永。
# flask.request是用來處理請求的
from flask import Flask, request
# 代碼中涉及到日志
import logging
# 切換目錄用
import os
# 執(zhí)行外部腳本用
import subprocess
app = Flask(__name__)
# 這樣寫篡殷,URL就是http://地址:端口號/hook
# WebHooks用的是POST請求
@app.route('/hook', methods=['POST'])
def git_hook():
# 請求頭要對應(yīng);請求頭有很多字段埋涧,所以找請求頭中是否有這個字符串
if str(request.headers).find('X-Gitlab-Event: Push Hook')!=-1:
# 找請求體的"ref"字段是否有"master"的字樣
if str(request.json['ref']).find('/master')!=-1:
# 切換目錄
os.chdir('網(wǎng)站根目錄')
# 使用subprocess.check_output執(zhí)行shell腳本板辽,腳本內(nèi)被空格隔開的部分用列表裝住
# 如果出錯,會拋出subprocess.CalledProcessError錯誤棘催,所以要用try...except接住錯誤
# subprocess.CalledProcessError.returncode是錯誤時的返回值
# subprocess.CalledProcessError.output是shell腳本在控制臺上輸出的字符
try:
output = subprocess.check_output(['git','pull'])
except subprocess.CalledProcessError as e:
app.logger.error('git err at %s', e.returncode)
app.logger.error('git err output:\n %s', e.output)
return 'git error'
app.logger.info('git pull master success')
return 'sucess'
else:
# 非master分支的提交
app.logger.info('not master')
# 返回不需要寫那么詳細
return 'sucess'
else:
# 請求頭錯誤
app.logger.info('head error')
return 'error'
在服務(wù)器上部署代碼劲弦。我用的是寶塔的Python項目管理器,因此具體的腳本我就不清楚了醇坝,反正我用了gunicorn邑跪。
之后,用Nginx進行端口的反向代理纲仍,并應(yīng)用SSL呀袱。具體設(shè)置方法看網(wǎng)上的文檔,因為我也是用寶塔的默認文檔魔改的郑叠。當(dāng)然夜赵,如果你對安全不那么在意的話,你也可以不用設(shè)置Nginx乡革。這時寇僧,監(jiān)聽主機要設(shè)置為'0.0.0.0'
。
此后就可以到CODE(Gitlab)去設(shè)置WebHooks了沸版。如果你用了反代什么的嘁傀,就根據(jù)反代的參數(shù)設(shè)置URL。如果你開了SSL视粮,就可以點下面的“開啟SSL證書驗證”了细办。
增加完成后,要多測試蕾殴。頁面下方給出了測試按鈕笑撞,會發(fā)送一條測試的請求。到主機的服務(wù)器上看日志钓觉、看返回結(jié)果什么的茴肥,看看執(zhí)行的怎么樣。
不僅要用這個測試按鈕荡灾,還要在本地實際進行提交瓤狐,進行測試瞬铸。我上面的代碼就測試了不下十次才寫出點樣子。
不僅僅是網(wǎng)站础锐,還可以是整個項目
實際上嗓节,一開始,這個倉庫只有一個分支郁稍。之所以開另一個分支赦政,是因為我希望能夠在不同的地方寫文章胜宇。
和WordPress這樣的博客系統(tǒng)不同耀怜,Hexo在線上是靜態(tài)的,源文件都在本地桐愉。我此前希望自己寫在線編輯器财破,但是因為我懶、菜从诲,就一直沒有寫左痢。后來我試了hexo-admin
、hexo-myadmin
這樣的在線編輯器系洛,但是前者太丑了俊性,后者在手機端上幾乎沒法用。最后我找了半天描扯,選擇了hexo-hey
定页,因為它的頁面適配手機端比較好。但是绽诚,作者不更新了典徊,手機端上也有一些問題。目前我還沒有找到滿意的在線編輯器恩够。
要使用在線編輯器卒落,就要把整個工作目錄放上去,而不僅僅是生成的文件蜂桶。我在知乎上找到了教程儡毕,將生成文件和工作目錄放到不同的分支下。然后扑媚,我按照組件腰湾、用pm2執(zhí)行,再用Nginx做反向代理钦购,以便使用SSL證書檐盟,終于搞好了。
這時押桃,如果在手機端上修改了文章的草稿(由于我修改了一些node-modules葵萎,加上我寫文章并不快,所以我目前不期望在手機端上發(fā)布文章),就要:在手機端上使用SSH登錄到服務(wù)器羡忘,再在工作目錄下執(zhí)行以下命令谎痢,以將更改上傳到CODE:
git add .
git commit -m "提交信息"
git push
然后,回去用電腦編輯之前卷雕,也要從CODE上下載更改节猿。更要命的是,編輯好了之后漫雕,還要上傳更改滨嘱,然后在服務(wù)器上下載更改,以便手機端修改浸间。
簡而言之太雨,每次編輯前后,都要拉取魁蒜、提交修改囊扳。電腦端上倒無所謂,但是手機端上執(zhí)行這一系列流程挺困難的兜看。而且锥咸,雖然關(guān)于git的所有流程都在電腦上進行,但是還是挺麻煩的细移,而且容易忘記拉取搏予,一旦忘了就尷尬了。
在我寫完上面的腳本之后葫哗,我想到了這個情況缔刹,便想到了一個方法:當(dāng)推送hexo
分支后,同時在服務(wù)器上拉取該分支劣针。這樣一來校镐,就能省下一半的工作量。
修改起來也簡單捺典,只要在上面的代碼中的# 非master分支的提交
處鸟廓,照著寫一下hexo
分支提交之后的處理方法,再測試一下襟己,就行了引谜。
至此,我的服務(wù)器的功能圖如下: