一次协作多端同步,打通看云、github互相同步(serverless实践)

小熊 CI/CD29,152字数 6714阅读22分22秒阅读模式

本文原创首发于 https://coding3min.com/1194.html

之前在看云上专门搞了个电子书来归档和协作一些文章,支持 webhook(钩子),但是一直没用上,今天端午放假,早上就突然醒了,突发奇想,不如弄个自动同步,把看云的同步到 github ,把 github 同步到看云,经过一个早上的努力总算是搞定了。

一次协作多端同步,打通看云、github互相同步(serverless实践)

网上的资料 只支持单向的同步,我稍微做了一下改动,下面是整个过程。

同步原理

kancloud+webhook+serverless+travis-ci+github

大致流程如下:

  • 在看云上编写文章
  • 配置看云仓库的 Webhook 通知,当更新文章时通知给 Serverless
  • 使用腾讯云提供的 Serverless ,编写云函数接收 Webhook 通知,然后通过 API 的方式触发 TravisCI 构建
  • Github 新建仓库,编写相关的仓库更新合并脚本进行同步,此脚本会对比两个仓库哪个更新来判断同步方向
  • Github 更新时自动触发 TravisCI 构建

同步目的:

  • 看云文档 github 备份分享
  • 看云文档分发到其他博客系统或导入到 gitbook
  • 实现基于分支仓库(github.io)的 hexo 静态博客自动发布
  • 多人协作共同完善文章库

github 配置

首先登陆github创建一个仓库,名称自定义,用来同步看云某一本书(git 仓库)。

手动同步看云项目到github,在本地执行以下命令,注意请替换所有的链接、变量为你自己的。

git clone https://git.kancloud.cn/coding3min/coding3min.git #clone看云git项目
cd coding3min #进程项目目录
git remote add github https://github.com/pzqu/coding3min-book.git #github链接
git push github master

在仓库根目录创建 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}@(echoKY_REPO | awk -F'//' '{print 2}')
GH_REPO_URL=https://{GITHUB_TOKEN}@(echoGH_REPO | awk -F'//' '{print 2}')
KY_REPO_NAME=(echo KY_REPO | awk -F'/' '{printNF}' | 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 "pzqu@qq.com"
  git config --global user.name "pzqu"
  rm -rf *
  git cloneSRC_REPO_URL
  repo_dir=(ls) && cp -rfrepo_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 originDEST_REPO_URL > /dev/null 2>&1
  git push origin master --quiet
}

compare_new() {
   github_last_commit_time=(git show -s --format=%ct)
   git cloneKY_REPO_URL && cd KY_REPO_NAME
   ky_last_commit_time=(git show -s --format=%ct)
   if [ github_last_commit_time -gtky_last_commit_time ];then
       SRC_REPO_URL=GH_REPO_URL
       DEST_REPO_URL=KY_REPO_URL
   else
       cd ..
   fi
   echo "sync SRC_REPO_URL toDEST_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 即可。

一次协作多端同步,打通看云、github互相同步(serverless实践)

一次协作多端同步,打通看云、github互相同步(serverless实践)

腾讯云 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 paramsqueryString = 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
* @paramrepos string 仓库ID、slug
* @param token string 登录验证token
* @parammessage string 触发信息
* @param branch string 分支
* @returninfo 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实践)

看云配置

找一本想要实时同步到 github 的书,在文档钩子中添加 serverless 访问路径( webhook 地址) ,保存即可。

开始写书,提交到版本库,查看腾讯云 serverless 日志,提示调用成功:

查看 travis-ci 触发成功构建

查看 github 仓库,内容已经更新

小结

这篇文章中的 serverless 是云行业非常火的一个概念,不再需要服务器,不再需要写应用,只需要专注于函数就可以处理一些问题,大大的节约了成本,支持很多语言,可以自己下来研究下。

webhook 是非常棒的一种架构模式,不仅仅是可以用在代码仓库的同步中,也可以设计到你写的程序里,只要制定好规范,你的程序就可以通过配置调用/被调用,变得更像插件,其他系统对接的时候就更容易。

涉及资源

引用

看云实时同步到 github

QA

Q:如果两端同时编辑,那怎么自动解决冲突合并问题,还是通过邮件通知开发人员去解决?
A:我也考虑过这个问题,如果同时对一个文件进行编辑,只有当同时提交的时候才会发生这个问题,首先是构建速度快,一般1分钟内就会同步完成,这个时候冲突会发生在提交成功前。

要是恰好在这 1 分钟内的,我是先pull,再覆盖代码再push,不会发生冲突,但是上一次提交记录会被覆盖掉。

Q:为什么我的云函数无法触发?
A:你可以在函数代码界面进行测试,注意,你得保证腾讯云中没有欠费,至少充1元

Q: 为什么我在travis里找不到我的项目?
A:请确保你的项目不是空项目,不然是找不到的,你可以先手动同步看云项目到github,在本地执行以下命令,注意请替换所有的链接、变量为你自己的。

git clone https://git.kancloud.cn/coding3min/coding3min.git #clone看云git项目
cd coding3min #进程项目目录
git remote add github https://github.com/pzqu/coding3min-book.git #github链接
git push github master

weinxin
公众号
扫码订阅最新深度技术文,回复【资源】获取技术大礼包
CI/CD最后更新:2020-8-31
小熊

评论已关闭!