Skip to main content

Github 上从 private 库向 public 库部署制品

wKevin

继续前面的文章 github pages 部署 docusaurus 进行优化,需求:

  1. Github Pages 与 jekyll 深度绑定,所以以前 wkevin/wkevin.github.io 上只有源码,但转用 Docusarurus 后需要自己维护 gh-pages 分支,几天下来还是让有强迫症的我感到不爽: git pull origin main 而不能简单的 git pull,否则 gh-pages 的内容会被 fetch 到本地。
  2. 因为 Github Pages 不给免费账号的 private 库提供服务,所以我的 blog 源码都暴露了。

解决方案:源码、编译后的制品拆分成 2 个 repo,从源码 repo(A) 向制品 repo(B) 进行 deploy。

GITHUB_TOKEN

先来解释一下前篇文章中方案 3 的一个实现细节: action 利用 GITHUB_TOKEN 向当前 repo 进行 push、pr 等操作。

Github 设计的 GITHUB_TOKEN 参数,它和我们在自己的 settings -- Developer settings -- Personal Access token 是相同道理:为 repo 生成一个有指定权限的 token,在各类操作(如:git push)中使用。

不过 GITHUB_TOKEN 是 github action 临时自动生成的,仅能在 actions 中使用一次的 token,生成的依据是 settings 的若干配置(总结为宽容 permissive、受限 restricted 2 种策略):

  • 个人 repo 默认 permissive:所以个人 repo 的 action 默认可以向 repo push ph-pages branch,或做其他危险操作。
  • 团队 repo 默认 restricted:以防止某人的 action 把团队的 repo 给毁了。

2 种策略对应的每项操作权限为:

ScopeDefault access
(permissive)
Default access
(restricted)
Maximum access
by forked repos
actionsread/writenoneread
checksread/writenoneread
contentsread/writereadread
deploymentsread/writenoneread
id-tokennonenoneread

表很长,参考文档

在个人或团队的 settings 中,和 repo 的 settings -- workflow permission 中,都可以配置 GITHUB_TOKEN 的策略:

在上图中所提示的文档中查看到,也可以在 .github/workflows/xxx.yml 中使用 yaml 精确配置每项权限,如:

permissions:
contents: read
pull-requests: write

策略修改整体的默认权限,每项细化权限也可以单独修改 —— Github 替我们想的很周到。

如果没有配置好,则会看到这样的报错:

personal_token

为了安全原因,Github 限制 GITHUB_TOKEN 仅供 action 操作当前所在的 repo,不能操作其他 repo,尚不能实现 A repo 的 action 向 B repo 部署。

网友给出的方案是:

  1. 在自己账号的 settings -- Developer settings -- Personal Access token 中创建一个有 repo 所有权限的 personal token。
  2. 在 A repo 的 settings -- Security -- Actions 中用上面的 personal token 创建一个环境变量,比如下图我命名为 MY_BLOG_TOKEN
  3. 在 A repo 中的 action(.github/workflows/xxx.yml)中使用 ${{ secrets.MY_BLOG_TOKEN }},该 token 对我的所有 repo 都有权限,所以可以向 B repo 进行部署。

高!既然 github 能够创建 personal token,就用它来操作 B repo。完美解决了从 A(private) repo 向 B(public) repo 部署的问题,源码可以 private 了。

话说既然解决方案这么简单,github 还设置“Pages repo 不能 private”这个规则有啥用呢?

deploy_key

在个人的 repo 中,GITHUB_TOKEN 帮我完成了 A 向 A 部署, personal_token 帮我完成 A 向 B 部署的需求,但今天当我在团队的 repo 中:

  • 正确配置 workflow.permission 后 A 向 A deploy 成功了。
  • 但 A 向 B 需要我的 personal_token 我犹豫了,这样会导致 A repo 中我配置的 token 被团队内所有人看到和使用,这个 token 可是能操作我个人 repo 的。

此时,要使用 github 设计的另一个特性了:deploy_key

之所以叫 key,因为它就是 ssh key(公钥),只不过是控制某个 repo 的 ssh key,可以用作我们账户下配置的 ssh key 的补充。

repo -- settings -- Security -- Deploy Keys 中配置的 deploy_key 是 ssh 的公钥,私钥可以在我们的电脑里,也可以在 action 跑起来的 docker 容器里 —— 这就是我想到的方案:

  1. 我在本机用 ssh-keygen 创建了一套特定的 ssh 公钥、私钥。
  2. 在 A repo -- settings -- Security -- Actions -- 将私钥注册为一个环境变量,如:DEPLOY_KEY_PRIVATE
  3. 在 B repo -- settings -- Security -- Deploy Keys -- 将公钥注册到 Deploy Keys 中。

这样,Actions 中的 docker 容器就可以用 DEPLOY_KEY_PRIVATE 变量做私钥,配合 B repo 中的公钥,完成对 B repo 的操作。

公钥、私钥是特定的,团队中所有人共享,与我个人的 repo 无关 —— 达到目的。

peaceiris/actions-gh-pages

利用 peaceiris/actions-gh-pages 项目,我们只需要送入几个参数,就可以完成 Pages 的编译和部署:

  • github_token
  • deploy_key
  • personal_token

比如:

向 A repo 中编译并部署 Pages:

github_token: ${{ secrets.GITHUB_TOKEN }}

个人 repo 中从 A 向 B(external_repository) 编译并部署 Pages:

personal_token: ${{ secrets.MY_BLOG_TOKEN }}
external_repository: wkevin/wkevin.github.io

团队 repo 中从 A(myOrg/doc) 向 B(myOrg/myOrg.github.io) 编译并部署 Pages:

deploy_key: ${{ secrets.DEPLOY_KEY_PRIVATE }}
external_repository: myOrg/myOrg.github.io

总算完美了。