使用 JS 開發 Github Actions 實現自動部署前後臺專案到自己伺服器
阿新 • • 發佈:2020-07-15
不想看前面這麼多廢話的可以直接跳到[具體實現](#開始動手了)
## Github Actions 是什麼?
說到 Github Actions 不得不提一下。
- **持續整合**(continuous integration):高質量的讓產品快速迭代
- **持續交付**(continuous delivery):交付給團隊測試
- **持續部署**(continuous deployment):持續交付的下一步核心概念團隊測試完成後自動部署到生產環境
CI/CD 是由很多操作組成的(如:執行單元測試、語法檢查、打包、部署等等)。Github 把這些操作稱為`action`,不同的專案很多的操作都是類似,Github 把這些操作整合成了一個[市場](https://github.com/marketplace?type=actions)允許大家釋出或使用別人寫好的`action`。
## Github Actions 的核心概念
![action](https://user-gold-cdn.xitu.io/2020/7/10/17336d21dfb92115?w=1331&h=625&f=png&s=64778)
### 操作(Action)
- `action`是工作流中最小的可移植模組
- 可以建立屬於自己的`action`,使用 Github 社群提供的`action`以及自定義公開的`action`
- 在工作流中使用需要將其作為`steps`包含
- 使用是 使用者名稱/倉儲名/版本(或分支) 如:`actions/checkout@master`
### 事件(Event)
- 觸發工作流執行的特定事件
- Github 本身事件`提交`、`建立問題`、`PR`等
- 使用 webhook 配置發生在外部的事件
[具體事件請參閱](https://docs.github.com/en/actions/reference/events-that-trigger-workflows)
### GitHub-hosted runner
| 虛擬主機環境 | YAML 工作流標籤 |
| -------------------- | ---------------------------------- |
| Windows Server 2019 | `windows-latest` or `windows-2019` |
| Ubuntu 20.04 | `ubuntu-20.04` |
| Ubuntu 18.04 | `ubuntu-latest` or `ubuntu-18.04` |
| Ubuntu 16.04 | `ubuntu-16.04` |
| macOS Catalina 10.15 | `macos-latest` or `macos-10.15` |
### 作業(Job)
- 在同一個執行程式上執行的一組步驟。
- 可以為作業在工作流檔案中的執行方式定義依賴關係規則。
- 作業可以同時並行執行,也可以按順序執行,具體取決於前一個作業的狀態。例如,一個工作流可以有兩個連續的作業來生成和測試程式碼,其中測試作業取決於生成作業的狀態。如果生成作業失敗,測試作業將不會執行。
- 對於 GitHub 託管的執行程式,工作流中的每個作業都在虛擬環境的新例項中執行。
[具體作業詳細配置請參閱](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobs)
### 步驟(Step)
- 步驟是可以執行命令或操作的單個任務。
- 一個作業可配置**一個**或**多個**步驟。
- 作業中的每個步驟都在同一個執行器上執行,從而允許該作業中的操作使用**檔案系統共享資訊**。
### 工作流(Workflow)
- 可配置的自動化過程。測試、打包、釋出或部署等等。
- 工作流由**一個**或**多個**作業組成,可以通過事件計劃或啟用。
### 工作流配置檔案(Workflow file)
- 所有需要執行的工作流都必須放在 GitHub 儲存庫的根目錄下的`.gitHub/workflows` 目錄中。
- 需要使用`YAML`檔案配置並以`.yml`字尾結尾
## 我為什麼要使用 Github Actions
在沒有使用 Github Actions 我部署程式是這樣的。
![之前](https://user-gold-cdn.xitu.io/2020/7/8/1732eb909f211a87?w=345&h=515&f=png&s=13437)
![我太難了](https://user-gold-cdn.xitu.io/2020/7/8/1732eb90a0a8628c?w=300&h=225&f=jpeg&s=43005)
## 如何使用?
使用 Github Actions 後。
![之後](https://user-gold-cdn.xitu.io/2020/7/8/1732eb90a0cb4527?w=161&h=169&f=png&s=4150)
## 為什麼要自己寫一個 Github Actions
1. 出來很久一直在用有點好奇是怎麼處理的
2. 網上找了一些各種測試不成功(~~其實這才是主要原因哈哈~~)
## 開始動手了
### 目錄結構
```
shh-deploy
|—— dist(編譯後的目錄可用直接執行)
| |—— index.js
|—— lib(TS輸出檔案)
|—— src(原始碼檔案)
| |—— main.ts
| |—— sftp.ts
| |—— ssh-deploy.ts
| action.yml(Github Actions的配置檔案)
| tsconfig.json(TS配置檔案)
```
### 思考?
我們既然要實現自動部署。
1. 需要連線到伺服器`ip`、`port`、`username`、`password`
1. 需要哪些檔案(`source`)
1. 部署到伺服器哪個目錄下(`target`)
1. 檔案複製完後需要執行安裝依賴重啟服務等等之內的工作(`after command`)
知道我們需要什麼後,接下來就來看具體實現。
### Github Actions 具體實現
```yml
# action.yml 配置檔案
name: 'SSH Auto Deploy' # 名稱
description: 'ssh auto deploy' # 描述
author: 'hengkx' # 作者
branding:
icon: 'crosshair' # 使用的是Feather的圖示
color: 'gray-dark' # 圖示顏色
inputs: # 輸入引數
HOST: # 伺服器地址
description: 'remote host' # 引數描述
required: true # 是否必填
USERNAME: # 使用者名稱
description: 'username'
required: true
PASSWORD: # 密碼
description: 'password'
required: true
PORT: # 埠
description: 'port'
default: 22 # 預設值
SOURCE: # 源目錄
description: 'local path'
required: true
TARGET: # 目標目錄
description: 'remote target'
required: true
AFTER_COMMAND: # 檔案上傳文成後執行
description: 'upload success execute command'
runs: # 執行環境
using: 'node12'
main: 'dist/index.js' # 所執行的檔案
```
有一點需要注意我們所提交的程式碼包含`node_modules`或者使用`@zeit/ncc`直接打包成可執行檔案
```ts
// main.ts
import * as core from '@actions/core';
import { Client } from 'ssh2';
import Sftp from './sftp';
function exec(conn: Client, command: string) {
return new Promise((resolve, reject) => {
conn.exec(command, (err, stream) => {
if (err) return reject(err);
stream
.on('close', function (code) {
resolve(code);
})
.on('data', function (data) {
core.info(data.toString());
})
.stderr.on('data', function (data) {
core.error(data.toString());
});
});
});
}
export async function run() {
try {
const host = core.getInput('HOST'); // 使用這個方法來獲取我們在action.yml配置檔案中設定的輸入引數
const port = parseInt(core.getInput('PORT'));
const username = core.getInput('USERNAME');
const password = core.getInput('PASSWORD');
const src = core.getInput('SOURCE');
const dst = core.getInput('TARGET');
const afterCommand = core.getInput('AFTER_COMMAND');
// 下面為ssh連結伺服器上傳檔案並執行命令
const conn = new Client();
conn.on('ready', async () => {
const sftp = new Sftp(conn);
core.info('begin upload');
await sftp.uploadDir(src, dst);
core.info('end upload');
let code: any = 0;
if (afterCommand) {
core.info('begin execute command'); // 輸出一條日誌
code = await exec(conn, `cd ${dst} && ${afterCommand}`);
core.info('end execute command');
}
conn.end();
if (code === 1) {
core.setFailed(`command execute failed`); // 告訴Github Actions執行失敗了
}
});
conn.connect({ host, port, username, password });
} catch (error) {
core.setFailed(error.message);
}
}
```
我的專案配置檔案
```yml
name: Deploy
on: # 在master分支上提交程式碼執行
push:
branches: [master]
jobs: # 作業
build-and-deploy: # 作業名稱
runs-on: ubuntu-latest # 執行的環境
steps: #步驟
- name: Checkout # 步驟名
uses: actions/checkout@master # 所使用的action
- name: Setup Node.js environment
uses: actions/[email protected]
with:
node-version: '12.x'
- name: Build Project
run: yarn && yarn run ci
- name: Deploy to Server
uses: hengkx/[email protected]
with: # 以下為引數
USERNAME: ${{ secrets.DEPLOY_USER }} # 為了使用者資訊保安對敏感資料可以在secrets中配置請看下圖
PASSWORD: ${{ secrets.DEPLOY_PASSWORD }}
HOST: ${{ secrets.DEPLOY_HOST }}
SOURCE: 'dist'
TARGET: '/root/task-market/api'
AFTER_COMMAND: 'npm run stop && npm install --production && npm run start'
```
![secrets](https://user-gold-cdn.xitu.io/2020/7/10/17336d21e2378048?w=1087&h=428&f=png&s=88669)
[原始碼地址](https://github.com/hengkx/ssh-deploy)
## 參考連結
- [GitHub Actions 官方文件](https://docs.github.com/en/actions)
- [GitHub Actions 入門教程](http://www.ruanyifeng.com/blog/2019/09/getting-started-with-github-actions.html)
![](https://user-gold-cdn.xitu.io/2020/7/10/17336dbf31634d13?w=1710&h=624&f=png&s=34906)