使用Travis进行持续集成

1. 持续集成

1.1. 持续集成的概念

Continuous Integration is a software development practice where members of a team integrate their work frequently, usually each person integrates at least daily - leading to multiple integrations per day. Each integration is verified by an automated build (including test) to detect integration errors as quickly as possible. Many teams find that this approach leads to significantly reduced integration problems and allows a team to develop cohesive software more rapidly. ——Martin Fowler

Martin Fowler关于持续集成的完整文章,可以从这里获取。

1.2. 持续集成的要素

  1. 统一的代码库
  2. 自动构建
  3. 自动测试
  4. 每次代码提交后都会触发一次构建
  5. 可以很容易地获取最新可执行的程序
  6. 自动化部署

现在使用Travis CI我们可以很容易的实现持续集成。

2. 使用Travis CI 实现自动测试

Travis CI是在软件开发领域中的一个在线的,分布式的持续集成服务,用来构建及测试在GitHub托管的代码。Travis只能在GitHub项目上使用,只要项目有改动,Travis就会为其提供一个运行环境,执行测试、构建甚至部署任务。下面将演示如何配置Node.js项目的自动测试。

2.1. 为项目开启Travis

进入Travis,使用GItHub账号登录,点击右上角的头像,进入Profile。选择项目,打开开关即可。

2.2. 基本配置

点击项目名和开关之间的齿轮可以进入设置页,在这里你可以进行相关的配置,不过大多数情况下保持默认即可。

2.2.1. 基础设置

建议打开Build only if .travis.yml is present可以避免在没有配置文件的时候进行无意义的构建工作。此外也可以配置是否对分支、PR进行构建,并限制并行数量。

2.2.2. 自动取消

建议开启,开启后如果队列中积压了大量的构建任务(比如一次性提交了大量的commit),那么Travis只会对最新的commit进行构建,避免过长的等待时间。要注意的是,自动取消只会对等待中的任务生效,也就是说,如果你的任务正在运行,那么即使有更新的任务出现,当前任务也不会被取消。

2.2.3. 环境变量与计划任务

在这里你可以定义任务中可以随时使用的环境变量,在本文的后续内容中也会有环境变量的简单使用。

此外,你还可以设置计划任务,让Travis定时对你的项目进行测试和构建。

2.3. 编写.travis.yml

首先假设你的node.js项目已经编写完成,并且可以通过npm test运行测试命令。

在项目根目录,创建文件.travis.yml

.travis.yml
1
2
3
4
5
language: node_js
node_js:
- '9'
install: npm install
script: npm test

这个配置文件描述了Travis将如何工作。首先我们将语言选定为了node.js并指定node.js的版本号为9.x.x,当然你可以指定多个版本号,Travis将在每个版本中都对你的代码进行构建和测试。

此外我们还定义了两个过程:installscript,它们分别告诉了Travis如何安装依赖,如何进行测试。

将这个文件推上GitHub,登录Travis,你会发现它正在自动的为你的项目进行构建、下载依赖并进行测试。如果测试通过,测试结果在GitHub的上有相应的标注。

至此,我们就利用Travis完成了自动测试,你在项目中的每一次改动,包括commitbranchpull request,Travis都会按照.travis.yml的配置进行测试。

2.4. 生命周期

除了上述的installscript,Travis还提供了大量的钩子函数

  • install:依赖安装
  • script:测试(如果install失败则不运行)
  • deploy:部署(如果script失败则不运行)
  • before_install:install之前运行
  • before_script:script之前运行
  • after_failure:script失败后运行
  • after_success:script成功后运行
  • before_deploy:deploy之前运行
  • after_deploy:deploy之后运行
  • after_script:在整个过程的最后执行

完整的构建生命周期如下:

1
2
3
4
5
6
7
8
9
before_install
install
before_script
script
after_success or after_failure
before_deploy
deploy
after_deploy
after_script

生命周期中的每一个钩子函数都可以定义多个脚本,例如

1
2
3
install:
- command1
- command2

此时当执行到install阶段时,install中的每一个命令都会被执行。且即使command1失败了,command2也会被执行。如果希望command1失败后不执行command2,可以用以下方法:

1
install: command1 && command2

3. 使用Travis CI 实现自动部署

自动测试完成后,我们希望将测试通过的版本自动部署到服务器上。基本的思路是,让Travis登录服务器,并在服务器上运行部署脚本。

3.1. Travis登录服务器

假设服务器地址是1.2.3.4。最简单的方法就是使用SSH Key登录。

3.1.1. 生成SSH Key

具体可见通过SSH Key登录Linux,这里只展示简单的命令:

1
2
3
4
5
# 生成key和key.pub两个文件
ssh-keygen -f key

# 将公钥上传到服务器
ssh-copy-id -i key.pub user@1.2.3.4

3.1.2. 配置

将当前目录下的key文件复制到项目根目录中

.travis.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
language: node_js
node_js:
- '9'
addons:
ssh_known_hosts: 1.2.3.4

install: npm install
script: npm test

before_deploy:
- eval "$(ssh-agent -s)"
- chmod 600 key
- ssh-add key

deploy:
provider: script
script: ssh -i key user@1.2.3.4

这样我们就可以让Travis登录上我们的服务器,但是如果项目是在GitHub上开源的,那么相当于登录服务器的私钥也公开了,这当然是不可接受的。因此,我们将使用一种更加安全的方法。

3.2. 更加安全地登录服务器

为了避免私钥公开,我们可以使用Travis的加密方法对私钥进行加密。

3.2.1. 下载Travis命令行工具

查看这里了解如何安装Travis,由于Windows环境比较复杂,推荐使用linux环境,Windows用户可以使用WSL(WSL相关教程见WSL安装与SSH配置)。

运行下列命令安装Travis CLI

1
2
3
4
5
6
7
8
# 安装ruby
sudo apt install ruby ruby-dev
# 安装travis
gem install travis -v 1.8.8 --no-rdoc --no-ri

# 测试
travis version
# > 1.8.8

3.2.2. 登录Travis

1
2
3
4
5
# 登录Travis
travis login
# 确认登录
travis whoami
# > You are xxx

3.2.3. 加密私钥文件

现在我们假设私钥文件key.travis.yml都处于项目根目录

1
2
3
4
5
# cd到key所在的目录(比如项目根目录)
cd path/to/key

# 加密文件
travis encrypt-file key --add

加密完后,当前目录下会增加一个key.enc文件,即key加密后的文件。请注意,不要将key上传到GitHub,取而代之的是key.enc

另外进入Travis网站中,项目的设置页面,可以看到新增的环境变量

并且.travis.yml中会增加一行这样的配置

.travis.yml
1
2
before_install:
- openssl aes-256-cbc -K $encrypted_57510f08f3ae_key -iv $encrypted_57510f08f3ae_iv

这里$xxxx就是引用了环境变量,并使用openssl对私钥文件进行解密。

3.3. .travis.yml配置

.travis.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
language: node_js
node_js:
- '9'

install: npm install

script: npm test

addons:
ssh_known_hosts: 119.29.252.110

before_deploy:
- openssl aes-256-cbc -K $encrypted_57510f08f3ae_key -iv $encrypted_57510f08f3ae_iv
-in deploy_rsa.enc -out deploy_rsa -d
- eval "$(ssh-agent -s)"
- chmod 600 key
- ssh-add key

deploy:
provider: script
script: bash deploy.sh
skip_cleanup: true
on:
branch: master

注意到这里我们新增了几行:

  • before_deploy中的第一个命令是用于解密私钥文件key
  • skip_cleanup是为了避免部署阶段一些文件被清除,具体可以参阅官方文档
  • on branch master这里是指定了只有master分支上的改动才需要进行部署
  • bash deploy.sh请见下一节

3.4. 编写部署脚本

为了便于管理,我们让deploy阶段运行我们的部署脚本deploy.sh,那么该如何编写这个脚本呢?

非常简单:

1
2
3
4
5
6
ssh -i key user@1.2.3.4 << eeooff

command1
command2

eeooff

按照上面的格式,将需要再服务端上运行的命令放在两个eeooff中间即可。