GitHub Actions是一个自动化平台,你可以直接从 GitHub 仓库内运行。
使用GitHub Actions,你可以建立由任何类型的事件触发的工作流程。这些工作流程将任意代码作为Jobs ,你可以将多个Steps ,以实现你想要的几乎任何东西。
除了在每个拉动请求上自动发布GIF之外,这个新平台最明显的用例是建立一个测试CI/CD管道。

为什么要这么做?我已经在使用CircleCI / Travis / Semaphore...
好问题。所有这些CI/CD平台,或多或少都是等同的。
像CircleCI这样的专用CI服务在运行linters、执行测试、报告结果和启动部署等使用情况下是经过考验的。GitHub Actions更类似于乐高--它是一个用于运行任意工作流程和自动化的通用平台。
另一方面,GitHub Actions也包含在你的GitHub项目中。对于公共仓库,GitHub Actions 是免费的(截至本文撰写时没有限制)。对于私有仓库,每个仓库每月有2000分钟的免费构建时间--如果你有一个现有的GitHub公司计划,你将得到超过10,000分钟的构建时间,而且不需要额外费用。(完整的计费/使用限制在这里)
所有的竞争平台都有免费层级,而且,对项目来说,最大的好处是建立了任何一种CI/CD工具。供应商之间的差异是相当小的。
我正在努力使我的Rails应用程序尽可能地保持无聊。在构建产品和解决客户问题方面有足够的复杂性,我不需要用异国的运营设置为自己和团队创造更多的工作。
我还在一家定制软件咨询公司工作。我帮助其他公司运送产品和工具,所以少了一个需要处理的外部服务是很有吸引力的。
建立CircleCI并不难,但它多了一个需要注册的账户,多了一个放置公司信用卡的地方,多了一个我的客户可能会问**"嘿,这又是干什么的?"的东西。**
此外,我喜欢我们可以让CI管道靠近代码。在当前的CI/CD工具的浪潮中,最大的好处之一是向基础设施即代码的转变:你用一个平面文件来配置你的构建,并与你的项目一起提交。这比在维护不善的Jenkins实例中摸索要好得多,对管道的修改可以像代码库的其他部分一样被审查和合并。
GitHub Actions把这一点推到了极致。现在,你有一个单一的地方来处理项目的源代码、问题跟踪器、项目管理(GitHub Projects)、代码审查、安全警报,以及现在。CI/CD测试。一个账户,一个服务,一个地方。
从CircleCI迁移到GitHub Actions的普通Rails设置
如果你像大多数人一样,你可能在项目的第一周就根据thoughtbot的博文设置了CircleCI的配置,然后就再也没有碰过它。我也一样。
不过不用担心,从CircleCI 2.0的配置格式转移到GitHub Actions的语法是很简单的。
你的配置可能有点不同,但从概念上讲,一个Rails应用的虚拟CI/CD管道。
- 检查出最新版本的代码
- 设置一个包含Ruby、Node和一些浏览器测试工具的基础镜像
- 设置一个PostgreSQL数据库服务
- 安装依赖项(
bundler,yarn,npm)并缓存结果,以便在不改变时加快构建速度 - 设置测试数据库
- 运行任何链接器/检查器 (
rubocop,eslint,brakeman, 等等) - 运行测试
这些步骤按顺序运行,如果其中任何一个步骤失败,构建就会失败。你在GitHub的提交上得到一个红色的:x:,你不能合并你的PR,有人在Slack上对你大喊大叫,你知道这种情况。
为了在GitHub Actions上运行构建,我们只需将这些高级动作转换为匹配的工作流语法。
我发现一个有用的变化是,利用GitHub Actions允许平行作业作为单一工作流的一部分来运行的优势。这个功能让我们可以把打结器和测试作为单独的作业来运行,这两个作业都必须通过,整个构建才能通过。
大多数竞争对手在免费/廉价层上没有提供太多的并行化。虽然每个作业都要设置基本环境,但我们可以通过同时运行最后的步骤来节省几分钟时间。(注意:这种方法将使用更少的挂钟时间,但你将使用更多的总构建时间)。
以下是我目前的 GitHub Actions 工作流程配置。
# Put this in the file: .github/workflows/verify.yml
name: Verify
on: [push]
jobs:
linters:
name: Linters
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Setup Ruby and install gems
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Setup Node
uses: actions/setup-node@v1
with:
node-version: 10.13.0
- name: Find yarn cache location
id: yarn-cache
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: JS package cache
uses: actions/cache@v1
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install packages
run: |
yarn install --pure-lockfile
- name: Run linters
run: |
bin/rubocop --parallel
bin/stylelint
bin/prettier
bin/eslint
- name: Run security checks
run: |
bin/bundler-audit --update
bin/brakeman -q -w2
tests:
name: Tests
runs-on: ubuntu-latest
services:
postgres:
image: postgres:11
env:
POSTGRES_USER: myapp
POSTGRES_DB: myapp_test
POSTGRES_PASSWORD: ""
ports: ["5432:5432"]
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Setup Ruby and install gems
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Setup Node
uses: actions/setup-node@v1
with:
node-version: 10.13.0
- name: Find yarn cache location
id: yarn-cache
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: JS package cache
uses: actions/cache@v1
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install packages
run: |
yarn install --pure-lockfile
- name: Setup test database
env:
RAILS_ENV: test
PGHOST: localhost
PGUSER: myapp
run: |
bin/rails db:setup
- name: Run tests
run: bin/rspec
一些注意事项。
-
由于GitHub Actions是通用的自动化平台(而不仅仅是CI/CD),你需要告诉该动作何时运行:在这种情况下,我们希望它在每一个
push。如果你愿意,你可以进一步配置它,只在某些分支或文件上运行。 -
在 GitHub Actions 中,我们建议你不要使用平台提供的 Docker 镜像和一堆常见的环境工具设置(例如
circleci/ruby:2.6.3-node-browsers),而是要组成其他第一方的 "设置 "动作,将二进制文件链接进来。在这种情况下,我们使用ruby/setup-ruby和actions/setup-node来包括我们想要使用的ruby和node的特定版本。这些步骤非常快,因为它们基本上是链接到预先建立的语言二进制文件。 -
你可能注意到,我正在使用
ruby/setup-ruby(一个社区行动),而不是第一方GitHubactions/setup-ruby。当涉及到Ruby时,官方行动在功能和发布版本上都已经落后了。在GitHub行动的测试阶段,我们不得不完全停止使用行动,因为你无法轻易选择Ruby的具体次要发布版本,而官方行动在某些情况下,在支持最新的Ruby版本(包括安全版本)方面落后了几个月。ruby/setup-ruby动作非常好用--你可以选择任何版本和任何口味(jruby,truffleruby, 等等),它将在5秒内拉出一个预构建的二进制文件。此外,ruby/setup-ruby动作支持自动运行bundle install并缓存你的宝石,而你不必手动操作cache动作。 -
许多设置PostgreSQL服务的文章(包括官方的例子)包括额外的健康检查选项,以确保数据库在继续进行之前已经启动。根据我的经验,等待健康检查的时间要多花15-60秒。由于我们在尝试连接到数据库之前必须安装宝石和进行其他设置,我删除了它们以减少运行时间,而且我没有遇到任何问题。至少可以考虑把例子中的健康间隔设置改成类似的。
--health-interval 10ms --health-timeout 500ms --health-retries 15.这使我的 "初始化容器 "步骤从30秒减少到10秒,在每一个BUILD。 -
实验一下
alpinePostgres Docker镜像。这些镜像使用的是一个精简的Linux系统,比基本的Docker镜像要小。在我的测试中,alpine镜像的下载和启动速度大约快33%。这样做的好处是,你可能无法运行与你的生产数据库完全相同的环境,但对于那些没有用Postgres做任何花哨事情的项目来说,对我来说,这似乎值得一试。要使用这些图像,在你的工作流程中用postgres:11-alpine替换例如postgres:11。你可以在Dockerhub上找到所有官方Postgres镜像的完整列表。我使用alpine镜像已经运行了~18个月,没有遇到任何奇怪的问题。 -
你应该检查你使用的各种工具,看看是否有选项可以加快CI环境下的运行时间。例如,
yarn有一个--pure-lockfile选项,告诉它不要试图创建一个锁文件(因为你已经检查了一个......),rubocop有一个--parallel标志,以节省一点时间。当涉及到CI构建时,在这里和那里节省几秒钟很快就会增加。
总体印象
我花了一个晚上设置工作流程,目的是让它与我们现有的CircleCI设置的功能相同,我能够实现这个目标。
也就是说,我不是一个DevOps专家--我只是想建立一个坚实的管道,以便我可以专注于更重要的事情。
GitHub Actions是一个引人注目的选择,值得成为社区的默认选项。让GitHub成为你的仓库、问题跟踪器和构建服务器的一站式场所是非常有意义的。少了一个外部服务就意味着少了一些精神上的负担,一个更 "无聊 "的设置可以让我们专注于快速发货和解决客户问题,而不是做集成工作。
如果我开始一个新项目,我绝对会开始使用GitHub Actions作为我的默认CI服务。
2020年2月更新
本文已经更新:原稿是在GitHub Actions测试期间写的,许多需要改进的地方(即缓存)都得到了修复
2021年3月更新
本文已更新:改用ruby/setup-ruby动作的内置捆绑缓存选项