WebHook结合GitHub Action与Coding实现博

2021-09-03  本文已影响0人  sssgoEasy
流程图

前言

什么是GitHub ActionGitHub Page

什么是Coding

对于Coding的作用,其实就类似Gitee一样,属于国内部署,速度提升非常明显,而且还可以被百度收录。由于众说周知的原因,国内访问GitHub速度感人,我也试过一开始直接获取GitHub的代码,但是速度太慢,效率很低,因此最终选择了Coding作为仓库镜像,当然这里换成Gitee也是一样的。

什么是Webhook

需求及场景

众所周知,GitHub作为全球最大同性交友社区,为了与大家更方便友好积极的交流 🐶,我的源码存放地址是存放在GitHub的,内容包括博客的更新、一些测试用例、教程、Demo等。

因为我的博客是部署在阿里云的个人服务器上,有时博客更新又比较频繁,一开始是通过ssh链接到服务器后然后通过命令git clonegit pullnpm run build 这种操作方法去手动进行更新,这里存在几个问题:

  1. 频繁登录服务器,非常繁琐
  2. GitHub 速度问题,每次更新都要等待很久
  3. 服务器性能问题,对于低配服务器执行 npm run build 打包很吃力

思路及流程图

思路

  1. 本地电脑通过git提交到GitHub仓库
  2. GitHub Action监听到push event事件触发ci.yml执行脚本
  3. build打包生成部署文件推送到GitHub gh-pages分支与Coding master分支
  4. Coding设置webhook监听push event事件触发webhook钩子
  5. webhook钩子通信到个人服务器内启动的http server,验证身份与仓库,执行webhook.sh脚本
  6. webhook.sh脚本cd进入nginx下的博客部署目录,进行git pull更新操作
  7. 根据结果生成日志,或添加邮件通知功能(结合Nodemailer库实现)

流程图

流程图

创建个人访问令牌 Access Token

接下来自动化部署与持续集成会用到以https方式提交代码到仓库的方案,这里需要配置下access token个人访问令牌作为环境变量提供给Action与脚本使用。

GitHub Token

第一步,按照官方文档 ,生成一个github token (令牌)。

第二步,将这个密钥储存到当前仓库的Settings/Secrets里面。

Settings/Secrets是储存私密的环境变量的地方。环境变量的名字可以随便起,这里用的是ACCESS_TOKEN。如果你不用这个名字,.github/workflows/ci.yml脚本里的变量名也要跟着改。

Coding Token

第一步,按照官方文档 ,生成一个coding token (令牌)。

第二步,同 GitHub

几种方案

:::tip
这里示例几种我尝试过的方案,下面会仔细分析介绍每种方案的优劣,以及我逐渐改进与尝试的方法,和最终实现。
:::

scp 命令直接部署到服务器

主要思路

通过使用scp命令直接把本地部署文件拷贝至远程服务器

scpsecure copy的简写,是 linux 系统下基于 ssh 登陆进行安全的远程文件拷贝命令,scp传输是加密的。

  1. 本地进行npm run build打包编译,获得部署文件

  2. 直接通过上传替换源文件进行更新

    # 1. 该命令会把当前目录下的dist文件遍历上传到blog目录下:/blog/dist
    scp -r ./dist root@ip/usr/local/app/blog/
    # 2. 输入服务器密码,等待传输完成即可
    # 当然也可以通过ssh的方式无密码上传,但这并不能达到我的最终目的,这里不再赘述
    

最终效果

GitHub Action Pages

主要思路

通过使用GitHub提供的PageAction服务实现gh-pages持续集成与部署

gh-pages 的搭建教程网上很多,具体实现我就不再重复了。
这里我提供一种我的方案:

通过动态生成gh-pages分支并推送到github仓库实现多地址部署

如我的博客的GitHub地址:https://JS-banana.github.io/vuepress

脚本编写

  1. 根目录下创建 .github>workflows>ci.yml

  2. ci.yml文件

    jobs: # 工作流
      build: # 自定义名称
      runs-on: ubuntu-latest #运行在虚拟机环境ubuntu-latest
    
      strategy:
        matrix:
        node-version: [14.x]
    
      steps: # 步骤
        - name: Checkout # 步骤1
        uses: actions/checkout@v1 # 使用的动作。格式:userName/repoName。
        #作用:检出仓库,获取源码。 官方actions库:https://github.com/actions
        - name: Use Node.js ${{ matrix.node-version }} # 步骤2
        uses: actions/setup-node@v1 # 作用:安装nodejs
        with:
          node-version: ${{ matrix.node-version }} # 版本
        - name: Deploy # 步骤3 部署到github gh-pages
        env: # 设置环境变量
          GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} # token私密变量
        run: |
          npm install
          npm run build
          cd ./dist
          git init
          git config user.name "name"
          git config user.email "email"
          git add .
          git commit -m "$(date) Update from Action"
          git push --force --quiet "https://JS-banana:${GITHUB_TOKEN}@github.com/JS-banana/vuepress.git" master:gh-pages
    

    该page项目可以作为备选地址使用,通过CNAME定向到我们自己的域名服务器下。
    CNAME:即别名记录。这种记录允许您将多个名字映射到另外一个域名。

  3. 执行 echo 'ssscode.com' > CNAME 命令,生成 CNAME 文件,然后把CNAME文件放到生成的dist目录下,这一步可以通过bash脚本处理

  4. 我们再把上面的脚本调整下

    jobs: # 工作流
      build: # 自定义名称
      runs-on: ubuntu-latest #运行在虚拟机环境ubuntu-latest
    
      strategy:
        matrix:
        node-version: [14.x]
    
      steps: # 步骤
        - name: Checkout # 步骤1
        uses: actions/checkout@v1 # 使用的动作。格式:userName/repoName。
        #作用:检出仓库,获取源码。 官方actions库:https://github.com/actions
        - name: Use Node.js ${{ matrix.node-version }} # 步骤2
        uses: actions/setup-node@v1 # 作用:安装nodejs
        with:
          node-version: ${{ matrix.node-version }} # 版本
        - name: Deploy # 步骤3 部署到github gh-pages
        env: # 设置环境变量
          GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} # token私密变量
        run: npm install && npm run deploy
    
  5. 创建deploy.sh文件,并在package.json字段scripts下添加"deploy": "bash deploy.sh"命令

    #!/usr/bin/env sh
    
    # 确保脚本抛出遇到的错误
    set -e
    
    # date
    nowDate=$(date "+%Y-%m-%d %H:%M:%S")
    
    # 生成静态文件
    npm run build
    
    # 进入生成的文件夹
    cd ./dist
    
    # CNAME
    echo 'www.ssscode.com\ssscode.com' > CNAME  # 自定义域名
    
    # github url
    githubUrl=https://JS-banana:${GITHUB_TOKEN}@github.com/JS-banana/vuepress.git
    
    # 配置 git 用户信息
    git config --global user.name "JS-banana"
    git config --global user.email "sss213018@163.com"
    
    # commit
    git init
    git add -A
    git commit -m "deploy.sh===>update:${nowDate}"
    git push -f $githubUrl master:gh-pages # 推送到github
    
    cd - # 退回开始所在目录
    rm -rf ./dist
    

Coding与GitHub同步部署

这一步其实原理和上面GitHub Action Pages做法一样,而我们要做的最重要的一步,就是把Codingtoken也配置到GitHub仓库下的Settings/Secrets里面。即新增一个环境变量CODING_TOKEN,该方法同样适用于Gitee,想要使用Gitee的小伙伴也可以亲自尝试。

ci.yml文件增加

env: # 设置环境变量
  GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} # github token 私密变量
+ CODING_TOKEN: ${{ secrets.CODING_TOKEN }} # coding token 私密变量

deploy.sh文件调整为

  # github url
  githubUrl=https://JS-banana:${GITHUB_TOKEN}@github.com/JS-banana/vuepress.git

+ # coding url
+ #注意!!!这里需要使用coding提供的个人令牌的用户名和token (https://[name]:[token]@e.coding.net/xx)
+ codingUrl=https://ptzv1yuleer1:${CODING_TOKEN}@e.coding.net/ssscode/blog/vuepress.git 

  git push -f $githubUrl master:gh-pages # 推送到github

- cd - # 退回开始所在目录
- rm -rf ./dist
  
+ git push -f $codingUrl master # 推送到coding

+ cd - # 退回开始所在目录
+ rm -rf ./dist

Webhook与Coding实现持续部署

实现思路

首先,实现一个 nodejs http server用于接收请求,即 webhook.js钩子服务,这里我建议域名解析一个二级域名供webhook专门使用,如:webhook.ssscode.com,当然,直接在当前项目下以 /webhook 路径作为触发路由也可以。这里我们以第一种方案为例,使用nodejspm2守护程序启动webhook.js,然后再通过nginx反向代理本地启动的服务映射到 webhook.ssscode.com 即可。

创建 webhook.js

主要使用了NodeJs模块http(创建server) 与 child_process (执行bash脚本)

废话不多说,直接上代码

// webhook.js
const server = require('http');
const { spawn } = require('child_process');

server
  .createServer((req, res) => {
    // accept request
    console.log(`accept request:${new Date()}`);
    // 接收POST请求
    if (req.method === 'POST') {
      //TODO: secret 验证

      let data = '';

      req.on('data', chunk => {
        data += chunk;
      });

      req.on('end', () => {
        // console.log(JSON.parse(data));
        try {
          const reqData = JSON.parse(data);
          // 确定身份
          if (reqData.pusher.username !== 'xxx') { // coding 个人访问令牌 Access Token 用户名
            res.writeHead(400);
            res.end('noooo!');
            return;
          }
          // 确定分支 master
          if (reqData.ref === 'refs/heads/master') {
            // 确定仓库
            const repository_name = reqData.repository.name;
            runCommand('sh', [`${repository_name}.sh`], console.log);
          }
          // response
          res.writeHead(200);
          res.end('ok');
        } catch (error) {
          console.log('error:', error);
          res.writeHead(500);
          res.end('error!');
        }
      });
    } else {
      res.writeHead(404);
      res.end('no info!');
    }
  })
  .listen(3010); // 端口

// run command
function runCommand(cmd, args, callback) {
  let response = '';
  const child = spawn(cmd, args);
  child.stdout.on('data', buffer => {
    response += buffer.toString();
  });
  child.stdout.on('end', () => callback(response));
}

核心代码是 runCommand 函数,server服务接收请求参数验证身份与仓库信息,满足条件执行对应脚本。这里没有像 github-webhook-handler 包一样对密匙进行处理验证,只是简单的验证了身份和条件。(github、coding等配置webhook时可以设置验证秘钥)

接下来编写我们的bash脚本,这里之后可以优化成执行对应项目下的对应脚本(即 如果存在多个项目或博客,相关脚本在对应项目下创建,由bash执行)。

创建bash脚本

vuepress.sh核心代码

#!/usr/bin/env sh
# 确保脚本抛出遇到的错误
set -e

cd /usr/local/app/vuepress-blog/dist

echo 'start===>git'

# 覆盖更新
git fetch --all
git reset --hard origin/master
# git clean -f
# git pull

cd - # 退回开始所在目录

为了方便记录和查看以及拓展通知消息等,这里增加异常判断处理及日志输出

- cd - # 退回开始所在目录

+ function log_info (){
+   DATE_N=`date "+%Y-%m-%d %H:%M:%S"`
+   USER_N=`whoami`
+   echo "${DATE_N} ${USER_N} execute $0 [INFO] $@" >> /usr/local/app/webhook/logInfo.txt #执行成功日志打印路径
+ }

+ function log_error (){
+   DATE_N=`date "+%Y-%m-%d %H:%M:%S"`
+   USER_N=`whoami`
+   echo -e "\033[41;37m ${DATE_N} ${USER_N} execute $0 [ERROR] $@ \033[0m"  >> /usr/local/app/webhook/logError.txt #执行失败日志打印路径
+ }

+ if [  $? -eq 0  ]
+ then
+   log_info "$@ sucessed."
+   echo -e "\033[32m $@ sucessed. \033[0m"
+ else
+   log_error "$@ failed."
+   echo -e "\033[41;37m $@ failed. \033[0m"
+   exit 1
+ fi

+ trap 'fn_log "DO NOT SEND CTR + C WHEN EXECUTE SCRIPT !!!! "'  2

+ cd - # 退回开始所在目录

创建nginx配置

nginx配置

server {
  #外网
  listen 80;
  server_name webhook.ssscode.com; #域名

  # 监听本地服务
  location / {
    # 开启反向代理
    proxy_pass http://127.0.0.1:3010/;
  }
}

执行 node ./webhook.js 即可启动测试~

pm2

PM2 是 node 进程管理工具,可以利用它来简化很多 node应用管理的繁琐任务,如性能监控、自动重启、负载均衡等,而且使用非常简单。PM2简易使用手册

为了更方便的管理NodeJs程序以及监控,这里推荐使用 pm2 start webhook.js 启动服务

Github或Gitee结合webhook

因为我采用的是Coding结合webhook的方式,如果有小伙伴对其他方式感兴趣,也可以自行搭建,原理类似,目前GithubGitee也有开源的npm包提供快速搭建webhook通信服务。github-webhook-handlergitee-webhook-handler

结语

大功告成~

总的来说就是反复尝试了很多方法,摸索找思路,再不断的改进,最终也是实现了当初的想法,不过也确实走了不少弯路。在此,把整个过程记录下来,供自己参考也为了加深理解,希望能帮到有需要的小伙伴,少走些弯路~

如果自己全部配置各种服务与代码业务逻辑,也确实挺花费时间和精力的,涉及的技术点也比较杂,很多地方只能达到勉强使用,还差得远,整个过程也是边学习便尝试。不过,全部弄完之后,对于自己的技术成长与业务理解也是很有帮助的~

类似Hexo这样的快速搭建博客的框架用起来倒是可以省不少事,感兴趣的可以尝试下,这个我也有在弄,不过还没达到我的预期效果,看看之后有没有可写的东西再分享分享吧

之后对于Docker也是很有必要深入学习一番了~

2021.06.08 补充

使用 Travis 结合 sshpass 工具实现本机文件上传到远程服务器。

.travis.yml

language: node_js
node_js:
  - 12
branchs:
  - master
addons:
  apt:
    packages:
    - sshpass
install:
  "npm install"
script:
  - "npm run build"
after_success:
  - ./deploy.sh

deploy.sh

#!/usr/bin/env sh

# 确保脚本抛出遇到的错误
set -e

# 打包静态资源
npm run build

# 将dist文件发送到远程
sshpass -p ${serverPass} scp -o stricthostkeychecking=no -r dist/ root@${serverIP}:/home/web/movie-trailer

优点:方便快捷

缺点:使用 sshpass 是最不安全的,因为所有系统上的用户在命令行中通过简单的 “ps” 命令就可看到密码。

说明

原文博客:https://ssscode.com/pages/337720/

参考

https://coding.net/

https://docs.github.com/cn/actions

https://xugaoyi.com/pages/6b9d359ec5aa5019/

https://docs.github.com/cn/github/authenticating-to-github/creating-a-personal-access-token

https://help.coding.net/docs/member/tokens.html

https://ipcmen.com/

http://www.ruanyifeng.com/blog/2016/07/yaml.html

https://juejin.cn/post/6844903710037016584

https://linux.cn/article-8086-1.html

https://bbs.huaweicloud.com/blogs/152237

上一篇下一篇

猜你喜欢

热点阅读