前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版

Github Action 部署应用实战 工作流配置与四种上线方式

首页2020-08-09 22:21:08Front-End
部署Github actionCI/CD

每次改完文档站,我都得本地跑一遍 npm run build,再把 dist 目录手动传到服务器,传完还要进控制台刷一次缓存。一次两次还好,一天来五六回就烦了,而且总有那么一回会忘了先构建,直接把上一版的文件传了上去。GitHub Actions 能把这段重复劳动整个接走,你只管 push,拉代码、装依赖、打包、上传、刷缓存都在云端跑完。这篇把我这阵子用 Actions 做部署的几种配置整理成一篇,从 workflow 文件的字段读法开始,一直写到四个不同目标环境的可复制配置,最后留一份上线前的自查清单。

在本篇文章中,我们将从浅入深,和大家一起学习以下知识:

  • GitHub Actions 的执行模型,workflow、job、step、action 四个概念分别对应什么
  • workflow 文件的核心字段,name、on、needs、runs-on、steps 怎么读怎么写
  • 发布到阿里云 OSS,顺带把静态资源的长缓存也设上
  • 跨仓库发布到 GitHub Pages,公钥私钥分别放在哪个仓库
  • 用 rsync 推到自己的云服务器,SSH 密钥对怎么配才不会连不上
  • 发布到腾讯云静态网站托管
  • 上线前的 checklist,以及这些老版本号今天该怎么处理

# 一、先把 GitHub Actions 的执行模型讲清楚

持续集成本身不是什么新东西,它就是一串固定动作:抓取代码、装依赖、跑测试、登录远程服务器、把产物发到第三方服务。GitHub 把这一串里的每一个动作单拎出来,称为 actions。

真正让它跟传统 CI 拉开差距的是复用方式。你需要某个动作,不用自己写脚本,直接引用别人写好的 action 就行,整个持续集成过程就变成了一堆 actions 的组合。

这个设计是真的舒服。

GitHub 为此做了一个 官方市场,能搜到别人提交的 actions。另外还有一个 awesome actions 仓库,也能捞到不少现成的。

既然每个 action 就是一个独立脚本,那它当然可以做成一个代码仓库。引用的时候用 userName/repoName 语法,比如 actions/setup-node 指的就是 github.com/actions/setup-node 这个仓库,它的作用是安装 Node.js。GitHub 官方维护的 actions 都放在 github.com/actions 下面。

是代码仓库就有版本概念,所以你可以精确引用某一个版本,用的是 Git 的指针语义:

actions/setup-node@74bc508 # 指向一个 commit
actions/setup-node@v1.0    # 指向一个标签
actions/setup-node@master  # 指向一个分支
@前端进阶之旅: 代码已经复制到剪贴板

这里有个坑要注意,指向分支(比如 @master)意味着对方一改你就跟着变,出问题的时候你连是哪次变更引起的都查不到。生产用的 workflow 建议锁大版本标签,安全要求高的直接锁 commit sha。

# 1.1 四个术语的层级关系

GitHub Actions 有一套自己的说法,四个词是层层嵌套的:

术语 含义 关系
workflow(工作流程) 持续集成一次运行的完整过程 最外层,一个 .yml 文件就是一个 workflow
job(任务) 一次运行里可以完成的多个任务之一 一个 workflow 由一个或多个 job 构成
step(步骤) 任务内部的一步 一个 job 由多个 step 串行构成
action(动作) 每个 step 执行的具体命令 一个 step 可以依次执行一个或多个 action

要记的关键点只有一条:同一个 workflow 里的多个 job 默认是并行跑的,而同一个 job 里的多个 step 是严格串行的。所以「装依赖、打包、上传」这种有先后依赖的动作必须写在同一个 job 里,写成三个 job 会同时启动,第二个 job 根本看不到第一个 job 装好的 node_modules。

# 1.2 整条链路长什么样

把上面这些串起来,一次自动部署的完整链路是这样:

本地 git push
      │
      ▼
GitHub 仓库(master 分支)
      │  触发 push 事件
      ▼
.github/workflows/*.yml   ← GitHub 自动发现目录下的 yml 并执行
      │
      ▼
┌──────────────── workflow ────────────────┐
│  runs-on: ubuntu-latest(一台干净的虚拟机)│
│                                          │
│  job: build                              │
│   ├─ step1  actions/checkout   拉代码     │
│   ├─ step2  npm install        装依赖     │
│   ├─ step3  npm run build      打包       │
│   └─ step4  第三方 action       上传/部署  │
└──────────────────────────────────────────┘
      │
      ▼
目标环境:阿里云 OSS / GitHub Pages / 自有服务器 / 腾讯云静态托管
@前端进阶之旅: 代码已经复制到剪贴板

有一点特别值得先说清楚,每次运行拿到的都是一台全新的虚拟机,跑完就销毁。所以本地能跑不代表 CI 能跑,凡是你在本机全局装过的东西(ossutil、某个 CLI、某个字体),CI 上都得在 step 里重新装一遍。我第一次配的时候就是栽在这个上,本地好好的,CI 上一片红。

# 二、workflow 文件的字段拆解

GitHub Actions 的配置文件叫 workflow 文件,固定放在仓库的 .github/workflows 目录下。

文件采用 YAML 格式,文件名随便取,后缀统一为 .yml,比如 foo.yml。一个仓库可以有多个 workflow 文件,GitHub 只要发现 .github/workflows 目录里有 .yml 就会自动运行。字段非常多,详见 官方文档,下面挑常用的几个说。

# 2.1 name

name 字段是 workflow 的名称,显示在仓库 Actions 页签的列表里。省略的话默认取当前 workflow 的文件名。

name: GitHub Actions Demo
@前端进阶之旅: 代码已经复制到剪贴板

小建议,如果你一个仓库配了三四个 workflow,这个 name 一定要写,而且写得能一眼区分。全靠文件名认的话,Actions 列表里全是 ci.yml,排查起来很难受。

# 2.2 on

on 字段指定触发 workflow 的条件,通常是某些事件。

on: push
@前端进阶之旅: 代码已经复制到剪贴板

上面这行的意思是 push 事件触发 workflow。它也可以写成事件数组:

on: [push, pull_request]
@前端进阶之旅: 代码已经复制到剪贴板

这样 push 或 pull_request 任意一个都能触发。完整的事件列表见 官方文档,除了代码库事件,GitHub Actions 还支持外部事件触发和定时运行。

# 2.3 限定分支和标签

只写 on: push 的问题是任何分支的推送都会触发一次构建,你在 feature 分支上推十次,就白跑十次部署。指定事件的同时限定分支就能解决:

on:
  push:
    branches:
      - master
@前端进阶之旅: 代码已经复制到剪贴板

这样只有 master 分支发生 push 事件时才会触发 workflow。部署类的 workflow 我基本都会加这一段。

# 2.4 jobs 与 job 之间的依赖

workflow 文件的主体是 jobs 字段,表示要执行的一项或多项任务。jobs 里面需要写出每一项任务的 job_id,具体名称自定义,job_id 里面的 name 字段是这项任务的说明文字。

jobs:
  my_first_job:
    name: My first job
  my_second_job:
    name: My second job
@前端进阶之旅: 代码已经复制到剪贴板

上面的 jobs 字段包含两项任务,job_id 分别是 my_first_job 和 my_second_job。

前面提过 job 默认并行,那要串起来怎么办?用 needs 声明依赖关系:

jobs:
  job1:
  job2:
    needs: job1
  job3:
    needs: [job1, job2]
@前端进阶之旅: 代码已经复制到剪贴板

上面这段里 job1 必须先于 job2 完成,而 job3 要等 job1 和 job2 都完成才能运行,所以整个 workflow 的运行顺序是 job1、job2、job3。

「构建镜像」和「登录服务器部署」这种典型的两段式流程,就是靠 needs 把部署 job 挂在构建 job 后面的。少了这一句,部署 job 会在镜像还没推上去的时候就开始拉,然后失败。

# 2.5 runs-on

runs-on 指定运行所需要的虚拟机环境,是必填字段。当时可用的虚拟机是这些:

ubuntu-latest,ubuntu-18.04或ubuntu-16.04
windows-latest,windows-2019或windows-2016
macOS-latest或macOS-10.14
@前端进阶之旅: 代码已经复制到剪贴板

这几个带具体版本号的镜像后来陆续退役过,我不在这里写死替换成哪一版,因为写死了过阵子又会过期。实际配的时候直接用 ubuntu-latest / windows-latest / macos-latest 这类滚动标签最省事,GitHub 会自动指向当前维护中的镜像。只有当你的构建对系统库版本敏感(比如依赖某个特定的 glibc 或者 Xcode),才需要去官方 runner 镜像文档里查当前支持的固定版本再钉住。

# 2.6 steps

steps 字段指定每个 job 的运行步骤,可以包含一个或多个步骤。每个步骤可以指定这三个字段:

  • jobs.<job_id>.steps.name:步骤名称
  • jobs.<job_id>.steps.run:该步骤运行的命令或者 action
  • jobs.<job_id>.steps.env:该步骤所需的环境变量

下面是一个完整的 workflow 文件范例:

name: Greeting from Mona
on: push

jobs:
  my-job:
    name: My Job
    runs-on: ubuntu-latest
    steps:
    - name: Print a greeting
      env:
        MY_VAR: Hi there! My name is
        FIRST_NAME: Mona
        MIDDLE_NAME: The
        LAST_NAME: Octocat
      run: |
        echo $MY_VAR $FIRST_NAME $MIDDLE_NAME $LAST_NAME.
@前端进阶之旅: 代码已经复制到剪贴板

steps 字段只包括一个步骤,这个步骤先注入四个环境变量,然后执行一条 Bash 命令。run 后面跟 | 表示这是一段多行脚本,多行命令都写在这个块里。

# 2.7 关于文中的版本号

下面几节的配置里会出现 actions/checkout@v2、peaceiris/actions-gh-pages@v2.5.1 这类版本号,那是当时能用的写法,我原样保留。这些常用 action 后来都发过更新的大版本,主体语法基本兼容,但底层 Node 运行时和一部分默认行为(比如 checkout 拉取的深度、缓存策略)有过调整。

所以你照抄之前,先去对应 action 仓库的 releases 页面看一眼当前推荐的大版本,把 @v2 换成它标注的那个。我这里不写具体数字,写了也是很快就旧。

# 三、发布到阿里云 OSS

第一种场景最简单,产物是一堆静态文件,直接扔进对象存储。

fe
  • 一、先把 GitHub Actions 的执行模型讲清楚
    • 1.1 四个术语的层级关系
    • 1.2 整条链路长什么样
  • 二、workflow 文件的字段拆解
    • 2.1 name
    • 2.2 on
    • 2.3 限定分支和标签
    • 2.4 jobs 与 job 之间的依赖
    • 2.5 runs-on
    • 2.6 steps
    • 2.7 关于文中的版本号
  • 三、发布到阿里云 OSS
  • 四、跨仓库发布到 GitHub Pages
  • 五、用 rsync 发布到自己的云服务器
    • 5.1 远程主机先装 rsync
    • 5.2 本地生成密钥对
    • 5.3 把公钥放进远程主机
    • 5.4 在仓库里配好私钥
    • 5.5 完整配置
  • 六、发布到腾讯云静态网站托管
  • 七、上线前 checklist
  • 总结
  • 参考

← Vue3笔记总结 组合式API与新特性完整梳理微信h5网页跳转小程序方案 开放标签实战 →