2022-06-01 凌晨发布787 次点击0 条评论需阅读18分钟
如何使用Github Actions实现一个简单的ci/cd
前言
在自己的个人博客项目开发中, 每次完成一个新功能, 前后端都需要重新部署进行更新, 发现每次手动打包部署比较麻烦(我就是一个懒狗), 所以想想能不能使用 Github Actions 来实现一个每次 push 上去就自动打包构建部署, 这样子我就可以少做很多事情.
经过我的不断捣鼓, 终于实现了, 所以写一篇文章来记录一下, 一个 ci/cd 可以帮你节约很多时间.
前置条件
- 服务器 (花 ?).
- 服务器安装
node
(自行百度). - 服务器安装
pm2
(npm i -g pm2
). - 如果没有的话, 先看看有个印象.
Github Actions
我觉得官方文档就写得挺好的, 所以这一节的介绍, 我就直接翻译过来了.
引用官方文档来解释是个啥:
使用 GitHub Actions 自动化, 自定义和执行软件开发工作流程. 可以创建和共享操作以执行您想要的任何工作, 包括 CI/CD, 并将操作结合在完全自定义的工作流程中.
Workflows
Workflows: Github Actions 有多个 Workflows, 工作流是一个可配置的自动化流程, 将运行一个或多个 Job. 工作流由签入到存储库的 yaml 文件定义, 并将在由存储库中的事件触发时运行, 也可以手动触发, 或按定义的时间表触发.
Event
Event: Git 仓库中的特定活动, 用来触发工作流. 例如, 某人 push 到 main 分支, push 到 main 分支就是一个 Event, 然后会执行工作流, 当然还有其他的操作也可以触发,比如说 pull request、tag 等操作.
Runner
Runner: Runner 是触发工作流程的服务器. 每个 Runner 一次都可以运行一个作业. GitHub 提供
Ubuntu Linux
、Microsoft Windows
和MacOS
来运行工作流程; 每个工作流程运行均以新的, 新的虚拟机执行.Jobs
Jobs: 工作流中在同一运行器上执行的一组步骤. 每个步骤要么是将要执行的 shell 脚本, 要么是将要运行的操作. 步骤按顺序执行, 并且相互依赖. 由于每个步骤都在同一个运行程序上执行, 因此您可以在一个步骤之间共享数据. 例如, 您可以有一个构建应用程序的步骤, 然后是一个测试所构建应用程序的步骤.
Actions
Actions: 执行复杂且经常重复任务的 GitHub Action 平台的自定义应用程序. 使用操作来帮助减少您在工作流文件中编写的重复代码的量. Actions 可以从 GitHub 中提取 Git 存储库, 为您的构建环境设置正确的工具链, 或为云提供商设置身份验证.
准备活动
创建一个项目
- 我平时使用
next.js
多一点, 就使用npx create-next-app silver-robot
来创建一个next.js
项目. - 包管理使用的
yarn
.
选择什么创建项目就是萝卜青菜各有所爱.
创建完成之后的目录:
创建 Github Actions
首先我们在 Github 仓库中点击 Actions 这个 tab, 如果是 node 项目的话, 点击红框里面的 node.js.
然后就会在当前项目新建一个文件夹
.github/workflows/node.js.yml
.# .github/workflows/node.js.yml# 当前整个文件就是一个Workflowsname: Node.js CI # 工作流的名称on: # 执行该工作流的事件, 上图的Eventspush:branches: [main] # 当main分支push之后就执行该工作流pull_request:branches: [main] # 当main分支pull_request就执行该工作流jobs: # 执行的一组任务, 上图中的Jobsbuild:runs-on: ubuntu-latest # 在什么机器上执行, 上图中的Runnerstrategy: # 矩阵策略matrix:node-version: [12.x, 14.x, 16.x] # 等同于使用12.x、14.x、16.x三个不同node版本的job跑同一个工作流steps: # 要执行的步骤 上图中的Actions- uses: actions/checkout@v3 # 相当于git clone到当前运行的机器上- name: Use Node.js ${{ matrix.node-version }} # 显示在step上的别名uses: actions/setup-node@v3 # 设置node环境(可以使用npm、yarn, 选择指定的node版本)with: # 传入参数node-version: ${{ matrix.node-version }} # 指定什么版本 ${{}} 引用变量cache: 'npm' #- run: npm ci # 运行npm ci- run: npm run build --if-present # 运行npm run build 命令- run: npm test # 运行 npm test
所以
node.js.yml
文件的意思就是:- 定义了一个 Node.js CI 的工作流.
- 当 main 分支有
push
、pull_request
操作的时候就在ubuntu-latest
机器上执行工作流. - 使用三个不同的 node 版本, 传入不同的参数依次运行
npm ci
、npm run build
、npm test
命令. - 结束.
等同于
// node 12.x// 1. git clone xxxx// 2. npm ci// 3. npm run build --if-present// 4. npm test// node 14.x// 1. git clone xxxx// 2. npm ci// 3. npm run build --if-present// 4. npm test// node 16.x// 1. git clone xxxx// 2. npm ci// 3. npm run build --if-present// 4. npm test
所以看看这个工作流的效果:
报错了, 原因也很简单, 因为
npm ci
它依赖于package-lock.json
, 因为包管理使用的 yarn, 只有yarn.lock
, 如果使用的 npm 就不会有这个问题啦.实现一个简单的工作流
我们来实现一个全局安装
@vue/cli
, 查看全局安装之后的 vue 版本, 在刚刚的node.js.yml
进行精简与修改.name: vue/cli # 显示名字on:push:branches: [main] # main分支push就执行该工作流jobs:build:runs-on: ubuntu-latest # 在最新的ubuntu机器上steps:- uses: actions/checkout@v3 # git 克隆到当前机器上- name: Use Node.jsuses: actions/setup-node@v3with:node-version: 16.x # 使用16的node版本cache: 'yarn'- run: yarn global add @vue/cli # 全局安装@vue/cli- run: vue -V # 查看版本
效果如下图所示:
实现一个简单的 ci/cd
有了上面这个例子的基础, 我们就可以一点点的实现简单的 ci/cd, 我感觉就是用 Github Actions 来模仿我们手动构建的步骤.
- 打包.
- 将打包后的文件上传到服务器.
- 在服务器初始化然后运行.
我将从这三个步开始拆解说明.
打包
第一步就是要进行打包, 我们想一下在本地是怎么进行打包的呢?
- 通过
git clone
拉一个项目下来. yarn
/npm install
安装依赖.- 最后再进行
yarn build
/npm run build
.
name: Deployon:push:branches: [main]jobs:build:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v3 # git 克隆到当前机器上- name: Use Node.jsuses: actions/setup-node@v3 # 设置node环境with:node-version: 16.x # 指定版本cache: 'yarn'- run: yarn # 运行 yarn- run: yarn build # 运行 yarn build- run: ls -a # 查看打包后的目录文件
可以使用
name
来设置在jobs
、steps
的名称显示, 更加清晰易懂, 所以再来修改一下.# 省略...steps:# 省略...- name: Install dependenciesrun: yarn # 运行 yarn- name: Run buildrun: yarn build # 运行 yarn build- name: view directory filesrun: ls -a # 查看打包后的目录文件
ok, 我们来看看运行的效果.
可以看到
ls -a
完美执行, 同时可以看到next.js
打包后的文件: .next
, 这第一步打包算是完成了.上传服务器
第二步就是将打包好的文件上传到服务器, 我自己在本地部署的时候, 就是利用
ssh
连接远程服务器上传.所以在使用 Github Actions 时, 可以使用scp-action这个库, 我们先来看看代码.
# 省略...steps:# 省略...- name: Rename build folderrun: mv .next build # 上传之前先重命名.next成build, 防止上传之后覆盖了.next- uses: appleboy/scp-action@masterwith:host: ${{ secrets.HOST }} # 服务器hostusername: ${{ secrets.USER }} # 服务器用户名password: ${{ secrets.PASSWORD }} # 服务器密码source: 'build,package.json,public' # 需要上传的文件, 多文件使用逗号隔开target: '~/demo' # 上传到服务器的什么位置
这里我们用到了
${{}}
以及secrets.HOST
, 前一个好理解, 就是引用一个变量, 后一个是啥呢?我们可以使用
env
进行环境变量的声明. 也可以使用 secrets
进行加密版环境变量的声明.env 环境变量
我们先看 Github Actions 中的一个环境变量的例子.
name: Greeting on variable dayon: workflow_dispatch # 手动触发工作流env:DAY_OF_WEEK: Monday # 定义整个工作流中的变量jobs:greeting_job:runs-on: ubuntu-latestenv:Greeting: Hello # 定义在jobs中的变量steps:- name: "Say Hello Mona it's Monday"run: echo "$Greeting $First_Name. Today is $DAY_OF_WEEK!"env:First_Name: Mona # 定义在steps中的变量
打印出来的结果是:
Run echo "$Greeting $First_Name. Today is $DAY_OF_WEEK!"Hello Mona. Today is Monday!
secrets
比如说有一些登陆、数据库的账号和密码、或者说一些第三方提供的密钥要使用的话, 使用环境变量无疑全部都暴露出来了, 所以我们不妨使用
secrets
来定义这些秘密变量.首先在这里找到 secrets 的定义:
点击右上角的 New repository secret.
添加对应的 key、value 即可添加 sercet 了.
是不是很简单, 这样子你就可以使用
${{secrets.xxxx}}
引用秘密变量了, 所以上面的代码中用到了HOST
、USER
、PASSWORD
三个密码变量需要在 sercets 中定义.在服务器运行命令
如果是
@vue/cli
、create-react-app
打包出来的静态文件, 只需要上传到服务器的指定文件夹就可以了, 就不需要进行这一步.
但是如果还需要在服务器把打包后的项目(比如说 ssr 项目)再次运行起来的话, 就需要进行当前这一步了.在本地的时候就是上传到服务器之后, 然后在服务器进行一些操作就可以部署成功, 所以在使用 Github Actions 时, 可以使用ssh-action这个库, 我们先来看看代码.
# 省略...steps:- name: Run Deployuses: appleboy/ssh-action@masterwith:command_timeout: 4mhost: ${{ secrets.HOST }}username: ${{ secrets.USER }}password: ${{ secrets.PASSWORD }}script: | # 运行多行命令echo "[deploy] start deployment..."# 进到当前文件夹cd ~/demo# 停止服务pm2 stop demopm2 delete demo# 删除之前的文件rm -rf .nextrm -rf node_modules# 将上传的文件的文件进行重命名 build -> .nextmv build .next# 安装依赖yarn --prod# 启动服务yarn pm2echo "[deploy] end deployment..."echo "[deploy] success"
所以我们还需要在
package.json
中写一个pm2
的运行命令.{"scripts": {"start": "next start","pm2": "pm2 start yarn --name 'demo' --interpreter bash -- start "},"dependencies": {// ...}}
最终代码
.github/workflows/node.js.yml
name: Node.js CIon:push:branches: [main]jobs:build:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v3 # git 克隆到当前机器上- name: Use Node.jsuses: actions/setup-node@v3 # 设置node环境with:node-version: 16.x # 指定版本cache: 'yarn'- name: Install dependenciesrun: yarn # 运行 yarn- name: Run buildrun: yarn build # 运行 yarn build- name: View directory filesrun: ls -a # 查看打包后的目录文件- name: Rename build folderrun: mv .next build # 重命名.next成build- uses: appleboy/scp-action@master # 使用scp-action进行文件上传with:host: ${{ secrets.HOST }} # 服务器hostusername: ${{ secrets.USER }} # 服务器用户名password: ${{ secrets.PASSWORD }} # 服务器密码source: 'build,package.json,public' # 需要上传的文件target: '~/demo' # 上传到服务器的什么位置- name: Run Deployuses: appleboy/ssh-action@masterwith:command_timeout: 4mhost: ${{ secrets.HOST }} # 服务器hostusername: ${{ secrets.USER }} # 服务器用户名password: ${{ secrets.PASSWORD }} # 服务器密码script: | # 运行多行命令echo "[deploy] start deployment..."# 进到当前文件夹cd ~/demo# 停止服务pm2 stop demopm2 delete demo# 删除之前的文件rm -rf .nextrm -rf node_modules# 将上传的文件的文件进行重命名 build -> .nextmv build .next# 安装依赖yarn --prod# 启动服务yarn pm2echo "[deploy] end deployment..."echo "[deploy] success"
package.json
{"scripts": {"start": "next start","pm2": "pm2 start yarn --name 'demo' --interpreter bash -- start "},"dependencies": {// ...}}
小结
以上就完成一个简单的 ci/cd, 每当 main 分支 push、pull_request 的时候就会运行该工作流, 经过我的使用感觉挺不错的, 当然这只是比较简单的.
后面自己捣鼓的话, 可以尝试加上
actions-cache
加快 Github Actions 的流程、使用几个job
进行解耦合, 在不同的job
进行分享数据、进行测试/ci 等操作, 这个完全取决于你自己怎么玩.如果本文对你有所帮助的话, 点个赞就是对作者最大的肯定!!!