一次協作多端同步,打通看雲、github互相同步(serverless實踐)

本文原創首發於 https://coding3min.com/1194.html

之前在看雲上專門搞了個電子書來歸檔和協作一些文章,支持 webhook(鉤子),但是一直沒用上,今天端午放假,早上就突然醒了,突發奇想,不如弄個自動同步,把看雲的同步到 github ,把 github 同步到看雲,經過一個早上的努力總算是搞定了。

網上的資料 只支持單向的同步,我稍微做了一下改動,下面是整個過程。

同步原理

kancloud+webhook+serverless+travis-ci+github

大致流程如下:

  • 在看雲上編寫文章
  • 配置看雲倉庫的 Webhook 通知,當更新文章時通知給 Serverless
  • 使用騰訊雲提供的 Serverless ,編寫雲函數接收 Webhook 通知,然後通過 API 的方式觸發 TravisCI 構建
  • Github 新建倉庫,編寫相關的倉庫更新合併腳本進行同步,此腳本會對比兩個倉庫哪個更新來判斷同步方向
  • Github 更新時自動觸發 TravisCI 構建

同步目的:

  • 看雲文檔 github 備份分享
  • 看雲文檔分發到其他博客系統或導入到 gitbook
  • 實現基於分支倉庫(github.io)的 hexo 靜態博客自動發佈
  • 多人協作共同完善文章庫

github 配置

首先登陸github創建一個倉庫,名稱自定義,用來同步看雲某一本書(git 倉庫)。

在倉庫根目錄創建 2 個文件 .travis.yml.travis-push.sh

.travis.yml

script:

  + sh .travis-push.sh

.travis-push.sh

#!/bin/sh

#看雲版本庫地址:
KY_REPO=https://git.kancloud.cn/coding3min/coding3min.git
#github倉庫地址:
GH_REPO=https://github.com/pzqu/coding3min-book.git

KY_REPO_URL=https://${KANCLOUD_USER}:${KANCLOUD_PASS}@$(echo $KY_REPO | awk -F'//' '{print $2}')
GH_REPO_URL=https://${GITHUB_TOKEN}@$(echo $GH_REPO | awk -F'//' '{print $2}')
KY_REPO_NAME=$(echo $KY_REPO | awk -F'/' '{print $NF}' | awk -F '.' '{print $1}')
DEST_REPO_URL=$GH_REPO_URL
SRC_REPO_URL=$KY_REPO_URL

setup_git() {
  git show -s --format=%ct
  git config --global user.email "[email protected]"
  git config --global user.name "pzqu"
  rm -rf *
  git clone --depth=50 --branch=master $SRC_REPO_URL
  repo_dir=$(ls) && cp -rf $repo_dir/* ./ &&  rm -rf $repo_dir
}

commit_country_json_files() {
  git status
  git checkout master
  # Current month and year, e.g: Apr 2018
  dateAndMonth= `date "+%b %Y"`
  # Stage the modified files in dist/output
  git add -A
  # Create a new commit with a custom build message
  # with "[skip ci]" to avoid a build loop
  # and Travis build number for reference
  git commit -m "Travis update: $dateAndMonth (Build $TRAVIS_BUILD_NUMBER)" -m "[skip ci]"
}

upload_files() {
  # Remove existing "origin"
  git remote rm origin
  # Add new "origin" with access token in the git URL for authentication
  git remote add origin $DEST_REPO_URL > /dev/null 2>&1
  git push origin master --quiet
}

compare_new() {
   github_last_commit_time=$(git show -s --format=%ct)
   git clone $KY_REPO_URL && cd $KY_REPO_NAME
   ky_last_commit_time=$(git show -s --format=%ct)
   if [ $github_last_commit_time -gt $ky_last_commit_time ];then
       SRC_REPO_URL=$GH_REPO_URL
       DEST_REPO_URL=$KY_REPO_URL
   else
       cd ..
   fi
   echo "sync $SRC_REPO_URL to $DEST_REPO_URL"
}

compare_new

setup_git

commit_country_json_files

# Attempt to commit to git only if "git commit" succeeded
if [ $? -eq 0 ]; then
  echo "A new commit with changed country JSON files exists. Uploading to GitHub"
  upload_files
else
  echo "No changes in country JSON files. Nothing to do"
fi

代碼過長,參考代碼位置 github

腳本說明

這個腳本實現了倉庫更新時間對比,同步推送代碼的功能。

腳本中只需修改看雲版本庫地址以及 github 地址即可,另外腳本中定義了 3 個變量,是從 ci 平臺裏取的,用於免密執行 git clone ,爲了安全不放在代碼庫裏。

KY_REPO #看雲版本庫地址
GH_REPO #github倉庫地址
$KANCLOUD_USER #看雲賬號
$KANCLOUD_PASS #看雲密碼
$GITHUB_TOKEN #github token

注意這 3 個參數可以配置在 travis-ci web 界面中,腳本能夠自行調用該參數,不要以明文配置在腳本中。

travis-ci 配置

開啓 github 倉庫追蹤

訪問traivs 官網,以 github 賬戶登錄:

點擊右上角用戶頭像 settings 開啓剛纔新建的 github 倉庫跟蹤

定義腳本變量

點擊 dashboard ,選擇對應倉庫,點擊右側 More options--Settings

配置 Environment Variales

  • 注意 TOKEN 和密碼不要以明文顯示。
  • 注意!!這 4 個參數如果用戶名或密碼中包含 @ 符號,需要替換爲 %40 ,比如密碼爲 admin@123 需要寫爲 admin%40123
  • github token 需要訪問 github 官網進行創建,點擊右上角頭像, Settings--Developer settings--Personal access tokens 生成一個新 token ,權限只需要 repo 就行,設置複製到 travis-ci 即可。

騰訊雲 serverless

阿里雲和騰訊雲都提供免費的 serverless 服務,免費服務每個月有流量限制,不過完全夠用,這裏以騰訊云爲例。

無服務器雲函數地址:https://cloud.tencent.com/product/scf

選擇產品–基礎–無服務器雲函數–立即使用–函數服務–新建。

選擇空白函數,運行環境選擇 PHP

下一步,複製以下函數

<?php
function main_handler($event, $context) {
    // 解析看雲post的數據
    $update_title = '';
    if($event->body){
        $kanyun_data= json_decode($event->body);
        $update_title .= $kanyun_data->data->title;
    }
    // default params
    $repos = 'xxxxxxxx';  // 你的倉庫id 或 slug擴展名
    $token = 'xxxxxxxxxxxxxxx'; // 你的登錄token
    $message = date("Y/m/d").':kanyun update:'.$update_title;
    $branch = 'master';
    // post params
    $queryString = $event->queryString;
    $q_token = $queryString->token ? $queryString->token : $token;
    $q_repos = $queryString->repos ? $queryString->repos : $repos;
    $q_message = $queryString->message ? $queryString->message : $message;
    $q_branch = $queryString->branch ? $queryString->branch : 'master';
    echo($q_token);
    echo('===');
    echo ($q_repos);
    echo ('===');
    echo ($q_message);
    echo ('===');
    echo ($q_branch);
    echo ('===');
    //request travis ci
    $res_info = triggerTravisCI($q_repos, $q_token, $q_message, $q_branch);

    $res_code = 0;
    $res_message = '未知';
    if($res_info['http_code']){
        $res_code = $res_info['http_code'];
        switch($res_info['http_code']){
            case 200:
            case 202:
                $res_message = 'success';
            break;
            default:
                $res_message = 'faild';
            break;
        }
    }
    $res = array(
        'status'=>$res_code,
        'message'=>$res_message
    );
    return $res;
}

/*

* @description  travis api , trigger a build
* @param $repos string 倉庫ID、slug
* @param $token string 登錄驗證token
* @param $message string 觸發信息
* @param $branch string 分支
* @return $info array 回包信息

*/
function triggerTravisCI ($repos, $token, $message='kanyun update', $branch='master') {
    //初始化
    $curl = curl_init();
    //設置抓取的url
    curl_setopt($curl, CURLOPT_URL, 'https://api.travis-ci.org/repo/'.$repos.'/requests');
    //設置獲取的信息以文件流的形式返回,而不是直接輸出。
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    //設置post方式提交
    curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST");
    //設置post數據
    $post_data = json_encode(array(
        "request"=> array(
            "message"=>$message,
            "branch"=>$branch
        )
    ));
    $header = array(
      'Content-Type: application/json',
      'Travis-API-Version: 3',
      'Authorization:token '.$token,
      'Content-Length:' . strlen($post_data)
    );
    curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
    curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);
    //執行命令
    $data = curl_exec($curl);
    $info = curl_getinfo($curl);
    //關閉URL請求
    curl_close($curl);
    return $info;
}
?>

函數說明:
serverless 函數中參數的設置:
該函數只需修改以下兩項:

$repos = 'xxx'; // 你的github倉庫id 或 slug擴展名
$token = 'xxx'; // 你的travis-ci登錄token

獲取 travis-ci token

travis-ci 官網上點擊右上角 Settings--Settings ,複製 token

倉庫 ID 獲取方法

方法 1,使用 curl 命令,注意需要 travis-ci token 以及 github 用戶名稱

curl -X GET \
  https://api.travis-ci.org/owner/替換成你的github用戶名/repos \
  -H 'apiexplore: 替換成你的token' \
  -H 'cache-control: no-cache' \
  -H 'travis-api-version: 3' \
  -H 'user-agent: API Explorer'

方法 2,使用 postman
下載地址:https://www.getpostman.com/downloads/
postman 發個 GET 請求,填寫 urlheaders 參數

完成後添加觸發方式,選擇 API 網關觸發器,其他默認,保存。

然後複製訪問路徑

看雲配置

找一本想要實時同步到 github 的書,在文檔鉤子中添加 serverless 訪問路徑( webhook 地址) ,保存即可。

開始寫書,提交到版本庫,查看騰訊雲 serverless 日誌,提示調用成功:

查看 travis-ci 觸發成功構建

查看 github 倉庫,內容已經更新

小結

這篇文章中的 serverless 是雲行業非常火的一個概念,不再需要服務器,不再需要寫應用,只需要專注於函數就可以處理一些問題,大大的節約了成本,支持很多語言,可以自己下來研究下。

webhook 是非常棒的一種架構模式,不僅僅是可以用在代碼倉庫的同步中,也可以設計到你寫的程序裏,只要制定好規範,你的程序就可以通過配置調用/被調用,變得更像插件,其他系統對接的時候就更容易。

涉及資源

  • 代碼倉庫 github
  • 看雲 [編程伐木累的技術小冊

](https://www.kancloud.cn/coding3min/coding3min/1748607)

引用

看雲實時同步到 github

QA

Q:如果兩端同時編輯,那怎麼自動解決衝突合併問題,還是通過郵件通知開發人員去解決?
A:我也考慮過這個問題,如果同時對一個文件進行編輯,只有當同時提交的時候纔會發生這個問題,首先是構建速度快,一般1分鐘內就會同步完成,這個時候衝突會發生在提交成功前。

要是恰好在這 1 分鐘內的,我是先pull,再覆蓋代碼再push,不會發生衝突,但是上一次提交記錄會被覆蓋掉。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章