回顾下
在上篇,我们成功的用 VuePress 搭建了博客并部署到 Github Pages 与 Gitee同名仓库里面,并手动同步推送了两个仓库,这还是很麻烦的。能不能实现自动同步仓库之间的文件呢?市面上还真有,这篇是我翻译总结出来 Github Actions 的官方文档,为大家介绍 Github Actions
的同时,也实现下仓库之间的同步操作。
GitHub Actions是啥
简单介绍下这个组件,GitHub Actions 是一个持续集成 (Continuous integration)和持续交付的平台,可以做到自动化构建、测试、部署。你可以创建出工作流,构建和测试每一个 pull request(拉取请求) 或者部署合并(merge) 后的代码到生产环境里面。
GitHub Actions 可以在你的代码仓库发生某个事件时运行一个工作流。当有人给你的代码仓库新建了一个 issue(问题),你可以跑一个工作流自动的添加合适的标签了,是不是很方便。
GitHub 提供了 Linux、Windows、和 macOS 虚拟机运行你的工作流,当然你也可以自定义自己的运行环境。
GitHub Actions 组件
你可以配置一个 GitHub Actions 工作流(workflow),它会在你的仓库发生某个事件时被触发,就比如一个 pull request
或者一个 issue 被创建的时候。
你的工作流包含了一个或者多个任务, 它们可以并行或者串行执行。每一个任务(jobs)都会在它自己的虚拟机运行器(vmare Operator)上,任务可以有一个或者多个步骤,可以运行一个自定义的脚本(script)或者可运行一个动作(action),所谓这个动作(action)说白点就是一个可复用的扩展,用于简化你的工作流,结构上看就像个套娃。
1 Workflows(工作流)
工作流是一个可配置的自动化的程序。创建一个工作流,你需要定义一个 YAML
文件,当你的仓库触发某个事件的时候,工作流自动的跑起来,当然也可以手动触发,或者定义一个时间表。
一个仓库可以创建多个工作流,每一个都会执行不同的步骤,举个例子,一个工作流用于构建和测试 pull request
,另外一个用于部署你的应用(APP),再来一个,当有人新建 issue(问题) 的时候自动添加一个标签上。
你也可以在一个工作流中引用另外一个工作流,具体查看文档「可复用工作流」。
2 事件(Events)
事件是指仓库触发运行工作流的具体的行为,如你创建一个 pull request(推送请求),新建一个 issue、或者推送一个 commit
。你也可以使用时间表触发一个工作流,或者通过请求一个 REST API
,再或者手动触发,都是可以的。
事件完整的列表,可以查看文档「触发工作流的事件」。
3 任务(Jobs)
任务是在同一个运行器上执行的一组步骤,就是示意图的白框。一个步骤(steps)要么是一个 shell 脚本(script)要么是一个动作(action)。步骤肯定会顺序执行,并彼此独立。因为每一个步骤都在同一个运行器上被执行,所以你可以从一个步骤(step)传递数据到另一个步骤(step) 。
你可以配置一个任务依赖其他任务,默认情况下,任务没有依赖,并行执行的。当一个任务需要另外一个任务的时候,它会等到依赖的任务完成了再执行。
4 动作(Actions)
动作是 GitHub Actions 平台的一个自定义的应用,它会执行一个复杂但是需要频繁重复的作业。使用动作可以减少重复代码。比如一个 action 可以实现从 GitHub 拉取你的 git 仓库,为你的构建环境创建合适的工具链等。
你可以写自己的动作,或者在 GitHub 市场里面去找已经实现好的动作。
工作流中定义的持续集成意思是由很多操作组成,比如说抓取代码、运行测试、登录远程服务器,发布到第三方服务等等。GitHub 都把这些操作就称为 actions。
很多操作在不同项目里面是类似的,完全可以共享的。GitHub团队 注意到了这一点,想出了一个很妙的点子,允许每个开发者把每个操作写成独立的脚本文件,存放到代码仓库,使得其他开发者可以引用。
如果你需要某个 action,不必自己写复杂的脚本,直接引用他人写好的 action 即可,整个持续集成过程,就变成了一个 actions 的组合。这就是 GitHub Actions 最特别的地方了。
GitHub 做了一个官方市场,可以搜索到他人提交的 actions。另外,还有一个 awesome actions 的仓库,也可以找到不少 action 如下图。
上面说了,每个 action 就是一个独立脚本,因此可以做成代码仓库,使用 userName/repoName 的语法引用 action。事实上,GitHub 官方的 actions 都放在 github.com/actions 里面。
既然 actions 是代码仓库,当然就有了版本的概念,用户可以引用某个具体版本的 action。下面都是合法的 action 引用,用的就是 Git 的指针概念,详见官方文档。
actions/setup-node@74bc508 # 指向一个 commit
actions/setup-node@v1.0 # 指向一个标签
actions/setup-node@master # 指向一个分支
5 运行器(vmare Operator)
一个运行器是一个可以运行任务的服务。每一个运行器一次只运行一个单独的任务。GitHub 提供 Ubuntu Linux,Microsoft Windows 和 macOS 运行器,每一个工作流都运行在一个独立新建的虚拟机中。如果说你需要一个不同的操作系统,你可以自定义运行器。具体请查看「自定义运行器」。
6 创建一个工作流
GitHub Actions 使用 YAML
语法定义工作流。每一个工作流具体保存为一个独立的 YAML 文件,目录是 .github/workflows。
现在我们在代码仓库创建一个示例工作流,当代码被推送的时候,肯定会自动执行一系列的命令。在这个示例工作流中,GitHub Actions 会检测出提交的代码,安装依赖,运行 bats -v
:
(1) 在你的仓库,首先要创建一个 .github/workflows/
目录
(2) 在 .github/workflows/
目录,创建一个文件,名为 learn-github-actions.yml
,添加下面的代码:
name: learn-github-actions
on: [push]
jobs:
check-bats-version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '14'
- run: npm install -g bats
- run: bats -v
(3) 提交这些改动,在 windows 本地推送到你的 GitHub 仓库。
你的新 GitHub Actions 工作流文件就会被安装在你的仓库,当有人提交代码的时候,工作流就会自动执行。关于一个任务的执行历史,具体查看「查看工作流活动」章节。
7 理解工作流文件
为了帮助你理解 YAML 语法,这节会解释例子中的每行代码:
name: learn-github-actions
这个你可选,是工作流的名字,会出现在 GitHub 仓库的 Actions 选项栏里。
on: [push]
指定工作流的触发事件。这个例子里,使用是 push
事件,当有人提交了一个代码修改或者合并了一个 pull request ,工作流就会触发。提交到每个分支都会被触发,如果你想在指定分支、路径、标签,需要查看 「GitHub Actions 工作流语法」;
jobs:
将运行在 learn-github-actions
工作流的所有任务分组在一起。
check-bats-version:
这个定义了一个名为 check-bats-version
的任务,子键(child key)会定义该任务的属性。
runs-on: ubuntu-latest
配置任务运行在最新的 Ubuntu Linux 运行器,你看的懂这个。
steps:
将 check-bats-version
任务下的所有步骤分为一组,嵌套的每一个条目都是一个独立的 action 或者 shell 脚本。
- uses: actions/checkout@v2
uses
关键字指定了这个步骤运行 actions/checkout
动作的 v2 大版本 。这是一个可以检出仓库代码到运行器的动作,它允许你运行脚本或者其他动作侵入你的代码(比如说构建或者测试工具)。
- uses: actions/setup-node@v2
with:
node-version: '14'
这个步骤会使用 actions/setup-node@v2
动作安装指定版本的 Node.js ,自动在你的 PATH
加上 node
和 npm
命令。
- run: npm install -g bats
run
关键字会告诉任务在运行器上执行一个命令。在这个例子中,你正在使用 npm
安装 bats
软件测试包。
- run: bats -v
最终,你运行 bats 命令,传一个可以打印软件版本的参数。
8 可视化工作流文件
在这个图表,你可以看到你刚创建的工作流文件,以及这些 GitHub Actions 组件是如何组织的。每一个步骤都会执行一个独立的动作或者脚本文件。任务1 和 2 是运行命令,任务3 和 任务4 是运行脚本文件。找到更多预构建的动作,查看 「查找和自定义动作」。
9 查看工作流活动
一旦你的工作流开始运行,你可以在 GitHub 看到一个可视化的运行进度图表 ,查看每一个步骤的执行情况。
在 GitHub.com ,导航至仓库主主页
在你的仓库名下,点击 Actions
。
在左侧 sidebar,点击你想查看的工作流
在 Workflow runs
,点击你想查看的运行记录的名称:
在 Jobs 或者在可视化图表中,点击你想看到的任务:
查看每一个步骤的结果:
完成自动同步
我们也可以利用 Github Actions
,写一个工作流,在发现 Github 博客仓库的 gh-pages 分支代码更新后,自动同步当前代码到 Gitee 上。
关于 Github Actions 的详细介绍,可以参考阮一峰老师的 《GitHub Actions 入门教程》。
详细介绍可以看这里(https://www.ruanyifeng.com/blog/2019/09/getting-started-with-github-actions.html)
为了实现 Gitee 和 Github 的同步,我们需要借助叫 action 的东西,还好业界已经有现成的实现了,这里我采用的是 Hub Mirror Action
,其实就在actions市场找的,我们可以看到使用的示例代码:
steps:
- name: Mirror the Github organization repos to Gitee.
uses: Yikun/hub-mirror-action@master
with:
src: github/Datalong
dst: gitee/Datalong
dst_key: ${{ secrets.GITEE_PRIVATE_KEY }}
dst_token: ${{ secrets.GITEE_TOKEN }}
account_type: org
其中有四个必填项:
-
src
表示需要被同步的源端账户名,即我们 Github 的账户名,因为我的 Github ID 是 Datalong,所以这里我应该改成github/Datalong
。 -
dst
表示需要同步到的目的端账户名,即我们 Gitee 的账户名,因为我的 Gitee ID 也是 Datalong,所以这里我应该改成gitee/Datalong
。 -
dst_key
表示用于在目的端上传代码的私钥,然后将其保存在 Secrets 中。
具体的操作步骤如下:
获取私钥:
cat ~/.ssh/id_rsa
复制你的私钥内容,然后在要同步的 Github 仓库中,选择 "Setting" -> "Secrets" -> "New repository secret"
填入 Secret 内容,Name 命名为 "GITEE_PRIVATE_KEY",Value 为复制的内容
然后点击 Add secret
即可。
dst_token 创建仓库的API tokens, 用于自动创建不存在的仓库。这里我们从 Gitee 上获取,具体地址为 https://gitee.com/profile/personal_access_tokens。生成并复制 Token,然后同样的步骤。
保存在 Github 的 Secrets 中,Name 为 "GITEE_TOKEN"
那么我们就可以在仓库建立的根目录下,建立目录 .github/workflows ,然后创建一个名为syncToGitee.yml 的文件如下:
name: syncToGitee
on:
push:
branches:
- gh-pages
jobs:
repo-sync:
runs-on: ubuntu-latest
steps:
- name: Mirror the Github organization repos to Gitee.
uses: Yikun/hub-mirror-action@master
with:
src: 'github/mqyqingfeng'
dst: 'gitee/mqyqingfeng'
dst_key: ${{ secrets.GITEE_PRIVATE_KEY }}
dst_token: ${{ secrets.GITEE_TOKEN }}
static_list: "blog"
force_update: true
debug: true
这个里面,我们定义了一个名为 syncToGitee
的工作流,指定代码提交到分支 gh-pages
才能触发工作流。
任务下只有一个名为 repo-sync
的任务,运行在 ubuntu-latest
, 具体的步骤下,也只有一个名为 Mirror the Github organization repos to Gitee
的步骤,使用了 Yikun/hub-mirror-action@master
动作,而 with
里的内容则是该动作需要的一些参数。
其中,static_list 表示单一仓库同步,force_update
为 true 表示启用 git push -f
强制同步,debug 为 true 表示启用 debug 开关,会显示所有执行命令。
当我们把这样的文件提交到 Github,Github 会自动检测并运行该脚本。但是现在还有几个问题要注意:
因为咱们是提交到 Github 的 gh-pages 分支上,这个文件和目录需要写在 gh-pages 分支,
观察咱们的脚本代码,我们就会发现,每次我们 sh deploy.sh 的时候,都是编译代码到 dist 目录,然后重新 git init ,最后强制提交。所以我们在项目的根目录建立 .github/woorkflows/syncToGitee.yml 并没有什么卵用,一来提交的是 dist 目录里的代码,二来是每次还都会清空重新编译生成代码提交。
为此,我们可以在脚本里添加代码,每次编译完后,再拷贝外层的 .github/woorkflows/syncToGitee.yml
到 dist 目录里,再提交到 Github 上,完美了。
所以我们依然在项目根目录添加目录和文件,此时的文件结构如下:
.
├─ docs
│ ├─ README.md
│ └─ .vuepress
│ └─ config.js
└─ .github
│ └─ workflows
│ └─ syncToGitee.yml
└─ package.json
└─ deploy.sh
脚本文件代码如下:
#!/usr/bin/env sh
# 确保脚本抛出遇到的错误
set -e
# 生成静态文件
npm run docs:build
# 进入生成的文件夹
cd docs/.vuepress/dist
# 拷贝目录和文件
cp -r ../../../.github ./
git init
git add -A
git commit -m 'deploy'
# 如果发布到 https://<USERNAME>.github.io/<REPO>
git push -f git@github.com:Datalong/blog.git master:gh-pages
cd -
此时我们再运行 sh deploy.sh 代码提交到 Github,就可以在仓库的 Actions 中看到运行记录
执行时间大概一分钟左右,Gitee 的代码就会自动同步。
到此这里,我们完成了 Github 代码自动同步 Gitee 的问题
Gitee如何部署Pages
在上面,咱们使用 GitHub Actions 解决了 GitHub 代码自动同步 Gitee 的问题,但我们的博客仓库代码同步到 Gitee 后,并不能像 GitHub 一样自动部署 Pages,如果不使用付费的 Gitee Pages Pro 服务,那我们该怎么实现 Gitee 自动部署 Pages 呢?
GitHub Actions
答案还是接着使用 GitHub Actions!你可能会想,Gitee 也有 GitHub Actions 服务吗? Gitee 也会像 GitHub 一样检测 .github/workflows/ 目录下的 YAML 文件,然后执行吗?
这当然是不可能的啦,Gitee 并不会支持 GitHub 的这套方式,但我们为什么一定要借用 Gitee 的能力呢?我们在 GitHub Actions 中,模拟登陆 Gitee,点击项目的部署按钮,不也是一种实现方式吗?
搜索 Actions
我们接下来去找一些合适的 GitHub Actions,我们可以在 GitHub 的官方市场【1】,或者 awesome actions仓库【2】,再或者直接在 GitHub 搜索比如 gitee pages actions 之类的关键词。
最终,我们决定使用 Gitee Pages Action【4】,查看一下主页的示例代码:
name: Sync
on:
push:
branches: [main]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Sync to Gitee
uses: wearerequired/git-mirror-action@master
env:
# 注意在 Settings->Secrets 配置 GITEE_RSA_PRIVATE_KEY
SSH_PRIVATE_KEY: ${{ secrets.GITEE_RSA_PRIVATE_KEY }}
with:
# 注意替换为你的 GitHub 源仓库地址
source-repo: git@github.com:doocs/leetcode.git
# 注意替换为你的 Gitee 目标仓库地址
destination-repo: git@gitee.com:Doocs/leetcode.git
- name: Build Gitee Pages
uses: yanglbme/gitee-pages-action@main
with:
# 注意替换为你的 Gitee 用户名
gitee-username: yanglbme
# 注意在 Settings->Secrets 配置 GITEE_PASSWORD
gitee-password: ${{ secrets.GITEE_PASSWORD }}
# 注意替换为你的 Gitee 仓库,仓库名严格区分大小写,请准确填写,否则会出错
gitee-repo: doocs/leetcode
# 要部署的分支,默认是 master,若是其他分支,则需要指定(指定的分支必须存在)
branch: main
我们之前已经实现了 GitHub
代码同步 Gitee
,这里我们直接使用后半部分的自动部署 actions,结合上篇的 YAML 文件代码,最终的修改如下:
name: syncToGitee
on:
push:
branches:
- gh-pages
jobs:
repo-sync:
runs-on: ubuntu-latest
steps:
- name: Mirror the Github organization repos to Gitee.
uses: Yikun/hub-mirror-action@master
with:
src: 'github/Datalong'
dst: 'gitee/Datalong'
dst_key: ${{ secrets.GITEE_PRIVATE_KEY }}
dst_token: ${{ secrets.GITEE_TOKEN }}
static_list: "blog"
force_update: true
debug: true
- name: Build Gitee Pages
uses: yanglbme/gitee-pages-action@main
with:
# 注意替换为你的 Gitee 用户名
gitee-username: Datalong
# 注意在 Settings->Secrets 配置 GITEE_PASSWORD
gitee-password: ${{ secrets.GITEE_PASSWORD }}
# 注意替换为你的 Gitee 仓库,仓库名严格区分大小写,请准确填写,否则会出错
gitee-repo: Datalong/blog
# 要部署的分支,默认是 master,若是其他分支,则需要指定(指定的分支必须存在)
branch: gh-pages
注意不要忘了在仓库的设置里添加 Secrets,输入 Gitee 的登陆密码,然后保存名为 GITEE_PASSWORD
YAML 文件语法报错
如果 Actions 运行失败并出现了这种错误:
这是因为你的 YAML 语法写的有问题,可能是哪里没有对齐。可以在这个网址[5]校验下你的 YAML 文件,也可以顺便看下阮一峰老师的 YAML 语言教程[6]。
再次运行
修改了代码之后,我们可以再执行一遍 sh deploy.sh
,然后在 GitHub 上查看运行情况:
当运行成功,我们再查看 Gitee 的地址,就会发现已经部署为最新的版本了。
至此,实现了 GitHub 和 Gitee 代码的同步与自动部署。
参考资料
【1】github actions官方市场:https://github.com/marketplace?type=actions
【2】awesome actions: https://github.com/sdras/awesome-actions
【3】Gitee Pages Action: https://github.com/marketplace/actions/gitee-pages-action
【4】yaml在线格式校验:https://www.bejson.com/validators/yaml_editor/
【5】yaml语言教程:https://www.ruanyifeng.com/blog/2016/07/yaml.html
系列文章
搜索公众号目录地址:https://github.com/Datalong/blog
点击公粽号:龙哥手记 「加群进阶」, 进龙歌唯一的读者群。
如果有错误或者不严谨的地方,请务必得出指正,非常感谢。如果喜欢或者 有所启发,欢迎star, 对作者也是一种鼓励。
跟着龙哥一起掉亿点点秀发吧~, `别忘给我点赞???? 哥哥姐姐们再走啊!!!