
前言
什么是GitHub Action、GitHub Page
Github Action 可以幫助您自動(dòng)完成軟件開發(fā)周期內(nèi)的任務(wù),它是事件驅(qū)動(dòng)的,而且已經(jīng)和
GitHub深度整合,可以運(yùn)行很多GitHub事件,最普遍的是推送到master分支。但是actions并不僅僅只是部署和發(fā)布,它們都是容器,毫不夸張地說你可以做任何事情 —— 有著無盡的可能性!你可以用它們壓縮合并CSS和JavaScript,有人在你的項(xiàng)目倉庫里創(chuàng)建issue的時(shí)候向你發(fā)送信息,以及更多……沒有任何限制。-
GitHub Page 是Github提供的一款免費(fèi)的~,用于托管個(gè)人的靜態(tài)網(wǎng)站,可以用它來搭建私人博客,也算是省去了購買服務(wù)器、域名等等一系列復(fù)雜的操作。
規(guī)則:建立一個(gè)倉庫命名為
用戶名.github.io,官方文檔使用:倉庫下創(chuàng)建一個(gè)
index.html文件訪問網(wǎng)址即可快速查看
什么是Coding
CODING 系騰訊旗下全資子公司, 旗下一站式軟件研發(fā)管理平臺(tái)—CODING(https://coding.net/ )是一站式軟件研發(fā)管理協(xié)作平臺(tái),提供 Git/SVN 代碼托管、項(xiàng)目協(xié)同、測試管理、制品庫、CI/CD 等一系列在線工具,幫助研發(fā)團(tuán)隊(duì)快速落地敏捷開發(fā)與
DevOps開發(fā)方式,提升研發(fā)管理效率,實(shí)現(xiàn)研發(fā)效能升級。Coding 想做的就是幫助開發(fā)者能夠高效的在云端完成軟件開發(fā)的工作。代碼托管,項(xiàng)目管理,演示平臺(tái),質(zhì)量管理等等都是為了幫助開發(fā)者在云端完成一系列高難度的軟件開發(fā)動(dòng)作。給開發(fā)者提供極致的云端開發(fā)體驗(yàn),強(qiáng)調(diào)的是私有庫,強(qiáng)調(diào)團(tuán)隊(duì)協(xié)作,強(qiáng)調(diào)整合體驗(yàn),強(qiáng)調(diào)訪問速度。
為什么要用Coding?
對于Coding的作用,其實(shí)就類似Gitee一樣,屬于國內(nèi)部署,速度提升非常明顯,而且還可以被百度收錄。由于眾說周知的原因,國內(nèi)訪問GitHub速度感人,我也試過一開始直接獲取GitHub的代碼,但是速度太慢,效率很低,因此最終選擇了Coding作為倉庫鏡像,當(dāng)然這里換成Gitee也是一樣的。
什么是Webhook
Webhook是一個(gè)
API概念,術(shù)語“網(wǎng)絡(luò)鉤子”,有時(shí)也被稱為“反向 API”。因?yàn)樗峁┝薃PI規(guī)則,你需要設(shè)計(jì)要使用的API。Webhook將向你的應(yīng)用發(fā)起http請求,典型的是post請求,應(yīng)用程序由請求驅(qū)動(dòng)。我們能用事件描述的事物越多,webhook的作用范圍也就越大。準(zhǔn)確的說
webhook是一種web回調(diào)或者h(yuǎn)ttp的push API,是向APP或者其他應(yīng)用提供實(shí)時(shí)信息的一種方式。Webhook在數(shù)據(jù)產(chǎn)生時(shí)立即發(fā)送數(shù)據(jù),也就是你能實(shí)時(shí)收到數(shù)據(jù)。使用webhooks,您可以在服務(wù)器上發(fā)生某些事件時(shí)獲得推送通知。你可以使用 webhooks“訂閱”活動(dòng)。
需求及場景
眾所周知,GitHub作為全球最大同性交友社區(qū),為了與大家更方便友好積極的交流 ??,我的源碼存放地址是存放在GitHub的,內(nèi)容包括博客的更新、一些測試用例、教程、Demo等。
因?yàn)槲业牟┛褪遣渴鹪诎⒗镌频膫€(gè)人服務(wù)器上,有時(shí)博客更新又比較頻繁,一開始是通過ssh鏈接到服務(wù)器后然后通過命令git clone、git pull、npm run build 這種操作方法去手動(dòng)進(jìn)行更新,這里存在幾個(gè)問題:
- 頻繁登錄服務(wù)器,非常繁瑣
-
GitHub速度問題,每次更新都要等待很久 - 服務(wù)器性能問題,對于低配服務(wù)器執(zhí)行
npm run build打包很吃力
思路及流程圖
思路
- 本地電腦通過
git提交到GitHub倉庫 -
GitHub Action監(jiān)聽到push event事件觸發(fā)ci.yml執(zhí)行腳本 -
build打包生成部署文件推送到GitHub gh-pages分支與Coding master分支 -
Coding設(shè)置webhook監(jiān)聽push event事件觸發(fā)webhook鉤子 -
webhook鉤子通信到個(gè)人服務(wù)器內(nèi)啟動(dòng)的http server,驗(yàn)證身份與倉庫,執(zhí)行webhook.sh腳本 -
webhook.sh腳本cd進(jìn)入nginx下的博客部署目錄,進(jìn)行git pull更新操作 - 根據(jù)結(jié)果生成日志,或添加郵件通知功能(結(jié)合
Nodemailer庫實(shí)現(xiàn))
流程圖

創(chuàng)建個(gè)人訪問令牌 Access Token
接下來自動(dòng)化部署與持續(xù)集成會(huì)用到以https方式提交代碼到倉庫的方案,這里需要配置下access token個(gè)人訪問令牌作為環(huán)境變量提供給Action與腳本使用。
GitHub Token
第一步,按照官方文檔 ,生成一個(gè)github token (令牌)。
第二步,將這個(gè)密鑰儲(chǔ)存到當(dāng)前倉庫的Settings/Secrets里面。
Settings/Secrets是儲(chǔ)存私密的環(huán)境變量的地方。環(huán)境變量的名字可以隨便起,這里用的是ACCESS_TOKEN。如果你不用這個(gè)名字,.github/workflows/ci.yml腳本里的變量名也要跟著改。
Coding Token
第一步,按照官方文檔 ,生成一個(gè)coding token (令牌)。
第二步,同 GitHub
幾種方案
:::tip
這里示例幾種我嘗試過的方案,下面會(huì)仔細(xì)分析介紹每種方案的優(yōu)劣,以及我逐漸改進(jìn)與嘗試的方法,和最終實(shí)現(xiàn)。
:::
scp 命令直接部署到服務(wù)器
主要思路
通過使用scp命令直接把本地部署文件拷貝至遠(yuǎn)程服務(wù)器
scp是secure copy的簡寫,是linux系統(tǒng)下基于ssh登陸進(jìn)行安全的遠(yuǎn)程文件拷貝命令,scp傳輸是加密的。
本地進(jìn)行
npm run build打包編譯,獲得部署文件-
直接通過上傳替換源文件進(jìn)行更新
# 1. 該命令會(huì)把當(dāng)前目錄下的dist文件遍歷上傳到blog目錄下:/blog/dist scp -r ./dist root@ip/usr/local/app/blog/ # 2. 輸入服務(wù)器密碼,等待傳輸完成即可 # 當(dāng)然也可以通過ssh的方式無密碼上傳,但這并不能達(dá)到我的最終目的,這里不再贅述
最終效果
- 服務(wù)器編譯的性能問題已解決
- GitHub更新拉取速度慢的問題已解決
- 每次更新執(zhí)行打包編譯、登錄操作等步驟依舊繁瑣
GitHub Action Pages
主要思路
通過使用GitHub提供的Page 與 Action服務(wù)實(shí)現(xiàn)gh-pages持續(xù)集成與部署
gh-pages 的搭建教程網(wǎng)上很多,具體實(shí)現(xiàn)我就不再重復(fù)了。
這里我提供一種我的方案:
通過動(dòng)態(tài)生成gh-pages分支并推送到github倉庫實(shí)現(xiàn)多地址部署
如我的博客的GitHub地址:https://JS-banana.github.io/vuepress
腳本編寫
根目錄下創(chuàng)建
.github>workflows>ci.yml-
ci.yml文件
jobs: # 工作流 build: # 自定義名稱 runs-on: ubuntu-latest #運(yùn)行在虛擬機(jī)環(huán)境ubuntu-latest strategy: matrix: node-version: [14.x] steps: # 步驟 - name: Checkout # 步驟1 uses: actions/checkout@v1 # 使用的動(dòng)作。格式: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: # 設(shè)置環(huán)境變量 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項(xiàng)目可以作為備選地址使用,通過
CNAME定向到我們自己的域名服務(wù)器下。
CNAME:即別名記錄。這種記錄允許您將多個(gè)名字映射到另外一個(gè)域名。 執(zhí)行
echo 'ssscode.com' > CNAME命令,生成CNAME文件,然后把CNAME文件放到生成的dist目錄下,這一步可以通過bash腳本處理-
我們再把上面的腳本調(diào)整下
jobs: # 工作流 build: # 自定義名稱 runs-on: ubuntu-latest #運(yùn)行在虛擬機(jī)環(huán)境ubuntu-latest strategy: matrix: node-version: [14.x] steps: # 步驟 - name: Checkout # 步驟1 uses: actions/checkout@v1 # 使用的動(dòng)作。格式: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: # 設(shè)置環(huán)境變量 GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} # token私密變量 run: npm install && npm run deploy -
創(chuàng)建
deploy.sh文件,并在package.json字段scripts下添加"deploy": "bash deploy.sh"命令#!/usr/bin/env sh # 確保腳本拋出遇到的錯(cuò)誤 set -e # date nowDate=$(date "+%Y-%m-%d %H:%M:%S") # 生成靜態(tài)文件 npm run build # 進(jìn)入生成的文件夾 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
- 到這一步,我們已經(jīng)完成通過GitHub Action實(shí)現(xiàn)持續(xù)集成,打包生成部署文件并推送到gh-pages分支。
Coding與GitHub同步部署
這一步其實(shí)原理和上面GitHub Action Pages做法一樣,而我們要做的最重要的一步,就是把Coding的token也配置到GitHub倉庫下的Settings/Secrets里面。即新增一個(gè)環(huán)境變量CODING_TOKEN,該方法同樣適用于Gitee,想要使用Gitee的小伙伴也可以親自嘗試。
ci.yml文件增加
env: # 設(shè)置環(huán)境變量
GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} # github token 私密變量
+ CODING_TOKEN: ${{ secrets.CODING_TOKEN }} # coding token 私密變量
deploy.sh文件調(diào)整為
# github url
githubUrl=https://JS-banana:${GITHUB_TOKEN}@github.com/JS-banana/vuepress.git
+ # coding url
+ #注意?。?!這里需要使用coding提供的個(gè)人令牌的用戶名和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實(shí)現(xiàn)持續(xù)部署
實(shí)現(xiàn)思路
首先,實(shí)現(xiàn)一個(gè) nodejs http server用于接收請求,即 webhook.js鉤子服務(wù),這里我建議域名解析一個(gè)二級域名供webhook專門使用,如:webhook.ssscode.com,當(dāng)然,直接在當(dāng)前項(xiàng)目下以 /webhook 路徑作為觸發(fā)路由也可以。這里我們以第一種方案為例,使用nodejs的pm2守護(hù)程序啟動(dòng)webhook.js,然后再通過nginx反向代理本地啟動(dòng)的服務(wù)映射到 webhook.ssscode.com 即可。
創(chuàng)建 webhook.js
主要使用了NodeJs模塊http(創(chuàng)建server) 與 child_process (執(zhí)行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 驗(yàn)證
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 個(gè)人訪問令牌 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 函數(shù),server服務(wù)接收請求參數(shù)驗(yàn)證身份與倉庫信息,滿足條件執(zhí)行對應(yīng)腳本。這里沒有像 github-webhook-handler 包一樣對密匙進(jìn)行處理驗(yàn)證,只是簡單的驗(yàn)證了身份和條件。(github、coding等配置webhook時(shí)可以設(shè)置驗(yàn)證秘鑰)
接下來編寫我們的bash腳本,這里之后可以優(yōu)化成執(zhí)行對應(yīng)項(xiàng)目下的對應(yīng)腳本(即 如果存在多個(gè)項(xiàng)目或博客,相關(guān)腳本在對應(yīng)項(xiàng)目下創(chuàng)建,由bash執(zhí)行)。
創(chuàng)建bash腳本
vuepress.sh核心代碼
#!/usr/bin/env sh
# 確保腳本拋出遇到的錯(cuò)誤
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 #執(zhí)行成功日志打印路徑
+ }
+ 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 #執(zhí)行失敗日志打印路徑
+ }
+ 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 - # 退回開始所在目錄
創(chuàng)建nginx配置
nginx配置
server {
#外網(wǎng)
listen 80;
server_name webhook.ssscode.com; #域名
# 監(jiān)聽本地服務(wù)
location / {
# 開啟反向代理
proxy_pass http://127.0.0.1:3010/;
}
}
執(zhí)行 node ./webhook.js 即可啟動(dòng)測試~
pm2
PM2 是 node 進(jìn)程管理工具,可以利用它來簡化很多 node應(yīng)用管理的繁瑣任務(wù),如性能監(jiān)控、自動(dòng)重啟、負(fù)載均衡等,而且使用非常簡單。PM2簡易使用手冊
為了更方便的管理NodeJs程序以及監(jiān)控,這里推薦使用 pm2 start webhook.js 啟動(dòng)服務(wù)
Github或Gitee結(jié)合webhook
因?yàn)槲也捎玫氖?code>Coding結(jié)合webhook的方式,如果有小伙伴對其他方式感興趣,也可以自行搭建,原理類似,目前Github與Gitee也有開源的npm包提供快速搭建webhook通信服務(wù)。github-webhook-handler、gitee-webhook-handler
結(jié)語
大功告成~
總的來說就是反復(fù)嘗試了很多方法,摸索找思路,再不斷的改進(jìn),最終也是實(shí)現(xiàn)了當(dāng)初的想法,不過也確實(shí)走了不少彎路。在此,把整個(gè)過程記錄下來,供自己參考也為了加深理解,希望能幫到有需要的小伙伴,少走些彎路~
如果自己全部配置各種服務(wù)與代碼業(yè)務(wù)邏輯,也確實(shí)挺花費(fèi)時(shí)間和精力的,涉及的技術(shù)點(diǎn)也比較雜,很多地方只能達(dá)到勉強(qiáng)使用,還差得遠(yuǎn),整個(gè)過程也是邊學(xué)習(xí)便嘗試。不過,全部弄完之后,對于自己的技術(shù)成長與業(yè)務(wù)理解也是很有幫助的~
類似Hexo這樣的快速搭建博客的框架用起來倒是可以省不少事,感興趣的可以嘗試下,這個(gè)我也有在弄,不過還沒達(dá)到我的預(yù)期效果,看看之后有沒有可寫的東西再分享分享吧
之后對于Docker也是很有必要深入學(xué)習(xí)一番了~
2021.06.08 補(bǔ)充
使用 Travis 結(jié)合 sshpass 工具實(shí)現(xiàn)本機(jī)文件上傳到遠(yuǎn)程服務(wù)器。
.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
# 確保腳本拋出遇到的錯(cuò)誤
set -e
# 打包靜態(tài)資源
npm run build
# 將dist文件發(fā)送到遠(yuǎn)程
sshpass -p ${serverPass} scp -o stricthostkeychecking=no -r dist/ root@${serverIP}:/home/web/movie-trailer
優(yōu)點(diǎn):方便快捷
缺點(diǎn):使用 sshpass 是最不安全的,因?yàn)樗邢到y(tǒng)上的用戶在命令行中通過簡單的 “ps” 命令就可看到密碼。
說明
原文博客:https://ssscode.com/pages/337720/
參考
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
http://www.ruanyifeng.com/blog/2016/07/yaml.html
https://juejin.cn/post/6844903710037016584