编写代码只是软件开发的一小部分,更多的时间往往花在构建(build)和测试(test)。

为了提高软件开发的效率,构建和测试的自动化工具层出不穷。Travis CI 就是这类工具之中,市场份额最大的一个。

本文介绍 Travis CI 的基本用法。用好这个工具不仅可以提高效率,还能使开发流程更可靠和专业化,从而提高软件的价值。而且,它对于开源项目是免费的,不花一分钱,就能帮你做掉很多事情。

一、什么是持续集成?

Travis CI 提供的是持续集成服务(Continuous Integration,简称 CI)。它绑定 Github 上面的项目,只要有新的代码,就会自动抓取。然后,提供一个运行环境,执行测试,完成构建,还能部署到服务器。

持续集成指的是只要代码有变更,就自动运行构建和测试,反馈运行结果。确保符合预期以后,再将新代码"集成"到主干。

持续集成的好处在于,每次代码的小幅变更,就能看到运行结果,从而不断累积小的变更,而不是在开发周期结束时,一下子合并一大块代码。

二、使用准备

Travis CI 只支持 Github,不支持其他代码托管服务。这意味着,你必须满足以下条件,才能使用 Travis CI。

  • 拥有 GitHub 帐号
  • 该帐号下面有一个项目
  • 该项目里面有可运行的代码
  • 该项目还包含构建或测试脚本

如果这些条件都没问题,就可以开始使用 Travis CI 了。

首先,访问官方网站 travis-ci.org,点击右上角的个人头像,使用 Github 账户登入 Travis CI。

Travis 会列出 Github 上面你的所有仓库,以及你所属于的组织。此时,选择你需要 Travis 帮你构建的仓库,打开仓库旁边的开关。一旦激活了一个仓库,Travis 会监听这个仓库的所有变化。

三、.travis.yml

Travis 要求项目的根目录下面,必须有一个.travis.yml文件。这是配置文件,指定了 Travis 的行为。该文件必须保存在 Github 仓库里面,一旦代码仓库有新的 Commit,Travis 就会去找这个文件,执行里面的命令。

这个文件采用 YAML 格式。下面是一个最简单的 Python 项目的.travis.yml文件。

1
2
3

language: python
script: true

上面代码中,设置了两个字段。language字段指定了默认运行环境,这里设定使用 Python 环境。script字段指定要运行的脚本,script: true表示不执行任何脚本,状态直接设为成功。

Travis 默认提供的运行环境,请参考官方文档 。目前一共支持31种语言,以后还会不断增加。

下面是一个稍微复杂一点的.travis.yml

1
2
3
4
5

language: python
sudo: required
before_install: sudo pip install foo
script: py.test

上面代码中,设置了四个字段:运行环境是 Python,需要sudo权限,在安装依赖之前需要安装foo模块,然后执行脚本py.test

四、运行流程

Travis 的运行流程很简单,任何项目都会经过两个阶段。

  • install 阶段:安装依赖
  • script 阶段:运行脚本

4.1 install 字段

install字段用来指定安装脚本。

1
2

install: ./install-dependencies.sh

如果有多个脚本,可以写成下面的形式。

1
2
3
4

install:
- command1
- command2

上面代码中,如果command1失败了,整个构建就会停下来,不再往下进行。

如果不需要安装,即跳过安装阶段,就直接设为true

1
2

install: true

4.2、script 字段

script字段用来指定构建或测试脚本。

1
2

script: bundle exec thor build

如果有多个脚本,可以写成下面的形式。

1
2
3
4

script:
- command1
- command2

注意,scriptinstall不一样,如果command1失败,command2会继续执行。但是,整个构建阶段的状态是失败。

如果command2只有在command1成功后才能执行,就要写成下面这样。

1
2

script: command1 && command2

4.3 实例:Node 项目

Node 项目的环境需要写成下面这样。

1
2
3
4

language: node_js
node_js:
- "8"

上面代码中,node_js字段用来指定 Node 版本。

Node 项目的installscript阶段都有默认脚本,可以省略。

  • install默认值:npm install
  • script默认值:npm test

更多设置请看官方文档

4.4 部署

script阶段结束以后,还可以设置通知步骤(notification)和部署步骤(deployment),它们不是必须的。

部署的脚本可以在script阶段执行,也可以使用 Travis 为几十种常见服务提供的快捷部署功能。比如,要部署到 Github Pages,可以写成下面这样。

1
2
3
4
5
6
7

deploy:
provider: pages
skip_cleanup: true
github_token: $GITHUB_TOKEN # Set in travis-ci.org dashboard
on:
branch: master

其他部署方式,请看官方文档

4.5 钩子方法

Travis 为上面这些阶段提供了7个钩子。

  • before_install:install 阶段之前执行
  • before_script:script 阶段之前执行
  • after_failure:script 阶段失败时执行
  • after_success:script 阶段成功时执行
  • before_deploy:deploy 步骤之前执行
  • after_deploy:deploy 步骤之后执行
  • after_script:script 阶段之后执行

完整的生命周期,从开始到结束是下面的流程。

  1. before_install
  2. install
  3. before_script
  4. script
  5. aftersuccess or afterfailure
  6. [OPTIONAL] before_deploy
  7. [OPTIONAL] deploy
  8. [OPTIONAL] after_deploy
  9. after_script

下面是一个before_install钩子的例子。

1
2
3
4

before_install:
- sudo apt-get -qq update
- sudo apt-get install -y libxml2-dev

上面代码表示before_install阶段要做两件事,第一件事是要更新依赖,第二件事是安装libxml2-dev。用到的几个参数的含义如下:-qq表示减少中间步骤的输出,-y表示如果需要用户输入,总是输入yes

4.6 运行状态

最后,Travis 每次运行,可能会返回四种状态。

  • passed:运行成功,所有步骤的退出码都是0
  • canceled:用户取消执行
  • errored:before_installinstallbefore_script有非零退出码,运行会立即停止
  • failed :script有非零状态码 ,会继续运行

五、使用技巧

5.1 环境变量

.travis.ymlenv字段可以定义环境变量。

1
2
3
4
5

env:
- DB=postgres
- SH=bash
- PACKAGE_VERSION="1.0.*"

然后,脚本内部就使用这些变量了。

有些环境变量(比如用户名和密码)不能公开,这时可以通过 Travis 网站,写在每个仓库的设置页里面,Travis 会自动把它们加入环境变量。这样一来,脚本内部依然可以使用这些环境变量,但是只有管理员才能看到变量的值。具体操作请看官方文档

5.2 加密信息

如果不放心保密信息明文存在 Travis 的网站,可以使用 Travis 提供的加密功能。

首先,安装 Ruby 的包travis

1
2

$ gem install travis

然后,就可以用travis encrypt命令加密信息。

在项目的根目录下,执行下面的命令。

1
2

$ travis encrypt SOMEVAR=secretvalue

上面命令中,SOMEVAR是要加密的变量名,secretvalue是要加密的变量值。执行以后,屏幕上会输出如下信息。

1
2

secure: ".... encrypted data ...."

现在,就可以把这一行加入.travis.yml

1
2
3
4

env:
global:
- secure: ".... encrypted data ...."

然后,脚本里面就可以使用环境变量$SOMEVAR了,Travis 会在运行时自动对它解密。

travis encrypt命令的--add参数会把输出自动写入.travis.yml,省掉了修改env字段的步骤。

1
2

$ travis encrypt SOMEVAR=secretvalue --add

详细信息请看官方文档

5.3 加密文件

如果要加密的是文件(比如私钥),Travis 提供了加密文件功能。

安装命令行客户端以后,使用下面的命令登入 Travis CI。

1
2

$ travis login

然后,进入项目的根目录,使用travis encrypt-file命令加密那些想要加密的文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

$ travis encrypt-file bacon.txt

encrypting bacon.txt for rkh/travis-encrypt-file-example
storing result as bacon.txt.enc
storing secure env variables for decryption

Please add the following to your build script (before_install stage in your .travis.yml, for instance):

openssl aes-256-cbc -K $encrypted_0a6446eb3ae3_key -iv $encrypted_0a6446eb3ae3_key -in bacon.txt.enc -out bacon.txt -d

Pro Tip: You can add it automatically by running with --add.

Make sure to add bacon.txt.enc to the git repository.
Make sure not to add bacon.txt to the git repository.
Commit all changes to your .travis.yml.

上面的代码对文件bacon.txt进行加密,加密后会生成bacon.txt.enc,该文件需要提交到代码库。此外,该命令还会生成一个环境变量$encrypted_0a6446eb3ae3_key,保存密钥,储存在 Travis CI,文件解密时需要这个环境变量。你需要把解密所需的openssl命令,写在.travis.ymlbefore_install字段里面。这些都写在上面的命令行提示里面。

--add参数可以自动把环境变量写入.travis.yml

1
2
3
4
5
6
7
8
9
10

$ travis encrypt-file bacon.txt --add

encrypting bacon.txt for rkh/travis-encrypt-file-example
storing result as bacon.txt.enc
storing secure env variables for decryption

Make sure to add bacon.txt.enc to the git repository.
Make sure not to add bacon.txt to the git repository.
Commit all changes to your .travis.yml.

详细信息请看官方文档,实际的例子可以参考下面两篇文章。

(完)

留言與分享

GitHub Actions 是 GitHub 的持续集成服务,于2018年10月推出

这些天,我一直在试用,觉得它非常强大,有创意,比 Travis CI 玩法更多。

本文是一个简单教程,演示如何使用 GitHub Actions 自动发布一个 React 应用到 GitHub Pages

一、GitHub Actions 是什么?

大家知道,持续集成由很多操作组成,比如抓取代码、运行测试、登录远程服务器,发布到第三方服务等等。GitHub 把这些操作就称为 actions。

很多操作在不同项目里面是类似的,完全可以共享。GitHub 注意到了这一点,想出了一个很妙的点子,允许开发者把每个操作写成独立的脚本文件,存放到代码仓库,使得其他开发者可以引用。

如果你需要某个 action,不必自己写复杂的脚本,直接引用他人写好的 action 即可,整个持续集成过程,就变成了一个 actions 的组合。这就是 GitHub Actions 最特别的地方。

GitHub 做了一个官方市场,可以搜索到他人提交的 actions。另外,还有一个 awesome actions 的仓库,也可以找到不少 action。

上面说了,每个 action 就是一个独立脚本,因此可以做成代码仓库,使用userName/repoName的语法引用 action。比如,actions/setup-node就表示github.com/actions/setup-node这个仓库,它代表一个 action,作用是安装 Node.js。事实上,GitHub 官方的 actions 都放在 github.com/actions 里面。

既然 actions 是代码仓库,当然就有版本的概念,用户可以引用某个具体版本的 action。下面都是合法的 action 引用,用的就是 Git 的指针概念,详见官方文档

1
2
3
4

actions/setup-node@74bc508 # 指向一个 commit
actions/[email protected] # 指向一个标签
actions/setup-node@master # 指向一个分支

二、基本概念

GitHub Actions 有一些自己的术语。

(1)workflow (工作流程):持续集成一次运行的过程,就是一个 workflow。

(2)job (任务):一个 workflow 由一个或多个 jobs 构成,含义是一次持续集成的运行,可以完成多个任务。

(3)step(步骤):每个 job 由多个 step 构成,一步步完成。

(4)action (动作):每个 step 可以依次执行一个或多个命令(action)。

三、workflow 文件

GitHub Actions 的配置文件叫做 workflow 文件,存放在代码仓库的.github/workflows目录。

workflow 文件采用 YAML 格式,文件名可以任意取,但是后缀名统一为.yml,比如foo.yml。一个库可以有多个 workflow 文件。GitHub 只要发现.github/workflows目录里面有.yml文件,就会自动运行该文件。

workflow 文件的配置字段非常多,详见官方文档。下面是一些基本字段。

(1)name

name字段是 workflow 的名称。如果省略该字段,默认为当前 workflow 的文件名。

1
2

name: GitHub Actions Demo

(2)on

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

1
2

on: push

上面代码指定,push事件触发 workflow。

on字段也可以是事件的数组。

1
2

on: [push, pull_request]

上面代码指定,push事件或pull_request事件都可以触发 workflow。

完整的事件列表,请查看官方文档。除了代码库事件,GitHub Actions 也支持外部事件触发,或者定时运行。

(3)on.<push|pull_request>.<tags|branches>

指定触发事件时,可以限定分支或标签。

1
2
3
4
5

on:
push:
branches:
- master

上面代码指定,只有master分支发生push事件时,才会触发 workflow。

(4)jobs.<job_id>.name

workflow 文件的主体是jobs字段,表示要执行的一项或多项任务。

jobs字段里面,需要写出每一项任务的job_id,具体名称自定义。job_id里面的name字段是任务的说明。

1
2
3
4
5
6

jobs:
my_first_job:
name: My first job
my_second_job:
name: My second job

上面代码的jobs字段包含两项任务,job_id分别是my_first_jobmy_second_job

(5)jobs.<job_id>.needs

needs字段指定当前任务的依赖关系,即运行顺序。

1
2
3
4
5
6
7

jobs:
job1:
job2:
needs: job1
job3:
needs: [job1, job2]

上面代码中,job1必须先于job2完成,而job3等待job1job2的完成才能运行。因此,这个 workflow 的运行顺序依次为:job1job2job3

(6)jobs.<job_id>.runs-on

runs-on字段指定运行所需要的虚拟机环境。它是必填字段。目前可用的虚拟机如下。

  • ubuntu-latestubuntu-18.04ubuntu-16.04
  • windows-latestwindows-2019windows-2016
  • macOS-latestmacOS-10.14

下面代码指定虚拟机环境为ubuntu-18.04

1
2

runs-on: ubuntu-18.04

(7)jobs.<job_id>.steps

steps字段指定每个 Job 的运行步骤,可以包含一个或多个步骤。每个步骤都可以指定以下三个字段。

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

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 命令。

四、实例:React 项目发布到 GitHub Pages

下面是一个实例,通过 GitHub Actions 构建一个 React 项目,并发布到 GitHub Pages。最终代码都在这个仓库里面,发布后的参考网址为ruanyf.github.io/github-actions-demo

第一步,GitHub Actions 目前还处在测试阶段,需要到这个网址申请测试资格。申请以后,可能需要几天才能通过。据说,2019年11月就会放开。

获得资格后,仓库顶部的菜单会出现Actions一项。

第二步,这个示例需要将构建成果发到 GitHub 仓库,因此需要 GitHub 密钥。按照官方文档,生成一个密钥。然后,将这个密钥储存到当前仓库的Settings/Secrets里面。

上图是储存秘密的环境变量的地方。环境变量的名字可以随便起,这里用的是ACCESS_TOKEN。如果你不用这个名字,后面脚本里的变量名也要跟着改。

第三步,本地计算机使用create-react-app,生成一个标准的 React 应用。

1
2
3

$ npx create-react-app github-actions-demo
$ cd github-actions-demo

然后,打开package.json文件,加一个homepage字段,表示该应用发布后的根目录(参见官方文档)。

1
2

"homepage": "https://[username].github.io/github-actions-demo",

上面代码中,将[username]替换成你的 GitHub 用户名,参见范例

第四步,在这个仓库的.github/workflows目录,生成一个 workflow 文件,名字可以随便取,这个示例是ci.yml

我们选用一个别人已经写好的 action:JamesIves/github-pages-deploy-action,它提供了 workflow 的范例文件,直接拷贝过来就行了(查看源码)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

name: GitHub Actions Build and Deploy Demo
on:
push:
branches:
- master
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@master

- name: Build and Deploy
uses: JamesIves/github-pages-deploy-action@master
env:
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
BRANCH: gh-pages
FOLDER: build
BUILD_SCRIPT: npm install && npm run build

上面这个 workflow 文件的要点如下。

  1. 整个流程在master分支发生push事件时触发。
  2. 只有一个job,运行在虚拟机环境ubuntu-latest
  3. 第一步是获取源码,使用的 action 是actions/checkout
  4. 第二步是构建和部署,使用的 action 是JamesIves/github-pages-deploy-action
  5. 第二步需要四个环境变量,分别为 GitHub 密钥、发布分支、构建成果所在目录、构建脚本。其中,只有 GitHub 密钥是秘密变量,需要写在双括号里面,其他三个都可以直接写在文件里。

第五步,保存上面的文件后,将整个仓库推送到 GitHub。

GitHub 发现了 workflow 文件以后,就会自动运行。你可以在网站上实时查看运行日志,日志默认保存30天。

等到 workflow 运行结束,访问 GitHub Page,会看到构建成果已经发上网了。

以后,每次修改后推送源码,GitHub Actions 都会自动运行,将构建产物发布到网页。

五、参考链接

(完)

留言與分享

  • 第 1 頁 共 1 頁
作者的圖片

Kein Chan

這是獨立全棧工程師Kein Chan的技術博客
分享一些技術教程,命令備忘(cheat-sheet)等


全棧工程師
資深技術顧問
數據科學家
Hit廣島觀光大使


Tokyo/Macau