持续集成和交付实用手册(三)
原文:
zh.annas-archive.org/md5/D4B1782DB08166E400DEF5DF3D2E1241译者:飞龙
第十一章:Travis CI UI Logging and Debugging
本章将概述 Travis 作业日志和作业日志中的各个部分。本章还将解释如何以几种不同的方式调试 Travis 构建作业,包括使用 Docker 在本地构建,然后以调试模式运行构建。我们将介绍所有获取作业 ID 的不同方式,以及如何在公共存储库中启用调试模式,然后使用 Travis API 以调试模式启动构建。我们将解释如何使用tmate,这是一个终端复用器,然后我们将讨论在 Travis Web 客户端中记录环境变量。最后,我们将介绍如何在 Travis CI 中使用 Heroku 进行部署以及如何调试部署失败。
本章将涵盖以下主题:
-
Travis Web 客户端概述
-
使用 Docker 在本地进行调试构建
-
以调试模式运行构建
-
Travis Web UI Logging
-
Travis CI 部署概述和调试
技术要求
这一章将需要一些基本的 Unix 编程技能以及一些 bash 脚本知识。对于如何进行 RESTful API 调用的基本理解将会有所帮助,因为我们将使用 curl 作为 REST 客户端来调用 Travis API。对于 Docker 和容器的基本理解也会有所帮助,因为我们将使用 Docker 来运行本地构建。
Travis Web 客户端概述
我们在第九章中简要介绍了 Travis CI 的 Web 仪表板,Travis CI 的安装和基础知识,但让我们再次看看 UI 的不同部分。
主仪表板概述
Travis CI Web 客户端有几个必须理解的不同部分:
在左侧分割的部分,您可以单独点击您感兴趣的每个存储库。此外,您可以按名称搜索存储库,因为您或您所属的组织可能拥有许多存储库。还请注意,有关上次在项目中运行的最后一个构建以及它是否通过或失败的详细信息,以及有关持续时间和上次运行构建的详细信息。
在右侧分割的部分,您将找到 Travis Web 客户端的主要导航组件。请注意,这里有几个导航链接,例如当前构建,这是您转到存储库时打开的默认链接。如果单击 Branches 链接,您将看到在所有不同分支上触发的所有构建,包括拉取请求。让我们推送一个新分支并在multiple-languages(github.com/packtci/multiple-languages)存储库中创建一个拉取请求,看看新的构建如何运行:
请注意,Travis CI 为我们推送的名为add-test-case的新分支创建了一个新的构建:
此外,您打开的任何拉取请求也将触发 Travis CI 的新构建:
当您将拉取请求合并到另一个分支时,Travis CI 会触发另一个 CI 构建。
工作日志概述
Travis CI 中的作业日志以构建系统配置信息开始:
请注意,构建语言设置为go,构建操作系统为 Ubuntu Trusty 14.04:
Travis CI 克隆了multiple-languages存储库的新副本,这是持续集成的重要方面。请记住,CI 构建应该在每次构建时构建一个新副本,并且不应该有任何假设的环境变量:
请注意,Travis CI 为我们设置了一些环境变量,包括GOPATH和PATH环境变量。Travis CI 运行go version命令来验证 CI 构建中是否安装了 Go 版本 1.10:
在 CI 构建的这一步中,我们安装了 Node.js 作为我们的第二编程语言。这是可选的,但请注意,Travis CI 在before_install右侧有一个构建标签,这是我们在第九章中讨论的构建步骤之一,Travis CI 的安装和基础知识,在构建自定义部分。还要注意,在before_install和install生命周期标签的右侧,有一个时间戳,显示了构建步骤实际花费的时间,以人类可读的格式显示为2.55秒和2.88秒,分别对应before_install和install生命周期事件:
请注意,这里没有脚本构建生命周期的构建标签,因为这是 CI 构建的主要部分。
任何其他生命周期事件,如after_success和after_script生命周期事件,都将有一个构建标签和一个时间戳。
使用 Docker 在本地调试构建
您可以通过拉取文档链接中保存的 Docker 镜像来在本地调试构建,链接为 Troubleshooting Locally in a Docker Image (docs.travis-ci.com/user/common-build-problems/#Troubleshooting-Locally-in-a-Docker-Image)。您可以在此链接中找到安装 Docker 的说明(docs.docker.com/install/)。
- 拉取 Go Docker 镜像:
docker pull travisci/ci-garnet:packer-1512502276-986baf0
请注意,我们运行docker pull命令来实际拉取 Docker 镜像
- 启动交互式 Docker 会话:
请注意,我们在分离模式下运行了一个交互式 shell 会话
- 在正在运行的容器中打开登录 shell:
docker exec -it travis-debug bash -l
此命令使用 Bash shell 启动一个与正在运行的 Docker 容器的交互式 shell 会话
- 切换到 Travis 用户:
su - travis
在此命令中,我们切换到 Travis 用户,而不是默认的 root 用户
- 将
multiple-languagesGit 存储库克隆到主目录中:
git clone --depth=50 --branch=master https://github.com/packtci/multiple-languages
cd multiple-languages
此命令将我们的multiple-languages存储库克隆到本地的 Docker 容器中,然后切换到此目录
- 检出我们想要在本地测试的 Git 提交。
运行git log命令并找到我们想要在本地检出的提交。很可能是我们将要检查的顶级 Git 提交。
git log
git checkout 2a663fc233d3ae3986fd99efc510369ded92ba94
在这一步中,我们要确保只测试与我们想要测试的更改相对应的更改。
- 安装库依赖和第二编程语言:
NODE_VERSION="6"
nvm install $NODE_VERSION
npm install
在这一步中,我们使用node 版本管理器(nvm)安装 Node.js 作为第二编程语言,然后运行npm install命令来安装所有库依赖项
- 运行脚本构建步骤。
在下面的截图中,我们在本地 Docker 容器中运行go test和npm test命令,以模拟脚本构建生命周期事件:
以调试模式运行构建
另一种调试构建时间问题的技术是在 Travis CI 中运行调试构建。您需要发送电子邮件至support@travis-ci.com,以为公共存储库切换此功能,而私有存储库默认启用调试模式。原因是任何人都可以遇到包含 SSH 访问的日志,然后可以连接到虚拟机,然后可能读取秘密环境信息,例如客户端 ID、密码等。
从个人资料页面获取 API 令牌
要通过 API 重新启动调试模式的作业,您需要向作业的调试端点发送一个 POST 请求。此请求需要通过将您的 Travis CI API 令牌添加到授权标头来进行身份验证。您可以在 Travis CI 个人资料页中找到您的 API 令牌,用于公共项目。
您需要访问一个 URL,例如 travis-ci.org/profile/packtci。然后您需要在个人资料页面中复制您的 API 令牌,如下所示:
接下来,您需要使用 REST 客户端和 API 令牌来访问调试端点。
使用 Travis CLI 获取令牌
您可以使用 Travis CLI 运行以下命令来获取访问令牌:
travis token
从构建日志中获取作业 ID
您可以通过展开“构建系统信息”选项卡并查找“作业 ID”标签来获取作业 ID。在下面的截图中,有一个箭头指向“作业 ID”:
从“查看配置”按钮的 URL 中获取作业 ID
如果您点击“查看配置”按钮,URL 将会改变,您可以从 URL 中复制作业 ID。在下面的截图中,我们点击了“查看配置”按钮,如下所示:
然后 URL 改变为这个配置:travis-ci.org/packtci/multiple-languages/jobs/401101740/config。
在此 URL 中,作业 ID 是 401101740。
通过对 /builds 端点进行 API 调用获取作业 ID
您还可以通过调用 Travis API 中的 /builds 端点来获取作业 ID。您需要发起一个 GET 请求,并提供有效的访问令牌以进行 REST 调用。以下是使用 curl REST 客户端的示例请求:
curl -s -X GET \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "Travis-API-Version: 3" \
-H "Authorization: token $(travis token)" \
-d '{ "quiet": true }' \
https://api.travis-ci.org/builds
这将获取与存储库关联的所有构建,这可能是一个大的 JSON 负载。您可以使用 jq (stedolan.github.io/jq/) 命令行 JSON 处理器来过滤出作业 ID 信息。以下是相同的 REST 调用,将 JSON 负载传输到 jq 命令行实用程序以过滤出与构建对应的作业 ID:
通过 API 调用启动调试模式的构建作业
只要您拥有有效的访问令牌,您可以使用任何 REST 客户端来调用 Travis API。
以下是针对作业 ID 40110174 的调试端点的示例 REST 调用:
请注意,在此截图中,我们添加了 Authorization HTTP 标头,并使用 Travis CLI 通过 Bash 字符串插值打印出我们的访问令牌:
Authorization: token $(travis token)
还要注意我们使用的是 api.travis-ci.org 的公共 Travis 端点。
获取调试模式的 SSH 会话
如果您返回 Travis web UI 并查看当前作业日志,您将看到以下内容:
现在,您只需转到命令提示符或终端会话,并输入 ssh 命令以启动与当前构建的交互式调试会话:
调试模式的 SSH 会话只会保持 30 分钟,然后您需要发起另一个 API 调用来开始另一个调试会话:
Travis 调试模式便利 Bash 函数
以下是可用的便利 Bash 函数列表:
-
travis_run_before_install对应 before_install 生命周期事件 -
travis_run_install对应 install 生命周期事件 -
travis_run_before_script对应 before_script 生命周期事件 -
travis_run_script对应 script 生命周期事件 -
travis_run_after_success对应 after_success 生命周期事件 -
travis_run_after_failure对应于after_failure生命周期事件 -
travis_run_after_script对应于after_script生命周期事件
在下面的截图中,我们运行travis_run_before_install函数:
请记住,这是在before_install生命周期事件中指定的内容,在multiple-languages存储库中:
before_install:
- nvm install $NODE_VERSION
现在我们将运行travis_run_install便利 Bash 函数,该函数安装了在 Travis install生命周期事件中指定的库依赖项:
在multiple-languages存储库的 Travis YML 脚本中,我们有以下条目:
install:
- npm install
请注意,这正是在运行travis_run_install便利函数时运行的内容。
接下来,我们运行travis_run_script便利函数,该函数运行在 Travis script生命周期事件中定义的任何脚本:
在multiple-languages存储库的 Travis YML 脚本中,我们在script生命周期事件中有以下条目:
script:
- go test
- npm test
如果我们指定了其他生命周期事件,我们可以使用剩余的便利 Bash 函数。
tmate shell 会话操作
SSH shell 会话使用了 tmux 的分支(github.com/tmux/tmux),这是一个名为 tmate 的终端复用程序(tmate.io/),您可以使用它打开窗口,滚动历史记录等。
- 如果您按下Control-b ,您将能够上下滚动您的命令历史记录
![
-
要退出历史滚动模式,只需按下字母q。
-
如果您按下Control-b c,您将创建一个可以使用的新窗口。
-
如果您按下Control-b [0..9],则可以在您创建的任何新窗口之间切换。请注意,这里的括号意味着,例如,Control-b 0,Control-b 1 等,以切换窗口会话。
Travis Web UI 日志
您当然可以在 Travis CI 中记录一些环境变量,但要小心,不要在日志中记录秘密信息。
Travis CI 采取的步骤来保护您的特定于环境的变量
Travis CI 默认会隐藏诸如令牌和环境变量之类的任何变量,并简单地显示字符串[secure]。
如果您转到构建#3 travis-ci.org/packtci/puppeteer-headless-chrome-travis-yml-script/builds/398696669),您将看到以下条目:
请记住,我们在此存储库中添加了以下加密环境变量第十章中的Travis CI CLI 命令和自动化:
travis encrypt SECRET_VALUE=SuperSecret12345 --add
请注意,此命令将以下条目添加到 Travis YML 脚本中:
env:
global:
secure:
WLiuzi0CTx/ta5zuoU5K2LeZgzrAhWATUjngx++Azz7Tw4+XqbxeHZ/6ITymE1YLDRMxdIh8hItvkoNCbPmJ6q1To6bdirloWZq2rlZ5BPGYfVY3cuoUuxTAz1uhhfnngkqd76eJfB4lBUfOIVNAg2rpI7QFAQr1aiIKxjthiTms57fR4dusEi/efVO90I7yzFtyxEa0tLTgW9x+dPSt2ApmJ0EP9tftk7M7Uw/F2Gm1/AzWpM1Blklm/iEHF3ZY6Ij/V+ZG2SCpfrF88m50a8nJF1a+KttZz/TTbwqA58dXNokxcD30HB468/oaGMTJxYLFmG3QMfbXuP2wUkuinIEWQxGBEDh3uw11ZhypCGVNvE6vbRpdIIzywcVcX95G1px+Dgcil+c8AebO1wbWlDXMuWNQHC7JjdQspvLUtsLeyyei3LKshTY7LktvhJEG/+sgd5sejeqnzFmLmC9TdbCazLMFWzqhl+SBcmQtFNVuqAGBlMFlT1l54zFnZl7mixetVeBziuS7xGG3XXm0BsYIQnkcJYxNGv8JrFMSoqBTdQV4C20UyyXAw8s+5lu6dGziiMPSUK4KUSVPJ3hyeNiGhLTBsJn4bnTPiJ5ilVdyNM8RD8X2EJRImT3uvGvuFqHraCBrBuZVaW4RtbGX0JYYtMMMr/P84jKrNC3iFD8=
请记住,Travis 作业日志中只显示字符串[secure]代替此环境变量。
Travis CI 部署概述和调试
我们在第三章,持续交付的基础知识中讨论了软件部署,但是为了回顾一下,部署是开发人员创建的软件的最终产品,您的最终用户将使用它。部署通常在成功的 CI/CD 流水线结束时完成。请记住,CI/CD 流水线可以包括提交阶段,在该阶段构建任何二进制文件并运行单元测试套件,然后是第二阶段,可能运行集成测试,然后可能是第三阶段,包括负载测试和/或安全测试,最后是第四阶段,包括一套验收测试。只有当所有 CI/CD 流水线的阶段都成功完成时,才应启动部署流水线。
在 Travis CI 中部署相对容易/请记住,您可以使用 Travis CLI 轻松设置一些部署工具。
Travis CI 中支持的提供商
以下是一些支持的提供商,您可以在 Travis CI 中用于部署:
-
AWS CodeDeploy (
docs.travis-ci.com/user/deployment/codedeploy/) -
AWS Elastic Beanstalk (
docs.travis-ci.com/user/deployment/elasticbeanstalk/) -
AWS Lambda (
docs.travis-ci.com/user/deployment/lambda/) -
Azure Web App (
docs.travis-ci.com/user/deployment/azure-web-apps/) -
Bluemix CloudFoundry (
docs.travis-ci.com/user/deployment/bluemixcloudfoundry/) -
Chef Supermarket (
docs.travis-ci.com/user/deployment/chefsupermarket/) -
CloudFoundry (
docs.travis-ci.com/user/deployment/cloudfoundry/) -
GitHub Pages (
docs.travis-ci.com/user/deployment/pages/) -
GitHub Releases (
docs.travis-ci.com/user/deployment/releases/) -
Google App Engine (
docs.travis-ci.com/user/deployment/google-app-engine/) -
Google Cloud Storage (
docs.travis-ci.com/user/deployment/gcs/) -
Google Firebase (
docs.travis-ci.com/user/deployment/firebase/) -
OpenShift (
docs.travis-ci.com/user/deployment/openshift/) -
Surge.sh (
docs.travis-ci.com/user/deployment/surge/)
有关支持的提供商的完整列表,请转到 Travis 用户文档(docs.travis-ci.com/user/deployment/#Supported-Providers)。
Travis CI 中的 Heroku 设置
我们可以使用 Travis CLI 来帮助我们在multiple-languages (github.com/packtci/multiple-languages)存储库中设置 Heroku (www.heroku.com/platform)。
我们需要做的第一步是确保我们已经使用 Heroku CLI 登录到 Heroku,您可以在devcenter.heroku.com/articles/heroku-cli#download-and-install下载并安装。一旦我们登录,我们将获得一个访问令牌,我们可以使用:
请注意,我们使用了heroku auth:token命令来打印出我们的访问令牌。
现在我们只需要使用travis setup命令进行设置:
请注意,由于我们已经登录到 Heroku,我们不需要提供访问令牌,travis setup命令足够智能,可以为我们抓取它。
travis setup 命令会自动更新我们的 Travis YML 脚本,添加 Heroku 提供商信息,现在我们的 Travis TML 脚本看起来是这样的:
language: go
go:
- '1.10'
env:
- NODE_VERSION="6"
before_install:
- nvm install $NODE_VERSION
install:
- npm install
script:
- go test
- npm test
deploy:
provider: heroku
api_key:
secure: ueVMBom+3LHS4xhXXi9hbPR8FIIS/z01Z7NW4hngea4WRHq3gU8AY70xz25w/FshMPtaHeCUdZ90eDDvLF5/hwI+9zup/XI4gONiTTOpxpiY3EyHkP2frra0sdSQhYBHETsq4hEQxODE83ClQjx2jCKM3LOTdzI6wrKXpI5UtoD73yIa7AbKCxl8IXGIeNePImyLe6Wl7ovfxq1zcXz5c6Tu6uIqO2VwkvILrQKB41Id6VQN1MpfY1kQMASuRwaiJQ8HCmi0NP8A067v0s83OM9bNVK+KXDTLsVyrovnpidUnVS/Gk2QDNz0Or5xEIM2iXCsQDoa8jGNSCNfPcXq3aYtl2hjgDSVnz28EoxYRBmx365UxzwRVpsgdf1b+sCfd9FBJge7xZqTCGwimoBJvrQH0qvgYzQ855EvmtEyBU5t0JRmU8x/Z74KryO24YHD/hSY0a1REPCnZqjBkBS5FHQprIJm5XQabwU/IOqPMdM1KvMYj34N+dxK0X92sf0TLSAv3/62oquQ7Lkhjl4nAsEa05v+kQNMQdLemYFBZi8/Qf6a4YQPNmLXmKwis1FLTzicccwPE8qJ2H3wPQRQUUZVYQxgjUkh5ni6ikqCkxmZRnNJgCbTWhw3ip1xaWjmm6jtvMhiWiUr6vDgIbvbty120ySBIe3k2P5ARW77fOA=
app: multiple-languages
on:
repo: packtci/multiple-languages
在 Travis YML 脚本中调试失败
如果我们查看multiple-languages项目的构建 8.1(travis-ci.org/packtci/multiple-languages/jobs/403102478#L548),我们可以看到它出错了,正如屏幕截图所示,因为我们实际上在 Heroku 中没有名为multiple-languages的应用:
我们只需要在 Heroku 中创建一个名为multiple-languages的应用:
现在让我们使用travis restart命令在 Travis 中重新启动构建:
现在让我们再次查看构建 8.1 的作业日志:
现在,如果我们查看 Heroku 仪表板,我们可以确认我们的应用已成功部署到 Heroku:
总结
在本章中,我们介绍了 Travis 作业日志的概述,并解释了作业日志的不同部分。我们查看了如何使用 Docker 在本地运行构建,并学习了如何使用 Travis API 启用调试模式构建。然后,我们查看了 Travis CI 采取的步骤来保护作业日志中的秘密和密钥。最后,我们查看了如何使用 Travis CLI 部署应用程序,然后查看了如何调试构建失败并在 Travis CI 中获得成功部署。
在下一章中,我们将解释如何在软件项目中设置 Circle CLI,然后介绍 Circle CI UI 的基础知识。
问题
-
当您在 GitHub 中合并拉取请求时,是否会触发另一个构建?
-
在运行脚本生命周期事件中,Travis 作业日志是否显示标签?
-
我们如何在 Travis CI 中本地调试构建?
-
调试构建模式是否适用于公共存储库?
-
您将如何使用 Travis API 获取作业 ID?
-
在运行调试模式构建时,您可以使用哪个方便的 bash 函数来进行 before_install 生命周期事件?
-
您会使用哪个 Travis CLI 命令来设置添加 Heroku 等附加组件以进行部署?
进一步阅读
您可以在 Travis 用户文档中进一步探索调试选项和更高级的配置信息:docs.travis-ci.com/。
第十二章:CircleCI 的安装和基础知识
在上一章中,我们展示了如何在本地调试 Travis CI 项目,并更详细地解释了 Travis CI 的 Web 界面。我们还看了如何在 Travis CI 中进行日志记录。本章将帮助您设置 CircleCI,并解释如何创建 Bitbucket 帐户,以及如何在新的 CircleCI 帐户上设置 GitHub 和 Bitbucket。我们将在 Bitbucket 中创建一个简单的 Java 项目,并为其运行 CircleCI 构建。我们还将讨论如何浏览 Bitbucket 的用户界面。最后,我们将通过创建一个新的 GitHub 存储库来结束本章,并讨论一个 CircleCI YML 脚本,该脚本将通过 Docker 镜像安装 Golang 并运行我们的单元测试。
本章将涵盖以下主题:
-
CircleCI 的介绍
-
CircleCI 和 Jenkins 的比较
-
CircleCI 先决条件
-
在 GitHub 中设置 CircleCI
-
在 Bitbucket 中设置 CircleCI
-
CircleCI 配置概述
技术要求
本章将需要一些基本的编程技能,并且我们将利用本章将讨论的一些持续集成/持续交付概念。如果您尝试自己创建 Bitbucket 帐户和 CircleCI 帐户,将会很有帮助。您可以按照CircleCI 先决条件部分中的步骤进行操作。我们将使用 Maven 创建一个基本的 Java 应用程序,因此了解一些 Java 的基本编程概念将会很有帮助,但如果您了解任何编程语言,应该也能够跟上。基本的 Git 和 Unix 知识将非常有帮助。
CircleCI
CircleCI 是一个托管和自动化的持续集成(CI)构建解决方案。CircleCI 使用一个应用程序配置文件,使用 YAML(yaml.org/spec/1.2/spec.html)语法,例如 Travis YML 脚本,我们在第九章中讨论过,Travis CI 的安装和基础知识,到第十一章,Travis CI UI 日志和调试。由于 CircleCI 托管在云端,它具有快速在其他环境中设置的优势,以及在不同操作系统中使用而无需担心像 Jenkins CI 那样的设置和安装。因此,CircleCI 比 Jenkins 快得多。
比较 CircleCI 和 Jenkins
Jenkins 是一个自包含的开源自动化服务器,可以在组织级别进行定制和配置。还记得在 Jenkins CI 章节中,我们花了一些时间在 Windows、Linux 和 macOS 操作系统中安装 Jenkins。我们还可以根据自己的需求配置 Jenkins。虽然这对于在运营、DevOps 等方面拥有专门团队的软件公司来说非常好,但对于通常是孤独开发者为其个人项目设置环境的开源项目来说,情况就不那么理想了。
CircleCI 是围绕开源开发原则和易用性而设计的。在 GitHub 和 Bitbucket 平台上创建项目后,可以在几分钟内设置好 CircleCI。虽然在这方面 CircleCI 不像 Jenkins CI 那样可定制,但它具有快速设置的明显优势。CircleCI 使用应用程序配置文件,使用 YAML 语法,可以在 GitHub(github.com/)平台以及 Bitbucket(bitbucket.org/)平台上使用,不同于 Travis CI。
CircleCI 先决条件
要开始使用 CircleCI,您需要在github.com/创建 GitHub 帐户或在bitbucket.org/product创建 Bitbucket 帐户。
创建 GitHub 帐户
我们在第九章中详细介绍了如何创建 GitHub 帐户,在创建 GitHub 帐户部分。
创建 Bitbucket 帐户
我们将创建一个 Bitbucket 帐户,并再次使用用户名packtci作为我们的用户名:
点击绿色的继续按钮后,您将被重定向到以下页面:
您需要输入您的全名和密码,您在上一页提供的电子邮件地址已经为您设置好。点击绿色的继续按钮后,您将收到一个新 Bitbucket 帐户的验证电子邮件,类似于以下内容:
点击验证我的电子邮件地址按钮后,您将被重定向到以下页面:
您必须为您的新 Bitbucket 帐户提供一个唯一的用户名,因为您不能使用任何现有的用户名。点击继续按钮后,您将被路由到以下页面:
您可以通过点击跳过按钮来跳过此部分,或者您可以输入您的信息,然后点击提交按钮,您将被路由到以下页面:
创建 CircleCI 帐户
您需要创建一个 CircleCI 帐户才能开始使用 CircleCI,您可以使用您的 GitHub 登录凭据或 Bitbucket 登录凭据:
您需要点击注册按钮以创建一个新的 CircleCI 帐户,然后您将被重定向到以下页面:
您可以选择其中一个进行注册,但我们将选择*使用 Bitbucket 注册**。一旦您点击按钮,您将被重定向到以下页面:
我们将点击授予访问权限按钮,然后我们将被路由到以下页面:
请注意,我们在 CircleCI 中没有设置任何项目,稍后需要添加项目。
即使我们注册了新的 Bitbucket 帐户,我们仍然可以将我们的 GitHub 帐户连接到我们的新 CircleCI 帐户。您需要点击屏幕右上角的头像,然后点击用户设置按钮:
点击用户设置按钮后,您将被路由到显示帐户集成的页面。我们需要通过点击连接按钮将我们的 GitHub 帐户连接到 CircleCI:
点击连接按钮后,您将被重定向到一个类似于此的授权 CircleCI 应用程序页面:
点击授权 circleci 按钮后,您将被重定向到 CircleCI 仪表板页面,现在您将分别拥有两个与您的 GitHub 帐户和 Bitbucket 帐户对应的packtci帐户:
在 GitHub 中设置 CircleCI
让我们使用我们的packtci (github.com/packtci) GitHub 帐户,为 CircleCI 添加一个新项目functional-summer (github.com/packtci/functional-summer)。我们需要做的第一件事是在仪表板上点击 GitHub 的添加项目按钮:
点击添加项目按钮后,您将被路由到以下页面:
我们将点击functional-summer GitHub 存储库的设置项目按钮,并将被路由到一个类似这样的页面:
CircleCI 自动选择了 Node 作为我们的语言,因为我们有一个package.json文件,并且因为我们在这个存储库中有 JavaScript 文件。不过,我们还没有完成。如果您在此页面向下滚动,您将注意到一些启动 CircleCI 在我们项目中的下一步:
我们需要在项目的根目录中创建一个名为.circleci的文件夹,并在此文件夹中添加一个名为config.yml的文件。让我们使用 GitHub UI 创建这个文件夹和文件。我们将转到以下 URL:github.com/packtci/functional-summer。然后点击创建新文件按钮:
一旦我们点击此按钮,我们将被重定向到 GitHub UI 中的一个类似这样的页面:
输入我们文件夹的名称为.circleci,然后输入/字符,然后命名我们的文件为config.yml。完成后,它将如下所示:
现在我们需要为我们的config.yml文件输入内容,.circleci为我们提供了一个样本config.yml文件,其中包含我们可以用于新的 CircleCI 项目的值:
# Javascript Node CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-javascript/ for more details
#
version: 2
jobs:
build:
docker:
# specify the version you desire here
- image: circleci/node:7.10
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
# - image: circleci/mongo:3.4.4
working_directory: ~/repo
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "package.json" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- run: yarn install
- save_cache:
paths:
- node_modules
key: v1-dependencies-{{ checksum "package.json" }}
# run tests!
- run: yarn testSetup Circle CI in Atlassian Bitbucket
我们将在后面更详细地解释其中的内容,但现在我们将只需将其复制并粘贴到 GitHub UI 编辑器中,然后点击提交新文件按钮:
我们需要做的最后一步是回到 CircleCI 的添加项目页面,点击开始构建按钮,启动我们新配置的 CircleCI 项目:
这也会在 CircleCI 中设置一个 Webhook,以便 CircleCI 监听我们提交到 GitHub 的任何新代码更改。
一旦我们点击开始构建按钮,我们将被重定向到我们的第一个构建作业,使用 CircleCI 构建functional-summer存储库:
如果我们继续向下滚动,我们将在 CircleCI 应用程序中看到构建的每个步骤:
我们将在后面的章节中更详细地解释这一点,但每个步骤都可以展开以显示该步骤的详细信息。例如,如果我们点击 yarn test 步骤,我们将看到以下详细信息:
在 Bitbucket 中设置 CircleCI
由于我们刚刚创建了一个新的 Bitbucket 账户,我们需要将我们的 ssh 密钥上传到 Bitbucket,以便能够将更改推送到 Bitbucket。我们在第九章中介绍了如何创建 SSH 密钥,在安装和 Travis CI 基础章节中,向新 GitHub 账户添加 SSH 密钥部分,因此如果您还没有设置任何 SSH 密钥,请阅读该章节。我们已经在第九章,安装和 Travis CI 基础的向新 GitHub 账户添加 SSH 密钥部分创建了一个 SSH 密钥。我们只需要将公共 ssh 密钥复制到我们的系统剪贴板中,通过运行以下命令:
pbcopy < ~/.ssh/id_rsa_example.pub
一旦我们将公共 SSH 密钥复制到系统剪贴板中,我们需要转到 Bitbucket 的以下页面:
我们需要点击添加密钥按钮。这将打开一个模态窗口,我们在其中输入一个标签和我们的公钥的内容,看起来像这样:
然后点击添加密钥按钮,现在我们已经准备好将更改推送到我们的 Bitbucket 账户。
在 Bitbucket 中使用 CircleCI 构建设置新的 Java 项目
我们将通过点击左侧导航窗格中的加号按钮在 Bitbucket 中创建一个名为java-summer的新 Java 项目:
接下来,我们将单击“存储库”按钮,它看起来像这样:
接下来,我们将通过提供存储库名称,将我们的版本控制系统设置为 Git,然后单击“创建存储库”按钮来创建一个新存储库:
请注意,这里我们点击了可选的高级设置下拉菜单,并将我们的语言设置为 Java 编程语言。一旦我们点击创建存储库按钮,我们将被重定向到一个看起来像这样的页面:
我们将使用 Maven 构建工具创建一个新的 Java 项目,该项目具有一个带有主目录和测试子目录的src目录。我们在第七章中详细解释了如何安装和使用 Maven 构建工具,开发插件,因此如果您尚未安装 Maven 并且不知道如何使用它,请重新阅读第七章,开发插件中的适当部分。
要使用 Maven 创建我们的新 Java 项目,我们将发出以下命令:
mvn archetype:generate -DgroupId=com.packci.app -DartifactId=java-summer -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
我们首先通过在 shell 会话中发出以下命令来克隆我们的存储库:
git clone git@bitbucket.org:packtci/java-summer.git java-summer-proj
然后我们将复制克隆存储库中隐藏的.git目录的内容,并将其粘贴到我们用 Maven 构建工具创建的新java-summer文件夹中。假设我们有正确的路径结构,我们可以发出以下命令:
mv java-summer-proj/.git java-summer
然后我们可以删除java-summer-proj文件夹,然后cd进入java-summer文件夹。然后我们将使用 Java 语言示例配置,您可以在 CircleCI 文档的language-java (circleci.com/docs/2.0/language-java/)中找到。我们将创建一个名为.circleci的文件夹,然后创建一个名为config.yml的文件。
我们将提交我们的更改并使用以下命令将其推送到 Bitbucket:
git push
现在,如果您查看 CircleCI 应用程序,我们可以通过单击应用程序左上角的 packtci Bitbucket 用户帐户来切换到该用户帐户,它看起来像这样:
接下来,我们需要在左侧导航窗格中单击“添加项目”按钮,它看起来像这样:
然后我们需要单击“设置项目”按钮,以便 CircleCI 知道我们在 Bitbucket 中的java-summer存储库,它看起来像这样:
然后我们将被路由到设置项目页面,在这里我们需要选择我们的操作系统,默认情况下在 CircleCI 中为 Linux。然后我们选择我们的构建语言,在我们的情况下应该是 Java。为了清晰起见,我们将在以下截图中再次显示此页面:
然后我们将把 CircleCI 为我们提供的示例配置文件复制到.circleci/config.yml文件中:
# Java Maven CircleCI 2.0 configuration file # # Check https://circleci.com/docs/2.0/language-java/ for more details # version: 2
jobs:
build: docker: # specify the version you desire here
- image: circleci/openjdk:8-jdk
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images # documented at https://circleci.com/docs/2.0/circleci-images/ # - image: circleci/postgres:9.4 working_directory: ~/repo
environment:
# Customize the JVM maximum heap limit
MAVEN_OPTS: -Xmx3200m
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys: - v1-dependencies-{{ checksum "pom.xml" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- run: mvn dependency:go-offline
- save_cache:
paths: - ~/.m2
key: v1-dependencies-{{ checksum "pom.xml" }}
# run tests!
- run: mvn integration-test
接下来,我们将提交更改并将其推送到 Bitbucket 版本控制系统,然后我们需要滚动到“下一步”部分,然后简单地单击“开始构建”按钮,它看起来像这样:
这将触发我们对java-summer项目的第一次构建,并使 webhook 为存储库工作。一旦我们点击“开始构建”按钮,我们需要点击“作业”按钮,以查看我们触发的新构建:
现在,为了测试 Webhooks 是否监听 Bitbucket 中的代码更改,让我们对java-summer文件进行更改,以便它实际上有一个对值数组求和的函数,并使用 JUnit(junit.org/junit4/javadoc/latest/)添加一个单元测试用例。
让我们在应用文件中添加一个静态函数,就像这样:
public static int average(int[] numbers) {
int sum = 0;
for (int i = 0; i < numbers.length; i++) {
sum += numbers[i];
}
return sum; }
然后让我们添加一个测试用例来测试平均函数,就像这样使用 JUnit:
public void testaverage() {
App myApp = new App();
int[] numbers = {
1, 2, 3, 4, 5
};
assertEquals(15, myApp.average(numbers)); }
我们可以使用mvn package命令在本地测试更改,以确保没有出现问题,然后提交我们的更改并将这些更改推送到 Bitbucket 版本控制系统。我们现在应该注意到,由于我们对主分支的代码更改,CircleCI 自动触发了一个构建。
如果我们回到 CircleCI Web 应用程序,我们可以看到触发了一个新的构建,并且通过了:
请注意,在上面的屏幕截图中,CircleCI 显示第二次构建已经触发。它还显示了提交 SHA 哈希和提交消息,并确认构建成功。
CircleCI 配置概述
CircleCI 使用 YAML(yaml.org/spec/1.2/spec.html)作为其配置语言的数据序列化语言,Travis CI 也是如此。
CircleCI 配置概述
我们将在后面的章节中讨论 CircleCI 中的许多概念和配置选项,但是作为概述,让我们看一下基本的config.yml文件,并解释一些概念。我们将在 GitHub 中使用我们的packtci(github.com/packtci) Github 用户创建一个新的存储库。您可以在github.com/packtci/go-template-example-with-circle-ci找到新的存储库。我们还将在 Golang 中创建一个解析模板的函数。然后编写一个解析模板文本的测试用例,然后创建一个 CircleCI config.yml文件。我们将把这些代码更改推送到 GitHub,然后最终使用 CircleCI 设置这个新项目。
将源文件添加到新的存储库
在新的存储库中,我们添加了一个名为template.go的文件,这是我们将要测试的函数:
func parseTemplate(soldier Soldier, tmpl string) *bytes.Buffer { var buff = new(bytes.Buffer) t := template.New("A template file") t, err := t.Parse(tmpl) if err != nil { log.Fatal("Parse: ", err) return buff } err = t.Execute(buff, soldier) if err != nil { log.Fatal("Execute: ", err) return buff } return buff }
我们在template_test.go文件中添加了以下单元测试用例来测试parseTemplate函数:
func TestParseTemplate(t *testing.T) { newSoldier := Soldier{ Name: "Luke Cage", Rank: "SGT", TimeInService: 4, } txt := parseTemplate(newSoldier, templateText) expectedTxt := ` Name is Luke Cage Rank is SGT Time in service is 4 ` if txt.String() != expectedTxt { t.Error("The text returned should match") } }
然后我们将以下 CircleCI YML 脚本添加到存储库中:
version: 2 jobs: build: docker: - image: circleci/golang:1.9 working_directory: /go/src/github.com/packtci/go-template-example-with-circle-ci steps: - checkout - run: name: "Print go version" command: go version - run: name: "Run Unit Tests" command: go test
在 CircleCI YML 脚本中添加的第一件事是版本(circleci.com/docs/2.0/configuration-reference/#version)字段。这是一个必填字段,目前版本 1仍然受支持,但很快将被弃用,因此建议使用 CircleCI YML 语法的版本 2。您可以在以下 CircleCI 博客文章中了解更多信息:circleci.com/blog/sunsetting-1-0/。
在这个config.yml脚本中,我们接下来要讨论的是作业(circleci.com/docs/2.0/configuration-reference/#jobs)字段,它由一个或多个命名作业组成。在我们的情况下,我们有一个名为 build 的作业,如果我们不使用 workflows 字段,则需要这个构建作业。我们将在后面的章节中更详细地讨论这个问题。
然后我们有一个名为docker的字段,其中包含了 Golang 的语言镜像。我们还可以有一个服务镜像来运行特定的服务,这将在后面的章节中讨论。
然后我们有一个名为steps的字段,它定义了我们想要在 CircleCI 构建中执行的步骤。请注意,steps字段中有三个字段条目,分别是checkout和两个run (circleci.com/docs/2.0/configuration-reference/#jobs) 命令。run 命令有一个名称和一个命令,但您也可以省略名称,只给出一个命令。
新存储库的 CircleCI 构建作业
以下截图显示 CircleCI 构建已通过:
以下是构建作业中的步骤:
请注意,这里有一个额外的步骤称为 Spin up Environment。此步骤创建一个新的构建环境,特别是对于我们的构建,它创建一个 Golang Docker 镜像,然后设置一些特定于 CircleCI 的环境变量。
总结
在本章中,我们介绍了 CircleCI 和 Travis CI 之间的区别,并介绍了 CircleCI 的先决条件。我们创建了一个新的 Bitbucket 帐户,并解释了 Bitbucket UI 的基础知识以及在 Bitbucket 中上传 SSH 密钥以访问存储库的位置。然后我们在 GitHub 和 Bitbucket 中设置了 CircleCI,并解释了 CircleCI Web 应用程序的部分内容以及如何在其中导航。最后,我们简要概述了 CircleCI YAML 配置语法。在下一章中,我们将介绍 CircleCI 命令,并介绍 CircleCI 的一些更高级主题,例如工作流程。
问题
-
Jenkins 和 Travis CI 之间的主要区别是什么?
-
CircleCI 可以在 Bitbucket 和 GitHub 中都使用吗?
-
在 CircleCI 中如何设置存储库?
-
如何在 CircleCI 中查看构建作业?
-
我们在 Bitbucket 的
java-summer存储库中使用了哪个构建工具? -
应该使用 CircleCI 语法的版本 1 吗?
-
在 CircleCI 的
config.yml脚本中,我们在哪个字段中输入我们的构建语言?
进一步阅读
您可以通过查看circleci.com/docs/2.0/.官方 CircleCI 文档,进一步探索 CircleCI 的概念。
第十三章:CircleCI CLI 命令和自动化
在上一章中,我们介绍了如何在 Bitbucket 和 GitHub 中设置使用 CircleCI,并向您展示了如何导航 Bitbucket UI,并介绍了 CircleCI Web UI 的基础知识。在本章中,我们将介绍如何在 macOS/Linux 上安装 CircleCI CLI,并向您展示如何从 CLI 获取夜间构建。我们将详细介绍每个 CircleCI CLI 命令,并解释 CircleCI 中的工作流程。我们将向您展示如何使用顺序作业设置更复杂的工作流程。最后,我们将介绍 CircleCI API,并向您展示如何在使用 HTTP 请求时使用jq JSON 命令实用程序转换 JSON。
在本章中,我们将涵盖以下主题:
-
CircleCI CLI 安装
-
CircleCI CLI 命令
-
在 CircleCI 中使用工作流
-
使用 CircleCI API
技术要求
本章将需要一些基本的 Unix 编程技能,并且我们将在前几章中讨论的持续集成(CI)和持续交付(CD)概念上进行一些构建。熟悉使用 RESTful API 可能会有所帮助,因为我们将在本章末尾使用 curl 作为 REST 客户端。
CircleCI CLI 安装
安装 CircleCI CLI 的第一个先决条件是已安装 Docker (docs.docker.com/install/)。要在您的操作系统上安装 Docker,请访问 Docker 商店store.docker.com/search?type=edition&offering=community,并单击适合您的操作系统或云服务的Docker CE链接。按照其网站上的安装说明进行安装。
通过在 Windows 命令提示符或 macOS/Linux 终端应用程序上运行类似以下命令的命令来确保已安装 Docker 版本:
这里我安装了 Docker 版本 18。
在 macOS/Linux 上安装 CircleCI CLI
您需要运行以下命令来安装 CircleCI:
curl -o /usr/local/bin/circleci https://circle-downloads.s3.amazonaws.com/releases/build_agent_wrapper/circleci && chmod +x /usr/local/bin/circleci
您需要在终端应用程序 shell 会话中运行此命令。
通过 GitHub 发布安装夜间版本的 CircleCI
您可以在 GitHub 发布页面安装 CircleCI CLI 的夜间版本:github.com/CircleCI-Public/circleci-cli/releases。您需要查看类似于以下内容的“Assets”部分:
我们将选择circleci-cli_0.1.771_darwin_amd64.tar.gz资产,因为我们将在 macOS 操作系统上运行本地 CLI。
在终端 shell 会话中运行以下命令:
# Go to the Downloads Folder
cd ~/Downloads
# Unpack the compressed asset
tar -xvzf circleci-cli_0.1.771_darwin_amd64.tar.gz
# Go into the uncompressed directory
cd circleci-cli_0.1.771_darwin_amd64
# Move the circleci binary into the folder /usr/local/bin
mv circleci /usr/local/bin/circleci-beta
# Make sure that the binary is executable
chmod +x /usr/local/bin/circleci-beta
# Check that the binary version to make sure that it is working
circleci-beta help
我们现在有一个更新版本的 CircleCI CLI,并且可以验证:
我们将这个二进制可执行文件命名为circleci-beta。这样我们就可以运行稳定版和夜间版本的 CircleCI CLI。这不是您必须做的事情;我们只是为了举例说明而这样做。
CircleCI CLI 命令
就功能对等性而言,CircleCI CLI 并不像 Travis CI CLI 那样功能齐全。未来将会有更多的命令可用,但目前您可以在 CircleCI CLI 中使用六个命令,它们是build、config、help、step、tests和version,如果您从官方 CircleCI 文档中的 AWS 发布的 CircleCI CLI 二进制文件中使用(circle-downloads.s3.amazonaws.com/releases/build_agent_wrapper/circleci)。我们将同时使用稳定版本和夜间构建版本,后者比稳定版本多了几个命令。请记住,我们在本章的通过 GitHub 发布安装 CircleCI 的夜间构建版本部分中安装了它。稳定版本的命令将是circleci,夜间构建将是circleci-beta。
在下面的屏幕截图中,我们运行了help命令,显示了可用的命令,并简要概述了每个命令的功能:
版本命令
version命令输出您在本地系统上安装的 CLI 的当前版本:
您还可以向 CLI 中的每个命令传递标志/选项,并且可以通过运行--help标志找到每个命令接受的选项:
我们可以向version命令传递的选项只有一个,即-h,--help,因为这是一个非常简单的命令。
帮助命令
help命令将显示所有 CLI 命令,就像我们在本节开头演示的那样,但它也可以用来解释每个命令的工作原理,并显示每个命令接受的任何标志/选项:
在这里,我们对help命令本身运行了帮助。
配置命令
config命令验证并更新 CircleCI 配置 YML 脚本:
这里config命令还接受validate命令,用于验证您的配置 YML 脚本文件。
让我们验证functional-summer存储库中的配置脚本(github.com/packtci/functional-summer):
让我们再次查看配置脚本:
version: 2 jobs: build: docker: # specify the version you desire here - image: circleci/node:7.10
working_directory: ~/repo steps: - checkout - restore_cache: keys: - v1-dependencies-{{ checksum "package.json" }} - v1-dependencies- - run: yarn install - save_cache: paths: - node_modules key: v1-dependencies-{{ checksum "package.json" }} # run tests! - run: yarn test
这实际上是配置 YML 脚本中的一个非常微妙的错误,我们只需要缩进build字段,因为 CircleCI 认为我们的脚本中没有任何作业。为了解决这个问题,我们只需要缩进build字段:
version: 2
jobs:
build:
...
当我们运行validate命令时,它报告说配置 YML 脚本是有效的。
构建命令
build命令帮助您在本地计算机上运行 CircleCI 构建,并且可以采用各种选项,如下面的屏幕截图所示:
让我们运行我们在第十二章中创建的go-template-example-with-circle-ci (github.com/packtci/go-template-example-with-circle-ci)GitHub 存储库,然后在本地系统上运行circleci build命令。
在运行构建命令之前,确保进入存储库所在的目录,因为它需要读取.circleci文件夹中的config.yml文件:
build命令将执行配置 YML 脚本中的步骤,首先会启动一个环境。如果您尚未拉取配置 YML 脚本中指定的语言映像,则circleci build命令将为您拉取 Docker 映像。
默认情况下,circleci build命令将运行在jobs部分的build字段中定义的步骤,因此如果您想运行其他作业,就需要传递--job string选项。
这是我们在go-template-example-with-circle-ci GitHub 项目中的当前config.yml脚本:
version: 2 jobs: build: docker: - image: circleci/golang:1.9 working_directory: /go/src/github.com/packtci/go-template-example-with-circle-ci steps: - checkout - run: name: "Print go version" command: go version - run: name: "Run Unit Tests" command: go test
如果我们想使用另一个作业,可以使用--job string选项,假设有另一个作业:
...
build:
...
integration:
docker:
- image: cypress/base:8
environment:
TERM: xterm
steps:
- checkout
- run: npm install
- run:
name: "Run Integration Tests"
command: $(npm bin)/cypress run
现在让我们验证我们的 config YML 脚本,以确保它仍然有效:
现在我们知道我们的 config YML 脚本仍然有效,我们可以使用--job string标志运行新作业。
在这里,CLI 正在下载 Docker 映像,因为我们尚未将此特定 Docker 映像拉入我们的本地计算机。
步骤命令
step命令将执行您定义的配置 YML 脚本中的特定步骤。目前,只有一个halt的子命令,它将停止当前执行。
这是step命令的一个示例运行:
circleci step halt
配置命令
configure命令仅在 CircleCI 的夜间构建版本中可用,它可以帮助您配置您的凭据和将要访问的 API 端点:
我们将以无标志运行configure命令,这将将其设置为交互模式,然后我们将设置我们的 API 令牌和我们希望访问的 API 端点。
在 CircleCI 中设置 API 令牌
您需要点击 CircleCI Web 应用程序右上角的用户头像,它看起来像以下截图:
一旦您点击“用户设置”链接,您将被重定向到账户 API 页面,它看起来像这样:
接下来,您需要点击“创建新令牌”按钮,这将弹出一个类似于这样的模态框:
在这里,我们输入了一个名为PacktCI的令牌名称。然后我们只需点击“添加 API 令牌”按钮,这将为我们生成一个新的 API 令牌。您需要将 API 令牌复制到安全位置,因为您只能使用一次。
在交互模式下设置 API 令牌和 API 端点
我们将在终端会话中运行circleci-beta configure命令,并设置我们的凭据和 API 端点:
在这里,我们设置了 API 令牌,但出于安全目的,该值被隐藏,我们将 API 端点设置为https://circleci.com/api/v1.1/。
configure命令仅在夜间版本中可用,而不是稳定版本。
测试命令
tests命令收集并拆分具有测试的文件:
让我们使用glob子命令在go-template-example-with-circle-ci(github.com/packtci/go-template-example-with-circle-ci)GitHub 存储库中查找所有 Go 测试文件:
在 CircleCI 中使用工作流程
CircleCI 中的工作流程是一种运行并行build作业的方式,可以用来定义一组作业并指定作业顺序。让我们在go-template-example-with-circle-ci(github.com/packtci/go-template-example-with-circle-ci)配置 YML 脚本中添加一个工作流字段:
version: 2
jobs:
build:
...
integration:
....
workflows:
version: 2
build_and_integration:
jobs:
- build
- integration
在这个工作流程中,我们分别创建了两个并行作业,分别称为build和integration。它们彼此独立,这将有助于加快构建过程。
CircleCI Web UI 中的工作流程
如果我们点击左侧导航窗格中的“工作流程”链接,我们可以在 CircleCI Web UI 中看到工作流程。然后您需要点击特定的项目,本例中是go-template-example-with-circle-ci,如下截图所示:
如果您点击“RUNNING”工作流程,您将看到以下页面:
构建作业运行了 2 秒,但集成测试运行的时间比构建作业长。最好将这两个作业分开,因为工作流程表明它们彼此不依赖。
顺序工作流程示例
我们之前展示的工作流程示例包含了两个独立运行的作业,但我们也可以有需要其他作业完成后才能运行的作业。假设我们有一个只有在构建运行时才运行的验收测试套件,然后我们的应用程序只有在验收测试套件通过后才会部署。
在我们的示例中,我们使用cypress.io(www.cypress.io/)运行端到端测试,这是一个端到端的 JavaScript 测试库。假设我们的验收测试在 CI 构建中通过,我们就可以将我们的应用程序部署到 Heroku。我们在第十一章中介绍了如何在 Travis CI 中设置 Heroku,Travis CI UI Logging and Debugging部分,所以如果您需要更多关于安装和设置 Heroku 以及在 Heroku 中创建可以部署的应用程序的信息,请阅读那部分。我们需要将 Heroku API 密钥和应用程序名称添加为环境变量。
向项目添加环境变量
在我们的 CircleCI 项目中,我们首先需要通过点击go-template-example-with-circle-ci(circleci.com/gh/packtci/go-template-example-with-circle-ci)项目旁边的齿轮图标进入项目设置。确保您在“作业”或“工作流程”视图中,然后您应该看到一个齿轮图标:
一旦您点击齿轮图标,您将被重定向到“项目设置”页面,您需要点击“环境变量”链接。然后您的页面将看起来像以下截图:
我们将通过点击“添加变量”按钮向我们的项目添加两个环境变量,这将弹出一个如下的模态框:
出于安全目的,我已经删除了项目的应用程序名称和 API 令牌的内容,但是一旦您点击“添加变量”按钮,项目中就会有一个环境变量可用。我们现在有两个可以使用的环境变量,即HEROKU_API_KEY和HEROKU_APP_NAME。这些环境变量将在我们的.circleci/config.yml脚本中可用。
更新了工作流程部分和配置 YML 脚本
我们的配置 YML 脚本现在有一个部署jobs部分,并且我们已经更新了我们的工作流程字段如下:
...
deploy:
docker:
- image: buildpack-deps:trusty
steps:
- checkout
- run:
name: Deploy Master to Heroku
command: |
git push https://heroku:$HEROKU_API_KEY@git.heroku.com/$HEROKU_APP_NAME.git master
workflows:
version: 2
build_integration_and_deploy:
jobs:
- build
- integration:
requires:
- build
- deploy:
requires:
-integration
这次更改的工作流程现在看起来不同,因为我们为作业设置了一个顺序管道:
在前面的截图中,构建作业首先运行,然后是集成作业,最后是部署作业。阅读circleci.com/docs/2.0/workflows/上的工作流程文档,了解更多类型的工作流程信息。
使用 CircleCI API
CircleCI API 文档可在circleci.com/docs/api/v1-reference/上找到。要开始使用 API,您需要添加一个 API 令牌。我们已经在本章的使用 CircleCI 设置 API 令牌部分设置了一个 API 令牌,因此如有必要,请阅读该部分。
测试 CircleCI API 连接
我们将使用curl命令和我们的 API 令牌来测试我们是否有一个良好的 CircleCI API 连接:
在这里,我们没有得到任何响应头或状态码。为了接收这些,您需要在curl命令中使用-i、--include选项。
使用 CircleCI API 获取单个 Git 存储库的构建摘要
我们将使用GET /project/:vcs-type/:username/:project API 端点来获取构建摘要信息。您可以在circleci.com/docs/api/v1-reference/#recent-builds-project上阅读单个项目的最近构建的文档。
在下面的截图中,我们使用curl命令进行 REST 调用,并使用jq(stedolan.github.io/jq/)JSON 命令行处理器来美化 JSON 输出,如下面的截图所示:
使用 jq 实用程序计算我们的 CircleCI 构建的一些指标
让我们使用jq命令行实用程序来计算 CircleCI API 提供的信息的一些指标。我们可能想要找到的一个是项目中所有已通过的构建。我们可以使用jq命令通过使用 jq 中的map和select内置函数来实现这一点(stedolan.github.io/jq/manual/#Builtinoperatorsandfunctions)。
在下面的截图中,我们获取了最近 30 次构建的构建摘要,然后只显示实际通过的构建:
在这里,我们使用jq实用程序进行了两个不同的查询。
-
第一个查询是
jq 'map(select(.failed == false)) | length',它对对象数组进行映射,并在failed为false时过滤掉顶级属性failed。 -
第二个查询是
jq '. | length',它只是计算数组的长度,即5。
我们运行了第二个命令,以确保第一个命令确实过滤了响应有效负载中的一些条目。从中我们可以得知,在最近的 30 次构建中,go-template-example-with-circle-ci(github.com/packtci/go-template-example-with-circle-ci)GitHub 存储库中有一次构建失败。
摘要
在本章中,我们介绍了如何在 macOS/Linux 环境中安装 CircleCI CLI,并向您展示了如何安装 CLI 的夜间构建。我们向您展示了如何使用 CircleCI CLI 中的每个命令,并向您展示了 CircleCI CLI 夜间构建中可用的一些命令功能。我们解释了工作流程为什么有用以及如何在 CircleCI 中使用它们。最后,我们向您展示了如何使用 CircleCI API 以及如何使用jq命令实用程序收集有用的指标。
问题
-
安装 CircleCI CLI 的主要先决条件是什么?
-
我们从哪里获取了 CircleCI CLI 的夜间构建?
-
CLI 中有多少个命令存在?
-
CLI 中的哪个命令对于了解特定命令的功能以及给定命令的选项很有用?
-
我们如何在 CircleCI 中运行并行作业?
-
我们使用哪个命令来验证我们的 CircleCI YML 脚本?
-
CircleCI RESTful API 的端点是什么?
进一步阅读
您可以通过查看circleci.com/docs/2.0/上的官方 CircleCI 文档进一步探索 CircleCI 中的概念。
第十四章:CircleCI UI 日志记录和调试
在上一章中,我们深入介绍了 CircleCI CLI 命令,并向您展示了一些自动化任务的技术。在本章中,我们将深入介绍作业日志,并更详细地解释运行步骤。我们将解释工作流程的概念,并向您展示如何使用 CircleCI API 查找项目的最新构建。我们将介绍如何通过在构建中实现缓存来调试慢作业,并最后使用一些故障排除技术来运行具有本地配置 YML 脚本的构建。
本章将涵盖以下主题:
-
作业日志概述
-
在 CircleCI 中调试慢构建
-
日志记录和故障排除技术
技术要求
在本章中,我们将介绍一些关于使用 RESTful API 的概念,并将使用curl实用程序进行 REST 调用,因此了解 API 是什么以及如何使用 REST 客户端(如curl)将是有益的。了解 Unix 编程环境的基本概念也会有所帮助,了解脚本编写和 Bash 环境是有益的。
本章的代码文件可以在以下链接找到:
作业日志概述
CircleCI 中的作业日志与 Travis CI 中的不同,因为每个作业中的每个步骤都在单独的非登录 shell 中运行,并且 CircleCI 为作业中的每个步骤设置了一些智能默认值。
默认构建作业中的运行步骤
我们将创建一个新的存储库,以演示默认构建作业中的多个作业。该存储库将被称为circleci-jobs-example(github.com/packtci/circleci-jobs-example),并将在构建作业中有多个运行声明。我们将使用 Node.js 作为我们的首选编程语言进行演示。请记住,我们需要将新项目添加到 CircleCI 中,以便它能够了解我们的项目。在之前的章节中,我们使用 CircleCI Web UI 添加了项目,但让我们使用 CircleCI API 将新项目添加到 CircleCI 中。
通过 API 将项目添加到 CircleCI
我们在第十三章中学习了如何使用 CircleCI API,因此请阅读该章节的使用 CircleCI API部分,以获取有关使用 API 的更多详细信息。如果您已经阅读过这部分内容,那么您已经有一个可以使用的 API 令牌。关于在 CircleCI 上关注新项目的 API 端点(circleci.com/docs/api/v1-reference/#follow-project)显示您需要进行POST HTTP请求并将 API 令牌作为查询字符串参数添加。
使用 curl 作为 REST 客户端
我们已经在整本书中使用curl作为 REST 客户端,所以您现在应该熟悉如何使用它。我们将向以下端点进行POST请求:https://circleci.com/api/v1.1/project/:vcs-type/:username/:project/follow?circle-token=:token:
curl -X POST "https://circleci.com/api/v1.1/project/github/packtci/circleci-jobs-example/follow?circle-token=$CIRCLECI_API_TOKEN_GITHUB"
在这里,我们使用了一个名为CIRCLECI_API_TOKEN_GITHUB的环境变量,该变量在我们的本地环境中设置,并且我们从 API 中获得了以下响应:
{
"following" : true,
"workflow" : false,
"first_build" : {
"compare" : null,
"previous_successful_build" : null,
"build_parameters" : null,
"oss" : true,
"committer_date" : null,
"body" : null,
"usage_queued_at" : "2018-08-04T21:36:26.982Z",
"fail_reason" : null,
"retry_of" : null,
"reponame" : "circleci-jobs-example",
"ssh_users" : [ ],
"build_url" : "https://circleci.com/gh/packtci/circleci-jobs-example/1",
"parallel" : 1,
"failed" : null,
"branch" : "master",
"username" : "packtci",
"author_date" : null,
"why" : "first-build",
"user" : {
"is_user" : true,
"login" : "packtci",
"avatar_url" : "https://avatars3.githubusercontent.com/u/40322425?v=4",
"name" : null,
"vcs_type" : "github",
"id" : 40322425
},
"vcs_revision" : "abc2ce258b44700400ec231c01529b3b6b8ecbba",
"vcs_tag" : null,
"build_num" : 1,
"infrastructure_fail" : false,
"committer_email" : null,
"previous" : null,
"status" : "not_running",
"committer_name" : null,
"retries" : null,
"subject" : null,
"vcs_type" : "github",
"timedout" : false,
"dont_build" : null,
"lifecycle" : "not_running",
"no_dependency_cache" : false,
"stop_time" : null,
"ssh_disabled" : true,
"build_time_millis" : null,
"picard" : null,
"circle_yml" : {
"string" : "version: 2\njobs:\n build:\n docker:\n - image: circleci/node:8.11.3\n steps:\n - checkout\n - run:\n name: Install Dependencies\n command: npm install\n - run:\n name: Run the Sort Test to sort by first name\n command: $(npm bin)/tape sort_test.js\n - run:\n name: Compute Standard Deviation\n command: $(npm bin)/tape standard_deviation_test.js\n - run:\n name: Find the Text and Replace It\n command: $(npm bin)/tape find_text_test.js\n - run: |\n echo \"Generate Code Coverage\"\n npm test\n echo \"Show the coverage\"\n npm run coverage\n "
},
"messages" : [ ],
"is_first_green_build" : false,
"job_name" : null,
"start_time" : null,
"canceler" : null,
"platform" : "2.0",
"outcome" : null,
"vcs_url" : "https://github.com/packtci/circleci-jobs-example",
"author_name" : null,
"node" : null,
"canceled" : false,
"author_email" : null
}
}
从 JSON 响应中解析 build_url 属性
让我们使用终端 shell 会话中的 cat 实用程序将此响应保存到一个名为circleci-jobs-example-follow.json的新文件中,就像这样:
cat > circleci-jobs-example-follow.json
# Paste the JSON Content from System Clipboard
# Press Enter
# Finally Press enter
现在让我们使用jq(stedolan.github.io/jq/manual/)并在 JSON 负载中找到build_url属性:
cat circleci-jobs-example-follow.json | jq '.first_build.build_url'
此命令返回以下构建 URL:https://circleci.com/gh/packtci/circleci-jobs-example/1。
现在你可以打开浏览器并粘贴此 URL,或者你可以使用操作系统上可用的命令行实用程序。我们将在 macOS 中使用open实用程序,就像这样:
open https://circleci.com/gh/packtci/circleci-jobs-example/1
前面的命令将在 macOS 中打开默认浏览器并使用您提供的 URL。在 Linux 中,您可能可以使用xdg-open,gnome-open或kde-open,具体取决于您安装的操作系统。无论哪种方式,您都可以简单地打开浏览器并粘贴构建 URL 的条目。
CircleCI Web UI 作业日志分析
当我们打开通过 API 触发的新工作的 URL 时,UI 的第一部分看起来像这样:
在这里,顶部显示基本信息,例如提交 SHA 哈希、贡献者信息和其他背景信息。如果您在作业日志中进一步向下滚动,您将看到作业的每个部分中运行的步骤:
构建完成需要 9 秒,注意到每个构建步骤都有自己的部分,这些部分都很方便地折叠起来。您只需点击每个部分即可获取步骤的详细信息。每个步骤的名称对应于配置 YML 脚本中的name字段。
请注意,多行命令的名称使用完整命令的名称作为其名称。
这是多行命令的条目:
...
- run: | echo "Generate Code Coverage" npm test echo "Show the coverage" npm run coverage
如果我们展开一个步骤,我们将看到每个步骤都有以下共同的条目:
Shebang行#!/bin/bash -eo pipefail为非登录 shell 设置了一些明智的默认值。
Bash 选项-e表示如果语句返回非真值,则脚本应该退出。Bash 选项-o pipefail表示使用第一个失败的错误状态,而不是管道中最后一项的错误状态。您可以不在 Shebang 行中添加这些选项,而是可以这样做:
#!/usr/bin/env bash
# Exit script if you try to use an uninitialized variable. set -o nounset # Exit script if a statement returns a non-true return value. set -o errexit # Use the error status of the first failure, rather than that of the last item in a pipeline. set -o pipefail
如果我们查看工作中的另一个步骤,我们会看到相同的事情:
CircleCI 在作业的每个步骤中都这样做,因为它有助于我们解决编写 shell 脚本时出现的问题,并有助于促进编写 shell 脚本的最佳实践。
这是一个可能失败的命令的示例,当使用 Unix 管道时,它将在构建的错误位置报告错误:
docker ps -a | grep -v "busybox:latest" | awk '{ print $1 }' - | grep -v "CONTAINER"
在这个流水线中,我们列出了所有正在运行、退出或因某种原因终止的容器,然后将其传输到grep实用程序中,并排除任何具有文本busybox:latest的条目,然后将其传输到awk实用程序中,并仅打印第一列。最后,我们将其传输回grep并排除文本CONTAINER。这个流水线可能在任何一条流水线链中失败,但因为我们使用了选项set -o pipefail,脚本将在返回非真选项的第一个命令上失败。这很有帮助,因为默认行为是报告管道中的最后一项。
运行声明命令的另一个方面是,默认情况下它们是使用非登录 shell 执行的。这意味着您必须显式地源化任何隐藏文件,例如dotfiles,作为运行的一部分,否则您可能没有准备好使用的环境变量。
这是一个示例来说明这一点:
# We source some environment variables here that we need
source ~/project/.env
npm run security-tests
还要注意,退出代码会打印在右上角的每个运行声明中:
您还可以在右上角看到一个有用的按钮,它将使您进一步滚动到您感兴趣的特定运行步骤中。
环境变量安全使用的最佳实践
重要的是,不要在.circleci/config YML 脚本文件中添加机密信息。如果这样做,可能会在作业日志中泄露机密信息,这些信息可能是公开可访问的。config.yml的完整文本对于在 CircleCI 上访问您的项目的开发人员是可见的,因此请将您的机密信息和/或密钥存储在 CircleCI 应用程序中的项目或上下文设置中。在配置中运行脚本可能会暴露机密环境变量,因此在运行步骤中使用set -o xtrace / set -x时要小心,因为它们可能会暴露环境变量。
需要注意的一点是,所有环境变量都使用 Hashicorp Vault (www.vaultproject.io/)进行加密,环境变量使用 AES256-GCM96 进行加密,并且对任何 CircleCI 员工都不可用。
使用工作流在作业中运行步骤
根据 Circle CI 文档关于工作流(circleci.com/docs/2.0/workflows/),一个工作流是一组规则,用于定义一组作业及其运行顺序。工作流支持使用一组简单的配置键进行复杂的作业编排,以帮助您更早地解决故障。
我们将使用工作流来将我们的作业分成更合适的部分,然后利用一些脚本彼此独立并可以分开运行的事实。通过在 CircleCI 中使用工作流,我们可以加快构建过程。
现在让我们考虑作业的哪些部分可以在我们的构建过程中分解为单独的步骤。我们可以将依赖步骤分解为构建的一个单独部分,然后我们可以将为三个测试运行的各个步骤合并为一个名为测试的步骤。请记住,配置 YML 脚本中的步骤如下所示:
...
- run:
name: Run the Sort Test to sort by first name
command: $(npm bin)/tape sort_test.js
- run:
name: Compute Standard Deviation
command: $(npm bin)/tape standard_deviation_test.js
- run:
name: Find the Text and Replace It
command: $(npm bin)/tape find_text_test.js
- run: |
echo "Generate Code Coverage"
npm test
echo "Show the coverage"
npm run coverage
...
在最后一步,我们有命令npm test,这个命令引用了package.json文件中指定的以下命令:
"scripts": { "test": "nyc tape *_test.js", "coverage": "nyc report --reporter=cobertura" }
请注意,此命令已经运行了所有测试,然后使用 NYC 代码覆盖率实用程序报告覆盖率。最后一个命令生成了一个 Cobertura XML 报告,我们将在本章后面使用。现在,我们将把一系列步骤重写为它们自己的字段,称为test,它将如下所示:
test:
docker:
- image: circleci/node:8.11.3
steps:
- checkout
- run:
name: Run Tests and Run Code Coverage with NYC
command: |
echo "Generate Code Coverage"
npm test
echo "Show the coverage"
npm run coverage
请注意,我给折叠命令一个更合适的名称,并且请注意,我们可以在command字段本身使用管道(|)运算符使用多行命令。
我们将添加一个部署部分,就像我们在第十三章中所做的那样,CircleCI CLI 命令和自动化,这将把我们的应用程序部署到Heroku (dashboard.heroku.com/apps)。如果您不了解 Heroku 是什么,请阅读第十一章,Travis CI UI 日志和调试,并阅读Travis CI 部署概述和调试部分以获取更多详细信息。
向配置 YML 脚本添加工作流部分
我们将在我们的配置 YML 脚本底部添加workflows部分,但我们也可以将其添加到配置 YML 脚本的开头。更新后的配置 YML 脚本如下:
...
workflows: version: 2 build_test_and_deploy: jobs: - build - test: requires: - build - deploy: requires: - test
完成更新配置 YML 脚本后,我们应该使用 CircleCI CLI 确保我们的配置 YML 脚本仍然有效,就像这样:
看起来我们在第 19 行的配置 YML 脚本中有问题:
...
- run: name: Run Tests and Run Code Coverage with NYC command: | echo "Generate Code Coverage" npm test echo "Show the coverage" npm run coverage
这实际上是我们配置 YML 脚本中的一个细微错误,因为我们没有正确缩进多行命令,所以 CircleCI 不知道我们的多行命令从哪里开始。现在更新的配置 YML 脚本部分如下:
... - run: name: Run Tests and Run Code Coverage with NYC command: | echo "Generate Code Coverage" npm test echo "Show the coverage" npm run coverage
现在让我们再次运行 CircleCI CLI 验证:
我们的配置 YML 脚本是有效的,现在让我们通过发出以下命令将其提交到源代码控制中:
请注意,我们在这里给出了一个描述性的提交消息,在版本控制中这是一个很好的做法,如果你正在处理的任何东西有一个特定的标签,比如 JIRA;你可以像这样添加它,例如:
git commit -m '[PACKT-1005] Update config yml script to different jobs and use workflows.'
使用 CircleCI API 查找最近的构建 URL
我们当然可以使用 CircleCI Web 应用程序,点击工作流部分,找到我们最近的构建,但让我们改用 CircleCI API,使用jq来解析 JSON 响应有效负载,就像我们以前对其他 API 端点所做的那样。
这是一个命令,它将从/recent-builds API 端点的输出传输到jq,并从对象数组中返回第一个build_url,这将是最近的构建,然后将其传输到系统剪贴板。我们可以在circleci.com/docs/api/v1-reference/#recent-builds-project文档中看到最近构建项目中 JSON 的形状:
curl -X GET \
--header "Accept: application/json" \
"https://circleci.com/api/v1.1/project/github/packtci/circleci-jobs-example?circle-token=$CIRCLECI_API_TOKEN_GITHUB" | jq '.[0].build_url'
这将在终端返回以下 URL:circleci.com/gh/packtci/circleci-jobs-example/6。
现在让我们转到这个 URL 并查看最近的构建;我们会注意到构建失败了:
构建失败是因为我们没有设置配置 YML 脚本引用的必要的环境变量,即HEROKU_API_KEY和HEROKU_APP_NAME。我们在第十三章中介绍了如何设置项目级别的环境变量,CircleCI CLI 命令和自动化,但我们只需要复制项目环境级别的变量。如果环境变量相同,CircleCI 有一种简单的方法可以做到这一点:
点击导入变量按钮,然后输入要复制的项目,就像这样:
请注意,我只检查了HEROKU_API_KEY环境变量,并且我将手动设置HEROKU_APP_NAME,因为它对于circleci-jobs-example(github.com/packtci/circleci-jobs-example)项目来说是不同的:
现在,设置了这些环境变量,让我们使用重试构建,使用circleci.com/docs/api/v1-reference/#retry-build API 端点。我们将使用curl来调用端点,就像这样:
curl -X POST https://circleci.com/api/v1.1/project/github/packtci/circleci-jobs-example/6/retry\?circle-token\=$CIRCLECI_API_TOKEN_GITHUB | jq '.build_url'
现在我们可以通过复制返回到标准输出的build_url值来验证构建是否已修复,即circleci.com/gh/packtci/circleci-jobs-example/7:
在 CircleCI 中调试慢构建
在 CircleCI 中构建可能会因为多种原因而变慢。让我们看一个go-template-example-with-circleci(circleci.com/workflow-run/533ee47a-a990-4679-826b-7b24221df2ca)的工作流示例:
特别要注意的是,集成作业花了一分钟多的时间才完成,部署作业也花了一分钟多的时间,这使得构建需要 3 分钟 20 秒才能完成。如果我们点击集成作业,我们会看到作业中的以下步骤:
请注意,npm install花了 1 分钟 3 秒才完成。让我们打开运行步骤调用npm install以获取更多细节:
我们唯一的依赖是cypress.io,但我们没有缓存这个依赖,所以它将每次运行这个步骤。CircleCI 有一种方法让我们通过利用称为save_cache(circleci.com/docs/2.0/configuration-reference/#save_cache)和restore_cache(circleci.com/docs/2.0/configuration-reference/#restore_cache)的两个字段声明来缓存我们的节点依赖。让我们更新配置 YML 脚本以使用这种缓存策略进行集成构建:
integration: docker: - image: cypress/base:8 environment: ## this enables colors in the output TERM: xterm steps: - checkout # special step to restore the dependency cache - restore_cache: key: v2-{{ checksum "package.json" }} - run: npm install # special step to save the dependency cache - save_cache: key: v2-{{ checksum "package.json" }} paths: - ~/.npm
- ~/.cache - run: name: "Run Integration Tests" command: npm test
注意到我们在npm install之前放置了restore_cache步骤,然后在npm install步骤之后放置了save_cache步骤。我们还在两个字段中使用了一个关键字段。关键值是不可变的,我们使用v2作为缓存关键值的版本,并获取package.json文件的校验和。如果我们想要使任何更改无效缓存,我们可以简单地将缓存值增加一,例如v3。还要注意,我们有一个路径字段,并且我们指定路径为~/.npm和~/.cache目录。Cypress 测试运行程序期望将二进制文件保存到这样的目录中,否则它将抛出错误。让我们将这个更改推送到源代码控制,并触发新的构建并查看作业日志。现在让我们使用对最近构建 API 端点的调用,并复制 URL 并查看构建的情况:
curl -X GET \
--header "Accept: application/json" \
"https://circleci.com/api/v1.1/project/github/packtci/go-template-example-with-circle-ci?circle-token=$CIRCLECI_API_TOKEN_GITHUB" | jq '.[0].build_url'
我们需要复制打印到标准输出的build_url条目,并将 URL 粘贴到浏览器中。build_url将打开当前构建,从这个页面我们可以轻松地通过点击一个类似这样的链接来导航到该特定作业的工作流程:
我们可以点击工作流标签下的build_integration_and_deploy链接来到工作流。现在我们在集成构建中有以下步骤:
如果我们展开恢复缓存下拉菜单,我们会看到以下内容:
注意到这里没有找到缓存,这是预期的,因为这是添加了这个步骤的构建的第一次运行。
如果我们展开保存缓存按钮,我们会看到以下内容:
注意到这里创建了一个缓存存档,并存储在配置 YML 脚本中指定的node_modules路径中。
让我们在README.md文件中进行一个简单的文本更改,并提交更改以触发新的构建。我们将使用 API 找到最新的构建,就像我们一直在做的那样。现在让我们看一下集成作业的新作业日志:
注意到构建时间从 1 分 20 秒减少到 33 秒。如果我们展开恢复缓存下拉菜单,我们会看到以下内容:
现在让我们看一下保存缓存的步骤:
注意到它跳过了缓存生成,因为它能够找到我们从上一次构建中保存的缓存。
日志记录和故障排除技术
我们可以使用 CircleCI API 来排除有问题的配置 YML 脚本,而无需进行 Git 提交。我们可以做的一个技巧是创建另一个文件夹,并将我们的配置 YML 脚本的副本放入其中,然后使用这个 YML 脚本作为我们的调试脚本。一旦我们可以验证 YML 脚本工作正常,我们可以更新原始的 YML 脚本。这很有用,因为我们不会用故障排除提交来堵塞 Git 历史,而是直接使用 CircleCI API。
使用本地配置 YML 脚本运行构建进行故障排除
假设我们想要尝试存储构建产物,比如项目的代码覆盖率。目前我们正在生成一个覆盖率报告,但在构建过程中没有保存下来供我们查看。这是一个很好的用例,可以创建一个单独的配置 YML 脚本来测试这个新功能。让我们存储circleci-jobs-example(github.com/packtci/circleci-jobs-example)项目的覆盖率产物,并且还要更新测试任务以缓存节点依赖,就像我们在前一节中学到的那样。
运行此命令将复制.circleci目录的内容并在 shell 中创建一个新目录:
cp -r .circleci store_and_cache_experiment
现在我们将使用store_and_cache_experiment文件夹来运行我们的本地配置 YML 脚本实验。这是我们将要对store_and_cache_experiment文件夹中的配置 YML 脚本进行的更改:
test:
docker:
- image: circleci/node:8.11.3
steps:
- checkout
# special step to restore the dependency cache
- restore_cache:
key: v2-{{ checksum "package.json" }}
# special step to save the dependency cache
- run:
name: Install Dependencies
command: npm install
- save_cache:
key: v2-{{ checksum "package.json" }}
paths:
- ~/.npm
- ~/.cache
- run:
name: Run Tests and Run Code Coverage with NYC
command: |
echo "Generate Code Coverage"
npm test
echo "Show the coverage"
npm run coverage
- store_artifacts:
path: coverage
prefix: coverage
我们添加了save_cache和restore_cache声明更改,还添加了store_artifacts声明更改。让我们使用circleci config validate命令验证配置 YML 脚本是否仍然有效。现在,为了在本地配置中测试这些更改,而不必进行 Git 提交,我们可以使用 CircleCI API,并在我们的请求正文中提供我们的本地配置 YML 脚本,并引用最近的 Git 提交。我们可以通过运行此命令获取最新的 Git 提交:
现在我们有一个修订号,可以用于我们将要进行的 API 调用。这是我们将用于调试新配置 YML 脚本更改的命令:
#! /bin/bash
curl --user ${CIRCLECI_API_TOKEN_GITHUB}: \ --request POST \ --form revision=09a95cb11914fe8cf4058bfe70547b0eec0656bc \ --form config=@config.yml \ --form notify=false \
https://circleci.com/api/v1.1/project/github/packtci/circleci-jobs-example/tree/master | jq '.build_url'
第一个选项--user接受我们保存在环境变量中的 API 令牌,然后后面的:表示后面没有密码。下一个选项--request是我们指定的HTTP POST动词。--form修订是我们放置之前得到的 Git 修订号的地方,然后在下一个选项中我们指定config.yml脚本。我们为通知指定了一个 false 的表单值,然后提供 URL。在这里,我们指定了 GitHub 的版本控制系统提供者,然后是我们的packtci用户名,接着是项目名称,然后是树,最后是我们的分支名称。然后我们将其传输到jq实用程序中,并解析出build_url。这是 API 端点的清晰表示:
POST: /project/:vcs-type/:username/:project/tree/:branch
在我们发出 REST 调用之后,我们应该收到一个 JSON 响应,其中包含一个构建 URL 供我们查看,这是我们得到的构建 URL:circleci.com/gh/packtci/circleci-jobs-example/8。如果我们在 CircleCI Web UI 中查看这个新构建,我们会看到它已经通过了:
让我们删除故障排除目录和配置 YML 脚本和 shell 脚本,并将配置 YML 脚本复制到.circleci目录中,就像这样:
cp store_and_cache_experiment/config.yml .circleci
rm -r store_and_cache_experiment
git add .
git commit -m 'Cache and Store artifacts.'
git push
现在,如果我们点击当前构建,然后转到工作流链接,我们会看到上传产物步骤已添加到任务中;看起来是这样的:
现在我们可以向上滚动并点击 Artifacts 选项卡,看到构建中已保存了一个产物,就像这样:
如果我们点击index.html,我们将被重定向到一个漂亮的覆盖率报告,看起来像这样:
摘要
在本章中,我们深入介绍了作业日志,并向您展示了如何使用 CircleCI API 添加项目。我们向您展示了如何分析作业日志,并更详细地解释了 CircleCI 中的工作流程。我们看了如何使用 CircleCI API 找到最近的构建。然后,我们看了如何在 Circle CI 中调试慢构建,并最后向您展示了如何使用本地配置 YML 脚本来尝试对 CircleCI YML 脚本进行新更改的实验。
在下一章中,我们将介绍一些持续集成/持续交付的最佳实践,并研究一些配置管理模式,特别是秘密管理,并在软件公司实施 CI/CD 时提供一些检查表。
问题
-
我们在 CircleCI 中用于关注新项目的 API 端点是什么?
-
cat 实用程序可以用来创建新文件吗?
-
如何在 CircleCI 配置 YML 脚本中运行多行命令?
-
在 CircleCI 中使用
set -x或脚本中的执行跟踪时是否存在安全漏洞? -
我们用什么 CLI 命令来验证我们的配置 YML 脚本?
-
环境变量可以从 CircleCI 中的其他项目导入吗?
-
我们在 CircleCI 中使用了哪些声明来缓存我们的依赖关系?
进一步阅读
要了解更多关于调试和故障排除以及其他有用信息,请阅读官方的 CircleCI 文档:circleci.com/docs/2.0/。
第十五章:最佳实践
在上一章,[第十四章](c8355d57-1eb8-4e45-93f5-a32513185de3.xhtml),CircleCI UI 日志记录和调试中,我们使用 CircleCI 涵盖了更高级的调试和日志记录技术,并介绍了使用 CircleCI API 的更多选项。在本书的最后一章中,我们将介绍不同类型测试的最佳实践,如单元测试、集成测试、系统测试和验收测试。我们将介绍密码管理的最佳实践,并以 Vault 库为例。最后,我们将介绍 CI/CD 中部署的最佳实践,并编写一个自定义的 Go 脚本来创建 GitHub 发布。
本章将涵盖以下主题:
-
CI/CD 中不同类型测试的最佳实践
-
密码和秘密存储的最佳实践
-
部署的最佳实践
技术要求
本章将需要一些基本的编程技能,因为我们将在部署脚本和单元测试示例中讨论一些特定于编程语言的材料。熟悉 Unix 编程和 Bash shell 将非常有帮助。
CI/CD 中不同类型测试的最佳实践
在[第三章](e80cf8c3-7464-4c16-865b-78e3c264a98e.xhtml),持续交付的基础知识中,我们介绍了验收测试,并简要讨论了验收测试套件如何作为回归测试套件。在本节中,我们将讨论您可以进行的不同类型的软件测试,并制定每种测试的最佳实践。我们将介绍以下类型的测试:
-
烟雾测试
-
单元测试
-
集成测试
-
系统测试
-
验收测试
烟雾测试
烟雾测试是一种特殊的测试,有助于验证应用程序的基本功能。烟雾测试将假定一些基本实现和环境设置。烟雾测试通常在测试周期开始时运行,作为完整测试套件开始之前的理智检查。
烟雾测试的主要目的是在软件系统的新功能开发中捕捉明显的问题。烟雾测试不是为了详尽无遗,而是为了快速运行。假设一个软件公司遵循敏捷软件开发实践,每两周进行一次冲刺,向产品添加新功能。当新功能合并到发布中,即软件的主干时,烟雾测试失败,这应立即引起警觉,表明新功能可能破坏了现有功能。
您可以创建特定上下文的烟雾测试,用于测试系统中的新功能,这些测试将采用一些基本假设,并断言是否满足要求。您可以在进行任何集成测试之前以及在为暂存环境进行任何部署之前运行这些烟雾测试,并且这些烟雾测试将检查每个暂存环境的不同条件。
烟雾测试示例
我们将使用我构建的现有应用程序,该应用程序显示表中的用户列表。该应用程序称为containerized-golang-and-vuejs(github.com/jbelmont/containerized-golang-and-vuejs),它展示了如何使用容器、Golang 和 Vue.js 作为参考。我们首先要做的是确保应用程序正在使用名为make dev的makefile任务运行。此命令执行以下操作:
docker-compose up frontend backend db redis
总之,此命令会启动四个 Docker 容器,当它运行时,我们应该能够访问http://localhost:8080。现实中,烟雾测试会访问运行中的应用程序,但这只是用于演示烟雾测试的目的。我们将使用一个名为Cypress(www.cypress.io/)的端到端测试库,但我们也可以使用另一个库。
我们将使用 JavaScript 编写以下简单的冒烟测试:
describe('The user list table is shown and buttons', function () { it('successfully loads table', function () { cy.visit('/') cy .get('.users-area-table') .find('tbody tr') .first() .screenshot() }) })
您可以在入门指南(docs.cypress.io/guides/getting-started/writing-your-first-test.html#)文档中了解更多关于 Cypress 的信息,但这个测试基本上是验证页面是否加载了数据,Cypress 会拍摄屏幕截图,以便我们可以直观地验证页面。
这是 Cypress 库拍摄的屏幕截图:
对于这个简单的应用程序,我们可以确信应用程序大致工作正常,但更完整的冒烟测试可能会通过登录界面,然后执行应用程序预期执行的基本操作。
Cypress 的另一个好功能是它可以录制测试的视频,显示测试所采取的所有步骤,这可以进一步验证应用程序是否满足基本要求。
单元测试
单元测试可以被认为是软件测试的基础,因为单元测试测试代码的单个块,比如一个函数或一个类/对象。通过单元测试,您可以测试函数和/或类的功能。由于这个事实,单元测试通常会存根或模拟任何外部依赖,以便测试可以完全专注于相关的函数和/或类。
单元测试在测试系统的组件行为正确方面是基础的。单元测试在这方面的限制意味着更容易隔离缺陷发生的位置。单元测试通常用于测试代码分支以及函数如何处理不同类型的输入。开发人员通常会在构建中首先运行单元测试,而 QA 工程师可能会首先运行冒烟测试,然后进行任何单元测试。
个别开发人员将在提交更改到版本控制项目(如 GitHub)之前在他们的工作站上运行单元测试。话虽如此,持续集成服务器(如 Jenkins、Travis CI 和 CircleCI)将在运行任何集成测试之前运行单元测试,正如我们在前几章中所看到的。
单元测试示例
我们将查看一个名为circleci-jobs-example(github.com/packtci/circleci-jobs-example)的先前项目,该项目有几个单元测试用于测试单个函数。在存储库中,我们有一个名为sort.js的文件,其中包含以下函数:
/ Takes an array of objects and sorts by First Name
function sortListOfNames(names) {
return names.sort((a, b) => {
if (a.firstName < b.firstName) {
return -1;
}
if (a.firstName > b.firstName) {
return 1;
}
if (a.firstName === b.firstName) {
return 0;
}
});
}
这个函数接受一个对象数组,并按照firstName属性对对象进行排序。对于我们的单元测试,我们只想测试sortListOfNames函数是否按字母顺序排序第一个名字。这是我们在tape.js(github.com/substack/tape)测试库中编写的单元测试:
test('Test the sort function', t => {
t.plan(1);
const names = [
{
firstName: 'Sam',
lastName: 'Cooke'
},
{
firstName: 'Barry',
lastName: 'White'
},
{
firstName: 'Jedi',
lastName: 'Knight'
}
];
const actual = sort.sortListOfNames(names);
const expected = [
{
firstName: 'Barry',
lastName: 'White'
},
{
firstName: 'Jedi',
lastName: 'Knight'
},
{
firstName: 'Sam',
lastName: 'Cooke'
}
];
t.deepEqual(actual, expected, 'The names should be sorted by the first name.')
});
您可以在这里看到,单元测试能够隔离并测试sortListOfNames函数的行为,这非常有用,因为如果sortListOfNames函数出现任何问题,我们可以快速确定回归发生的位置。当然,虽然这个函数非常基本和简单,但您可以看到单元测试在持续集成构建中捕捉软件回归方面起着重要作用。
集成测试
集成测试将测试软件组件组合在一起的方式。虽然单元测试可以帮助验证代码块在隔离状态下的功能,但集成测试可以帮助测试代码块在彼此交互时的情况。集成测试很有用,因为它们可以帮助捕捉软件组件交互时出现的不同类型的问题。
虽然单元测试可能在开发人员的工作站上运行,但集成测试通常在代码检入源代码控制时运行。CI 服务器将检出代码,执行构建步骤,然后进行任何冒烟测试,然后运行单元测试,然后运行集成测试。
由于集成测试是更高级的抽象级别,并且测试软件组件相互交互,它们有助于保护代码库的健康。当开发人员向系统引入新功能时,集成测试可以帮助确保新代码与其他代码块按预期工作。集成测试可以帮助确保系统中的新功能可以安全地部署到环境中。集成测试通常是在开发人员的工作站之外进行的第一种测试类型,并有助于显示是否存在环境依赖性破坏以及新代码是否与外部库、外部服务和/或数据正常行为。
集成测试示例
我们将查看一个公共 API,比如 CircleCI,并编写一个集成测试,以访问 API 端点并验证请求的状态代码和主体是否符合我们的预期。这通常是您正在使用的本地 API,并且希望验证正确行为,但作为示例,我们将仅用于说明目的访问 CircleCI。我们将在 GitHub 中使用我们的packtci用户创建一个名为integration-test-example的新存储库(github.com/packtci/integration-test-example)。我们将使用几个库,包括supertest (github.com/visionmedia/supertest),一个 Node.js 库,baloo (github.com/h2non/baloo),一个用于访问 API 端点的 Golang 库,最后是curl和bash。您使用哪个库并不重要;我只是为了演示目的而使用这些库。
使用 supertest Node.js 库的 API 测试示例
在这个集成测试示例中,我们访问 CircleCI 中的GET /projects (circleci.com/docs/api/v1-reference/#projects)端点。以下是测试此端点的代码:
'use strict'; const request = require('supertest'); const assert = require('assert'); const CIRCLECI_API = { // List of all the projects you're following on CircleCI, with build information organized by branch getProjects: 'https://circleci.com/api/v1.1' }; describe('Testing CircleCI API Endpoints', function() { it('the /projects endpoints should return 200 with a body', function() { return request(CIRCLECI_API.getProjects) .get(`/projects?circle-token=${process.env.CIRCLECI_API_TOKEN_GITHUB}`) .set('Accept', 'application/json') .expect(200) .then(response => { assert.ok(response.body.length > 0, "Body have information") assert.equal(response.body[0].oss, true); }); }); });
在这里,我们测试端点是否返回200的 HTTP 响应,是否有主体,以及oss对象数组中是否有属性。
使用 baloo Golang 库的 API 测试示例
在这个集成测试中,我们访问 Travis API 中的GET /user (developer.travis-ci.com/resource/user#User)端点。以下是测试此端点的代码:
package main import ( "errors" "net/http" "os" "testing" "gopkg.in/h2non/baloo.v3" ) var test = baloo.New("https://api.travis-ci.com") func assertTravisUserEndpoint(res *http.Response, req *http.Request) error {
if res.StatusCode != http.StatusOK {
return errors.New("This endpoint should return a 200 response code")
}
if res.Body == nil {
return errors.New("The body should not be empty")
}
return nil
}
func TestBalooClient(t *testing.T) { test.Get("/user"). SetHeader("Authorization", "token "+os.Getenv("TRAVIS_PERSONAL_TOKEN")). SetHeader("Travis-API-Version", "3"). Expect(t). Status(200). Type("json"). AssertFunc(assertTravisUserEndpoint). Done() }
在这里,我们测试响应是否为200,并且主体具有值。
使用 curl、bash 和 jq 的 API 测试示例
在这个集成测试示例中,我们将访问 CircleCI API 中的最近构建端点GET: /project/:vcs-type/:username/:project (circleci.com/docs/api/v1-reference/#recent-builds-project)。以下是测试此端点的代码:
#! /bin/bash GO_TEMPLATE_EXAMPLE_REPO=$(curl -X GET \ --header "Accept: application/json" \ "https://circleci.com/api/v1.1/project/github/packtci/go-template-example-with-circle-ci?circle-token=$CIRCLECI_API_TOKEN_GITHUB" | jq '.[0].author_name' | tr -d "\n") if [[ -n ${GO_TEMPLATE_EXAMPLE_REPO} ]]; then echo "The current owner was shown" exit 0 else echo "No owner own" exit 1 fi
在这里,我们测试是否从应该在 JSON 有效负载中返回的端点接收了author_name属性。
系统测试
系统测试通常是扩展集成测试的更广泛的集成测试。系统测试将聚合应用程序中的功能组,并且比集成测试的范围更广。系统测试通常在集成测试之后运行,因为它们正在测试应用程序中的更大行为,并且运行时间更长。
系统测试示例
系统测试可以包括:
-
可用性测试:一种测试类型,测试系统的易用性以及系统满足其拟议功能的整体能力
-
负载测试:一种测试类型,用于测量系统在真实负载下的行为
-
回归测试:一种测试类型,用于检查系统在添加新功能时是否正常运行
还有其他类型的系统测试,但我们只包括了一些常见的系统测试类型。
验收测试
我们在整本书中都讨论了验收测试,但是,验收测试是对应用程序行为的正式验证。验收测试通常是 CI/CD 流水线中的最后一种测试,因为它们运行时间较长,并且在验收测试的验证方面更为复杂。
验收测试也可以作为回归测试套件,因为它们可以保证应用程序的行为符合预期。有一些库使用一种称为Gherkin的正式领域特定语言(docs.cucumber.io/gherkin/reference/)。这些库有特定的文件,写下了所谓的验收标准。这些标准规定了新功能需要做什么,对于软件公司来说,编写一个在冲刺开始时失败,一旦满足验收标准就会通过的验收测试并不罕见。
验收测试示例
我们可以在我的名为cucumber-examples(github.com/jbelmont/cucumber-examples)的存储库中查看一个非常简单的验收测试示例,其中有一个 Gherkin 文件,检查我们的验收标准是否满足了一个简单的计算器程序:
# features/simple_addition.feature
Feature: Simple Addition of Numbers
In order to do simple math as a developer I want to add numbers
Scenario: Easy Math Problem
Given a list of numbers set to []
When I add the numbers together by []
Then I get a larger result that is the sum of the numbers
请注意,Gherkin 语法是人类可读的,意在被阅读为新功能声明的列表。在这里,我们声明我们想要能够进行简单的数学加法运算,然后提供一个场景来实现这一点。以下是实现此功能的代码:
const { setWorldConstructor } = require('cucumber')
class Addition {
constructor() {
this.summation = 0
}
setTo(numbers) {
this.numbers = numbers
}
addBy() {
this.summation = this.numbers.reduce((prev, curr) => prev + curr, 0);
}
}
setWorldConstructor(Addition)
这个文件是一个执行简单加法的 JavaScript 类,这里还有另一个类,其中列出了一系列将数字相加的场景:
const { Given, When, Then } = require('cucumber')
const { expect } = require('chai')
Given('a list of numbers set to []', function () {
this.setTo([1, 2, 3, 4, 5])
});
When('I add the numbers together by []', function () {
this.addBy();
});
Then('I get a larger result that is the sum of the numbers', function () {
expect(this.summation).to.eql(15)
});
这是一个非常简单的验收测试,但它旨在说明验收测试是对新功能行为的正式验证。
在 CI/CD 流水线中运行不同测试的最佳实践
我们在第三章中描述了以下阶段,持续交付的基础:
-
CI/CD 流水线的第一个阶段通常包括构建和提交阶段。这是您构建流水线其余部分所需的任何构件并在构建中运行单元测试套件的地方。第一阶段旨在运行非常快,因为开发人员需要有一个短的反馈循环,否则您可能会冒着开发人员绕过此阶段的风险。
-
CI/CD 流水线的第二阶段通常会运行集成测试,因为它们是运行时间较长的测试类型,并且可以在流水线的第一阶段运行并通过后运行。第二阶段是对系统的任何新功能是否破坏了系统集成组件的一层保证。
-
CI/CD 流水线的第三阶段可能包括一套负载测试、回归测试和安全测试,并且比 CI/CD 流水线的前两个阶段运行时间更长。
-
第四阶段可以是验收测试的运行阶段,尽管我个人见过一些公司同时运行验收测试套件和集成测试,因此它们的 CI/CD 流水线只有三个阶段。我们在本章中列出的阶段并不是硬性规定,而只是一些建议,因为每个应用程序在行为上都是独特的。
密码和秘密存储的最佳实践
正如我们在涵盖 Jenkins、Travis CI 和 CircleCI 的章节中所看到的,每个持续集成服务器都有一种存储安全信息(如密码、API 密钥和秘密)的方法。在 CI 服务器中运行某些操作是危险的,比如在 Bash 中使用set -x选项进行执行跟踪。最好是使用 CI 服务器的功能来安全地存储密码和秘密,比如 CircleCI 中每个项目的上下文设置,只有项目所有者才能看到。您也可以使用诸如Vault(www.vaultproject.io/intro/index.html)这样的工具来安全地存储密码,并可以使用 RESTful API 检索,或者使用Amazon Key Management Service(aws.amazon.com/secrets-manager/)等工具。我们将简要介绍在本地开发环境中使用 Vault 来满足密码需求,并调用 Vault 的 RESTful API。
Vault 安装
安装 Vault(www.vaultproject.io/)可以在安装 Vault(www.vaultproject.io/intro/getting-started/install.html)链接中完成。下载 Vault 后,您需要将单个二进制文件移动到操作系统可以找到的PATH中。以下是我在本地机器上运行的示例:
echo $PATH
## This prints out the current path where binaries can be found
mv ~/Downloads /usr/local/bin
最后一个命令将把名为vault的二进制文件移动到我的路径中的/usr/local/bin目录中,然后我现在应该能够运行vault命令并查看帮助菜单,如下所示:
请注意,vault命令有常用命令和其他命令可以运行。
启动 Vault 的开发服务器
我们需要运行vault server -dev命令来启动开发服务器:
请注意,我们得到了一系列指令,以设置我们的本地开发环境。
请记住,这仅用于演示目的,开发模式不适用于生产实例。
检查 Vault 服务器的状态
在以下截图中,我们检查了开发 Vault 服务器的状态:
我们首先在新的 shell 中导出VAULT_ADDR环境变量,因为我们将使用此命令,然后检查我们的开发 Vault 服务器的状态。
在 Vault 中设置 API 密钥
在以下截图中,我们设置了一个 API 密钥,然后使用 Vault 检索它:
我们还可以列出 Vault 中的所有秘密,如下所示:
使用 Vault RESTful API
请记住,我们正在运行开发 Vault 服务器实例,因此我们可以在本地机器上作为 REST 客户端运行curl到 Vault API。让我们运行以下curl命令,检查我们的 Vault 实例是否已初始化,此时应该已经初始化:
curl http://127.0.0.1:8200/v1/sys/init
我们需要创建一个名为config.hcl的文件,以便使用以下内容绕过 Vault 的 TLS 默认设置:
backend "file" {
path = "vault"
}
listener "tcp" {
tls_disable = 1
}
我们需要解封 Vault 并登录,如下截图所示:
请注意,我们得到了一个令牌,这是我们将需要使用以下 HTTP 标头进行 RESTful API 请求的令牌:X-Vault-Token: 3507d8cc-5ca2-28b5-62f9-a54378f3366d。
Vault RESTful API 端点 GET /v1/sys/raw/logical
以下是用于端点的示例curl GET请求:
请注意,我们在运行 Vault 登录ROOT_KEY命令后从标准输出中打印出的令牌。此端点返回给定路径的密钥列表,在本例中为/sys/raw/logical。
秘密管理的总体最佳实践
正如我们在整本书中所述,将原始密码和秘密提交到源代码控制中并不是一个好的做法,您需要有一种安全地检索密码的方法来运行 CI/CD 流水线。您可以使用 CI 服务器本身来存储密码和秘密,然后使用环境变量检索它们,或者您可以使用 Vault 等服务来安全地存储密码。请记住,在 CI 环境中使用 shell 脚本的执行跟踪可能是不安全的,因此在调试构建和在 Bash 中使用set -x标志时要谨慎。
部署最佳实践
在第三章中,持续交付的基础,我们讨论了部署是什么,解释了部署流水线,并谈到了部署流水线中的测试门。我们还谈到了部署脚本和部署生态系统。
让我们在部署时突出一些其他好的策略:
-
创建部署清单
-
发布自动化
创建部署清单
每家公司都会有独特的限制,因此不可能创建一个满足每家公司限制的部署清单,但是总的来说,以下是一些可能在所有部署中有所帮助的指南。
开发人员和运维之间的协作
开发团队和运维之间应该进行沟通,以便正确协调部署。这很关键,因为误解是不可避免的,因此在部署过程中应该进行密切的沟通,以避免中断和数据丢失。
自动化发布
手动流程容易出错,因此应尽可能自动化部署,以避免人为错误。手动流程不可重复,也不可持续,因为部署变得更加复杂。最好有自动化脚本,可以排除人为错误。
部署脚本示例
在软件部署的位置方面有许多不同的选择。因此,根据项目是开源、私有还是企业,部署脚本可能会有很大的不同。许多开源项目只是为每个新版本创建一个 GitHub 发布(help.github.com/articles/creating-releases/),并通过使用 Bash 脚本自动化该过程。一些公司可能使用Heroku(devcenter.heroku.com/start)作为他们的提供商,或者一些公司可能使用AWS CodeDeploy(aws.amazon.com/codedeploy/),但最终,您希望自动化您的部署过程,以便有一个标准和自动化的方式来部署您的软件。还很好地拥有一个部署脚本,可以整理版本控制提交,并能够在每个软件发布中显示新功能和错误修复。
自动化的 GitHub 发布示例
我们将使用 GitHub API 中的以下端点来自动化发布策略:POST /repos/:owner/:repo/releases。此端点的文档可以在developer.github.com/v3/repos/releases/#create-a-release找到。我们将在multiple-languages(github.com/packtci/multiple-languages)GitHub 存储库中创建一个 Golang 脚本,用于创建新的 GitHub 发布。
Golang 脚本示例
我们将使用 Golang 发出 HTTP 请求,并为 Go 脚本提供一些命令行参数。这些参数将用于构建以下request主体,其形式如下:
{
"tag_name": "v1.0.0",
"target_commitish": "master",
"name": "v1.0.0",
"body": "Description of the release",
"draft": false,
"prerelease": false
}
以下是部署脚本的第一部分:
在脚本的这一部分中,我们声明了main包,并获取了一些我们需要发出 HTTP 请求的命令行参数。我们需要解析它们并检查它们是否设置,这就是在main函数中调用checkArgs函数时所做的,如下面的屏幕截图所示:
现在,在脚本的第二部分中,我们在main函数中,解析命令行参数,然后调用我们的checkArgs函数。接下来,我们创建一个匿名结构,用于创建我们的请求体,然后设置 HTTP 请求并设置 HTTP 头。在脚本的最后部分,我们发出请求并打印发布 URL。
让我们在终端会话中展示这个部署脚本的运行:
请注意,我们在go run deploy.go之后提供了四个命令行参数,脚本在最后打印出了发布 URL。
让我们转到multiple-languages(github.com/packtci/multiple-languages/releases)存储库中的 Releases 选项卡,并单击我们的新发布,它看起来像这样:
部署脚本的最佳实践
在为消费者发布新软件时,最好自动化部署过程。不必像我们在这里所做的那样创建自定义部署脚本,因为有很多优秀的库可供使用,它们比我们编写的这个小脚本更结构化和功能丰富。例如,您可以使用GoReleaser(goreleaser.com/)自动化发布脚本,非常适用于 Go 项目。还有许多可用的库是特定于语言的,以及 CI 提供者的选项,例如 TravisCI,可以将您的软件部署到提供者,例如 Google App Engine(docs.travis-ci.com/user/deployment/google-app-engine/)等。
总结
在这最后一章中,我们涵盖了 CI/CD 流水线中不同类型测试的最佳实践,包括单元测试、集成测试、系统测试和验收测试。我们提供了代码示例,并展示了如何使用 Node.js、Golang 和 shell 脚本测试 API 端点的方法。我们介绍了密码管理的最佳实践,并展示了如何使用 Vault 库安全管理秘密以及如何使用 Vault API。我们最后讨论了一些关于部署的最佳实践,包括部署清单、发布自动化以及使用 Golang 编写自定义发布脚本。
这是书的结尾,我希望您已经学到了很多关于 CI/CD、测试和自动化以及使用 Jenkins CI、CircleCI 和 Travis CI 的知识。
问题
-
为什么将集成测试与单元测试分开很重要?
-
提交阶段是什么?
-
提到一种系统测试类型。
-
我们使用的密码管理工具的名称是什么?
-
为什么在 shell 脚本中要小心执行跟踪?
-
提到部署清单中的一项内容。
-
我们提到的 Golang 的部署工具的名称是什么?
进一步阅读
您可以查看 Packt Publishing 出版的书籍Continuous Integration, Delivery, and Deployment(www.packtpub.com/application-development/continuous-integration-delivery-and-deployment)了解更多有关 CI/CD 最佳实践的知识。