通過Gitlab的WebHooks實現(xiàn)網(wǎng)站內(nèi)容同步

背景

我自己有一個用Hexo搭建的網(wǎng)站——https://4ading.com/倦畅,用來發(fā)布自己寫的文章。文章使用阿里云的代碼托管服務(wù)(以下簡稱CODE呻粹,使用Gitlab搭建)存儲闸翅,并在ECS上進行克隆,通過Nginx進行訪問贵扰。

網(wǎng)絡(luò)圖

開始的時候,我發(fā)布文章的流程如下:

  1. 在電腦上編輯文章流部。
  2. 要發(fā)布的時候戚绕,執(zhí)行hexo g -d,通過Git發(fā)布到CODE上枝冀。
  3. 使用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)的處理流程才是最重要的囊骤。

WebHooks示意圖

設(shè)置WebHooks的方法

在CODE(其他基于Gitlab的托管平臺的操作流程應(yīng)該也差不多)中晃择,設(shè)置WebHooks的方式很簡單:

在項目的設(shè)置中,點擊“WebHooks”選項也物,填入接收請求的URL宫屠,選擇觸發(fā)事件,點擊下面的“增加WEBHOOKS”滑蚯,即可成功添加一條浪蹂。可以看到告材,在“觸發(fā)”的選項中坤次,有一個“推送事件”,當(dāng)推送內(nèi)容至倉庫后斥赋,即觸發(fā)缰猴。

增加WebHooks

這樣一來,我們可以寫實現(xiàn)如下操作的代碼:當(dāng)接收到請求后疤剑,即在服務(wù)器中對網(wǎng)站根目錄進行git pull操作滑绒。

其實,這種要求非常低隘膘。如果你裝了寶塔面板疑故,只要裝一下“寶塔WebHook”的插件,在插件里面設(shè)置好如下的shell腳本就可以了棘幸。

cd 網(wǎng)站根目錄
git pull

點“查看密鑰”焰扳,就可以看到插件給你的URL,用這個URL做WebHook的鏈接就行了误续。

寶塔WebHook給出的URL

僅在提交特定分支時調(diào)用URL

但是吨悍,我的這個倉庫有兩個分支:masterhexomaster分支存儲生成的網(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í)行的怎么樣。

WebHooks測試

不僅要用這個測試按鈕荡灾,還要在本地實際進行提交瓤狐,進行測試瞬铸。我上面的代碼就測試了不下十次才寫出點樣子。

不僅僅是網(wǎng)站础锐,還可以是整個項目

實際上嗓节,一開始,這個倉庫只有一個分支郁稍。之所以開另一個分支赦政,是因為我希望能夠在不同的地方寫文章胜宇。

和WordPress這樣的博客系統(tǒng)不同耀怜,Hexo在線上是靜態(tài)的,源文件都在本地桐愉。我此前希望自己寫在線編輯器财破,但是因為我懶、菜从诲,就一直沒有寫左痢。后來我試了hexo-adminhexo-myadmin這樣的在線編輯器系洛,但是前者太丑了俊性,后者在手機端上幾乎沒法用。最后我找了半天描扯,選擇了hexo-hey定页,因為它的頁面適配手機端比較好。但是绽诚,作者不更新了典徊,手機端上也有一些問題。目前我還沒有找到滿意的在線編輯器恩够。

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ù)器的功能圖如下:

服務(wù)器功能圖
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末擎浴,一起剝皮案震驚了整個濱河市员咽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌贮预,老刑警劉巖贝室,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件契讲,死亡現(xiàn)場離奇詭異,居然都是意外死亡滑频,警方通過查閱死者的電腦和手機捡偏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事穴亏。” “怎么了篙骡?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長忠藤,這世上最難降的妖魔是什么挟伙? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任楼雹,我火速辦了婚禮,結(jié)果婚禮上尖阔,老公的妹妹穿的比我還像新娘贮缅。我一直安慰自己,他們只是感情好介却,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布谴供。 她就那樣靜靜地躺著,像睡著了一般齿坷。 火紅的嫁衣襯著肌膚如雪桂肌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天永淌,我揣著相機與錄音崎场,去河邊找鬼。 笑死遂蛀,一個胖子當(dāng)著我的面吹牛谭跨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播李滴,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼螃宙,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了所坯?” 一聲冷哼從身側(cè)響起谆扎,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎芹助,沒想到半個月后堂湖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體籍凝,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年苗缩,在試婚紗的時候發(fā)現(xiàn)自己被綠了饵蒂。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡酱讶,死狀恐怖退盯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情泻肯,我是刑警寧澤渊迁,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站灶挟,受9級特大地震影響琉朽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜稚铣,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一箱叁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧惕医,春花似錦耕漱、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至峡钓,卻和暖如春妓笙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背能岩。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工寞宫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人捧灰。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓淆九,卻偏偏與公主長得像,于是被迫代替她去往敵國和親毛俏。 傳聞我的和親對象是個殘疾皇子炭庙,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容