介绍
Concourse CI 是一个现代的、可扩展的持续集成系统,旨在使用可组合的声明性语法自动化测试管道。 在之前的指南中,我们 在 Ubuntu 16.04 服务器 上安装了 Concourse,并且 使用 Let's Encrypt 的 SSL 证书保护了 Web UI。
在本指南中,我们将演示如何在将新更改提交到存储库时使用 Concourse 自动运行项目的测试套件。 为了演示,我们将为使用 Node.js Web 框架 Hapi.js 编写的“hello world”应用程序配置一个持续集成管道。
为了确保构建和测试过程始终与它们关联的代码保持同步,我们将 CI 定义添加到应用程序存储库本身。 之后,我们将使用 Concourse 的 fly
命令行工具将管道加载到 Concourse 中。 最后,我们会将更改推送回存储库,以便更永久地保存它们并在新的 CI 工作流程中启动新的测试。
先决条件
在开始之前,您需要一台 Ubuntu 16.04 服务器 ,至少 1G 内存 。 完成以下指南以设置非 root 用户、安装和配置 Concourse、安装 Nginx、获取 TLS/SSL 证书以及设置 Concourse Web UI 的安全反向代理。 您将需要一个指向您的 Concourse 服务器的 域名 以正确保护它:
- 使用 Ubuntu 16.04 进行初始服务器设置
- 如何在 Ubuntu 16.04 上安装 Concourse CI
- 如何在 Ubuntu 16.04 上安装 Nginx
- 如何在 Ubuntu 16.04 上使用 Let's Encrypt 保护 Nginx
- 如何在 Ubuntu 16.04 上使用 Nginx 使用 SSL 保护 Concourse CI
在本教程中,大部分工作将在您的本地计算机上完成,而不是在 Concourse 服务器上完成。 因此,您还需要确保本地机器上有一些工具可用。 您将需要一个文本编辑器(您可能会在各种操作系统中找到一些示例,例如 nano
、vim
、TextEdit、Sublime Text、Atom 或记事本)来创建和修改存储库中的文件。 您还需要在本地系统上安装和设置 Git,您可以按照我们的 贡献于开源:Git 入门 指南来完成。
设置好 Concourse 服务器并在本地计算机上安装 Git 和文本编辑器后,继续下面的操作。
在本地安装 Fly 命令行工具
当我们在先决条件中在服务器上安装 Concourse 时,我们在服务器上安装了 fly
命令行工具,以便我们可以从命令行管理 Concourse 实例。 但是,对于日常使用,在本地系统上安装 fly
二进制文件的副本会更方便,因为您可以使用常用的开发工具和源代码。
要获取与您的服务器版本匹配的 fly
的本地副本,请在 Web 浏览器中访问您的 Concourse 实例:
https://your_concourse_url
如果您已注销或当前未配置管道,窗口中央将显示各种平台的 fly
下载链接:
如果您已登录并配置了管道,则屏幕右下角将提供 fly
的下载链接:
单击代表本地计算机操作系统的图标以下载 fly
二进制文件。
接下来,按照平台特定说明在本地系统上设置 fly
。
Linux 或 macOS
如果您的本地计算机运行 Linux 或 macOS,请在下载相应的二进制文件后按照这些说明进行操作。
首先,将下载的二进制文件标记为可执行文件。 我们假设您已将文件下载到 ~/Downloads
目录,因此如有必要,请调整下载位置:
chmod +x ~/Downloads/fly
接下来,通过键入以下命令将二进制文件安装到 PATH 中的某个位置:
sudo install ~/Downloads/fly /usr/local/bin
您可以通过键入以下内容来验证可执行文件是否可用:
fly --version
Output3.3.1
如果能显示版本,说明fly
安装成功。
视窗
如果您的本地计算机运行 Windows,请按键盘上的 Windows 键,键入 powershell,然后按 ENTER。
在出现的窗口中,通过键入以下命令创建一个 bin
文件夹:
mkdir bin
接下来,通过键入以下命令将 fly.exe
文件从 Downloads
文件夹移动到新的 bin
文件夹:
mv Downloads/fly.exe bin
检查您是否已有可用的 PowerShell 配置文件:
Test-Path $profile
如果响应是 True
,则您已经有一个配置文件。
如果响应是 False
,您需要通过键入以下内容来创建一个:
New-Item -path $profile -type file -force
Output Directory: C:\User\Sammy\Documents\WindowsPowerShell Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 7/9/2017 5:46 PM 0 Microsoft.PowerShell_profile.ps1
获得个人资料后,使用编辑器对其进行编辑:
notepad.exe $profile
在编辑器窗口(如果您必须创建配置文件,它将是空白的),添加以下行:
Microsoft.PowerShell_profile.ps1
$env:path += ";C:\Users\Sammy\bin"
完成后保存并关闭文件。
接下来,将当前用户的执行策略设置为“RemoteSigned”,以允许 PowerShell 读取配置文件:
Set-ExecutionPolicy -scope CurrentUser RemoteSigned
最后,通过键入以下命令获取 PowerShell 配置文件:
. $profile
您现在应该能够从任何位置调用 fly.exe
可执行文件。 通过让二进制打印其版本来测试它:
fly.exe --version
Output3.3.1
在本指南中,您需要将 fly
命令的每个实例替换为 fly.exe
以匹配 Windows 命令。
使用 Concourse 服务器进行身份验证
安装 fly
后,登录到您的远程 Concourse 服务器,以便您可以在本地管理您的 CI 环境。 单个 fly
二进制文件可用于联系和管理多个 Concourse 服务器,因此该命令使用称为“目标”的概念作为标签来标识您要向其发送命令的服务器。
在本指南中,我们使用 main 作为我们的 Concourse 服务器的目标名称,但您可以替换任何您想要的目标名称。 在 -c
选项之后输入您的 Concourse 服务器的域名,并使用 https://
协议规范来指示您的服务器位置:
fly -t main login -c https://example.com
系统将提示您输入您在 Concourse 服务器上的 /etc/concourse/web_environment
文件中配置的用户名和密码:
Outputlogging in to team 'main' username: sammy password: target saved
一旦您通过身份验证,fly
工具将创建一个名为 ~/.flyrc
的配置文件来存储您的凭据以供将来使用。
注意:如果你以后升级Concourse的版本,你可以通过键入以下命令安装fly
命令的匹配版本:
fly -t main sync
这将更新系统上的 fly
二进制文件,同时保持配置不变。
分叉和克隆示例存储库
现在您已经在系统上设置了 fly
,我们可以继续设置我们将用于演示 Concourse 管道的存储库。
在您的网络浏览器中,访问我们将作为示例的 GitHub 上的 “hello hapi”应用程序。 这个应用程序是一个简单的“hello world”程序,带有一些单元和集成测试,使用 Hapi.js,一个 Node.js Web 框架编写。
由于此示例用于演示各种持续集成系统,您可能会注意到一些用于为其他系统定义管道的文件。 对于 Concourse,我们将在我们自己的存储库分支中创建持续集成管道。
要创建存储库的分支,请登录 GitHub 并导航到 项目存储库 。 单击右上角的 Fork 按钮,在您的帐户中制作存储库的副本:
如果您是 GitHub 组织的成员,系统可能会询问您要将存储库分叉到哪里。 选择帐户或组织后,存储库的副本将添加到您的帐户中。
接下来,在本地计算机上的终端中,移动到您的主目录:
cd $HOME
使用以下命令将存储库克隆到本地计算机,替换为您自己的 GitHub 用户名:
git clone git@github.com:your_github_user/hello_hapi
将在您的主目录中创建一个名为 hello_hapi
的新目录。 进入新目录开始:
cd hello_hapi
我们将为此存储库中的示例项目定义一个持续集成管道。 在进行任何更改之前,最好在 Git 中创建并切换到一个新分支以隔离我们的更改:
git checkout -b pipeline
OutputSwitched to a new branch 'pipeline'
现在我们有了一个新的分支,我们可以开始定义我们的持续集成管道。
为应用程序设置持续集成过程
我们将在项目存储库本身中定义我们的管道及其所有相关文件。 这有助于确保持续集成过程始终与其测试的代码保持同步。
测试套件已在名为 test
的目录中定义。 它包括一个单元测试和两个基本集成测试。 运行测试的命令定义在 scripts
对象内名为 test
的 package.json
文件中。 在安装了 npm
和 Node.js 的环境中,您可以通过键入 npm test
来运行测试(在使用 npm install
安装项目依赖项之后)。 这些是我们需要在管道中复制的程序。
首先,在存储库中创建一个名为 ci
的目录来存放项目的持续集成资产。 我们还将创建两个名为 ci/tasks
和 ci/scripts
的子目录来保存管道引用的各个任务定义和任务调用的脚本。
通过键入以下内容创建必要的目录结构:
mkdir -p ci/{tasks,scripts}
接下来,我们可以开始创建 Concourse 将使用的单个文件。
定义管道
使用文本编辑器在 ci
目录中创建并打开一个名为 pipeline.yml
的文件(我们将在本指南中展示 nano
编辑器,但您应该用文本编辑器替换您的系统)。 如扩展名所示,Concourse 文件使用 YAML 数据序列化格式 定义:
nano ci/pipeline.yml
我们现在可以开始设置我们的管道。
定义 NPM 缓存资源类型
在文件中,我们将首先定义一个新的资源类型:
ci/pipeline.yml
--- resource_types: - name: npm-cache type: docker-image source: repository: ymedlop/npm-cache-resource tag: latest
为了将持续集成中的过程与通过系统的数据分离,Concourse 将所有状态信息卸载到称为 资源 的抽象中。 资源是 Concourse 可以用来从中提取信息或向其推送信息的外部数据源。 这就是所有数据进入持续集成系统的方式,以及所有数据在作业之间共享的方式。 Concourse 不提供任何用于在作业之间在内部存储或传递状态的机制。
resource_types 标题允许您定义可以在管道中使用的新型资源,例如电子邮件通知、Twitter 集成或 RSS 提要。 我们定义的新资源类型告诉 Concourse 如何使用 npm-cache-resource,这是一种作为 Docker 映像提供的资源,允许 Concourse 安装 Node.js 项目的依赖项并在作业之间共享它们.
定义存储库和缓存资源
接下来,我们需要为管道定义实际资源:
ci/pipeline.yml
. . . resources: - name: hello_hapi type: git source: &repo-source uri: https://github.com/your_github_user/hello_hapi branch: master - name: dependency-cache type: npm-cache source: <<: *repo-source paths: - package.json
本节定义了 Concourse CI 作业完成其任务所需的两个资源。 Concourse 使用资源定义来观察上游系统的变化,并了解如何在工作需要时提取资源。 默认情况下,Concourse 每分钟检查一次每个资源的新版本。 当新版本可用时,需要设置了“触发器”选项的资源的作业将自动启动新构建。
第一个资源代表您在 GitHub 上的 hello_hapi
存储库的分支。 “源”行包含一个名为“repo-source”的 YAML 锚 ,它标记元素以供将来参考。 这让我们可以在文档后面的不同位置包含元素的内容(“uri”和“branch”定义)。
第二个资源,称为“dependency-cache”,使用我们定义的“npm-cache”资源类型来下载项目的依赖项。 在此资源的“源”规范中,我们使用 <<: *repo-source
行来 reference 和 extend &repo-source
锚点指向的元素。 这会将我们的应用程序存储库资源中的 uri 和分支设置插入到第二个资源中。 一个名为“paths”的附加元素指向定义项目依赖关系的 package.json
文件。
定义依赖收集和测试作业
最后,我们使用 Concourse jobs 定义实际的持续集成过程:
ci/pipeline.yml
. . . jobs: - name: Install dependencies plan: - get: hello_hapi trigger: true - get: dependency-cache - name: Run tests plan: - get: hello_hapi trigger: true passed: [Install dependencies] - get: dependency-cache passed: [Install dependencies] - task: run the test suite file: hello_hapi/ci/tasks/run_tests.yml
在本节中,我们定义了两个作业,每个作业都包含一个名称和一个计划。 反过来,我们的每个计划都包含“获取”和“任务”元素。 task 项指定如何执行操作,而 get 项指示任务的资源依赖关系。
第一个作业没有任何任务语句。 这有点不寻常,但是当我们查看它在做什么以及如何使用它时,它是有道理的。 第一个 get 语句需要 hello_hapi
资源并指定 trigger: true
选项。 这告诉 Concourse 每次在 hello_hapi
存储库中检测到新的提交时,自动获取存储库并开始新构建此作业。
第一个作业中的第二个 get 语句 (get: dependency-cache
) 需要我们定义的资源,用于下载和缓存项目的 Node.js 依赖项。 此语句评估 package.json
文件中的要求并下载它们。 如果没有为此作业定义任务,则不会执行其他操作,但下载的依赖项将可用于后续作业。
注意:在这个特定的例子中,只有一个额外的作业,所以缓存 Node.js 依赖项作为一个独立步骤的好处并没有完全实现(将 get 语句添加到测试作业中以下就足以下载依赖项)。 但是,几乎所有使用 Node.js 的工作都需要项目依赖项,因此如果您有可能并行完成的单独作业,单独依赖项缓存的好处会变得更加明显。
第二个作业(name: Run tests
)首先声明了相同的依赖项,但有一个显着的区别。 “通过”约束导致 get 语句仅匹配已成功遍历管道中先前步骤的资源。 这就是如何形成作业之间的依赖关系以将管道流程链接在一起。
在 get 语句之后,定义了一个名为“运行测试套件”的任务。 它没有定义完成内联的步骤,而是告诉 Concourse 从它获取的存储库中的文件中提取定义。 接下来我们将创建这个文件。
完成后,完整的管道应如下所示:
ci/pipeline.yml
--- resource_types: - name: npm-cache type: docker-image source: repository: ymedlop/npm-cache-resource tag: latest resources: - name: hello_hapi type: git source: &repo-source uri: https://github.com/your_github_user/hello_hapi branch: master - name: dependency-cache type: npm-cache source: <<: *repo-source paths: - package.json jobs: - name: Install dependencies plan: - get: hello_hapi trigger: true - get: dependency-cache - name: Run tests plan: - get: hello_hapi trigger: true passed: [Install dependencies] - get: dependency-cache passed: [Install dependencies] - task: run the test suite file: hello_hapi/ci/tasks/run_tests.yml
完成后保存并关闭文件。
定义测试任务
虽然管道定义概述了我们持续集成过程的结构,但它将实际测试任务的定义推迟到另一个文件。 提取任务有助于保持管道定义简洁且易于阅读,但确实需要您阅读多个文件才能理解整个过程。
在 ci/tasks
目录下打开一个名为 run_tests.yml
的新文件:
nano ci/tasks/run_tests.yml
要定义任务,您需要指定工作人员需要拥有的操作系统类型,定义用于运行任务的映像,命名任务将使用的任何输入或输出,并指定要运行的命令。
粘贴以下内容来设置我们的测试任务:
ci/tasks/run_tests.yml
--- platform: linux image_resource: type: docker-image source: repository: node tag: latest inputs: - name: hello_hapi - name: dependency-cache run: path: hello_hapi/ci/scripts/run_tests.sh
在上面的配置中,我们指定这个任务需要一个 Linux worker。 Concourse 服务器本身无需额外配置即可满足此要求。
接下来,我们指示工作人员将用于运行任务的图像。 尽管您可以创建和使用自己的镜像类型,但实际上,这几乎总是一个 Docker 镜像。 由于我们的存储库是一个 Node.js 应用程序,我们选择最新的“节点”映像来运行我们的测试,因为它已经安装了适当的工具。
Concourse 任务可以指定输入和输出以指示它需要访问的资源以及它将产生的工件。 输入对应于之前在“作业”级别下拉的资源。 这些资源的内容作为可以在任务运行期间操作的顶级目录提供给任务环境。 在这里,应用程序存储库将在 hello_hapi
目录下可用,Node.js 依赖项将在名为 dependency-cache
的目录下可用。 您的执行步骤可能需要在任务开始时将文件或目录移动到其预期位置,并在任务结束时将工件放置在输出位置。
最后,run 项列出了要运行的命令的 path。 每个任务只能是带有参数的单个命令,因此虽然可以通过组合 bash 字符串来构建内联命令,但更常见的是将任务指向脚本文件。 在这种情况下,我们指向位于 hello_hapi/ci/scripts/run_tests.sh
的 hello_hapi
输入目录中的脚本。 接下来我们将创建这个脚本。
完成后保存并关闭文件。
定义测试脚本
最后,我们需要创建任务将执行的脚本。 打开位于 ci/scripts/run_tests.sh
的名为 run_tests.sh
的新文件:
nano ci/scripts/run_tests.sh
该脚本将操纵测试环境的输入以将项目移动到正确的位置。 然后它将通过运行 npm test
来运行存储库中定义的测试套件。
将以下内容粘贴到新文件中:
ci/scripts/run_tests.sh
#!/usr/bin/env bash set -e -u -x mv dependency-cache/node_modules hello_hapi cd hello_hapi && npm test
首先,我们指出这个脚本应该由 Docker 容器的 bash
解释器执行。 set
选项修改 shell 的默认行为,以导致任何错误或未设置的变量停止脚本执行并在执行每个命令时打印它们。 这些有助于使脚本更安全,并为调试目的提供更大的可见性。
我们运行的第一个命令将位于 node_modules
目录中的缓存依赖项从 dependency-cache
目录中移动到 hello_hapi
目录中。 请记住,这两个目录都是可用的,因为我们在任务定义中将它们指定为输入。 这个新位置是 npm
将在其中查找所需下载的依赖项的位置。
之后,我们进入应用程序存储库并运行 npm test
来执行定义的测试套件。
完成后,保存并关闭文件。
在继续之前,将新脚本标记为可执行文件,以便它可以直接运行:
chmod +x ci/scripts/run_tests.sh
我们的管道和所有相关文件现在都已经定义好了。
在大厅中设置管道
在我们将 pipeline
分支合并回 main
并将其推送到 GitHub 之前,我们应该继续将我们的管道加载到 Concourse。 Concourse 将监视我们的存储库是否有新的提交,并在检测到更改时运行我们的持续集成程序。
虽然我们需要手动加载管道,但当 Concourse 执行管道时,它会从存储库中的目录中读取任务和脚本。 对管道本身的任何更改都需要重新加载到 Concourse 中才能生效,但是由于我们没有内联定义所有内容,因此在将任务或脚本作为提交的一部分上传时会自动注意到它们的更改。
要设置新管道,请使用 set-pipeline
操作使用 fly 命令定位您的 Concourse 服务器。 我们需要使用 -p
选项传递新管道的名称,并使用 -c
选项传递管道配置文件:
fly -t main set-pipeline -p hello_hapi -c ci/pipeline.yml
在继续之前,系统将提示您确认配置。 输入 y 并点击 ENTER:
Output. . . apply configuration? [yN]: y pipeline created! you can view your pipeline here: https://example.com/teams/main/pipelines/hello_hapi the pipeline is currently paused. to unpause, either: - run the unpause-pipeline command - click play next to the pipeline in the web ui
如输出所示,管道已被接受但当前已暂停。 您可以使用 fly
或 Web UI 取消暂停管道。 我们将使用 Web UI。
在您的网络浏览器中,访问您的 Concourse 服务器并登录。 您应该会看到以可视方式定义的新管道:
待处理的作业由灰色框表示,资源是较小的黑色块。 由资源变化触发的作业用实线连接,非触发资源用虚线连接。 资源流 out 个作业表明已在下一个作业上设置了 passed
约束。
蓝色标题表示管道当前已暂停。 点击左上角的【X10X】菜单图标【X23X】(三条堆叠的水平线)打开菜单。 您应该会看到管道条目(如果管道不可见,您可能需要注销并重新登录)。 单击管道旁边的蓝色 play 图标以取消暂停:
管道现在应该已取消暂停并将开始运行。
一开始,各种资源和作业可能会变成橙色,表示发生了错误。 发生这种情况是因为需要下载各种 Docker 映像,并且仍然需要将 pipeline
分支合并到我们存储库的 main
分支中,以使任务和脚本文件可用。
提交对 Git 的更改
现在定义了持续集成过程,我们可以将其提交到我们的 git
存储库并将其添加到 Concourse。
通过键入以下命令将新的 ci
目录添加到暂存区:
git add ci
通过检查状态来验证要提交的文件:
git status
OutputOn branch pipeline Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: ci/pipeline.yml new file: ci/scripts/run_tests.sh new file: ci/tasks/run_tests.yml
通过键入以下内容提交更改:
git commit -m 'Add Concourse pipeline'
这些更改现在已提交到我们的 pipeline
分支。 我们可以通过切换分支和合并将分支合并回 master
分支:
git checkout master git merge pipeline
现在,将带有新更改的 master
分支推送回 GitHub:
git push origin master
提交将在 60 秒内启动新的构建,并且 Concourse 将在拉下更改后访问管道任务和脚本。
查看新版本
回到 Concourse Web UI,一个新的构建将在下一分钟内开始通过管道进行:
黄色轮廓表示作业当前正在进行中。 要监控进度,请单击 Run tests 作业以查看当前输出。 作业完成后,完整的输出将可用,作业应变为绿色:
单击 主页图标 返回主管道屏幕。 每个作业的绿色状态表示最近一次提交已通过管道的所有阶段:
管道将继续监视存储库并在提交更改时自动运行新测试。
结论
在本指南中,我们设置了一个 Concourse 管道来自动监控存储库的更改。 当检测到更改时,Concourse 会下载最新版本的存储库并使用 Docker 容器来安装和缓存项目依赖项。 然后构建进入测试阶段,在该阶段复制依赖项并运行存储库的测试套件以检查是否引入了任何重大更改。
Concourse 提供了很大的灵活性和功能来定义独立的测试程序并将它们存储在存储库本身中。 如果你想了解更多关于如何在你自己的项目中使用 Concourse,查看官方文档。