通過Gitlab的WebHooks實現網站內容同步


通過Gitlab的WebHooks實現網站內容同步

背景

我自己有一個用Hexo搭建的網站——https://4ading.com/,用來發布自己寫的文章。文章使用阿里雲的代碼託管服務(以下簡稱CODE,使用Gitlab搭建)存儲,並在ECS上進行克隆,通過Nginx進行訪問。

通過Gitlab的WebHooks實現網站內容同步

網絡圖

開始的時候,我發佈文章的流程如下:

  1. 在電腦上編輯文章。
  2. 要發佈的時候,執行hexo g -d,通過Git發佈到CODE上。
  3. 使用SSH登錄服務器,在服務器上的網站根目錄下執行git pull,完成同步。

在此之前,我把連接配置名、密鑰什麼的都設置好了。新版的Windows 10自帶SSH客戶端,因此基本上不需要下載其他的SSH客戶端。

不過,上面的流程要輸入好幾行代碼,中間等待的時間還是比較長的。後來,我將第二步和第三步用一行PowerShell命令行搞定了:

<code>hexo g -d; ssh 之前設置好的連接配置名 "cd 服務器上的網站根目錄 && git pull"/<code>

不過,這行代碼太長,我一般都是在終端裡面上翻歷史命令,找到這一條再執行的。結果,幾周前重裝系統後,這條記錄就沒了,我還因為命令中的分隔符糾結了半天。

於是,我便想:如果能夠在執行hexo g -d之後自動更新服務器上的文件,就好了。

WebHooks介紹

我以前聽說,GitHub上面可以通過Travis CI這樣的持續集成工具,在推送時進行測試、構建、部署等工作。

後來,我發現,Gitlab中,有一個叫WebHooks的東西,如果加以利用,可以自建屬於自己的持續集成工具(其他的代碼託管平臺也有類似的功能,包括上面提到的GitHub)。

簡單來說,WebHooks就是:當項目進行了某項操作(如提交),代碼託管平臺就會調用給定的URL,發送一條POST請求。至於URL那邊的服務器,接收到請求之後,就可以為所欲為了——這篇文章中提到的同步存儲庫什麼的,只是冰山一角,如果你會寫一些腳本,理論上可以做到發郵件、在社交平臺/即時通信平臺上發送消息、甚至如前面提到的那樣,自動進行測試、構建、部署。當然,和一般的請求一樣,服務器也會給一個返回。不過這個返回的意義並不是非常大,相比之下,服務器內的處理流程才是最重要的。

通過Gitlab的WebHooks實現網站內容同步

WebHooks示意圖

設置WebHooks的方法

在CODE(其他基於Gitlab的託管平臺的操作流程應該也差不多)中,設置WebHooks的方式很簡單:

在項目的設置中,點擊“WebHooks”選項,填入接收請求的URL,選擇觸發事件,點擊下面的“增加WEBHOOKS”,即可成功添加一條。可以看到,在“觸發”的選項中,有一個“推送事件”,當推送內容至倉庫後,即觸發。

通過Gitlab的WebHooks實現網站內容同步

增加WebHooks

這樣一來,我們可以寫實現如下操作的代碼:當接收到請求後,即在服務器中對網站根目錄進行git pull操作。

其實,這種要求非常低。如果你裝了寶塔面板,只要裝一下“寶塔WebHook”的插件,在插件裡面設置好如下的shell腳本就可以了。

<code>cd 網站根目錄
git pull/<code>

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

通過Gitlab的WebHooks實現網站內容同步

寶塔WebHook給出的URL

僅在提交特定分支時調用URL

但是,我的這個倉庫有兩個分支:master和hexo。master分支存儲生成的網頁文件,hexo分支存儲網站的源文件和設置。我只想讓服務器在master分支被提交時更新網站內容,而hexo分支更新與否與網站無關。

Gitlab中並沒有關於“提交某個特定的分支”的觸發事件,但是,在調用URL的時候,會發送特定的請求頭和請求體。

幫助文檔中給出了比較詳細的事例。比如推送事件的請求頭如下:

<code>X-Gitlab-Event: Push Hook/<code>

請求體為JSON格式的內容,如下:

<code>{
  "object_kind": "push",
  "before": "95790bf891e76fee5e1747ab589903a6a1f80f22",
  "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
  "ref": "refs/heads/master",
  "user_id": 4,
  "user_name": "John Smith",
  "user_email": "[email protected]",
  "project_id": 15,
  "repository": {
    "name": "Diaspora",
    "url": "[email protected]:mike/diasporadiaspora.git",
    "description": "",
    "homepage": "http://example.com/mike/diaspora",
    "git_http_url":"http://example.com/mike/diaspora.git",
    "git_ssh_url":"[email protected]: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": "[email protected]"
      }
      "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

}/<code>

可以看到,請求體裡面的"ref"字段中包含了推送的分支,只要做到這個操作就可以了:在服務器收到請求後,讀取請求體,判斷該字段中是否表示master分支,如果是,則更新網站根目錄的內容。

當然,為了安全,還要驗證請求頭是否正確。

通過Gitlab的WebHooks實現網站內容同步

流程圖

但是,“寶塔WebHook”插件太簡單了,做不到這個。它的傳值在查詢字符串中,讀不了請求頭和請求體。這樣一來,我就只能寫服務器腳本來實現這個功能了。

你可以使用你熟悉的語言和框架寫服務器腳本,只要能夠收到請求並作出正確的反應就行了。我接觸JS和Python比較多,不過Python中的Flask框架足夠輕量(說實話貌似有更輕量的),開發起來也容易得多。於是,我便使用了Python和Flask框架來寫這個腳本。

使用Python和Flask框架實現功能

安裝好Flask,然後寫入如下的代碼。

基本骨架是Flask提供的Hello World示例,我翻了很多資料,東拼西湊出來可以運行的代碼。不過確實非常簡單。

一些必要的註釋我放在代碼裡面了。

<code># flask.request是用來處理請求的
from flask import Flask, request
# 代碼中涉及到日誌
import logging
# 切換目錄用
import os
# 執行外部腳本用
import subprocess

app = Flask(__name__)

# 這樣寫,URL就是http://地址:端口號/hook
# WebHooks用的是POST請求
@app.route('/hook', methods=['POST'])
def git_hook():
    # 請求頭要對應;請求頭有很多字段,所以找請求頭中是否有這個字符串
    if str(request.headers).find('X-Gitlab-Event: Push Hook')!=-1:
        # 找請求體的"ref"字段是否有"master"的字樣
        if str(request.json['ref']).find('/master')!=-1:
            # 切換目錄
            os.chdir('網站根目錄')
            # 使用subprocess.check_output執行shell腳本,腳本內被空格隔開的部分用列表裝住
            # 如果出錯,會拋出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'/<code>

在服務器上部署代碼。我用的是寶塔的Python項目管理器,因此具體的腳本我就不清楚了,反正我用了gunicorn。

之後,用Nginx進行端口的反向代理,並應用SSL。具體設置方法看網上的文檔,因為我也是用寶塔的默認文檔魔改的。當然,如果你對安全不那麼在意的話,你也可以不用設置Nginx。這時,監聽主機要設置為'0.0.0.0'。

此後就可以到CODE(Gitlab)去設置WebHooks了。如果你用了反代什麼的,就根據反代的參數設置URL。如果你開了SSL,就可以點下面的“開啟SSL證書驗證”了。

增加完成後,要多測試。頁面下方給出了測試按鈕,會發送一條測試的請求。到主機的服務器上看日誌、看返回結果什麼的,看看執行的怎麼樣。

通過Gitlab的WebHooks實現網站內容同步

WebHooks測試

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

不僅僅是網站,還可以是整個項目

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

和WordPress這樣的博客系統不同,Hexo在線上是靜態的,源文件都在本地。我此前希望自己寫在線編輯器,但是因為我懶、菜,就一直沒有寫。後來我試了hexo-admin、hexo-myadmin這樣的在線編輯器,但是前者太醜了,後者在手機端上幾乎沒法用。最後我找了半天,選擇了hexo-hey,因為它的頁面適配手機端比較好。但是,作者不更新了,手機端上也有一些問題。目前我還沒有找到滿意的在線編輯器。

通過Gitlab的WebHooks實現網站內容同步

hexo-hey的效果

要使用在線編輯器,就要把整個工作目錄放上去,而不僅僅是生成的文件。我在知乎上找到了教程,將生成文件和工作目錄放到不同的分支下。然後,我按照組件、用pm2執行,再用Nginx做反向代理,以便使用SSL證書,終於搞好了。

這時,如果在手機端上修改了文章的草稿(由於我修改了一些node-modules,加上我寫文章並不快,所以我目前不期望在手機端上發佈文章),就要:在手機端上使用SSH登錄到服務器,再在工作目錄下執行以下命令,以將更改上傳到CODE:

<code>git add .
git commit -m "提交信息"
git push/<code>

然後,回去用電腦編輯之前,也要從CODE上下載更改。更要命的是,編輯好了之後,還要上傳更改,然後在服務器上下載更改,以便手機端修改。

簡而言之,每次編輯前後,都要拉取、提交修改。電腦端上倒無所謂,但是手機端上執行這一系列流程挺困難的。而且,雖然關於git的所有流程都在電腦上進行,但是還是挺麻煩的,而且容易忘記拉取,一旦忘了就尷尬了。

在我寫完上面的腳本之後,我想到了這個情況,便想到了一個方法:當推送hexo分支後,同時在服務器上拉取該分支。這樣一來,就能省下一半的工作量。

修改起來也簡單,只要在上面的代碼中的# 非master分支的提交處,照著寫一下hexo分支提交之後的處理方法,再測試一下,就行了。

至此,我的服務器的功能圖如下:

通過Gitlab的WebHooks實現網站內容同步

服務器功能圖


分享到:


相關文章: