持续集成和交付实用手册-全-

持续集成和交付实用手册(全)

原文:zh.annas-archive.org/md5/D4B1782DB08166E400DEF5DF3D2E1241

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

编写现代软件很困难,因为在软件交付中涉及许多团队,包括开发人员、质量保证、运维、产品所有者、客户支持和销售。需要有一个流程,通过该流程,软件的开发是以自动化的方式进行的。持续集成和持续交付的过程将有助于确保交付给最终用户的软件具有最高质量,并经过 CI/CD 流水线的一系列检查。在本书中,您将学习如何使用 Jenkins CI,以及如何编写自由风格脚本、插件,以及如何使用更新的 Jenkins 2.0 UI 和流水线。您将了解 Travis CI 的 UI、Travis CLI、高级日志记录和调试技术,以及 Travis CI 的最佳实践。您还将学习 Circle CI 的 UI、Circle CLI、高级日志记录和调试技术,以及 CircleCI 的最佳实践。在整本书中,我们将讨论诸如容器、安全性和部署等概念。

本书适合对象

本书适用于系统管理员、质量保证工程师、DevOps 和站点可靠性工程师。您应该了解 Unix 编程、基本编程概念和 Git 等版本控制系统。

本书涵盖内容

第一章,自动化测试的 CI/CD,介绍了自动化的概念,并解释了与手动流程相比自动化的重要性。

第二章,持续集成的基础知识,介绍了持续集成的概念,解释了软件构建是什么,并介绍了 CI 构建实践。

第三章,持续交付的基础知识,介绍了持续交付的概念,特别是解释了软件交付、配置管理、部署流水线和脚本编写的问题。

第四章,CI/CD 的商业价值,通过解释沟通问题来介绍 CI/CD 的商业价值,例如能够向团队成员传达痛点、在团队成员之间分享责任、了解您的利益相关者,并展示为什么 CI/CD 很重要。

第五章,Jenkins 的安装和基础知识,帮助您在 Windows、Linux 和 macOS 操作系统上安装 Jenkins CI。您还将学习如何在本地系统上运行 Jenkins 以及如何管理 Jenkins CI。

第六章,编写自由风格脚本,介绍了如何在 Jenkins 中编写自由风格脚本,以及如何配置 Jenkins 中的自由风格脚本,包括添加环境变量和调试自由风格脚本中的问题。

第七章,开发插件,解释了软件中插件的概念,如何使用 Java 和 Maven 创建 Jenkins 插件,并介绍了 Jenkins 插件生态系统。

第八章,使用 Jenkins 构建流水线,详细介绍了 Jenkins 2.0,并解释了如何在 Jenkins 2.0(Blue Ocean)中导航,还详细介绍了新的流水线语法。

第九章,Travis CI 的安装和基础知识,向您介绍了 Travis CI,并解释了 Travis CI 与 Jenkins CI 之间的区别。我们将介绍 Travis 生命周期事件和 Travis YML 语法。我们还将解释如何开始并在 GitHub 上设置。

第十章,Travis CI CLI 命令和自动化,向您展示如何安装 Travis CI CLI,详细解释 CLI 中的每个命令,展示如何在 Travis CI 中自动化任务,并解释如何使用 Travis API。

第十一章,“Travis CI UI 日志和调试”,详细解释了 Travis Web UI,并展示了 Travis CI 中日志和调试的高级技术。

第十二章,“CircleCI 的安装和基础知识”,帮助您在 Bitbucket 和 GitHub 上设置 CircleCI,并展示如何使用 CircleCI Web UI。我们还将解释 CircleCI YML 语法。

第十三章,“CircleCI CLI 命令和自动化”,帮助您安装 CircleCI CLI,并解释 CLI 中的每个命令。我们还将介绍 CircleCI 中的工作流程以及如何使用 CircleCI API。

第十四章,“CircleCI UI 日志和调试”,详细解释了作业日志,并展示了如何在 CircleCI 中调试缓慢的构建。我们还将介绍 CircleCI 中的日志记录和故障排除技术。

第十五章,“最佳实践”,涵盖了编写单元测试、集成测试、系统测试、CI/CD 中的验收测试的最佳实践,以及密码和秘密管理的最佳实践。我们还将介绍部署的最佳实践。

充分利用本书

为了充分利用本书,您需要熟悉 Unix 编程概念,比如使用 Bash shell、环境变量和 shell 脚本,并了解 Unix 的基本命令。您应该熟悉版本控制的概念,知道提交是什么意思,并且需要了解如何使用 Git。您应该了解基本的编程语言概念,因为我们将使用诸如 Golang、Node.js 和 Java 之类的语言,这些语言将作为我们在 CI/CD 流水线和示例中使用的构建语言。

本书不受操作系统限制,但是为了使用本书中的一些概念,您需要访问 Unix 环境和命令。因此,如果您使用 Windows,最好安装 Git Bash (git-scm.com/downloads)和/或 Ubuntu 子系统。您需要在系统中安装 Git (git-scm.com/downloads)、Docker (docs.docker.com/install/)、Node.js (nodejs.org/en/download/)、Golang (golang.org/dl/)和 Java (java.com/en/download/)。最好安装文本编辑器,如 Visual Studio Code (code.visualstudio.com/download)和终端控制台应用程序。

下载示例代码文件

您可以从www.packtpub.com的帐户中下载本书的示例代码文件。如果您在其他地方购买了本书,可以访问www.packtpub.com/support并注册,文件将直接通过电子邮件发送给您。

您可以按照以下步骤下载代码文件:

  1. www.packtpub.com上登录或注册。

  2. 选择“支持”选项卡。

  3. 单击“代码下载和勘误”。

  4. 在搜索框中输入书名,然后按照屏幕上的说明操作。

下载文件后,请确保使用以下最新版本的解压缩或提取文件夹:

  • Windows 上的 WinRAR/7-Zip

  • Mac 上的 Zipeg/iZip/UnRarX

  • Linux 上的 7-Zip/PeaZip

本书的代码包也托管在 GitHub 上,网址是github.com/PacktPublishing/Hands-On-Continuous-Integration-and-Delivery,在 README 部分,您可以找到按章节划分的所有代码文件的链接。如果代码有更新,链接将在现有的 GitHub 存储库中更新。

我们还有来自我们丰富的图书和视频目录的其他代码包,可在github.com/PacktPublishing/上找到。去看看吧!

使用的约定

本书中使用了许多文本约定。

CodeInText:表示文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 句柄。这是一个例子:“Chocolatey 安装说明可以在chocolatey.org/install找到。”

代码块设置如下:

{
  "@type": "env_vars",
  "@href": "/repo/19721247/env_vars",
  "@representation": "standard",
  "env_vars": [

  ]
} 

任何命令行输入或输出都以以下方式编写:

 Rules updated
Rules updated (v6) 

粗体:表示一个新术语、一个重要词或屏幕上看到的词。例如,菜单或对话框中的单词会以这种方式出现在文本中。这是一个例子:“点击继续,确保点击同意按钮。”

警告或重要说明会显示为这样。提示和技巧会显示为这样。

第一章:自动化测试的 CI/CD

在本书中,我们将探讨持续集成CI)和持续交付CD)的概念,并使用 Jenkins、Travis CI 和 CircleCI 等工具应用这些概念。我们将编写许多实用脚本,并探索真实世界的 CI/CD 自动化脚本和场景。本章将通过解释一个名为比利·鲍勃机械零件的虚构公司的当前实践来帮助阐明自动化的概念。比利·鲍勃机械零件公司有许多手动流程,并且由于软件发布只由首席开发人员完成,质量保证(QA)和开发团队之间存在一些紧张关系。

本章将涵盖以下主题:

  • 手动流程-一个假设的场景

  • 员工的挫败感

  • 引入自动化

  • 开发人员的生产力

  • 打破沟通障碍

  • 创建协作环境

业务场景

本章将描述一个模拟的手动流程以及手动测试和手动流程中固有的缺陷,并将解释如何使用 CI/CD 可以大大提高开发人员的生产力。在这种情况下,每个成员都设置了一套手动流程,完成起来非常耗时。此外,如果 QA 在最新的发布版本中遇到问题,这些步骤必须重新执行。

我们将在我们虚构的公司的多个部门中看到不同的场景。一些场景将关注开发团队、QA 团队、客户成功团队和销售团队的痛点。我们将构建可能发生在这些团队中的场景,识别适合自动化的领域,并且通过这些团队之间的沟通揭示出可以通过自动化大大改进的领域。

以下图表显示了一些业务场景:

手动流程-一个假设的场景

贝蒂·苏是比利·鲍勃机械零件公司的 QA 部门的一部分。在比利·鲍勃机械零件公司,有一个中等规模的开发团队。首席开发人员埃里克在季末的星期四早上开始手动发布流程。埃里克需要两天时间来完成发布周期。不幸的是,他是开发团队中唯一能够进行发布的人。埃里克在本地工作站上运行所有测试,并在必要时集成紧急补丁。埃里克完成后,他会将一个 ZIP 文件通过电子邮件发送给 QA 部门的贝蒂·苏。

贝蒂·苏有几名 QA 工程师,并在星期一早上开始新版本的手动测试周期。贝蒂通知埃里克,她已经在最新版本中发现了几个问题。贝蒂准备了一个 Excel 电子表格,记录了最新版本引入的问题。在周末结束时,贝蒂已经将最新版本的问题列表分解为关键、高、中和低优先级的错误。

软件缺陷是软件产品中未按预期运行的缺陷。

在发布周期中,埃里克和贝蒂在解决问题时都要重新执行每个步骤。埃里克必须重新打包所有软件组件,并在本地工作站上重新运行所有测试。贝蒂必须重新进行测试周期,因为她必须检查回归,并确保最新的修复不会破坏软件组件中的现有功能。

迈克尔是团队中的初级开发人员,也在进行手动流程。迈克尔从埃里克那里得到了一个问题清单,并开始处理列表中更重要的错误。迈克尔试图解决每个错误,但没有编写任何回归测试,以确保新代码没有破坏现有功能。当迈克尔完成时,他告诉埃里克他这边一切都很好。不幸的是,埃里克在本地工作站上运行所有测试时看到了测试失败。埃里克告诉迈克尔,在处理错误列表时需要更加小心。

QA 部门的成员迪伦开始测试新版本的部分,并告诉贝蒂该版本存在几个问题。他已经创建了一个问题清单,并将其发送给贝蒂。不幸的是,迪伦所做的一些工作已经被贝蒂重复,因为他们在两个不同的清单中都突出显示了相似的项目。贝蒂告诉迪伦,QA 需要确保不会重复做相同的工作。迪伦回去突出显示他将要测试的版本的部分。

詹妮弗领导客户成功团队,并在质量保证部门通知新版本准备好向客户开放时收到通知。詹妮弗开始准备最新版本功能的视频,并因此向质量保证部门询问新版本的变化。

鲍比是客户成功团队的经验丰富的成员,并开始制作关于最新功能的视频。当发布公司博客上的版本视频时,质量保证部门意识到一些视频错误地说明了仍处于测试版本计划中的功能。詹妮弗现在迅速召集客户成功团队,并要求质量保证部门在将一些功能发送给客户成功团队之前清楚地标记为测试版本。

销售团队一直在通过电子邮件发送销售工程师在与潜在客户会议期间所做的笔记。桑迪手动输入了关于每个潜在客户的详细笔记,并使用 Excel 电子表格对重要的销售信息进行分类。不幸的是,销售团队将新更改的 Excel 电子表格发送给销售部门。有时会出现混乱,因为销售工程师会打开旧的 Excel 文档,并错误地向其他销售工程师提供过时的信息。

UI/UX 团队倾向于使用大量的模型和线框图。通常,在原型阶段,UI/UX 工程师会在模型中嵌入注释,详细说明验证状态和页面交互。维克多在其中一个线框图中看到一个注释,并意识到线框图中嵌入了重要的页面逻辑。维克多询问 UI/UX 团队是否可以与开发团队共享注释。UI/UX 团队还使用艺术板并为每个功能工作创建 ZIP 文件。例如,桑迪被分配了关于新页面 UI 交互的工作,并一直在做详细的笔记。UI/UX 团队的许多工作往往是高度视觉化的,颜色代表着不同的含义。工作的视觉方面往往意味着在 UI 流程的各个阶段应该发生某些动作。开发人员往往处理更具体的项目,因此并不总是清楚自然流程应该发生什么。例如,如果删除一个项目,是否会弹出一个模态,即一个小窗口进行确认,还是立即删除一个项目?提交表单时,UI 是否以特定颜色显示错误指示,以另一种颜色显示警告验证应该放在什么位置?有时,UI 交互流程没有详细描述,开发人员必须与 UI/UX 来回沟通。记录决策文件中的决定原因是很重要的。

员工的挫败感

贝蒂·苏给维克多发了一份按优先级分类的问题列表。必须首先处理更高优先级的问题,而较低优先级的问题则稍后处理。维克多拿到了最新发布的问题列表,并通知开发团队他们必须立即停止正在进行的新功能工作,并开始修复最新发布的问题。大卫是团队中的一名高级开发人员,他感到沮丧,因为他之前进展顺利,现在又在匆忙地重新适应一个月前的工作。

迈克尔是团队中的一名初级开发人员,对代码库还不太熟悉,他担心列表上的一个更高优先级的问题。迈克尔急忙解决了更高优先级的问题,但没有考虑编写任何回归测试用例。迈克尔迅速为他的高优先级工单编写了一个补丁,并将补丁发送给了维克多。维克多很快发现了迈克尔的补丁中的回归和破损的测试用例。迈克尔不知道他应该编写回归测试用例来确保没有回归。

发布新补丁的流程没有得到适当的记录,而迈克尔这样的新开发人员经常会产生破坏现有工作的回归。维克多教迈克尔回归测试的概念,迈克尔迅速编写了带有回归测试用例的软件补丁。

一旦维克多准备好了所有的新软件补丁,他就开始进行热修复发布,并在本地机器上重新运行所有测试。贝蒂得到了最新发布的新 ZIP 文件,并再次开始手动测试流程。QA 部门正在手动测试产品的部分,因此测试所有产品部分是一个耗时的任务。贝蒂发现了最新发布的一些问题,并给维克多发送了一个较小的列表,以便在本周晚些时候开始处理。

大卫被维克多突然叫停,并被告知放弃他的新功能工作,因为最新的更改存在缺陷。大卫花了接下来的两个小时试图重新适应最新发布的问题。一旦他确信自己已经追踪到问题,他花了下午时间进行修复。大卫通知维克多最新的更改已经准备好进行测试。维克多开始在他的工作站上运行测试,立即发现由于最新更改,一些集成测试现在失败了,并通知大卫这些问题必须得到解决。大卫现在感到沮丧,他加班到深夜又进行了一次修复。第二天早上,维克多运行了所有的测试,一切都通过了,所以他给贝蒂发送了最新热修复的 ZIP 文件。贝蒂第二天开始进行手动测试流程,不幸的是,她再次发现了一些小问题,并在下午通知维克多最新发布仍然存在一些问题。

维克多此刻承认感到沮丧,他把所有开发人员都召集到一个房间里,并说除非所有问题都得到解决,否则谁也不能离开。在度过了一个漫长的晚上后,所有最新的问题都得到了解决,维克多让每个人回家。第二天早上,维克多打包了最新的发布,并给贝蒂发送了一个新的 ZIP 文件。贝蒂在上一次测试周期后有些担心,但她很高兴所有的错误都得到了解决,并给予了 QA 的批准,并告诉维克多最新发布已经准备就绪。开发团队和 QA 团队一起庆祝了一周工作的结束,并享受了公司赞助的午餐,然后回家过周末。

在 QA 部门测试热修复时,一些 QA 团队成员的工作出现了重叠。Dillon 感到沮丧,因为他的一些工作与 Betty 的工作重叠。QA 部门没有自动化,因此每次发布都需要手动完成所有工作,无论是补丁还是常规发布,QA 都必须重新测试 UI 的所有部分。QA 团队的新成员 Nate 问 Dillon 是否有比手动测试更好的工作方式,但被告知这些做法已经在 QA 部门实施。

客户成功团队的 Tony 对新版本感到沮丧,因为他花了很多时间为客户X创建新视频,却被告知他的一些视频无法发布,需要放入储备库。QA 部门在最后一刻做出了停止功能Y的决定,但没有将这些信息传达给其他部门。

销售工程师领队之一的 Victor 正在进行公司演示,并向潜在客户展示导出 PDF 功能。在演示过程中,Victor 点击导出 PDF 功能,出现了一个显眼的错误消息。Victor 迅速转移到产品的另一个方面,称这是一个暂时的故障,并表示他将在另一个演示中展示这个功能。Victor 发现其中一个开发人员在后端服务中进行了一个本应该是简单更改的操作,却在生产环境中破坏了导出 PDF 功能。Victor 发现潜在客户已决定选择另一种软件解决方案,现在他显然很沮丧,因为他指望这位新客户来获得年终奖金。

UI/UX 团队的成员 Samantha 被告知她的一份模型缺少验证流程。Samantha 在功能Z的原型设计阶段寻求澄清,被告知页面不需要任何验证,但 David 认为需要验证流程。Samantha 显然很沮丧,决定休息一天,现在 David 在功能Z的工作上落后了进度。

QA 的 Betty Sue 和开发团队的 John 之间有双向沟通。在寻找自动化帮助的领域时,沟通是至关重要的。随着各方之间的互动次数增加,参与方对手动流程的意识也在增加。手动流程一直隐藏着,直到更多的参与方,如营销、销售、客户成功和开发团队开始更频繁地合作。开发人员特别适合发现手动流程,因为非开发人员并不总能意识到一个流程是手动的,可以被自动化。

引入自动化

这是一个名为Johnny The Automation Bot的插图,用于描述公司的不同部门。Johnny 的每个肢体代表公司的一个不同部门:

Johnny The Automation Bot 是一个可以极大受益于自动化流程的领域的插图。自动化可以被视为一种程序或系统,其中机器完成了人类通常会做的工作。自动化需要了解正在进行的手动流程,与其他部门进行沟通,并找出哪些流程是手动进行的。CI 和 CD,正如我们将在本书中看到的,是极大增强公司生产力和流程的过程,因为它们消除了开发人员的假设和特定环境设置。

约翰尼自动化机器人的每个部分都有一个可以自动化的领域。销售部门目前正在向销售团队发送 Excel 工作表,并且很难跟上其他销售工程师所做的更改。约翰尼自动化机器人建议销售工程师有一个简单的方法将销售信息上传到公司内部网络,以更好地跟踪销售信息。约翰尼建议开发团队编写一个 Excel 集成,销售工程师可以轻松地将新的销售数据上传到公司内部网络。例如,可以添加一个菜单选项,连接到公司的 API 端点,自动将新的 Excel 更改上传到公司内部网络页面,该页面具有最新的销售信息。

质量保证部门正在手动测试产品,而手动测试是一项耗时且容易出错的活动。约翰尼建议质量保证部门开始使用Selenium WebDriver编写验收测试。Selenium 是一个浏览器自动化工具,质量保证部门可以使用诸如 Python 之类的语言来编写验收测试。约翰尼表示,使用 Selenium 编写自动化测试的优势在于可以编写一次并反复重用。这将带来额外的好处,即这些测试可以连接到 CI/CD 流水线中,我们将在本书的后面看到。

贝蒂从质量保证部门发现客户成功团队正在制作一系列视频,教授客户每个构建中的新功能。客户成功团队通过 FTP 上传视频,其中一些团队成员花费大部分时间上传文件。约翰尼自动化机器人建议通过脚本自动化这个过程。该脚本应该足够直观,以便客户成功团队的任何成员都可以运行它,并且应该能够在上传过程中出现任何网络延迟时进行重试。贝蒂分享了质量保证部门编写的一个可以自动化这个过程并作为后台进程运行的脚本。

托尼,客户成功团队的成员,现在在工作日中解放了几个小时的工作时间,可以专注于工作的更重要方面,比如通过创建出色的视频来帮助客户取得成功。托尼已经开始与质量保证团队合作,将开始发布视频并对产品的部分进行用户验收测试。由于手动测试已经委托给客户成功团队,质量保证现在能够更好地测试功能。质量保证正在专注于使用新的库自动化端到端测试套件,这将帮助他们更快地编写测试,并通知开发团队功能出现故障。

营销团队一直在 PowerPoint 幻灯片中嵌入注释,有时在演示或公司展示中注释会丢失或被覆盖。约翰尼建议开发团队创建一个可以将 PowerPoint 幻灯片转换为 markdown 的脚本,然后 markdown 文件可以进行版本控制,因为 markdown 只是文本文件。这将有额外的好处,即营销团队可以与销售团队分享信息,以创建更具说明性的图表。

维克多意识到手动流程正在破坏生产力,并且手动流程具有明显的劣势。维克多可以在发布周期中引入一个自动化系统,任何开发人员都可以通过一个单击部署按钮来运行。与维克多目前正在做的在本地工作站上运行所有测试不同,每个软件构建都可以推送到诸如 GitHub 之类的版本控制系统,并且所有测试都可以在 CI 环境(如 Jenkins)上运行,并且开发人员可以自动收到测试是否通过或失败的通知。例如,布鲁斯是团队中的一名新开发人员,他可以快速阅读开发人员文档,并在几乎没有指导的情况下开始进行下一个发布。约翰自动化机器人对这种做法表示赞许。

贝蒂也有机会自动化手动测试过程。使用诸如BrowserStack之类的工具,贝蒂可以编写一系列测试脚本,测试产品的每个部分。在一个小时内,贝蒂可以在测试环境中运行一套验收测试,并让维克多知道发布中的最新问题。维克多可以开始将问题分配给开发团队,并开始编写回归测试用例,以确保当前构建中没有回归。维克多对最新更改的工作原理感到自信,可以指导贝蒂到一个新的 URL,她可以下载最新的软件发布。Johnny 自动化机器人指出,创建 ZIP 文件并通过电子邮件发送的旧做法并不是一个好的做法,因为每次都需要额外的步骤,如果发送了错误的 ZIP 文件,可能会出现错误。约翰建议 QA 部门有一个专用的 URL,所有最新的发布都在那里,并且每个发布都有版本和特定信息,比如热修复。例如,最新的热修复可以是v5.1.0-hotfix 1,因此,对于每个热修复,QA 部门都会有一个压缩文件,其中包含最新的构建和一个说明符,比如热修复。如果这个构建是一个常规构建,那么它可以被命名为v5.1.0

维克多发现 QA 部门有一个 Browser Stack 账户。Browser Stack 提供对整套浏览器和移动客户端的访问,这可以帮助自动化 UI 的负载测试。负载测试是使用开发团队用于特殊场景(如负载测试)的自定义服务器进行的。约翰自动化机器人建议使用诸如 Browser Stack 之类的服务,或者拥有一个可以提供必要资源进行负载测试的自定义服务。

维克多发现 QA 团队在测试开发团队编写的电子邮件服务时遇到了问题。约翰自动化机器人建议开发团队确保 QA 拥有可以帮助使用电子邮件服务的脚本。维克多告诉贝蒂,新的电子邮件服务正在代理到SendGrid服务,并且开发团队已经编写了一系列 QA 可以使用的脚本。这些脚本有助于编写测试电子邮件,并可以帮助 QA 测试在失败条件下会发生什么。

UI/UX 团队正在将模型上传到Sketch - Sketch 是一个原型工具 - 并嵌入有关页面可能的验证状态和流程的注释。这些注释非常详细,对开发团队在公司冲刺中开始功能工作时非常有帮助。Johnny 自动化机器人建议开发团队编写一个插件,可以帮助 UI/UX 团队轻松共享这些信息。维克多决定创建一个 Sketch 插件,该插件可以创建一个带有嵌入式注释的 PDF,UI/UX 团队在原型制作完成后可以通过电子邮件发送给开发团队。这个插件对 UI/UX 团队来说很容易安装,因为他们只需双击文件,它就会自动安装插件。访问 PDF 和嵌入的注释将帮助开发人员了解新功能的用例和 UI 流程。

文森特,一位主要销售工程师,已经与开发团队沟通,他需要及时了解产品的流程变化,特别是在向潜在客户介绍公司路线图上的新功能时。约翰尼自动化机器人建议开发团队利用 Git 提交日志,其中包含有关最新功能更改的详细信息。维克多编写了一个脚本,用于抓取 Git 提交日志并编写一个包含所有最新功能工作的漂亮的 Markdown 文档。反过来,客户成功团队可以与开发团队合作,使用 Markdown 文件在公司博客上详细介绍所有最新功能。

这里有一个共同的主题。部门之间的沟通是发现手动流程并创建帮助自动化流程的合作伙伴关系的关键。除非了解手动流程,否则无法进行自动化,有时,唯一的自动化发生的方式是其他部门传达特定的痛点。

让我们重申一些通过开放协作自动化和增强的流程。维克多通过提供开发团队创建的脚本来帮助 QA 自动化了电子邮件测试服务问题。QA 通过分享一个上传视频并具有重试逻辑的脚本,帮助客户成功团队自动化了视频上传任务。销售部门表示需要更好地了解产品的新功能;这促使开发团队编写了一个从 Git 提交日志中获取信息并生成 Markdown 文件的脚本,客户成功团队使用该文件在公司博客上写了一篇漂亮的博客文章。UI/UX 团队现在在其 Sketch 应用程序中集成了一个插件,他们可以简单地点击一个按钮来生成在原型阶段记录的笔记的 PDF 文档,这反过来有助于开发团队开发新功能。开发团队发现 QA 正在使用一个名为 BrowserStack 的工具,并开始使用它来对产品进行负载测试。营销团队现在拥有营销 PowerPoint 幻灯片的版本副本,并将这些信息分享给销售团队,以创建公司演示的新图表。

UI/UX 团队决定创建一个样式指南,开发人员可以在软件产品中查找常见的 UI 模式。UI/UX 团队发现不同页面中使用了许多不同的样式,这导致许多客户感到困惑。例如,零部件供应页面在一个页面上有一个大蓝色保存按钮和一个红色取消按钮,但在供应商详细信息页面上有一个大红色保存按钮和一个蓝色取消按钮。客户因为 UI 没有统一使用颜色而点击错误的按钮。有时,页面使用确认模态框来添加和删除项目;其他时候,没有确认模态框。UI/UX 团队已经开始制作样式指南,并在公司内部网站上创建一个特殊的 URL,用于存放实时样式指南。其目的是明确创建和列出页面可用的十六进制颜色,设计产品中的所有按钮,并决定页面上表单的外观和行为。

此外,将有一个特殊的小部件页面,其中包含产品中所有专用小部件的 HTML 标记和样式嵌入:

这个样式指南具有十六进制颜色值,并嵌入了一些 HTML 元素和一个切换开关,这是一个专门的小部件,有关闭状态和开启状态。样式指南的目的是让开发人员能够简单地右键单击并复制 HTML 标记和 CSS,并建立统一的 UI 呈现。这是一种自动化形式,因为开发人员可以简单地重用现有的标记和样式,而不必手动创建 HTML 和自定义样式,其中统一性最好使用。每当产品的用户不得不猜测该做什么时,你肯定会招致灾难。

开发人员的生产力

由于维克多将 CI/CD 管道引入了构建中,许多耗时的活动现在都被自动化管道所取代。每当软件被推送到版本控制系统(如 Git)等上游时,Jenkins 会触发自动构建,运行所有单元测试和集成测试。开发人员可以迅速知道他们编写的代码是否引入了缺陷。记住,维克多不得不整合所有软件补丁,并在本地工作站上手动运行所有测试。这是乏味、耗时且不必要的。

由于所有软件都是向上游推送的,维克多已经为发布分支设定了代码截止日期,并开始对软件发布二进制文件进行版本控制,以便质量保证人员可以更清晰地划分每个构建版本。维克多立即变得更有效率,因为他可以开始将发布周期委托给团队中的其他开发人员。发布周期中遇到的任何问题都可以由任何开发人员在发布中记录下来。维克多现在有更多时间开始规划下一个软件周期,并在团队中指导初级开发人员。大卫现在很高兴,因为他可以将最新的更改推送到源代码控制,并在 CI 环境中运行所有测试,并更有信心地确保他的更改按预期工作。

贝蒂已经建立了一整套验收测试,检查软件产品的每个部分。产品中的任何回归问题都会立即在 CI 环境中显现出来,并且所有测试都可以每天运行。质量保证团队运行的测试比开发团队的测试更耗时和资源密集,因为它们是端到端的测试,但对于质量保证团队来说,优势在于所有测试都可以每天运行,并且质量保证团队每晚都会收到详细的测试失败报告。贝蒂编写了一组页面对象,帮助质量保证团队的其他成员重用其他测试脚本并减少测试周期。贝蒂现在在质量保证周期中有时间指导质量保证部门的新成员进行测试实践,并教他们如何正确地标记问题,以便开发团队知道最新发布中的问题所在:

这里的螺栓象征着已经就位的流程;在这种情况下,需要自动化的发布流程。

大卫现在可以开始帮助维克多指导团队中的初级开发人员,开发团队已经开始进行午餐学习系列,其他开发人员可以在团队中分享知识。开发团队很快意识到这些午餐学习会议也适用于质量保证部门。在其中一次午餐学习会议中,质量保证部门提出了一个关于协调质量保证和开发团队之间发布的变更。通过这种合作,发布周期从一周的过程缩短到 3 小时的过程。开发团队轮流进行发布工作,以便团队中的每个开发人员都可以学习如何进行发布。值班的开发人员确保质量保证部门有一个可供质量保证开始测试的构建,并且可以使用 CI 系统(如 Jenkins、Travis 或 CircleCI)自动触发此构建。在这些 CI 环境中,您可以设置在指定日期和时间运行的构建触发器。质量保证部门通知开发团队发布中的任何回归,每当开发团队准备推送热修复时,构建都清楚地用以下模式标明 - vMAJOR.MINOR.PATH-[hotfix]-[0-9]*。为了清晰起见,这里有一个例子 - v6.0.0-hotfix-1。这标明了主要版本6,次要版本0,补丁版本0和热修复编号1。这种命名方案有助于质量保证部门区分常规构建和hotfix构建。

客户成功团队已经向开发团队传达了一些客户在使用 Billy Bob's Machine Parts 应用程序编程接口(API)服务时遇到问题。客户成功团队询问开发团队是否有任何方法可以帮助新的第三方 API 消费者上手。为了澄清,API 消费者是指正在使用现有 API 的人,而 API 提供者是维护实际 API 服务的人;因此,在这方面,Billy Bob's Machine Part 是提供运行 API 供第三方开发人员使用的 API 提供者。开发团队告诉客户成功团队,他们一直想要创建一个开发者门户,这将帮助 API 消费者轻松地使用 API。然而,开发团队很难说服高层管理层开发者门户的价值,因为没有人要求这个特定的功能。客户成功团队迅速说服了高层管理层,开发者门户对 Billy Bob's Machine Parts API 消费者来说将是一项巨大的资产,API 消费者可以开始使用 API 服务的数据构建漂亮的仪表板。

在其中一次开发者会议中,发现营销团队正在使用 Google Docs 共享文档,但很难找到上传的文档,因为你必须知道你在找什么。维克多很快意识到开发团队可以帮助建立一个公司内部网,销售和营销团队可以以更一致的方式共享数据。几个月后,公司内部网被公开,销售和营销团队兴奋地提到公司内部网已经帮助他们自动化了文档共享流程,过去销售和营销之间的许多会议浪费了无数时间来寻找某些文档。公司内部网暴露了一个过滤机制,可以使用标签系统快速找到文档。公司内部网还实现了一个功能,即销售和营销团队可以编辑共享文档。

销售团队现在有一个很好的工具可以与公司博客一起使用,展示新产品功能。维克多现在可以查看公司博客,了解产品中的最新功能。所有这些都是因为维克多编写的脚本而实现的,该脚本会从 Git 提交日志中提取提交消息,然后发布一个漂亮的 markdown 文件。该脚本在每个发布上都会使用,并列出所有已处理的项目,开发团队将新创建的 markdown 文件发送给客户成功团队,他们又根据这个 markdown 文件撰写了一篇详细介绍最新发布的博客文章。

QA 团队开始处理一个问题单,其中部分限制导致特定的 UI 错误。特别是,如果客户在产品详细页面上有超过 10,000 个零件列表,那么产品详细页面会崩溃,而没有任何有用的指示发生了什么。开发团队发现 QA 团队正在手动在新产品页面上创建新产品。开发团队通过让 QA 了解一个可以通过管理员端点来程序化创建语音邮件的端点来帮助 QA 团队。开发团队帮助编写了一个脚本,通过程序化生成新零件,从而节省了 QA 团队手动创建零件的耗时任务。

打破沟通障碍

为了实现自动化,必须打破团队之间的沟通障碍。有时,不同的团队可能认为他们在同一个页面上,但实际上他们在谈论不同的事情。

打破沟通障碍是很重要的,为了解决误解:

有趣的是,在发布周期内仍然有更多的自动化空间。维克多询问贝蒂关于一些在源代码控制中的验收测试。维克多意识到他可以将验收测试集成到 CI 环境中,并创建一个次要构建,每晚运行所有验收测试,并且 QA 将在每天早上得到一份详细最新失败的报告。然后 QA 可以在每天早上审查失败的验收测试,并通知开发团队特定功能x已经出现问题,例如零件供应页面,正在处理这个新功能的开发人员需要重新检查新的业务逻辑。

大卫开始与 UI/UX 团队交谈,并发现新公开的 API 端点和构建新页面之间存在瓶颈。前端开发人员在这些页面中模拟数据,并经常对意外的 JSON 负载感到惊讶。前端开发人员有时要等待几周才能发布 API 端点,而他们开始模拟数据而不是坐等,这带来了意想不到的后果,他们开始假设数据模型将会是什么样子,这反过来使得更改页面变得更加困难。大卫让维克多知道存在可以快速为端点搭建数据模型并为 API 端点提供当前数据模型的工具。大卫开始使用 Swagger 作为构建 API 服务中的新 API 的工具。Swagger 有助于减少开发团队和 UI/UX 团队之间的摩擦,因为 UI/UX 团队不必再等待数据模型。资深 UI/UX 开发人员杰森现在可以快速开始构建新页面,因为他确切地知道可以从新 API 端点期望的负载类型。

QA 团队成员阿曼达已经开始与客户成功团队合作进行负载测试和用户验收测试。在用户验收测试周期中添加了验收测试,暴露了核心产品中可以改进 UI/UX 的地方。客户成功团队现在有了测试新页面和暴露可能的 UI 问题的额外责任。验收测试适用于测试顺利的路径场景,也就是当一切都按预期完成时,但用户验收测试可以暴露 UI 中不直观的工作流程。例如,拉里开始测试零部件供应页面中的新过滤功能,并发现为了开始过滤,需要点击复选框。拉里问 QA 为什么过滤不能默认进行,为什么需要复选框;然后开发人员开始着手添加默认过滤:

该图示没有复选框,而只是一个使用输入框的页面,每当用户输入Enter、逗号或Tab时,就会应用一个新的过滤器,然后页面会自动过滤。如果没有结果显示,则会显示文本“未找到结果”。

客户成功团队成员贾斯汀问 QA 团队成员弗朗西斯是否可以借用 QA 部门测试的新功能视频。贾斯汀意识到 QA 团队拥有一套非常有价值的视频,客户成功团队可以利用它来教客户如何使用最新功能。弗朗西斯为客户成功团队创建了一个内部门户网站,用于 QA 部门发布新视频时使用。客户成功团队一直在为新客户创建入职视频,并设计了一个知识门户,解释如何设置,例如,一个新的零部件供应页面。

销售团队一直在通过个人电子邮件向客户和潜在潜在客户讨论发送笔记。维克多发现销售经理哈里最近丢失了一些有价值的笔记,因为他不小心删除了与潜在客户共进午餐时所做的笔记。维克多告诉哈里公司有一个新的公司内部网,有一个项目页面,可以创建卡片,所以销售团队可以为每个潜在客户创建销售展示。哈里为一个潜在客户创建了一个新的销售展示,并与首席销售执行官吉姆分享。吉姆表达了极大的兴奋,因为他也意识到公司内部网也可以用来创建图表。吉姆使用以下图表向首席销售执行官展示最新的销售线索:

销售团队可以为每个潜在客户创建一个项目展示。公司内部网正在帮助公司内部更多团队整合,因为团队成员正在打破沟通障碍。

开发团队发现客户成功团队一直在使用界面进行耗时且容易出错的流程。领导开发人员维克多制定了一个创建命令行界面CLI)的计划,客户成功团队可以利用它来自动化当前手动流程的许多部分。开发团队解释了 CLI 如何可以为客户成功团队节省大量时间,还可以帮助 API 消费者更好地使用 API 服务。CLI 可以快速为界面提供重要的页面数据。随着每个新版本可能会暴露出新的端点,开发团队制定了一个计划,为 CLI 添加与新 API 端点一起使用的额外命令。

CLI 应用程序将与开发者门户倡议协同工作,并提高 API 消费者的采用率。除此之外,开发团队决定启动一个关于 API 消费者可以利用的软件开发工具包SDK)的倡议来使用 API。SDK 可以极大地改善和增强 API 提供者的第三方采用,并因此增加 API 的采用率。SDK 特别有用,因为开发人员和质量保证人员使用不同的编程语言。机器零件 API 的开发人员正在使用 Golang 编程语言,而质量保证人员大部分工作都在使用 Python。SDK 将支持许多编程语言,并将帮助 API 消费者快速上手,因为他们可以选择自己喜欢的语言来使用 API 服务。

为了使手动流程自动化,组织内部不同团队之间必须进行沟通。组织中的团队肯定会有手动流程。开发领导、质量保证领导、客户成功领导和 UI/UX 领导开始每月开会,讨论新的实践,并开始寻找公司内需要自动化的其他手动流程。

手动流程本质上并不是坏事,用户验收测试UAT)在公司中仍然有效,并且可以帮助揭露自动化测试无法发现的问题。UAT 对于测试自动化测试有时无法发现的边缘情况场景尤其有帮助,就像之前展示的例子一样,客户成功团队测试了一个新功能,并发现零件详情页面只有在复选框被选中时才能进行筛选。

营销、销售和客户成功团队通常使用电子表格应用程序,如 Excel,来计算数字,以及演示应用程序,如 PowerPoint,来创建图表。通常,在 Excel 电子表格中计算的数字会保存多个版本,但团队成员必须通过电子邮件向其他团队成员发送副本。开发人员可以要求营销、销售和客户成功团队以逗号分隔值CSV)形式导出 Excel 中的值,这是一个文本文件,更容易处理。这样做的附加价值是公司内部网可以使用数据可视化工具创建漂亮的 HTML 图表和演示文稿。可以利用 D3 等库创建许多不同类型的可视化效果,非常强大。

创建协作环境

为了使团队开始合作并公开讨论问题,必须存在一种开放的精神。团队很容易被隔离,意味着他们与其他团队的工作脱节。开发团队很容易选择与质量保证部门保持脱节。问题在于沟通是揭露团队之间手动流程的最重要元素。没有沟通,团队将独立地处理他们认为重要的事项。

不同团队参与的社交活动可以帮助打破障碍,建立友好的环境。通常,开发人员喜欢参加会议,仅仅是为了与其他开发人员进行社交互动,通常会有一个走廊轨道,开发人员在会议中站在外面,而不是参加会议中的活动。

公司可以赞助社交活动,并帮助在不同团队中任命代表,帮助打破团队之间的僵局。例如,在公司保龄球活动中,人们可能会被故意分配到与他们平时工作不同的团队中。一个小的保龄球团队可以由一个开发人员、一个客户成功团队成员、一个 QA 团队成员、一个营销团队成员和一个销售团队成员组成。这可以激发团队成员之间的工作关系,让他们彼此了解,并公开交流他们在公司活动之外遇到的问题。

比利鲍勃机械零件公司安排了一场棒球比赛活动,主要开发人员维克多和主要 QA 成员贝蒂与几名营销团队成员、销售团队成员和客户成功团队成员一起工作。为棒球比赛活动组成了两个团队,并安排了公司烧烤活动,让人们可以一起吃饭并交谈。

鼓励积极合作的另一种方式是改变公司的楼层平面图,使其更加开放。许多软件公司一直在采用开放式平面图,因为它们消除了隔间在人们之间产生的自然分隔。这个想法是,如果你有一个开放的平面图,你更有可能接近不同的团队,因为你可以轻松地走到不同的人面前,而不会感觉自己侵犯了他们的空间。

总结

沟通是发现手动流程的关键,重要的是要找到手动流程以便自动化这些手动流程。手动流程往往容易出错且耗时,正如我们在各种业务场景中所阐述的那样。这就是自动化的价值所在,比如实施 CI 构建和编写脚本来自动化手动流程。开发人员和 QA 可以帮助开发可以惠及许多不同部门的自动化脚本,如销售、营销和客户成功。在本章中,您已经了解了自动化相对于手动流程的好处以及开放沟通的价值。

在下一章中,我们将学习 CI 的基础知识。

问题

  1. 什么是手动流程?

  2. 什么是自动化?

  3. 为什么打开部门之间的沟通很重要?

  4. CI/CD 代表什么?

  5. 自动化脚本为什么有用?

  6. 公司内部网的价值是什么?

  7. 为什么其他部门应该共享数据?

进一步阅读

要更多地了解 CI 并使用流行的 CI/CD 工具,可以考虑阅读

使用 Jenkins 学习持续集成-第二版 (www.amazon.com/dp/1788479351/),作者Packt Publishing

第二章:持续集成的基础知识

本章将帮助介绍持续集成CI)的概念,并为我们在后续章节中探讨的 CI/CD 概念奠定基础。了解 CI 构建的目的很重要,因为这些概念超越了您可能使用的任何特定的 CI/CD 工具。CI 很重要,因为它有助于保持代码库的健康,并帮助开发人员保持软件系统独立于任何特定的开发人员机器运行。CI 构建强制执行软件组件的独立性和本地环境配置。CI 构建应该与任何一个开发人员的配置解耦,并且应该能够重复和隔离状态。每次运行的构建本质上是独立的,这可以保证软件系统正常工作。

本章将涵盖以下主题:

  • 什么是 CI?

  • CI 的价值

  • 利用 CI 来减轻风险

  • 源代码检入时的软件构建

  • 小型构建和大型构建分解

  • CI 构建实践

技术要求

本章只假设对版本控制系统有一定的了解,但读者至少应该了解配置文件是什么,并且对编程有基本的了解。我们将简���查看一个示例 makefile,并在本章中提供一些代码片段。

在本章中,我们将查看几个代码示例,包括一个 API Workshop(github.com/jbelmont/api-workshop),在那里我们将解释一个 Makefile 和一个 Demo Application(github.com/jbelmont/advanced-tech-in-wilmington-react-app),它使用 React/Node.js/Express.js/RethinkDB,并且我们还将展示一个gulp.js脚本文件。

什么是 CI?

CI 本质上是一个软件工程任务,其中源代码同时合并和测试在主干上。CI 任务可以执行任何多种任务,包括测试软件组件和部署软件组件。CI 的行为本质上是规定的,可以由任何开发人员、系统管理员或运维人员执行。持续集成是持续的,因为开发人员可以在开发软件时持续集成软件组件。

软件构建到底是什么?

软件构建不仅仅是一个编译步骤。软件构建可以包括编译步骤、测试阶段、代码检查阶段和部署阶段。软件构建可以作为一种验证步骤,检查您的软件是否作为一个统一的单元工作。静态编译语言,如 Golang 和 C++,通常具有生成二进制文件的构建工具。例如,Golang 的构建命令,如go build,将生成一个静态编译的二进制文件,并对代码库运行 linting。其他语言,如 JavaScript,可以使用诸如gulp.js/grunt.js之类的工具来执行被认为是构建步骤的操作,例如缩小 - 将多个 JavaScript 源文件转换为一个文件 - 和丑化,它会剥离源文件的注释和任何空白,以及 linting 和运行测试运行器。

CI 流程步骤概述

开发人员可以向版本控制 项目VCP)系统提交代码,例如 GitHub 和 GitLab。CI 服务器可以轮询仓库以获取更改,或者可以配置 CI 服务器通过 WebHook 触发软件构建。我们稍后将在 Jenkins、Travis 和 Circle CI 中进行讨论。CI 服务器将从 VCP 系统获取最新的软件版本,然后可以执行集成软件系统的构建脚本。CI 服务器应该生成反馈,将构建结果通过电子邮件发送给指定的项目成员。CI 服务器将持续轮询更改,或者将从配置的 WebHook 响应。

CI 的价值

CI 有很多价值。首先,CI 构建有价值,因为它可以减少风险,软件的健康状况变得可衡量。CI 有助于减少开发者的假设。CI 环境不应依赖环境变量,也不应依赖某个人机器上设置的特定配置文件。

CI 构建应该干净地独立于每个开发者的本地机器,并且应该与任何本地环境解耦。如果一个开发者说构建在他/她的机器上可以运行,但其他开发者无法运行完全相同的代码,那么你就知道构建可能无法正常运行。CI 构建可以帮助解决这些问题,因为 CI 构建与任何给定开发者的设置和环境变量解耦,并且独立运行。

CI 构建应该减少重复的手动流程,构建过程应该在每次构建时都以相同的方式运行。CI 构建过程可能包括编译步骤、测试阶段和报告生成阶段。CI 构建过程应该在开发者向 Git、Subversion 和 Mercurial 等版本控制系统提交代码时运行。CI 构建应该释放开发者的时间,让他们可以从事更有价值的工作,并减少重复手动流程可能带来的错误。

一个良好的 CI 构建应该能够在任何时间、任何地点生成可部署的软件。CI 构建应该提供项目的可见性,并且应该在开发团队中建立对软件的信心。开发者可以放心,CI 构建会比在本地运行构建更容易捕捉到代码变更的问题。

利用 CI 来减轻风险

CI 可以帮助减轻软件构建中普遍存在的风险,比如“在我的机器上可以运行”的症状。CI 还有助于统一集成失败点,比如数据库逻辑以及其他类型的问题。

但在我的机器上可以运行!

开发者之间的一个共同问题是,一个开发者的机器上可以运行软件构建,但另一个开发者的机器上却无法运行。每个开发者的机器应尽可能地与软件集成相似。完成软件构建所需的一切都需要提交到版本控制系统中。开发者不应该有只存在于他们本地机器上的自定义构建脚本。

数据库同步

完成软件构建所需的任何数据库构件都应存储在版本控制中。如果你有一个关系型数据库,那么任何数据库创建脚本、数据操作脚本、SQL 存储过程和数据库触发器都应存储在版本控制中。

例如,如果你使用 NoSQL 数据库系统,比如 MongoDB (www.mongodb.com/),并且使用 RESTful API,那么一定要在文档中记录 API 端点。记住,开发者可能需要数据库特定的代码来实际运行软件构建。

缺少部署自动化阶段

应该使用部署工具自动化软件部署。你使用的部署工具可能因不同的软件架构而有所不同。

以下是一些部署工具的列表:

部署工具很有价值,因为它们往往是跨平台的,并且可以在许多不同的软件架构中使用。例如,如果开发人员编写了一个 Bash 脚本,那么其中有一个基本假设,即其他开发人员正在使用类 Unix 系统,而在 Windows 环境中工作的开发人员可能无法运行脚本,这取决于他们使用的 Windows 版本。现在 Windows 10 提供了一个 bash 子系统,Windows 开发人员可以在操作 Windows 操作系统时运行 Unix 命令和脚本。

缺陷的晚发现

CI 构建可以帮助防止软件缺陷的晚发现。CI 构建应该具有足够好的测试套件,覆盖代码库的大部分代码。一个健康的代码库可能的度量标准是代码库中 70%或更多的代码覆盖率。稍后我们将讨论代码覆盖率,但任何软件测试都应该被检入源代码,并且应该在 CI 构建上运行测试。您拥有的任何软件测试都应该在 CI 系统上持续运行。

测试覆盖率未知

总的来说,高百分比的代码覆盖率表示一个经过充分测试的代码库,但并不一定保证代码库没有软件错误,只是测试套件在整个代码库中具有良好的测试覆盖率。尝试使用代码覆盖工具,以查看您的测试实际上覆盖了多少源代码。

代码覆盖工具

以下是一些流行的代码覆盖工具:

  • Istanbul (https://istanbul.js.org/):又一个 JavaScript 代码覆盖工具,可以计算语句、行、函数和分支覆盖率,并通过模块加载器钩子在运行测试时透明地添加覆盖。支持所有 JS 覆盖使用情况,包括单元测试、服务器端功能测试和浏览器测试。专为规模而构建。

  • Goveralls (github.com/mattn/goveralls):用于coveralls.io/持续代码覆盖跟踪系统的 Go 集成。

  • dotCover (www.jetbrains.com/dotcover/):JetBrains dotCover 是一个.NET 单元测试运行器和代码覆盖工具,可以与 Visual Studio 集成。

确保您知道您的代码在多大程度上受到单元测试的覆盖。dotCover 可以计算并报告针对.NET Framework、Silverlight 和.NET Core 应用程序的语句级代码覆盖率。

项目可见性不足

CI 系统应该配置为以多种方式发送警报:

  • 电子邮件

  • 短信

  • 通过智能手机的推送通知警报

一些软件开发办公室还使用一些其他创造性的方式来发送软件构建的问题通知,比如某种环境光变化或甚至对讲系统。主要的观点是开发人员需要被通知 CI 构建已经中断,以便他们可以快速修复构建。CI 构建不应该保持中断,因为这可能会干扰其他开发人员的工作。

源代码检入时的软件构建

软件构建应该在版本控制系统中的每次源代码检入时触发。这是部署流水线中的一个重要步骤,我们将在下一章中看到。

软件构建是什么?

软件构建可以仅包括编译软件组件。构建可以包括编译和运行自动化测试,但总的来说,您在构建中添加的进程越多,反馈循环就会变得越慢。

脚本工具

建议使用专门用于构建软件的脚本工具,而不是个人脚本。自定义 Shell 脚本或批处理脚本往往不具备跨平台性,并且可能隐藏环境配置。脚本工具是开发一致、可重复的构建解决方案的最有效过程。

以下是一些脚本工具的列表:

执行单一命令构建

努力实现单一命令构建,以便简化构建软件的过程,因为您使运行构建过程变得更容易,您将加快采用速度和开发人员参与度。如果构建软件是一个复杂的过程,那么最终只有少数开发人员会真正进行构建,这不是您想要的。

简而言之构建您的软件

  1. 使用脚本工具(例如 Ant (ant.apache.org/), Make (www.gnu.org/software/make/), Maven (maven.apache.org/), 或 Rake (ruby.github.io/rake/))创建您的构建

  2. 从 CI 构建中开始一个简单的过程

  3. 将每个过程添加到构建脚本中以集成您的软件

  4. 从命令行或 IDE 运行您的脚本

这是一个示例 makefile,它从我的开源github.com/jbelmont/api-workshop运行一个 Golang API 服务:

BIN_DIR := "bin/apid"
 APID_MAIN := "cmd/apid/main.go"
all: ensure lint test-cover
ensure:
 go get -u github.com/mattn/goveralls
 go get -u github.com/philwinder/gocoverage
 go get -u github.com/alecthomas/gometalinter
 go get -u github.com/golang/dep/cmd/dep
 go get -u golang.org/x/tools/cmd/cover
 dep ensure
lint:
 gometalinter --install
 gometalinter ./cmd/... ./internal/...
compile: cmd/apid/main.go
 CGO_ENABLED=0 go build -i -o ${BIN_DIR} ${APID_MAIN}
test:
 go test ./... -v
test-cover:
 go test ./... -cover
## Travis automation scripts
travis-install:
 go get -u github.com/mattn/goveralls
 go get -u github.com/philwinder/gocoverage
 go get -u github.com/alecthomas/gometalinter
 go get -u github.com/golang/dep/cmd/dep
 go get -u golang.org/x/tools/cmd/cover
 dep ensure
travis-script:
 set -e
 CGO_ENABLED=0 go build -i -o ${BIN_DIR} ${APID_MAIN}
 gometalinter --install
 gometalinter ./cmd/... ./internal/...
 go test ./... -cover
 gocoverage
 goveralls -coverprofile=profile.cov -repotoken=${COVERALLS_TOKEN}

以下是使用gulp.js的示例构建脚本,该脚本从sass源文件生成 CSS 构建并运行 linter。第一块是初始化变量和准备配置对象以供使用:

'use strict';

const gulp = require('gulp');
const webpack = require('webpack');
const sourcemaps = require('gulp-sourcemaps');
const sass = require('gulp-sass');
const autoprefixer = require('gulp-autoprefixer');
const uglify = require('gulp-uglify');
const concat = require('gulp-concat');
const runSequence = require('run-sequence');
const gutil = require('gulp-util');
const merge = require('merge-stream');
const nodemon = require('gulp-nodemon');
const livereload = require('gulp-livereload');
const eslint = require('gulp-eslint');

// Load Environment constiables
require('dotenv').config();
const webpackConfig = process.env.NODE_ENV === 'development'
  ? require('./webpack.config.js')
  : require('./webpack.config.prod.js');

const jsPaths = [
  'src/js/components/*.js'
];
const sassPaths = [
  'static/scss/*.scss',
  './node_modules/bootstrap/dist/css/bootstrap.min.css'
];

const filesToCopy = [
  {
    src: './node_modules/react/dist/react.min.js',
    dest: './static/build'
  },
  {
    src: './node_modules/react-dom/dist/react-dom.min.js',
    dest: './static/build'
  },
  {
    src: './node_modules/react-bootstrap/dist/react-bootstrap.min.js',
    dest: './static/build'
  },
  {
    src: './images/favicon.ico',
    dest: './static/build'
  },

  {
    src: './icomoon/symbol-defs.svg',
    dest: './static/build'
  }
];

这段代码的第二块是我们设置 gulp 任务的地方:复制 React.js 文件,对 JavaScript 文件进行丑化,创建构建 JavaScript 文件,并从 Sass 文件创建 CSS 文件。

gulp.task('copy:react:files', () => {
  const streams = [];
  filesToCopy.forEach((file) => {
    streams.push(gulp.src(file.src).pipe(gulp.dest(file.dest)));
  });
  return merge.apply(this, streams);
});

gulp.task('uglify:js', () => gulp.src(jsPaths)
    .pipe(uglify())
    .pipe(gulp.dest('static/build')));

gulp.task('build:js', (callback) => {
  webpack(Object.create(webpackConfig), (err, stats) => {
    if (err) {
      throw new gutil.PluginError('build:js', err);
    }
    gutil.log('[build:js]', stats.toString({ colors: true, chunks: false }));
    callback();
  });
});

gulp.task('build:sass', () => gulp.src(sassPaths[0])
    .pipe(sourcemaps.init())
    .pipe(sass({
      outputStyle: 'compressed',
      includePaths: ['node_modules']
    }))
    .pipe(autoprefixer({ cascade: false }))
    .pipe(concat('advanced-tech.css'))
    .pipe(sourcemaps.write('.'))
    .pipe(gulp.dest('./static/build'))
    .pipe(livereload()));

gulp.task('build:vendor:sass', () => gulp.src([...sassPaths.slice(1)])
    .pipe(sourcemaps.init())
    .pipe(sass({
      outputStyle: 'compressed',
      includePaths: ['node_modules']
    }))
    .pipe(autoprefixer({ cascade: false }))
    .pipe(concat('vendor.css'))
    .pipe(sourcemaps.write('.'))
    .pipe(gulp.dest('./static/build')));

在这段最后的代码中,我们运行一些监视任务,将监视 JavaScript 文件和 Sass 文件中的任何更改,进行 linting,并创建一个 nodemon 进程,该进程将在任何文件更改时重新启动 Node 服务器:

gulp.task('watch:js', () => {
  const config = Object.create(webpackConfig);
  config.watch = true;
  webpack(config, (err, stats) => {
    if (err) {
      throw new gutil.PluginError('watch:js', err);
    }
    gutil.log('[watch:js]', stats.toString({ colors: true, chunks: false }));
  });
  gulp.watch('static/js/components/*.js', ['uglify:js', 'build:js']);
});

gulp.task('watch:sass', () => {
  gulp.watch('static/scss/*.scss', ['build:sass']);
});

gulp.task('watch-lint', () => {
  // Lint only files that change after this watch starts
  const lintAndPrint = eslint();
  // format results with each file, since this stream won't end.
  lintAndPrint.pipe(eslint.formatEach());

  return gulp.watch(['*.js', 'routes/*.js', 'models/*.js', 'db/*.js', 'config/*.js', 'bin/www', 'static/js/components/*.jsx', 'static/js/actions/index.js', 'static/js/constants/constants.js', 'static/js/data/data.js', 'static/js/reducers/*.js', 'static/js/store/*.js', 'static/js/utils/ajax.js', '__tests__/*.js'], event => {
    if (event.type !== 'deleted') {
      gulp.src(event.path).pipe(lintAndPrint, {end: false});
    }
  });
});

gulp.task('start', () => {
  nodemon({
    script: './bin/www',
    exec: 'node --harmony',
    ignore: ['static/*'],
    env: {
      PORT: '3000'
    }
  });
});

gulp.task('dev:debug', () => {
  nodemon({
    script: './bin/www',
    exec: 'node --inspect --harmony',
    ignore: ['static/*'],
    env: {
      PORT: '3000'
    }
  });
});

gulp.task('build', (cb) => {
  runSequence('copy:react:files', 'uglify:js', 'build:js', 'build:sass', 'build:vendor:sass', cb);
});

gulp.task('dev', (cb) => {
  livereload.listen();
  runSequence('copy:react:files', 'uglify:js', 'build:sass', 'build:vendor:sass', ['watch:js', 'watch:sass', 'watch-lint'], 'start', cb);
});

gulp.task('debug', (cb) => {
  livereload.listen();
  runSequence('copy:react:files', 'uglify:js', 'build:sass', 'build:vendor:sass', ['watch:js', 'watch:sass', 'watch-lint'], 'dev:debug', cb);
});

将构建脚本与 IDE 分开

尽量避免将构建脚本与任何特定的集成开发环境IDE)耦合。构建脚本不应依赖于任何 IDE。

这很重要,有两个原因:

  • 每个开发人员可能使用不同的 IDE /编辑器,并且可能具有不同的配置

  • CI 服务器必须在没有任何人为干预的情况下执行自动化构建

软件资产应该是集中的

以下软件资产应该在集中式版本控制存储库上可用:

  • 组件,例如源文件或库文件

  • 第三方组件,例如 DLL 和 JAR 文件

  • 配置文件

  • 需要初始化应用程序的数据文件

  • 构建脚本和构建环境设置

  • 需要一些组件的安装脚本

您必须决定应该放入版本控制的内容。

创建一致的目录结构

您必须为软件资产选择一致的目录结构,因为它可以帮助您从 CI 服务器执行脚本检索。

这是我为骨架 React/Redux 应用程序做的一个示例文件夹结构:

  • ca(证书颁发机构)

  • 配置(配置文件)

  • db(与数据库相关的内容)

  • 文档(文档)

  • 图像

  • 模型(数据文件)

  • 测试(所有我的测试文件)

  • 单元

  • 集成

  • e2e

  • 助手

  • 静态

  • 构建

  • js

  • 操作

  • 组件

  • 常量

  • 数据

  • reducers

  • 存储

  • 实用程序

  • scss

  • utils(实用文件)

这是我遵循的另一个目录结构,它是面向包的,并且是由Golang 社区的 Bill Kennedy推荐的(www.ardanlabs.com/blog/2017/02/package-oriented-design.html):

  • 工具包

  • 为现有的不同应用项目提供基础支持的软件包

  • 日志记录,配置或 Web 功能

  • cmd/

  • 为正在启动,关闭和配置的特定程序提供支持的软件包

  • 内部/

  • 为项目拥有的不同程序提供支持的软件包

  • CRUD、服务或业务逻辑

  • internal/platform/

  • 为项目提供内部基础支持的软件包

  • 数据库、身份验证或编组

主要的一点是,你应该遵循一个标准的命名约��,所有开发人员都遵循。这将有助于开发团队的工作,因为他们将熟悉代码中的特定事物。并不是每个人都会同意特定的目录布局,但拥有一个标准是最重要的部分。例如,任何在新服务上工作的人应该能够根据文件夹的命名约定设置项目结构,源文件放在哪里,测试文件放在哪里:

这是我在 GitHub 上为 API Workshop 创建的一个示例目录结构(github.com/jbelmont/api-workshop)。

软件构建应该快速失败。

可以通过以下方式实现:

  1. 集成软件组件。

  2. 运行真正的单元测试——不依赖于数据库但在隔离环境中运行的单元测试。

  3. 确保单元测试能够快速运行。如果一个单元测试需要几分钟的时间,那么这可能是一个问题的迹象。

  4. 运行其他自动化流程(重建数据库,检查和部署)。

对于他们的构建,其他步骤是必要的,这取决于每家公司。

为任何环境构建

应该为不同的环境设置配置文件和环境变量,例如 dev/prod/test。日志详细程度应该能够根据环境进行设置。开发人员可能需要增加日志以进行调试。应用服务器配置信息可以在构建文件中设置,以及数据库连接信息和框架配置。

这是一个可以使用的示例文本文件。需要注意的一点是,这些文件不应该提交到源代码控制,因为它们可能包含客户机密和 API 密钥:

API_URL=http://localhost:8080
PORT=8080
AUTH_ZERO_CLIENT_ID=fakeClientId
AUTH_ZERO_JWT_TOKEN=someFaketToken.FakedToken.Faked
AUTH_ZERO_URL=https://fake-api.com
REDIS_PORT=redis:6379
SEND_EMAILS=true
SMTP_SERVER=fakeamazoninstance.us-east-1.amazonaws.com
SMTP_USERNAME=fakeUsername
SMTP_PASSWORD=FakePassword
SMTP_PORT=587
TOKEN_SECRET="A fake Token Secret"

这样的配置文本文件可以帮助其他开发人员连接到第三方服务,并有助于组织客户端秘密信息的存储位置。

小型构建和大型构建的分解

通常,小型构建是可以由 CI 服务器快速运行的构建,通常包括编译步骤以及运行所有单元测试。小型构建可以通过运行分阶段构建来优化,这将在CI 构建实践部分讨论。

大型构建实际上是运行所有构建任务的一个大型构建。进行大型构建的缺点是会阻止开发人员运行它们。如果软件构建需要很长时间才能运行,那么许多开发人员将避免运行构建。快速运行的较小构建鼓励开发人员不断地在版本控制系统上检入他们的更改,并有助于保持代码库的健康。

CI 构建实践

CI 构建实践就像是阶梯;它们相互累积。正如我们将在下一章中看到的,CI 构建过程中的每一步都很重要,并提供了确保代码库处于健康状态的保证。

私有构建

开发人员在提交代码到存储库之前应该运行私有构建。

这是一个使用 Git 的示例开发人员会话:

  1. 从存储库检出要更改的代码:

  2. 进入版本控制的文件夹。

  3. git checkout -b new_branch

  4. 对代码进行更改:

  5. 编辑myFile.go

  6. 从存储库获取最新的系统更改:

  7. git pull

  8. 在本地机器上运行一个执行所有单元测试和可能的集成测试的构建。

  9. 将代码更改提交到存储库。

  10. CI 构建应该自动触发构建并运行存储库中的任何测试。

  11. CI 构建还应该执行其他任务,如报告和调用其他服务(如果需要)。

使用 CI 服务器

CI 服务器应该定期轮询版本控制存储库系统(如 GitHub)的更改,或者通过 WebHook 配置以触发软件构建。CI 构建应该按计划执行某些操作-每小时或每天,如果需要的话。您应该确定一个安静期,在此期间不执行项目的集成构建。CI 服务器应该支持不同的构建脚本工具,如 Rake、Make、NPM 或 Ant。CI 服务器应该向相关方发送电子邮件,并显示先前构建的历史记录。

CI 服务器应该显示一个 Web 访问的仪表板,以便所有相关方在必要时可以查看集成构建信息。Jenkins、Travis 和 Circle CI 都有 Web 访问的仪表板。CI 服务器应该支持不同的版本控制系统,如 svn、Git 和 mercurial。

手动集成构建

手动运行集成构建是减少集成构建错误的一种方法,如果有长时间运行的功能,将很难在 CI 服务器上运行,但要谨慎使用这种技术。例如,您可以指定一个未被使用的机器来执行手动集成任务;尽管使用云,现在只需按需启动服务器实例就更容易了。

运行快速构建

努力通过增加计算资源尽快运行软件构建。将运行较慢的测试,如系统级测试,转移到次要构建或每夜构建。将代码检查转移到第三方服务。例如,对于代码覆盖分析,可以使用以下第三方服务:

运行分阶段的构建以促进快速构建。第一次构建可以编译并运行所有单元测试。第二次构建可以运行所有集成测试和系统级测试。您可以有尽可能多的阶段来实现快速构建。可以说,第一次构建应该是最快的,因为这将是开发人员在向代码库签入代码时使用的主要构建。

摘要

本章介绍了 CI 概念的基础,并介绍了在开发团队中使用成功的 CI 服务器的技术。我们研究了脚本工具和构建工具。我们讨论了软件构建是什么,创建构建脚本时要遵循的良好实践,以及一些测试概念,如代码覆盖。下一章是关于持续交付CD),这是对 CI 的自然延伸,我们将详细介绍部署流水线、配置管理、部署脚本和部署生态系统。

问题

  1. 什么是软件构建?

  2. 什么是分阶段构建?

  3. 您能说出一些脚本工具的名称吗?

  4. 为什么要遵循命名约定和文件夹结构?

  5. CI 的价值是什么?

进一步阅读

阅读的一本好书,更多关于 CI 的内容是《使用 Jenkins 进行持续集成学习-第二版:使用 Jenkins 2 实施持续集成和持续交付的初学者指南》(www.amazon.com/dp/1788479351/),由 Packt Publishing 出版。

第三章:持续交付的基础知识

可以说,软件的最重要部分实际上是将其交付并准备供最终用户使用。持续交付(CD)是您将软件产品交付给最终用户的时刻,也是本章的基础。只有当您的预期用户实际上可以使用产品时,产品才有用。在本章中,我们将讨论部署流水线,并结合自动化和 CD 的概念。

本章将涵盖以下主题:

  • 交付软件的问题

  • 配置管理

  • 部署流水线

  • 部署脚本

  • 部署生态系统

技术要求

本章假定您了解自动化和持续集成的概念。如果您对这些主题中的任何一个感到不确定,请先阅读第一章,“自动化测试的 CI/CD”和第二章,“持续集成的基础知识”,然后再阅读本章。

本章的代码文件可以在github.com/jbelmont/api-workshop/blob/master/Gopkg.toml找到。

交付软件的问题

在尝试将软件产品交付给最终用户时,可能会出现许多问题,我们将看看影响软件交付的几种情况。一个可能的情况是开发人员正在开发一个新功能,但新功能可能实际上无法通过 CI 构建阶段,或者可能不像产品所有者最初提出的那样运行。另一个可能的情况是,预期的受众没有得到适当的理解,这将影响用户对最终产品的使用。另一个可能的情况是,软件产品没有得到适当的解耦,并且是用泡泡糖和胶带拼凑在一起的,新功能请求会导致许多回归问题。

我们所说的交付软件是什么意思?

关于软件交付实际意味着什么可能会有很多争论。在本章中,所指的是实际的软件产品已交付给预期的用户,而不仅仅是软件产品被质量保证(QA)部门批准为有效。

常见的发布反模式

存在一些常见的发布反模式,您应该避免,例如手动部署软件,手动配置管理以及每个环境的不同环境配置。

手动部署软件

这种类型的反模式很常见,可能会导致软件交付中的瓶颈。软件交付的那一天是充满压力和错误的。运维人员 Tom 的一天是从将软件构件从版本控制系统复制到生产环境开始的。Tom 通过文件传输协议(FTP)复制文件,但忘记添加一个新的配置文件,登录页面不再工作。Tom 不得不与开发团队交谈,询问是否添加了新的配置文件,并等待数小时才得到回复。

一旦 Tom 得到了新的配置文件,他将其上传到生产环境。现在登录页面可以工作了,但一些页面加载时出现了奇怪的图像放置和不规则性。Tom 联系了 UI/UX 团队,发现生产环境中缺少一个 CSS 文件。Tom 上传了 CSS 文件,现在页面可以正确加载了。Tom 询问客户成功团队是否可以进一步测试生产环境中的新更改,最终在晚上 7 点左右结束了一天的工作。

如果存在一份详细描述软件产品交付的长文档,这可能表明存在手动流程。这进一步使产品的交付变得更加复杂,因为在整个过程中的任何错误都可能导致更多问题。如果交付变得不可预测,这也可能指向这种反模式。

部署自动化来拯救

正如我们在第一章中讨论的那样,自动化测试的 CI/CD,自动化是一个行为以可重复和自动化的方式完成的过程。软件交付应该是一个自动化的过程,因为这将有助于确保软件交付的一致实践和行为。我们将在本章后面看到一些工具,这些工具将帮助您自动化软件交付流程。

手动配置管理

这种反模式可能会让运维人员感到沮丧,因为他们将是最后一个了解产品新行为的人。如果软件交付的当天是运维团队第一次看到新功能,那么他们可能会对软件行为感到惊讶。辛迪是运维团队的成员,她被委托交付软件,并注意到安装脚本完全失效,因为它无法与识别(ID)服务器通信。辛迪向开发团队发送日志消息,发现 ID 服务器的一个客户端密钥已更改,并且安装脚本需要使用这个新值才能正确连接。

如果辛迪意识到 ID 服务器的这个新变化,这种问题可能会得到缓解,但开发人员正在另一个环境中工作,QA 部门得到了这个信息来测试新功能,但直到交付当天他们才遇到这个问题,没有人想到将这个信息传递给运维部门。

配置管理自动化

我们将讨论可以帮助解决配置管理问题的工具,比如之前遇到的问题。使用适当的工具,运维/DevOps 人员可以快速获得每个环境的正确环境配置,包括生产环境。

生产环境与其他环境的不同之处

这种反模式可能特别具有挑战性,因为在开发中测试了所有的更改,暂存环境在生产环境中可能表现不稳定。例如,特拉维斯在 QA 部门担任测试员,自新功能推出以来一直在测试暂存环境。比利是一名运维人员,由于暂存环境与生产环境完全不同,他无法看到新功能。比利还注意到生产环境中缺少在暂存环境中显示的关键信息。比利联系开发团队,发现必须运行数据库迁移脚本才能使新功能在生产环境中运行。

生产环境应该与暂存环境相同

所有环境,包括测试、暂存和生产环境,都应该有必要的迁移脚本和任何其他软件资产,以防止生产环境的故障,并且开发团队应该确保将操作指向脚本文件的任何更改,或者在共享文档中清楚地标记这些更改。

如何进行软件发布

在进行软件发布时,有一些重要的步骤需要考虑,比如频繁发布以避免一次引入太多的更改,并确保发布是自动化的。

频繁发布

软件发布必须频繁。大型软件发布往往充斥着问题,因此最好使发布之间的增量(更改)较小。通过增加软件发布的频率,您还可以获得更快的反馈。大型软件发布往往需要更长的时间,关键反馈可能无法及时传达。

自动化发布

手动发布存在问题,因为它们不可重复。每次进行手动发布时,由于配置更改、软件更改和环境更改,都会有所不同。手动发布步骤充满错误,因为每个步骤都是手动的,可能导致级联错误。手动更改的危险的一个很好的例子是,当最受欢迎的云提供商亚马逊网络服务(AWS)在美国东部地区遭受重大故障时,因为运维人员在手动流程的一系列步骤中输入了错误的命令。自动化是软件发布的关键,因为它确保了软件交付流程的可重复性和控制。在本章中,我们将进一步探讨部署脚本工具,以帮助自动化软件交付。

自动化在交付软件方面的好处

正如我们之前所阐述的,自动化在软件交付中非常重要,因为它确保了软件发布的可重复性、可靠性和可预测性。通过自动化软件交付流程,可以避免或减轻灾难性事件,而不是通过漫长的手动流程。

团队赋能

如果自动化已经实施,QA 部门可以安全地选择旧版本的软件发布来测试回归。运维人员可以运行在暂存中使用的脚本,而不会因为环境级别的差异而遇到问题。通过自动化软件流程,运维人员可以在交付过程中出现灾难时安全地回滚发布。此外,正如我们在第二章中所讨论的,自动化可以帮助实现一键式发布。

减少错误

自动化可以帮助减少手动流程可能造成的错误。正如我们之前所看到的,配置管理问题可能导致软件交付不佳。手动软件发布无法有效地确保可重复性,因此容易出错。

减轻压力

另一个好处是在软件交付期间减少所有人员的压力。手动流程往往会造成不必要的压力,因为执行手动流程的人必须要细心,不能在交付过程中犯任何错误。自动交付流程非常好,因为它确保每次运行都会以相同的方式执行。手动流程中的错误可能需要高级人员的支持来解决问题。

配置管理

包含重要信息的配置文件,如客户端密钥和密码,必须得到妥善管理,并且必须在其他环境中保持同步。每个环境可能有不同的环境变量,必须被使用并传递到应用程序中。

配置管理到底意味着什么?

配置管理可以简要描述为检索、存储、识别和修改与每个给定项目相关的所有软件工件以及软件工件之间的任何关系的过程。

版本控制

版本控制是保持所有软件工件之间修订的手段。版本控制对于配置管理非常重要,因为任何包含环境文件的文件的更改都应该在版本控制下。

托尼是开发团队的一员,他一直在使用一个未纳入源代码控制的属性文件,并且一直在对产品中的单点登录(SSO)流程进行更改。托尼不小心删除了文件,并且丢失了在 SSO 流程中必要的所有客户端 ID 和密钥。现在,托尼必须去不同的 API 门户,并重新生成一些属性的客户端密钥,因为它们在创建时只显示一次,现在他必须通知团队的其他成员更新他们的属性文件。

示例属性文件

我已经添加了一个示例属性文件,其中包含客户端秘密信息和身份验证秘密信息。这对于给定的环境正常运行是必要的,但不应该检入源代码控制,这里仅用于演示目的。

API_URL=http://localhost:8080
PORT=8080
AUTH_ZERO_CLIENT_ID=fakeClientId
AUTH_ZERO_JWT_TOKEN=someFakeToken.FakedToken.Faked
AUTH_ZERO_URL=https://fake-api.com
REDIS_PORT=redis:6379
SEND_EMAILS=true
SMTP_SERVER=fakeamazoninstance.us-east-1.amazonaws.com
SMTP_USERNAME=fakeUsername
SMTP_PASSWORD=fakePassword
SMTP_PORT=587
TOKEN_SECRET="A fake token secret"

TOKEN_SECRET环境变量只能看到一次,所以如果丢失了,那么你必须在 API 门户中重新生成它。

版本控制管理工具

这是一个版本控制管理工具的列表:

版本控制实践

一个重要的做法是尽可能将所有东西都放在版本控制下,以避免在软件产品中丢失重要工作。网络文件、配置文件、部署脚本、数据库脚本、构建脚本以及任何其他对应用程序正常运行很重要的工件都应该在版本控制下,否则你会冒着丢失关键数据的风险。

经常进行软件检查

经常检查主分支非常重要,否则你会冒着在代码库中引入破坏性更改的风险。此外,频繁的检查可以帮助开发人员随时注意带入小的更改。应避免对代码库进行大规模的更改,因为这样更难测试并且可能会引起回归。频繁的检查也是有益的,因为破坏性更改会更快地被注意到。

编写描述性和有意义的提交消息

使用包括问题跟踪信息的描述性提交消息,比如 Jira 问题,清楚地描述提交的意图。避免编写模糊的提交消息,比如修复错误完成,因为这些类型的提交消息是没有用的,对开发人员以后也没有帮助。

这是一个示例描述性提交消息[DEV-1003],添加了一个新的导航链接到零部件供应列表。还添加了一个新导航的测试用例。这显然更加描述性。此外,在 Jira 中,当你提供一个像 DEV-1003 这样的问题时,它会在 Jira 问题中创建一个引用此问题的链接。此外,如果你创建一个拉取请求并在git commit中放入 Jira 问题,它将把你的拉取请求与问题链接起来。

依赖管理

应用程序通常具有对软件产品至关重要的第三方依赖项。依赖管理是任何应用程序的重要部分,不同的编程语言以不同的方式处理依赖管理。

示例 Node.js 依赖文件和 Gopkg.toml 依赖文件

这是一个Gopkg.toml文件,其中包含存储库中每个依赖项的版本和包信息。

# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
#   name = "github.com/user/project"
#   version = "1.0.0"
#
# [[constraint]]
#   name = "github.com/user/project2"
#   branch = "dev"
#   source = "github.com/myfork/project2"
#
# [[override]]
#   name = "github.com/x/y"
#   version = "2.4.0"
#
# [prune]
#   non-go = false
#   go-tests = true
#   unused-packages = true

[prune]
  go-tests = true
  unused-packages = true

[[constraint]]
  branch = "v2"
  name = "gopkg.in/mgo.v2"

[[constraint]]
  name = "github.com/dgrijalva/jwt-go"
  version = "3.1.0"

[[constraint]]
  name = "github.com/go-playground/locales"
  version = "0.11.2"

[[constraint]]
  name = "github.com/pkg/errors"
  version = "0.8.0"

[[constraint]]
  name = "github.com/pborman/uuid"
  version = "1.1.0"

[[constraint]]
  name = "gopkg.in/go-playground/validator.v9"
  version = "9.9.3"

像这样管理依赖是很重要的,因为第三方依赖项很容易给应用程序带来破坏性更改,第三方依赖项中的 API 更改可能会破坏任何正在运行的应用程序中的关键行为。

管理软件组件

通常,软件项目将以单片构建开始,所有工作组件都在一个层中。随着应用程序的规模和成熟度的增长,应用程序的层将分解为服务或不同的层,这就是需要单独的构建流水线的地方。也许一个 ID 服务用于应用程序中的身份验证,也许一个管理服务在单独的构建流水线中运行用于管理门户。微服务架构是应用程序的服务级组件化的延续,其中每个微服务在应用程序中有一个清晰而专注的目的。

软件配置管理

配置是任何应用程序的重要部分,应该像您在代码中使用的业务逻辑一样小心对待。因此,配置需要像源代码一样得到适当的管理和测试。

可配置性和灵活性概念

乍一想,似乎将配置尽可能灵活是合适的。为什么不尽可能地使系统灵活,并允许它适应任何类型的环境呢?这通常被称为终极可配置性的反模式,意味着配置可以像编程语言一样行为,并且可以被制作成任何方式。以这种方式进行的配置管理可能会使软件项目陷入困境,因为其用户将期望这种灵活性是必要的。为您的配置管理设置一些约束更有用。约束可以帮助控制配置环境中过度灵活性的影响。

特定类型的配置

以下是应用程序可以利用的可能类型的配置列表:

  • 配置可以在构建时间中被拉取并合并到应用程序二进制文件中:

  • 像 C/C++和 Rust 这样的语言可以进行构建时配置

  • 在创建组件或 gems 时,配置可以在打包时间注入:

  • 像 C#、Java 和 Ruby 这样的语言可以使用这样的配置选项

  • 配置可以在部署时间进行,意味着部署脚本或安装程序可以根据需要获取任何必要的信息,或者部署脚本可以要求用户传递这样的信息:

  • 我们将在本书的后面使用 Jenkins、Travis 和 CircleCI 工具进行讨论

  • 配置可以在启动时间或 运行时进行,意味着应用程序启动时:

  • 像 Node.js 这样的语言在 Node.js 服务器运行时通常会注入环境变量

跨应用程序的配置管理

当您跨不同应用程序进行配置时,配置管理变得更加复杂。有一些工具可以帮助跨应用程序边界进行配置,以下是这样的工具列表:

环境管理

应用程序依赖的硬件、软件、基础设施和任何外部系统可以被视为应用程序的环境。任何环境的创建都应该以完全自动化的方式进行,因为能够复制环境是重要的,我们将会说明。

手动环境设置

手动设置基础设施可能会出现几个问题:

  • 手动设置的服务器实例可能被配置以适应单个运维人员。这个运维人员可能已经离开组织,导致核心基础设施破损。

  • 修复手动设置的环境可能需要很长时间,而在这样的环境中解决问题是不可重现和可重复的。

  • 手动设置的环境可能无法复制以进行测试。

环境的重要配置信息

以下是所有环境都需要的重要配置信息列表:

  • 需要在每个环境上安装的第三方依赖和软件包

  • 网络拓扑信息

  • 应用程序运行所需的外部服务,如数据库服务

  • 应用程序数据或种子数据,以便设置和运行新环境

容器化环境

像 Docker 和 Kubernetes 这样的工具因其隔离环境级别信息和创建可重现/可重复环境的能力而变得越来越受欢迎。使用 Docker,您可以声明所有外部服务,如 Redis 和 MongoDB。

这是 API 研讨会存储库(github.com/jbelmont/api-workshop)的docker-compose YML 脚本示例:

version: '3'
services:
  mongo:
    image: mongo:3.4.5
    command: --smallfiles --quiet --logpath=/dev/null --dbpath=/data/db
    ports:
      - "27017:27017"
    volumes:
      - data:/data/db
  redis:
    image: redis:3.2-alpine
    ports:
      - "6379:6379"
 apid:
    build:
      context: .
      dockerfile: Dockerfile-go
    depends_on:
      - mongo
      - redis
    env_file:
      - ./common.env
    links:
      - mongo
      - redis
    ports:
      - "8080:8080"
volumes:
    data:

我们已经声明了一个数据库,以及一个缓存服务(Redis)和一个 API,它们都作为独立的容器运行,所有这些都可以具有环境级别的信息,比如环境变量,可以单独配置。

部署管道

我们在第二章中谈到了 CI 的重要性,持续集成的基础知识,虽然 CI 是一个重要的生产力增强器,但它主要对开发团队有用。在等待修复或更新文档时,软件生命周期中常见的瓶颈是 QA 和运维团队。QA 可能需要等待开发团队的良好构建。开发团队可能在完成新功能后的几周内收到错误报告。所有这些情况都会导致无法部署的软件,最终导致无法交付给最终用户的软件。创建可以部署到测试、分级和生产环境的一键式部署构建可以帮助缓解这些问题,正如我们之前所指出的。

部署管道是什么?

部署管道可以被认为是构建、部署、测试和发布过程的端到端自动化。部署管道也可以被认为是将开发人员编写的软件交到用户手中的过程。

部署管道实践

在本节中,我们将讨论一些部署管道实践,比如只编译一次二进制文件,以相同的方式处理每个环境中的部署,并在部署管道中设置提交阶段。

只编译一次二进制文件

多次编译的二进制文件可能会出现问题,原因有几个:

  • 二进制文件在每次运行时可能具有不同的上下文,这会给系统引入不可预测性

  • 静态编译语言,如 C/C++,每次运行可能具有不同的编译器版本

  • 第三方软件可能在不同的编译执行上下文中指定不同的版本

  • 多次编译二进制文件也会导致低效的部署管道

  • 重新编译二进制文件也可能很耗时

如果可以的话,最好在编译时只编译一次二进制文件。

在每个环境中应该以相同的方式进行部署

考虑到每次源代码检入时都会运行 CI 构建,开发人员通常会经常部署他们的软件。QA/测试人员不会那么频繁地部署,运维人员更少。与开发环境相比,部署到生产环境的频率要低得多,这是有充分理由的。

应该创建一个部署脚本,可以在开发、分级和生产环境中运行。每个环境中需要的任何更改都可以通过在版本控制中管理的属性文件来管理。例如,您可以在部署脚本中使用环境变量来区分不同的环境。

提交阶段-部署管道的第一步

部署流水线的第一个阶段是提交阶段,或者说是开发人员将代码提交到版本控制时。一旦代码提交到 CI 构建流水线,构建流水线应该在必要时编译任何代码,运行一套单元测试(希望有一些存在)和集成测试,如果需要为后续部署流水线创建任何二进制文件,则创建任何二进制文件,运行静态分析工具来检查代码库的健康状况,并准备后续部署流水线所需的任何构建工件。

提交阶段构建的其他重要指标包括代码覆盖率、代码库中的重复代码、圈复杂度(衡量代码库中的复杂性)、监控大量警告消息以及代码风格(通常由代码检查工具报告)。

如果提交构建阶段通过,那么我们可以将其视为通过的第一个关卡,尽管这是一个重要的关卡。

测试关卡

在极限编程中,开发人员创建验收测试,这些测试作为功能级别的测试,测试软件系统的某个方面。例如,用户登录系统和用户退出系统。另一个例子是用户访问其个人资料并更新信息。这些测试比单元测试和集成测试要广泛得多,因此如果存在系统级问题,它们会揭示出来。

验收测试构建阶段

运行一套验收测试应该是部署流水线的第二个关卡。验收测试还充当回归测试套件,以验证新功能是否已引入系统。在此阶段,验收测试套件中发生的任何测试失败都需要逐案评估。失败可能是由于系统中的有意行为更改,因此需要更新验收测试套件,或者失败可能代表需要解决的回归。无论哪种情况,都必须尽快修复验收测试套件。验收测试充当了部署流水线继续前进的另一个关卡。

手动测试

验收测试确实提供了系统行为应该如何的一定程度的保证,但只有人类才能发现系统中的异常。质量保证/测试人员可以对系统进行用户级测试,以确保系统的正确可用性。测试人员还可以对系统进行探索性测试。自动化验收测试套件有助于释放测试人员的时间,以进行这种更高价值的测试。

非功能测试

非功能测试的命名恰如其分,因为这些类型的测试不是系统的功能要求。相反,非功能测试测试系统中的容量和安全性等方面。在部署流水线的这一步中,失败可能不需要将构建标记为失败,而只是作为构建的决策指标。

发布准备

进行发布时总会伴随一定的风险,因此最好在进行软件发布时建立相应的流程。发布过程中出现的问题可以通过建立流程来预防或减轻。

在发布过程中可能要遵循的一些步骤如下:

  • 创建一个涉及并由所有参与产品交付的人员共同创建的发布计划

  • 尽可能自动化发布流程,以防止错误发生

  • 在生产环境中经常排练发布,以帮助调试可能出现的问题

  • 建立流程,以迁移正在使用的任何生产数据,并在回滚(将发布回滚到上一个版本)或升级系统时迁移配置信息

自动化发布流程

尽量自动化尽可能多的发布流程,因为自动化越多,您对发布流程的控制就越多。手动步骤往往容易出错,并可能导致意外结果。在生产环境中发生的任何更改都需要得到适当的锁定,这意味着更改是通过自动化流程完成的。

进行回滚

发布日往往会很紧张,因为在发布过程中发生的错误可能会导致难以检测的问题,或者正在发布的新系统可能存在缺陷。排练发布可以帮助减轻这些问题,并可以帮助人们快速解决可能遇到的问题。

在发布之前和发布之后,最佳策略是准备好软件系统的先前版本,以防需要将系统回滚到先前版本;这不包括任何必要的数据迁移或配置。作为另一种可行的选择,您可以重新部署已知的良好版本的应用程序。回滚应该能够通过点击按钮完成。

部署脚本

部署脚本是必要的,因为开发团队编写的软件不仅在他们的 IDE 或本地环境中运行,而是需要在部署流水线期间运行。部署脚本是指您用于编写部署流水线脚本的特定构建工具。

构建工具概述

已经有许多构建工具,每个都有其优缺点。以下是一小部分构建工具的列表:

部署脚本概念

无论您使用什么构建工具,进行部署脚本时都需要遵循某些实践。

为部署流水线的每个阶段编写脚本

在部署流水线的提交阶段,您将需要部署脚本执行的操作。例如,您可能需要编译任何源文件,运行一套单元和集成测试,并运行一个检查代码风格和静态分析工具的代码检查工具。所有这些步骤可能需要使用不同的工具,因此编写一个执行所有这些操作的脚本是最好的。根据脚本的特定操作,您可能希望进一步将脚本分解为执行专注操作的子脚本。在验收测试阶段,您的脚本可能会运行整个验收测试套件,并额外生成一些关于测试的报告和指标。

每个环境应该使用相同的脚本

你应该在所有环境中使用完全相同的脚本,这将确保在每个环境中都以相同的方式进行构建和部署过程。如果每个环境都有不同的脚本,那么就无法确保在不同环境中运行的特定脚本的行为是相同的。开发人员在其本地环境中运行的部署脚本应该与在其他环境中运行的脚本相同,否则就会存在环境泄漏的风险。我们在这里的意思是,开发人员的环境可能设置了特定的环境变量,而部署脚本或每个环境,如开发、暂存和生产,可能设置了不同的环境变量,这将使在出现问题时调试变得更加困难。

部署过程不应在每次运行时都发生变化

部署过程应该在每次运行时保持不变。在数学中,有一个术语叫做幂等,它基本上表示某个操作可以多次执行并产生相同的结果。如果你的部署过程在任何给定的运行中发生变化,那么你无法保证每次运行的行为,这反过来将使故障排除变得更加困难。

部署脚本最佳实践

本节讨论了部署脚本的最佳实践,例如确保仅测试已知良好的基础、测试环境配置、使用相对路径和消除手动流程。

仅测试已知良好的基础

你不应该测试甚至无法编译的源代码,当单元测试和集成测试失败时也不应该运行任何验收测试。基本上,任何额外阶段的部署过程都必须存在已知良好的基线才能运行和继续。

测试环境配置

随着部署流水线通过每个阶段并随后通过每个阶段,检查相应阶段是否正常运行是很重要的。你对相关阶段进行的测试可以被视为烟雾测试。例如,通过访问 URL 检查网站是否正常运行,并检查数据库中的记录是否仍然可以获取。

使用相对路径

最好使用相对路径而不是绝对路径。开发人员可能有一定的文件系统或文件夹结构,在部署流水线运行的环境中不存在,因此最好使用相对路径,以免造成意外的破坏。有时可能会很难做到这一点,但尽量遵循这一点是最好的。Docker 容器可以为每个容器映射文件夹结构;例如,如果 Docker 容器在部署流水线的特定部分生成,它也可以映射到某个特定的相对文件夹结构。

消除手动流程

避免制作包含必须完成部署特定部分的步骤列表的构建脚本。

以下是手动流程中可能的步骤列表:

  • 将项目根目录中的所有图像复制到static/build文件夹中

  • 在新的生产发布中运行数据的手动迁移

  • 如果有人必须 SSH 到一个盒子并运行一个脚本,这可能会有问题

任何必须手动完成的步骤在文档中很快就会过时,因此最容易遵循的指令是,如果你必须再次执行某个操作,就要制定自动化流程。

部署生态系统

在本节中,我们将简要介绍一些可以帮助你的部署流水线并提供不同用途的工具。

基础设施工具

我们在本章前面简要提到了 Chef;Chef 是一个很好的工具,可以可靠地自动化基础架构的搭建。如果没有适当的工具,很难确保每个新环境的设置都是以相同的方式进行的。潜在地,您可能会创建具有不同配置的新环境,这在故障排除时可能会带来很大问题。

云服务提供商和工具

三大主要云服务提供商都有各自相关的工具:

您可以使用 Jenkins、Travis 和 CircleCI/CD 工具与所有主要的云服务提供商,尽管 Microsoft Azure 和 AWS 也已经创建了自己的 CI/CD 工具供您使用。

总结

CD,正如我们所见,围绕自动化的概念展开。在本章中,我们学习了软件交付的含义。我们首先研究了在交付软件时出现的常见问题。我们还详细讨论了配置管理以及版本控制和依赖管理在任何配置中所起的作用。我们还研究了部署流水线,并深入了解了不同的构建阶段。在部署脚本部分,我们研究了一些已有的构建工具,并制定了一些最佳实践。最后,我们简要介绍了部署生态系统和一些云服务提供商。在下一章中,我们将讨论不同团队之间沟通的问题,如何向其他团队成员传达痛点,如何在不同团队之间分享责任,向利益相关者展示 CI/CD 的重要性,以及如何获得业务利益相关者对 CI/CD 的批准。

问题

  1. 我们所说的交付软件是什么意思?

  2. 列举一些常见的发布反模式。

  3. 在交付软件时,自动化的一些好处是什么?

  4. 配置管理到底是什么意思?

  5. 为什么应该编写描述性和有意义的提交消息?

  6. 什么是部署流水线?

  7. 为什么在每个环境中部署应该以相同的方式进行?

进一步阅读

考虑阅读 Packt Publishing 出版的《DevOps: Continuous Delivery, Integration, and Deployment with DevOps》一书,以更深入地了解 CD。

第四章:CI/CD 的商业价值

现在我们清楚了自动化、持续集成(CI)和持续交付(CD)的概念,我们需要向业务利益相关者传达这些实践的商业价值,否则我们就会在不考虑这些实践的情况下构建功能。本章将讨论如何说服利益相关者这些价值,以及我们将讨论沟通问题、如何向团队成员传达痛点、在不同团队之间分享责任、了解你的关键利益相关者、演示 CI/CD 的重要性以及从利益相关者那里获得 CI/CD 的批准。

本章将涵盖以下主题:

  • 沟通问题

  • 向团队成员传达痛点

  • 在不同团队之间分享责任

  • 了解你的利益相关者

  • CI/CD 的重要性演示

  • 从利益相关者那里获得 CI/CD 的批准

技术需求

本章假设你已经熟悉自动化和 CI/CD 的概念;如果你对这些主题感到不确定,请在阅读本章之前阅读第一章《自动化测试的 CI/CD》和第二章《持续集成的基础知识》。本章主要讨论如何向利益相关者传达这些实践的价值,因此不会有任何代码示例或需要安装的内容。

沟通问题

在任何工作环境中,沟通问题都是不可避免的,但特别是在敏捷工作环境中存在问题。一些沟通问题包括需求的误解、缺乏适当的文档、时区差异、缺乏信任和相互尊重、文化差异、语言障碍和长时间的反馈循环。

需求的误解

这是一个需求清单的示例。需求清单的目的是为特定功能列出所有必要的事实。

在敏捷工作环境中,需求的误解是一个常见问题。虽然不可能完全消除需求的误解,但通过确保在功能请求的最初阶段与最终用户或客户进行沟通,可以最大程度地减少这种风险。

重要的是,你正在实施的功能请求必须清晰陈述,并且每个功能都必须有明确的商业意图。这很重要,因为它有助于开发人员、DevOps 人员和 QA/测试人员在实施阶段做好充分准备。

在前期了解关键业务需求将有助于减少团队之间的需求误解,因为缺少需求可能会在开发过程中轻易造成瓶颈。任何关键需求信息都需要得到适当的文档记录。

缺乏适当的文档

文档需要在定义任何需求的同时编写,并在功能开发过程中持续更新。只有在一切都尽可能清晰地定义和陈述之后,你才能开始编写实施特定功能的计划。如果开发人员遇到问题并需要向客户澄清,那么答案需要直接放在需求中以供将来参考。

避免有多个包含需求信息的文档,而是使用一个文档包含所有需求信息,否则你就有可能出现信息过时的情况,甚至更糟糕的是,不同的需求分散在不同的地方并相互矛盾。

业务需求应该有一个统一的真相来源,并且所有相关方都应该理解这些需求。

时区差异

随着越来越多的团队变得分布式和全球化,时区差异可能会导致沟通瓶颈。在时区差异较大的开发人员需要确保良好的 CI/CD 实践。CI 构建失败和配置管理问题可能会因时区差异而迅速恶化,因为一个时区的团队可能会无法有效地进行工作。在分布式团队中,沟通尤为重要,因为缺乏面对面的互动可能导致沟通失败,最坏的情况下甚至会导致团队之间的敌意。

我曾在一家初创公司工作,那里有 3 小时的时区差异,这本身并不是问题,但每天的站立会议是在我们的工作日结束时进行的,而另一个团队在我们的中午开始工作。自然而然地,这导致了其他团队的更改会阻塞我们,直到我们的中午才能解决。

缺乏信任和相互尊重

以下是一张图,说明了信任和相互尊重是相辅相成的,团队需要这一点才能高效运作:

团队之间的信任至关重要,很容易失去,但很难获得。最好有一个优秀的项目经理,可以促进团队之间的沟通,并帮助澄清必然会发生的问题。健康的团队会在功能工作中出现问题时进行开放的沟通,定期进行回顾也有助于排解团队成员之间的挫折感,并建立信任。

如果可能的话,最好能组织团队外出活动,让多个团队之间可以互动并建立合作关系。一些公司会定期举行会议,让团队一起参加有趣的活动,比如体育运动或游戏。定期进行团队建设活动也可以保持团队成员的参与度,并建立合作精神。

文化差异和语言障碍

随着敏捷工作环境的全球化,全球团队变得更加普遍。团队之间的文化差异使得沟通成为项目成功的更加重要的因素。幽默可能是一把双刃剑,因为如果幽默被误解,很容易导致分裂和敌意,所以最好能教导团队有关文化规范和习俗,以避免沟通问题。

语言障碍也可能会导致问题,因为对功能请求的需求可能会被误解。最好是由项目经理作为团队之间的联络人,确保所有需求在团队之间清晰地理解,并帮助澄清任何沟通瓶颈。

长反馈循环周期

以下是一个反馈循环周期的图示。反馈循环越长,做出改变所需的时间就越长。在部署流水线上,有一个短的反馈循环是非常重要的,这样在必要时可以及时做出改变。

我们在第一章中谈到了长反馈循环,特别是长反馈循环的危险以及缩短反馈循环周期的重要性,以便在正确的时间将正确的信息传递给正确的人。同样,团队之间长时间的反馈循环周期可能会产生问题和自然瓶颈。

理想情况下,团队应尽快获得他们所需的信息,但这并不总是现实。适当的联络人或项目经理可以帮助缩短团队之间的反馈循环,团队需要适当记录任何流程,并确保这些文档对其他团队可见和已知,否则团队之间的流程可能会有所不同。

记住,短的反馈循环会导致更快的响应时间。

向团队成员传达痛点

团队成员能够有效地传达阻碍进展的特定痛点或障碍是很重要的。在本节中,我们将讨论几个痛点,包括等待需求信息、部署流水线中未记录的步骤、王国的密钥持有者过多以及沟通渠道过多。

等待需求信息

开发人员通常会开始处理特定的故事/功能,但并没有所有必要的需求来完成他们分配的工作。这对开发人员来说尤为棘手,因为他们所处理的任何代码可能需要根据需求与正确完成的距离有多远而被废弃并重新完成。开发人员需要在开始故事之前就提前获得所有的需求;每个功能必须存在抓取所有需求的流程,并且每个故事理想情况下都将有验收测试作为特性工作的行动项来考虑完成。在理想的世界中,开发人员将在开始特定的功能工作之前准备好所有必要的信息,并且在需求文档中指定的特性完成时,故事的验收测试将通过。

在第一章中,自动化测试的 CI/CD,我们讨论了比利·鲍勃机械零件公司的例子。现在,想象一下,开发团队的汤姆已经开始了显示供应商名称的工作,并且汤姆发现这个工作的范围似乎很大,他可能无法及时完成。这种情况也因需求文档严重缺乏和开发过程中缺少关键细节而变得复杂。汤姆询问产品负责人是否可以就某些项目提供反馈,但必须等待数天才能获得这些必要信息。

部署流水线中未记录的步骤

部署流水线过程中的每个步骤都应适当记录和自动化。我们在第五章中谈到了在 Jenkins 的安装和基础知识中尽可能自动化部署流水线的重要性。重申一下,手动流程是有问题的,因为它们是可重复和可靠的。自动化很重要,因为它为部署流水线带来了可重复性和可靠性。每当有人必须执行手动步骤时,就无法保证流程是否会正确执行,并且在每次运行中都以相同的方式执行;只有通过自动化,您才能保证部署流水线阶段的可重复性。

作为 DevOps 团队的一部分,阿尔文正在为软件产品的最新版本进行发布,并在部署流水线中运行一个复杂的手动流程。阿尔文输入了错误的命令,结果清除了生产数据库。幸运的是,阿尔文有一个一天前的备份,他可以将生产数据库恢复到这个备份。如果当时有自动化流程,这种情况就不会发生。

王国的密钥只交给少数人

以下图表代表一个关键点,关于王国的关键点要记住的主要事情是只有少数人可以访问/拥有生产环境的密钥:

重要的是要控制谁可以在生产环境中进行更改,许多软件公司通常会选出少数几个甚至一个人可以在生产中进行更改。如果这个特定的个人不可用或离开公司,这可能会成为问题,但一些公司已经实行了开发团队全权拥有特定功能的做法,负责修复部署管道中遇到的问题的是同一个开发人员。在我工作过的一家公司,我们亲切地说,“只有少数人拥有王国的钥匙”。

阿尔文是为数不多的 DevOps 人员之一,拥有王国的钥匙。客户支持代表向开发团队发出关于生产中断的通知,开发团队正在努力恢复生产环境以满足客户需求。阿尔文和另一名 DevOps 成员是唯一可以触及生产环境的人。这个问题变得更加严重,因为阿尔文或其他指定的 DevOps 人员都不可用。

太多的沟通渠道

在沟通方面应该有低信号噪音比。如果开发人员通过电子邮件、短信、语音邮件和 Slack 消息收到关于问题的警报,他们可能很快就会忽略并不关注这些问题。重要的是要引起开发人员的注意,以便在遇到问题时及时解决,但你可能不应该像军事指挥中心那样被来自许多不同来源的通知轰炸。

想象一下,布鲁斯是团队中的一名新开发人员,他收到了一个关于他处理的低优先级工单的警报。布鲁斯收到了有关这个工单的电子邮件、短信警报、Slack 消息和电话。布鲁斯经常收到这样的消息,很快就决定忽略它们。在一个下午,布鲁斯因为觉得这是一个毫无意义的警报而忽略了一个高优先级的工单。布鲁斯已经对这些警报麻木不仁了。

在所有这些警报中,噪音太大,真正的信号几乎没有。

痛苦驱动开发(PDD)

如果 CI/CD 管道中的某些部分让你感到痛苦,那么自动化这个过程可能是一个好主意。如果你有一个 15 步的过程,在部署管道中容易出错,并且由于执行错误而在发布过程中引起许多问题,那么这可能是其他人在某个时候也会感到痛苦的地方。这个想法是痛苦应该引导你找到更好的解决方案。如果你在处理过程中遇到问题,那么你可能需要自动化这个过程。并不总是为了自动化而自动化一个任务;你需要不断评估你的过程,而 PDD 可以是发现需要改进的过程的有效工具。

吉米在每次提交阶段都遇到了 linting 失败的问题。吉米忘记在将代码推送到代码库之前检查 lint 任务。这尤其麻烦,因为吉米确保运行所有单元测试来检查它们是否通过,但习惯性地忘记检查 linting 错误。吉米决定痛苦已经足够了,需要建立一个新的流程。吉米编写了一个预 Git 推送挂钩脚本,每次 Git 推送到主分支时都会运行 linter。现在,每当有人推送到主分支时,脚本都会运行 linter,以确保不会引入 linting 错误到代码库中。

将责任分享给不同的团队

如果可能的话,你应该轮换团队成员,尝试征求开发实践的反馈意见,并尝试创建跨职能团队。

轮换团队成员

以下图表象征着团队成员的轮岗。如果可能的话,创建一个团队轮岗,让不同的团队成员可以轮换不同的工作职责,可以帮助团队成员分享责任,建立高效的流程,并有可能激发创新:

通过将团队成员轮岗到不同的团队,可以帮助塑造他们的视角,并提供更广泛的开发实践理解和增加他们的产品知识。这并非总是可能的,特别是对于高度专业化的团队,比如安全团队或机器学习团队,因为任何开发人员有效性所需的上手时间可能会有所不同。如果可能的话,将团队成员轮岗到相关项目和技术中可以帮助防止开发人员的倦怠,并可以帮助开发人员互相学习。变得自满并习惯于事情是如何进行的是很容易的,而通常情况下,一双新的眼睛可以以新的视角看待事物,并帮助开发团队带来必要的变化。

布鲁斯在 API 开发团队工作,并被调入网络工程团队。轮岗期大约为 3 到 6 个月,布鲁斯已经学到了一些对 API 开发团队有帮助的实践。跨培训工程师的一些优势是,他们在其他开发团队学到的技能可以转移到其他团队。布鲁斯学到了一些缓存优化的方法,可以应用在网络层和 OSI 层,这将有助于 API 开发团队。开放系统互连OSI)是一个将通过网络发送的信息分解成不同层次的概念模型。OSI 模型有七层——应用层(第七层)、表示层(第六层)、会话层(第五层)、传输层(第四层)、网络层(第三层)、数据链路层(第二层)和物理层(第一层)。布鲁斯一直在应用层利用优化策略,但通过对网络层的新知识,他提出了新的优化策略。

在开发实践中寻求反馈

团队成员之间的沟通对于团队的长期成功至关重要。开发人员不应该害怕询问为什么以某种特定方式进行事情的反馈,重要的是要创造一个健康的环境,建设性的批评是受欢迎的。团队成员可能会对团队流程变得自满,并可能会错过优化流程的机会。

让我们回到我们的例子公司,比利·鲍勃的机械零件公司。假设汤姆最近加入了团队,并注意到在 API 存储库中设置的步骤过于复杂,需要许多步骤才能使特定环境运行起来。汤姆询问是否有人考虑使用构建工具自动化一些步骤,并被告知可以自行自动化他认为有帮助的任何步骤。汤姆决定编写一个 Makefile,可以通过简单运行make命令来封装所有开始特定环境的步骤。汤姆向 API 存储库创建了一个拉取请求,并引入了这个新功能,这有助于自动化创建特定环境的步骤。

创建跨职能团队

如果可能的话,并且如果你有资源,尝试创建跨职能团队,这样团队成员可以在其他团队成员中分享专业知识。例如,一个团队可以有两到三名开发人员,一个质量保证团队成员,一个安全团队成员,一个 DevOps 团队成员和一个产品负责人,他们都可以一起工作,并且能够开发出效率,否则如果他们独立工作则不会发生。

回到我们的示例公司——想象一下以下跨职能团队的阵容。汤姆、史蒂文和鲍勃都是开发人员,瑞奇是安全团队成员,苏珊是 DevOps 团队成员,尼基是产品负责人。他们都在同一个空间里共同工作,并每天早上进行晨会。现在,团队成员能够全权拥有部署流程的各个阶段,因为他们可以共同合作,互相帮助自动化流程。汤姆和史蒂文使用新库编写了自动化测试套件,瑞奇能够添加第三个构建阶段,对对主分支进行的更改进行安全检查。苏珊在每个项目通过部署流程时添加了监控和报告指标。尼基迅速更新了鲍勃的需求文档,因为他注意到了新功能工作中的边缘情况。团队成员在他们的流程中每一步都进行了公开交流,并且能够优化流程,因为他们之间进行了公开的合作。

了解你的利益相关者

对于开发团队来说,了解所有利益相关者是很重要的,因为利益相关者将持有关键信息,这些信息可以帮助团队成功或失败。开发团队应该能够在必要时与项目经理进行沟通,向高管团队成员公开沟通,并能够与最终用户交流。

项目经理

尽管产品负责人可能承担项目经理的角色,并且可以帮助促进 Scrum Master 的职责,但最好由不同的人来担任这些角色。项目经理可以被视为适应动态工作环境的变革者。在一天结束时,项目经理希望能够将交付内容交付给最终用户,并且可以帮助打开不同团队之间的沟通渠道。开发人员能够公开沟通,并通知项目经理在他们的功能工作中遇到的任何问题是很重要的。

一些公司还雇佣了负责敏捷工作环境中工作流程和方法的敏捷项目经理。敏捷项目经理将为冲刺计划制定路线图,并确保开发团队中的每个开发人员都得到了按计划分配的工作。这种类型的经理通常会更加了解团队的所有工作,并确保所有利益相关方都拥有完成其交付内容所需的工具和信息。

高管团队

公司文化很大程度上会受到高管团队的影响,比如首席执行官(CEO)、首席信息官(CIO)、首席技术官(CTO)和首席运营官(COO)。除非在这些高管层面上运作,否则很难对公司产生广泛的影响。如果开发团队觉得决策是一种命令,并且他们对所做的决定没有发言权,他们可能无法防止本来可以避免的问题。许多公司声称他们有开放式政策,并欢迎建设性的反馈,但通常开发团队在与破碎的流程作斗争时无法发声。

假设汤姆在周末读了一篇博客文章,发现了一种减少自动接受测试套件中反馈循环的方法。汤姆想引入这种变化需要大量工作。汤姆试图在周一早上的站立会议上提到这一点,但被团队拒绝了,因为有更有价值的工作要做。汤姆决定这对上层管理意识到是很重要的。汤姆继续使用开放式政策与首席技术官谈论这个问题,但第二天因未经过适当的领导渠道而受到口头斥责。这样,汤姆无法做出最有利于团队的决定,因为团队成员都没有被授权对工作流程进行更改。

最终用户

这是最终用户的描述;最终用户是最重要的利益相关者。您的最终用户的反馈意见最为重要:

最终,最终用户将使用您为产品添加的新功能。在这方面,他们可以帮助澄清开发人员的必要需求。通常情况下,最终用户在看到产品之前并不清楚他们在寻找什么。如果需要,产品所有者必须从客户那里提前获取所有必要的需求,有些软件组织甚至要求产品所有者/客户编写测试,以代码指定必须实现的需求。无论如何,在开发人员开始工作之前,产品所有者和最终用户必须就所请求的功能达成一致。

开发团队基本上与最终用户隔离,不会与任何最终用户接触。然而,对于开发团队来说,了解最终用户在使用软件系统时遇到的具体痛点是很重要的。在这个意义上,开发人员是最有能力对系统进行改变以使最终用户受益的人,但如果开发人员不了解这些痛点,他们将无法进行必要的改变以使最终用户受益。在适当的时候,让开发人员与客户成功团队合作可能会有所帮助,以了解最终用户如何使用软件系统。

展示 CI/CD 的重要性

CI/CD 流水线的重要性不容小觑,开发人员需要通过提供指标、报告和一般教育领导层自动化的重要性来证明其重要性。

指标和报告

以下图表是图表和图形的描述,您可以用它向利益相关者展示 CI/CD 的重要性。开发图表和图表是一个很好的主意,因为视觉很有说服力。

通常在公司的高管层,数字和 PowerPoint 幻灯片必须证明某事的重要性。开发人员应该能够用指标(图表、图表和任何其他可视形式)说明 CI/CD 如何改进现有流程。已经存在企业解决方案可以帮助生成这些信息,但开发团队可以将这些信息汇总到 Excel 电子表格中。

让我们假设开发团队的鲍勃已经决定,手动流程在发布日期间已经够了,迫切需要自动化。鲍勃汇总了过去 6 个月在紧急修复上花费的时间,以及在发布日出现问题时每个开发人员浪费的工时。鲍勃创建了一个漂亮的可视化图表,帮助说服管理层创建一个处理自动化部署流程的敏捷史诗。

教育领导层关于自动化

开发团队不能假设领导了解自动化的含义以及哪些领域适合自动化。最好是由技术代表,如首席技术官,作为自动化的倡导者,并帮助向高管团队解释。像首席技术官这样的人可以成为变革的代理人,代表开发人员发言,但无论是谁传达这些信息,高管团队必须了解自动化的含义以及哪些事情可以自动化。

领导团队往往与开发人员日常工作相距甚远。领导团队对公司有更全局性的关注,并且往往会与其他成员合作,如销售、营销、运营和项目经理。对于高管领导团队来说,了解自动化仍然很重要,以便开发人员有足够的时间来开发自动化部署流水线,并且他们在每个迭代期间有时间进行测试,并不断将自动化流程添加到 CI/CD 构建流水线和部署流水线中。组织的最高层需要对自动化有清晰的理解,以便开发人员、系统管理员和 DevOps 人员可以将自动化实践纳入公司路线图的关键交付成果中。

获得利益相关者对 CI/CD 的批准

即使强调自动化的重要性并向利益相关者解释其重要性,您可能需要在没有官方批准的情况下采取行动。许多软件项目都是作为一项未经官方批准的秘密项目开始的。开发人员也可以在本地机器或未使用的机器上工作部署流水线自动化任务。

启动秘密项目

术语“秘密项目”的起源有待讨论,但一般的想法是,这是一个由个别人或一组人秘密进行的项目,旨在为组织带来创新和变革。开发人员不一定能够获得对于某项任务的批准,他们可能需要采取替代策略来表达自己的观点。

想象一下,开发团队的鲍勃有一个想法,要编写一个 CLI 应用程序,帮助第三方开发人员利用公司的仪表板。鲍勃试图向高层管理层传达这个想法,但没有成功。鲍勃决定在接下来的几周内编写一个 CLI 应用程序,并决定使用一种名为Rust的新编程语言来编写 CLI 项目。鲍勃创建了一个直观的 CLI 应用程序,易于使用和可插拔。鲍勃能够向团队展示这个新应用程序,并说服高层管理层投入资源来开展 CLI 项目。

在本地机器上启动 CI/CD

开发团队可能无法获得启动 CI/CD 流水线的财务批准。为了发现并说服他人自动化 CI/CD 流水线的重要性,开发人员可以在自己的机器上复制部署流水线,并向团队和高层管理层展示构建自动化流水线阶段的好处。

如今,像 Azure、AWS 和 Google App Engine 这样的大型云服务提供商可以提供免费的账户计划来提供云服务。通过这种方式,开发人员可以轻松地设置更真实的部署流水线,通过展示一个小项目,并展示 CI/CD 流水线中的所有阶段,如提交阶段、自动化验收测试阶段和可选的安全和容量构建阶段。

公司演示

在您的组织中,公司范围的演示可能是获得 CI/CD 批准的最有效方式。一些公司赞助黑客马拉松,您可以在赞助的黑客马拉松上为公司创建一个新的自动化流程。这样做的优势是您可以在公司演示期间将自动化信息传达到组织的最高层。

假设开发团队的汤米正在尝试 Docker,并且有创建每个部署流水线的 Docker 镜像的想法。汤米表明 Docker 容器可以用作 QA 测试软件产品版本的隔离版本控制系统,这也具有环境隔离的优势。汤米构建了这个自动化流程,并在公司演示中展示了这可以节省 QA 部门 25 小时的回归测试工时。CEO 并不知道 QA 在部署过程中花费了很多时间来设置环境进行回归测试。汤米通过令人信服的演示向领导层展示了自动化的重要性。

午餐和学习

以下图表只是叉子和刀子的描绘,但主要观点是与他人共进晚餐是打开沟通渠道和团结人们的好方法。您可以在午餐时将自动化演示纳入公司会议中:

您可以邀请高层管理人员,并使用图表和带有指标的幻灯片来解释自动化是什么,并展示手动流程上的花费。通常,高层管理人员更关心活动的货币影响,如果您能向他们展示手动流程的成本,他们会更愿意倾听。

总结

传达 CI/CD 的业务价值非常重要,正如本章所阐述的那样。我们从讨论传达问题开始,讨论了一些传达痛点给团队成员的策略。我们讨论了在不同团队成员之间分享责任,了解您的利益相关者,向利益相关者展示为什么 CI/CD 对他们重要,最终从您的利益相关者那里获得 CI/CD 的批准。

下一章将介绍如何在本地环境中设置 Jenkins CI。这一章将介绍本书的第一个 CI/CD 工具。

问题

  1. 为什么您应该在开始时拥有所有的需求信息?

  2. 什么是痛苦驱动开发?

  3. 为什么拥有许多沟通渠道是有问题的?

  4. 轮换团队成员的一些好处是什么?

  5. 要求对现有开发实践进行反馈的好处是什么?

  6. 使用指标和报告如何有助于向利益相关者展示 CI/CD 的价值?

  7. 为什么需要教育领导层关于自动化?

进一步阅读

考虑阅读Packt PublishingContinuous Integration, Delivery, and Deployment,因为这本书讨论了软件组织的 CI/CD 的价值:

第五章:Jenkins 的安装和基础知识

本章将帮助您在 Windows、Linux 和 macOS 中安装 Jenkins。我们还将了解 Jenkins UI 的基础知识。

本章将涵盖以下主题:

  • Windows 安装有我们的第一个构建

  • Linux 安装

  • macOS 安装

  • 在本地运行 Jenkins

  • 管理 Jenkins

技术要求

本章是关于使用 Jenkins 进行 CI/CD 流程。在本章中,我们不会讨论 CI/CD 概念,因为我们正在设置环境以使用 Jenkins。

Windows 安装

有一些初步步骤可以安装 Jenkins。

安装 Jenkins 的先决条件

您需要确保已安装 Java,并且从 Jenkins 2.54 开始。Jenkins 现在需要 Java 8。

查找您的 Windows 版本

单击开始 Windows 图标,键入system在搜索框中,然后单击程序列表中的系统。

现在打开了系统小程序,标题为查看有关计算机的基本信息,在大的 Windows 徽标下找到位于系统区域下的系统区域。

系统类型将显示 64 位操作系统或 32 位操作系统。

安装 Java

要安装 Java,请转到 Java 下载页面(www.oracle.com/technetwork/java/javase/downloads/index.html):

确保点击接受许可协议单选按钮,然后点击 Windows 下载,并确保选择正确的架构;即 32 位或 64 位操作系统。

然后使用安装程序在 Windows 中安装 Java。

Windows 安装程序

在 Windows 操作系统中安装 Jenkins 相对容易;只需转到 Jenkins 下载页面(jenkins.io/download/):

如果您滚动到页面底部,您将看到根据当前版本可以在其上安装 Jenkins 的操作系统列表:

在 Windows 中安装 Jenkins

我已从 Jenkins 下载页面下载并解压了 Jenkins 文件,如下图所示:

在 Windows 中运行 Jenkins 安装程序

以下屏幕截图显示了 Windows 中的 Jenkins 安装程序:

一旦您完成安装程序中的所有步骤,您将看到以下屏幕:

单击完成后,您可以在 Web 浏览器中转到http://localhost:8080,您将看到以下屏幕截图:

使用 Chocolatey 软件包管理器安装 Jenkins

Chocolatey 安装说明可以在chocolatey.org/install找到。

您可以使用以下命令在cmd.exe中安装 Chocolatey:

@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "iex ((New-Object
System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"

安装了 Chocolatey 后,您只需运行choco install jenkins来通过 Chocolatey 安装 Jenkins。

在 Windows 中使用命令提示符启动和停止 Jenkins

单击开始按钮,键入cmd并按Enter。这将打开一个命令提示符会话。

接下来,您可以在命令提示符中输入以下命令:

cd 'C:\Program Files (x86)\Jenkins'

然后您可以使用以下命令:

$ C:\Program Files (x86)\Jenkins>jenkins.exe start
$ C:\Program Files (x86)\Jenkins>jenkins.exe stop
$ C:\Program Files (x86)\Jenkins>jenkins.exe restart

您还可以使用curl并使用以下命令:

$ curl -X POST -u <user>:<password> http://<jenkins.server>/restart
$ curl -X POST -u <user>:<password> http://<jenkins.server>/safeRestart
$ curl -X POST -u <user>:<password> http://<jenkins.server>/exit
$ curl -X POST -u <user>:<password> http://<jenkins.server>/safeExit
$ curl -X POST -u <user>:<password> http://<jenkins.server>/quietDown
$ curl -X POST -u <user>:<password> http://<jenkins.server>/cancelQuietDown

Linux 安装

我们将在 Ubuntu 16.04 Digital Ocean Droplet 上安装 Jenkins;请按照 Jenkins 下载页面上的按钮链接上的说明在您特定的 Linux 发行版上安装 Jenkins(jenkins.io/download/)。您可以单击 Jenkins 官方支持的 Linux 发行版之一安装 Jenkins,但是,出于本节的目的,我们将看看如何在 Digital Ocean Droplet 上的 Ubuntu 操作系统上安装 Jenkins。

在 Ubuntu 上安装 Jenkins

运行以下命令将存储库密钥添加到您的系统中:

wget -q -O - https://pkg.jenkins.io/debian/jenkins-ci.org.key | sudo apt-key add -

添加密钥后,系统将返回OK

接下来,我们将通过运行此命令将 Debian 软件包存储库地址追加到服务器的sources.list中:

echo deb https://pkg.jenkins.io/debian-stable binary/ | sudo tee /etc/apt/sources.list.d/jenkins.list

接下来,我们需要通过运行以下命令更新系统中的存储库:

sudo apt-get update

确保安装 Java,因为它是 Jenkins 运行的依赖项,因此运行以下命令:

sudo apt install openjdk-9-jre

接下来,我们在 Ubuntu 上安装 Jenkins:

sudo apt-get install jenkins

在 Ubuntu 中启动 Jenkins 服务

最后,我们需要通过以下命令启动 Jenkins 服务:

sudo systemctl start jenkins

现在我们需要确认 Jenkins 已经启动并且没有问题:

sudo systemctl status jenkins

您应该会得到以下输出:

打开网络流量防火墙

默认情况下,Jenkins 在 HTTP 端口8080上运行,因此我们需要确保该端口允许流量:

sudo ufw allow 8080

您将得到以下输出:

Rules updated
 Rules updated (v6)

接下来,我们需要查看规则的状态:

sudo ufw status

您将看到以下输出:

Status: inactive

解锁 Jenkins 进行首次登录

第一次在 Digital Ocean Droplet 上运行 Jenkins 时,您将看到以下屏幕:

在 Ubuntu 终端会话中运行以下命令:

cat /var/lib/jenkins/secrets/initialAdminPassword

将打印到标准输出的密码复制到系统剪贴板中,然后将此密码粘贴到初始登录屏幕中,然后单击“继续”按钮。

接下来,您将看到一个屏幕,您可以在其中安装建议的插件或选择要安装的插件:

这个屏幕在一开始并不是 100%必要运行,所以您可以单击屏幕右上角的 X:

单击 X 并决定启动 Jenkins 后,您将看到此屏幕:

macOS 安装

在 macOS 上安装 Jenkins 相对容易,您可以通过几种方式完成。

Jenkins 下载软件包

在本节中,我们将介绍如何使用 Mac 软件包安装程序(.pkg)文件安装 Jenkins:

  1. 转到 Jenkins 下载 URL(jenkins.io/download/)。

  2. 滚动到页面底部,您应该会看到可以在其上安装 Jenkins 的操作系统列表。

  3. 单击 Mac OS X 按钮链接,您应该会看到类似这样的页面:

  1. 单击浏览器窗口底部可以看到的.pkg文件,或者在下载文件夹中双击 Jenkins 的.pkg文件:

  1. 请注意,右下角有两个按钮,分别命名为“返回”和“继续”。只需单击“继续”,您将进入下一个窗口,即许可协议。

  2. 单击“继续”,并确保单击“同意”按钮:

  1. 通常,您只需单击“安装”按钮,但如果您想自定义,您可以选择不安装文档等:

  1. 除非您担心磁盘空间,通常最容易的方法是只需单击“标准安装”,然后单击“安装”:

  1. 安装脚本运行完成后,您将看到以下屏幕:

  1. 单击“关闭”,Jenkins 应该在您的本地机器上运行。

解锁 Jenkins 进行首次登录

第一次在主机上本地运行 Jenkins 时,您将看到以下屏幕:

如果 Jenkins 在主用户帐户中运行,请在 Mac 终端中运行以下命令:

pbcopy < /Users/jean-marcelbelmont/.jenkins/secrets/initialAdminPassword

这将把初始管理员密码复制到系统剪贴板上。如果您的初始密码运行在Users/Shared/Jenkins中,也可能会出现这种情况,请尝试以下命令:

pbcopy < /Users/Shared/Jenkins/Home/secrets/initialAdminPassword

然后,将此密码粘贴到初始登录屏幕,然后单击“继续”:

这个屏幕一开始并不是 100%必要运行,所以您可以单击屏幕右上角的 X。单击 X 并决定启动 Jenkins 后,您将看到这个屏幕:

通过 Homebrew 安装 Jenkins

您还可以通过 macOS 中的 Homebrew 软件包管理器安装 Jenkins。

如果您尚未安装 Homebrew,请首先转到 Homebrew 页面(brew.sh/)。

安装 Homebrew 相对容易。单击 Mac Finder 按钮打开终端应用程序,按Ctrl + Shift + G,然后输入/applications,并单击“前往”按钮。确保双击Utilities文件夹,然后双击终端应用程序图标。

只需将 Homebrew 安装脚本粘贴到终端应用程序提示中:

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

一旦 Homebrew 安装成功,只需在终端应用程序中运行以下命令:

安装 Jenkins 后,您可以通过在终端应用程序中输入以下命令来启动 Jenkins 服务:

brew services start jenkins

运行此命令后,只需简单地访问localhost:8080,然后您可以按照我们在首次登录解锁 Jenkins部分中运行的相同步骤。

在本地运行 Jenkins

以下是 Jenkins 主仪表板页面的屏幕截图。我们将详细介绍每个项目:

创建新项目

在接下来的步骤中,我们将创建一个自由风格项目作为新项目,但根据安装的插件,可能还可以添加更多项目:

  1. 如果单击“新建项目”链接,您将进入以下页面:

  1. 我们还没有安装任何插件,因此我们可以使用的唯一类型的项目是自由风格项目。

  2. 让我们为自由风格项目输入一个名称,然后单击“确定”:

  1. 您将看到以下屏幕以配置您的自由风格项目:

  1. 让我们为 Jenkins 创建一个简单的构建,打印出Hello World

  1. 确保单击“添加构建步骤”按钮,然后选择“执行 shell”。

  2. 最后,单击“保存”,您将返回到项目仪表板屏幕。

  3. 接下来,确保单击“立即构建”按钮以触发构建,您将看到一个文本弹出窗口,上面写着“构建已计划”:

  1. 请注意,在以下屏幕截图中,我们的第一个构建标记为#1,在“构建历史”部分:

  1. 请注意,我们现在有一个“构建历史”部分,通常您会想要查看控制台输出,以查看构建的日志信息。

控制台输出

以下是 Jenkins 中典型的控制台输出屏幕:

这是一个非常简单的屏幕,我们只是向屏幕打印了Hello World

管理 Jenkins

登录到 Jenkins 后,只需单击“管理 Jenkins”链接:

然后确保单击“管理插件”链接:

然后您将进入插件页面,看起来像这样:

确保单击“可用”选项卡,您将看到可以安装的可用插件列表。

我们将安装 Go 插件(您可以通过使用“筛选”输入框快速找到插件):

请注意,我们在过滤输入框中输入了golang。然后,您可以单击“无需重启安装”按钮或“立即下载并在重启后安装”按钮。我们将使用“无需重启安装”按钮。

单击按钮后,您将看到以下屏幕:

我们将点击“返回到顶部页面”按钮。

让我们回到 Jenkins 仪表板,点击“管理 Jenkins”,然后点击“管理插件”。

确保在过滤输入框中输入git

现在我们将点击“立即下载并在重启后安装”按钮。

现在,如果您单击“重启 Jenkins”标志,Jenkins 将重新启动,然后您将被提示登录。

接下来,确保点击“返回仪表板”链接:

配置环境变量和工具

现在我们将看看如何在 Jenkins 仪表板中添加环境变量。确保点击“管理 Jenkins”,然后点击“配置系统”:

然后您需要向全局属性中滚动:

然后确保配置所有工具,比如添加 GitHub 和 golang 的路径。

配置作业以轮询 GitHub 版本控制存储库

确保单击“新建项”按钮,现在请注意我们已添加了一个附加项。

现在我们将创建另一个名为“Golang 项目”的 Jenkins 构建作业:

您可以继续向下滚动或单击“源代码管理”选项卡:

现在,如果您向下滚动,您将进入“构建触发器”部分:

在这里,我们配置了 Jenkins 的轮询并指定了一个 cron 计划。cron 作业显示如下:分钟,小时,日,月和工作日。

您可以在 Linux 手册页下阅读有关 Crontab 的更多信息(man7.org/linux/man-pages/man5/crontab.5.html)。

然后我们添加以下配置:

我们将创建另一个 shell 脚本,其中我们执行测试。

确保点击“立即构建”按钮:

然后确保点击构建编号,然后点击控制台输出链接:

请注意,控制台输出打印出 Jenkins 正在执行的每个步骤。

总结

本章介绍了 Jenkins 的安装以及导航 Jenkins UI 的基础知识。下一章将更多地介绍 Jenkins 仪表板和 UI。

问题

  1. 在 Windows 中,我们用什么软件包管理器来安装 Jenkins?

  2. Jenkins 需要安装哪些先决条件?

  3. 在 Windows 操作系统中,重新启动 Jenkins 的一种方法是什么?

  4. 我们用什么命令来打开 Linux 中的网络流量防火墙?

  5. 我们在 macOS 中用来安装 Jenkins 的软件包管理器叫什么?

  6. 您在哪里安装 Jenkins 的插件?

  7. 您在哪里配置 Jenkins 的环境变量?

进一步阅读

请查看《使用 Jenkins 进行持续集成学习-第二版》(www.amazon.com/dp/1788479351),由 Packt Publishing 出版。

第六章:编写自由样式脚本

本章将详细介绍如何添加新的构建项目、配置构建作业、全局添加环境变量和项目级环境变量。您还将学习如何调试自由样式作业的问题。

本章将涵盖以下主题:

  • 创建一个简单的自由样式脚本

  • 配置自由样式作业

  • 添加环境变量

  • 调试自由样式作业的问题

技术要求

本章介绍了如何使用 Jenkins 创建简单的自由样式脚本。您应该对 Unix、Bash 有基本的了解,以及环境变量的含义。

创建一个简单的自由样式脚本

我们在本章中在 Jenkins 中创建了一个简单的自由样式脚本,但我们将快速回顾一下设置自由样式脚本项目所需做的事情。

Jenkins 仪表板导航

如果您按照第五章中的说明进行操作,Jenkins 的安装和基础知识,那么您应该已经在本地计算机上安装和/或运行了 Jenkins 服务。如果 Jenkins 没有在本地运行,请重新查看第五章,Jenkins 的安装和基础知识,并阅读与您的操作系统相对应的部分。

Jenkins 登录屏幕

如果 Jenkins 正在本地运行,您应该会看到一个登录屏幕。

输入您的用户名和密码信息,然后点击登录按钮。

Jenkins 仪表板

一旦您登录,您应该会被路由到 Jenkins 仪表板,它看起来像这样:

添加新的构建作业项目

在 Jenkins 仪表板中,有一个名为“新项目”的链接;确保单击它以添加新项目:

单击新项目后,您将被带到以下屏幕:

现在,根据您安装的 Jenkins 插件数量,您将在构建项目方面在屏幕上看到更多或更少的内容。我们将在本章中为目的输入一个名为Freestyle Scripting的名称,但您可以为构建作业选择任何名称。输入名称后,请确保点击 Freestyle Project 按钮,然后点击 OK 按钮。

构建配置选项

每当在 Jenkins 下创建新项目时,您将看到以下屏幕:

根据您已安装的 Jenkins 插件,您可能会看到更多选项卡或构建配置中的项目。

配置自由样式作业

请注意,构建作业配置有多个选项卡。您可以滚动到选项卡中的每个部分,也可以单击选项卡本身。每个选项卡都有不同的功能,您可以在 Jenkins 构建作业中配置它们。

一般

一般选项卡包含有关您正在创建的 Jenkins 构建的基本信息,例如描述和其他一般构建信息。查看一般选项卡信息:

通常由您决定要切换哪些选项;您可以单击问号符号获取信息。让我们看看“安静期”选项的含义:

要删除详细信息,只需再次单击问号符号。

源代码管理

源代码管理选项卡是您指定正在使用的版本控制管理系统类型的地方,例如 Git、SVN 和 Mercurial。为了构建作业的目的,我们将点击 Git 单选按钮并指定 GitHub 存储库 URL:

请注意,分支指定器默认为*/master 分支,但您可以通过点击“添加分支”按钮指定任意数量的分支。我们不会添加凭据,因为我们在本地工作,但如果您点击带有钥匙的“添加”按钮,您将看到以下叠加屏幕:

您可以通过点击“种类”输入框选择不同类型的凭据:

您还可以点击源代码管理选项卡底部的“添加”按钮,您将看到可以添加的其他行为:

还有许多高级配置选项,比如子模块,您可以进行配置。

构建触发器

构建触发器选项卡配置部分涉及配置构建作业触发的时间。这可能包括配置 GitHub 挂钩触发器,每当您将提交推送到 GitHub 的主分支时触发,触发另一个项目构建时触发,定期构建时触发,或者轮询您的版本控制系统以获取更改时触发:

我们勾选了“轮询 SCM”选项,这在我们的情况下是 GitHub,并且使用了一个在特定时间和日期运行 Jenkins 作业的 cron 语法。在我们的情况下,我们将触发轮询作业每 15 分钟运行一次。您可以点击问号符号了解更多语法信息。

稍后,我们将讨论如何使用 GitHub 和 Bitbucket 在您将代码推送到远程存储库时触发 Jenkins 作业,这比轮询更好。

构建环境

此部分将根据您安装的 Jenkins 插件的多少而有更多或更少的环境选项可供使用。在我的情况下,我安装了 Golang 和 Node.js 插件,但您可以安装任意数量的环境,比如 Clojure 和 Ruby:

由于我们正在构建一个 Golang 微库,我们在此配置部分中勾选了“设置 Go 编程语言工具”复选框。

构建

构建部分是您指定如何构建项目的地方:

如果您点击“添加构建步骤”按钮,您将看到以下选项:

我们将点击“执行 shell”选项,这将为我们提供一个 Unix shell 脚本环境来使用。

请注意,现在我们有一个文本区域可用,我们可以在其中添加 Unix 脚本命令:

我们将在此 shell 脚本中添加以下命令:go test

后置构建操作

在此构建部分,您可以指定成功构建后要运行的任何操作,比如运行代码覆盖和生成 JUnit 报告:

如果您点击“添加后置构建操作”按钮,您将看到以下选项:

根据您安装的具体 Jenkins 插件,您将看到更多或更少的选项。

一旦您对构建配置满意,点击“应用”按钮,这将保存您当前的配置选项,或者点击“保存”按钮,这将保存您的选项并将您导航到一个新配置的构建项目:

后置构建操作部分非常有价值,因为您可以在成功构建时调用其他服务,比如报告和收集指标。

添加环境变量

您可以以多种不同的方式在 Jenkins 中添加环境变量。

全局环境变量配置

从 Jenkins 仪表板,点击“管理 Jenkins”按钮:

一旦您点击“管理 Jenkins”按钮,您将需要点击“配置系统”按钮:

然后您将被导航到“配置系统”部分,然后可以使用全局属性部分添加环境变量:

请注意,我在这里添加了一个名称为SAMPLE_VALUE的值为Hello Book Readers的内容。现在,这个全局属性在 shell 环境变量中作为一个环境变量可用。您可以在这个部分添加尽可能多的环境变量。请注意,这个全局属性现在将对每个作业都可用。

EnvInject 插件

您还可以为每个特定的构建项选择更细粒度的环境变量设置。

通过执行以下步骤安装 EnvInject 插件(wiki.jenkins.io/display/JENKINS/EnvInject+Plugin)。点击 Jenkins 主仪表板链接:

请确保点击 Jenkins 链接,您将被路由到 Jenkins 仪表板。然后点击“管理 Jenkins”按钮,就像您添加全局属性时所做的那样。

接下来点击“管理插件”按钮,它看起来像这样:

您现在将被带到以下屏幕:

请注意,我们点击了“可用”选项卡,然后将EnvInject放入筛选框中。确保点击您想要的 Jenkins 插件,然后点击“无需重启安装”或“立即下载并在重启后安装”按钮。

请注意,我们现在在构建配置区的“构建环境”部分有了一些新的构建选项:

如果您点击“将环境变量注入到构建过程中”,您可以添加您的新环境变量,就像这样:

确保保存您的更改。需要注意的一点是,这个环境变量只适用于这个特定的构建项;它不像我们之前设置的全局属性那样是一个全局属性。

调试自由样式作业的问题

每当您在 Jenkins 中为一个构建项运行构建时,您可以通过点击您想要查看的特定构建作业来查看构建的所有细节。

构建项目视图

这是您需要关注的构建历史:

现在,如果您点击一个实际的构建,您将进入以下屏幕:

如果您点击“控制台输出”链接,您将看到一个详细的 CI 构建日志,显示 CI 服务器执行的所有步骤。请记住,我们编写了一个自由样式的 shell 脚本。我将添加 shell 脚本的内容供您查看:

echo "$SAMPLE_VALUE"
echo "$ANOTHER_SAMPLE_NAME"
go test

请注意,我在这里添加了我们之前定义的两个不同的环境变量,并且我只是将它们发送到标准输出。

现在,如果您查看构建作业的输出,您将看到以下输出:

请注意,Jenkins 通过当前登录的用户来启动作业。接下来,EnvInject 插件运行并注入我们在项目中指定的任何环境变量。然后 Jenkins 从 GitHub 仓库获取最新的更改。然后 EnvInject 插件再次运行并注入任何必要的环境变量。

最后一个操作是实际执行 shell 脚本。在前面的屏幕截图中要注意的一件事是,因为在 Jenkins 中启用了执行跟踪,所以 shell 脚本中的每个命令都会被打印到标准输出。请记住,执行跟踪只是意味着在 shell 脚本中运行的每个命令以及命令本身的输出都将显示出来。例如,echo "$ANOTHER_SAMPLE_NAME"命令的值为echo "Hello Book Readers",将被打印到标准输出,然后打印出消息Hello Book Readers。最后要注意的是,构建显示了文本PASS,并以SUCCESS完成。

调试自由风格脚本的问题

请注意我们如何记录具有简单信息的环境变量。有时候值在 CI 环境中没有被设置,这是您所期望的,这就是将值记录到标准输出中非常有帮助的地方。使用 EnvInject 插件的一个好处是,它将掩盖您注入到构建作业中的密码,以便您不会意外记录机密或保密信息:

请注意,在前面的屏幕截图中,我们已经检查了将密码注入到构建作业中作为环境变量,并为环境变量指定了名称和密码。如果您在构建作业中意外执行echo $SecretName,它将掩盖$SecretName的值,以便您不会在构建中泄露机密信息。

总结

在本章中,您学习了更多关于 Jenkins 仪表板的知识。您学习了如何添加构建作业项以及配置自由风格构建作业的所有部分,如何将环境变量添加到 Jenkins 作业中,以及如何调试自由风格作业中的问题。

下一章将介绍如何构建 Jenkins 插件,并具体介绍构建过程,包括编写 Java 代码和使用 Maven 构建工具。

问题

  1. 为什么在构建配置中点击问号符号很有用?

  2. 如果您想在构建触发器部分轮询您的版本控制系统,您应该写什么类型的语法?

  3. 在构建环境中可以使用多种编程语言吗?

  4. 自由风格脚本操作在什么类型的环境中——是 Unix 环境吗?

  5. 全局属性和项目级环境变量之间有什么区别?

  6. 您认为 Jenkins 为什么在控制台输出中使用执行跟踪?

  7. 在构建配置的后构建操作部分的价值是什么?

进一步阅读

请查看使用 Jenkins 进行持续集成学习-第二版www.amazon.com/dp/1788479351),来自 Packt Publishing。

第七章:开发插件

本章将详细介绍 Jenkins 中的插件,我们将首先看如何在 Windows、Linux 和 macOS 中设置 Maven。然后我们将通过为 Jenkins 创建一个Hello World插件来了解插件开发,然后我们将简要介绍 Jenkins 插件站点以及如何浏览和使用它来找到各种插件。

本章将涵盖以下主题:

  • Jenkins 插件解释

  • 构建一个简单的 Jenkins 插件

  • Jenkins 插件开发

  • Jenkins 插件生态系统

技术要求

本章是关于在 Jenkins 中构建插件的,您需要对 Java 编程语言有基本的了解,并了解 Maven 等构建工具的用途。

Jenkins 插件解释

Jenkins CI 已经提供了某些功能,包括构建、部署和自动化软件项目。您通常可以通过 Jenkins 中大量的插件生态系统获得您想要的任何额外行为。

插件有什么用?

软件中插件/扩展的目的是为软件组件添加特定功能。诸如 Chrome 的网络浏览器有扩展程序来扩展浏览器的功能,Firefox 有附加组件来实现与 Chrome 中扩展程序相同的目的。其他软件系统中也存在插件,但我们将专门关注 Jenkins 中的插件。

Jenkins 插件文档

转到插件索引以找到您需要的任何插件,我们将在本章的后面部分讨论这个问题。如果您访问 Jenkins 维基中的插件教程,您将获得创建 Jenkins 插件的完整说明。还有一些 Jenkins 维基之外的教程可以使用。您可以转到 Jenkins 原型库查看Hello World插件示例。

在 Jenkins 中安装插件

您需要转到 Jenkins 仪表板中的“管理 Jenkins”链接:

点击“管理 Jenkins”链接后,您将被重定向到以manage结尾的 URL 路径,例如http://localhost:8080/manage,或者根据您是否在本地运行 Jenkins 而有其他域。您需要点击“管理插件”链接,然后确保点击“已安装”选项卡和/或筛选您希望安装的任何插件。我们之前已经介绍过这个,但我们将通过安装我们自己的 Jenkins 插件来安装它,就像安装任何其他 Jenkins 插件一样。

构建一个简单的 Jenkins 插件

创建 Jenkins 插件有一些先决条件。您需要安装 Java,如果您一直在跟进,那么应该已经安装了。您还需要安装 Maven 软件项目管理工具(maven.apache.org/)。

Java 安装

您需要确保已安装 Java 1.6 或更高版本,我建议您安装 Java 1.9。要安装 Java,请转到 Java 下载页面(www.oracle.com/technetwork/java/javase/downloads/index.html):

确保点击“接受许可协议”单选按钮,然后点击 Windows。下载并确保选择正确的架构,即 32 位或 64 位操作系统。

安装 Java 后,只需使用以下命令验证安装:

java -version

这应该返回已安装的 Java 的当前版本。

Maven 安装说明

要安装 Maven,请转到 Maven 安装页面(maven.apache.org/install.html)并确保按照给定操作系统的说明进行操作。

Windows 安装

您可以在 Windows 上以几种不同的方式安装 Maven,但请确保您至少安装了 Windows 7 操作系统和 Java 软件开发工具包SDK)1.7 或更高版本。如果您在第五章中跟随了Jenkins 的安装和基础知识,那么您应该已经安装了 Java。

通过 Chocolatey 软件包管理器安装 Maven

如果您已经安装了 Chocolatey 软件包管理器(chocolatey.org/install),那么您可以简单地运行以下命令:

choco install maven

您还可以从 Maven 安装页面(maven.apache.org/install.html)下载 Maven 二进制可执行文件,并且您还需要额外找到 Java 环境变量的值。您可以通过在命令提示符中运行以下命令来找到:

echo %JAVA_HOME%

然后,您需要通过以下方式将此 Maven 二进制可执行文件添加到 Windows 路径中:

  1. 右键单击我的电脑

  2. 点击属性

  3. 点击高级系统设置

  4. 点击环境变量

  5. 点击新用户变量,添加 Maven_Home,值为C:\apache-maven-3.5.3

  6. 将其添加到路径变量中,使用%Maven_Home%\bin

  7. 在命令提示符中打开,并在桌面上询问mvn -version

通过 Maven 源代码安装 Maven

首先确保您已安装 Java SDK,您可以在命令提示符中确认:

echo %JAVA_HOME%

这应该打印出您已安装的 Java 当前版本。接下来,从 Maven 源代码库(gitbox.apache.org/repos/asf?p=maven-sources.git)下载 Maven 源代码,然后在 Windows 操作系统中的合适位置解压 Maven 源代码。

C:\Program Files\Apache\maven是一个可能的位置,你可以使用。

为 Windows 操作系统设置环境变量

您需要使用系统属性将M2_HOMEMAVEN_HOME变量都添加到 Windows 环境中,并且您需要将环境变量指向您的 Maven 文件夹。

通过附加 Maven bin文件夹%M2_HOME%\bin来更新 PATH 变量,这样您就可以在系统中的任何位置运行 Maven 可执行文件。

要验证 Maven 是否正确运行,请在命令提示符中运行以下命令:

mvn --version

该命令应显示当前的 Maven 版本、Java 版本和操作系统信息。

macOS 安装

您需要确保 macOS 操作系统中已安装 Java SDK。如果您在第五章中跟随了Jenkins 的安装和基础知识,那么您应该已经安装了 Java。

通过 Homebrew 软件包管理器安装 Maven

首先确保 Java 已安装,通过在 Mac 终端应用程序中运行以下命令:

java -version
java version "1.8.0_162"
Java(TM) SE Runtime Environment (build 1.8.0_162-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.162-b12, mixed mode)

您需要在系统上安装 Java 1.7 或更高版本。

接下来,如果您已经安装了 Homebrew 软件包管理器(brew.sh/),那么您可以通过在 Mac 终端应用程序中发出以下命令来轻松安装 Maven:

brew install maven

确保在您的.bashrc.zshrc文件中设置以下环境变量:

export JAVA_HOME=`/usr/libexec/java_home -v 1.8`

确保 Maven 已经正确安装,通过在 Mac 终端中运行以下命令:

mvn --version
Apache Maven 3.5.3 (3383c37e1f9e9b3bc3df5050c29c8aff9f295297; 2018-02-24T14:49:05-05:00)
Maven home: /usr/local/Cellar/maven/3.5.3/libexec
Java version: 1.8.0_162, vendor: Oracle Corporation
Java home: /Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "mac os x", version: "10.13.4", arch: "x86_64", family: "mac"

请注意,mvn二进制可执行文件打印出了已安装的 Maven 版本、Java 版本和特定于操作系统的信息。

Unix 安装

我们将在 Ubuntu 16.04 Digital Ocean Droplet 上安装 Maven,但您应该能够在其他 Linux 发行版上运行类似的命令。请按照说明在您特定的 Linux 发行版上安装 Maven。

通过 apt-get 软件包管理器安装 Maven

确保 Java 已安装在您的 Linux 发行版中,您可以通过在终端 shell 中运行以下命令来检查:

java -version
openjdk version "9-internal"
OpenJDK Runtime Environment (build 9-internal+0-2016-04-14-195246.buildd.src)
OpenJDK 64-Bit Server VM (build 9-internal+0-2016-04-14-195246.buildd.src, mixed mode)

如果尚未安装 Java,则运行以下命令:

sudo apt-get update && sudo apt install openjdk-9-jre

接下来,在终端应用程序中运行以下命令安装 Maven:

sudo apt-get install maven

接下来,您需要确保您的JAVA_HOME环境变量已设置。由于我们在 Ubuntu Linux 操作系统中安装了 Java 1.9,我们将运行以下命令:

export JAVA_HOME=/usr/lib/jvm/java-1.9.0-openjdk-amd64/

您使用的目录可能不同,但如果您未设置此环境变量,则 Maven 将报告此警告。

通过在终端应用程序中运行以下命令来检查 Maven 是否已正确安装:

mvn --version
Apache Maven 3.3.9
Maven home: /usr/share/maven
Java version: 9-internal, vendor: Oracle Corporation
Java home: /usr/lib/jvm/java-9-openjdk-amd64
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "4.4.0-127-generic", arch: "amd64", family: "unix"

请注意,Maven 二进制可执行文件打印出当前安装的 Maven 版本、当前安装的 Java 版本和特定于操作系统的信息,就像 Windows 和 Mac 操作系统一样。

Jenkins 插件开发

有几个步骤是必要的,以便设置、运行和安装 Jenkins 插件。

Maven 设置文件

根据您当前的操作系统,您需要创建/编辑.m2/settings.xml文件。

通过在命令提示符中发出以下命令,Windows 用户将找到settings.xml文件:

echo %USERPROFILE%\.m2\settings.xml

Mac 操作系统用户可以在~/.m2/settings.xml中编辑/创建settings.xml文件。

settings.xml文件中的settings元素包含用于以各种方式配置 Maven 执行的值的元素,例如pom.xml,但不应捆绑到任何特定项目或分发给受众。这些值包括本地存储库位置、替代远程存储库服务器和身份验证信息。

将以下内容放入settings.xml文件中:

请注意,我们输入了与 Jenkins 插件相关的特定信息。

强烈建议您将您的settings.xml文件设置为正确运行您的 Jenkins 插件!

HelloWorld Jenkins 插件

要创建 Jenkins 插件,您需要使用 Maven 原型,您可以在这里阅读有关它的信息(maven.apache.org/guides/introduction/introduction-to-archetypes.html)。

我们将发出以下命令以生成 Jenkins 的Hello World插件:

mvn archetype:generate -Dfilter=io.jenkins.archetypes:hello-world

以下是我创建插件的示例运行会话:

请注意,我为原型输入了1,然后选择了插件版本4,并定义了jenkins-helloworld-example-plugin的值,然后按Enter获取默认值:

如果一切顺利,您应该在命令提示符中获得BUILD SUCCESS的输出。

您需要确保能够构建您的 Jenkins 插件,因此请确保在命令提示符中运行以下命令:

// First go into the newly created directory
cd jenkins-helloworld-example-plugin
// Then run the maven build command
mvn package

mvn package命令将创建一个target目录,并运行您在该目录中创建的任何测试:

请注意,Jenkins 原型实际上为我们的Hello World Jenkins 插件示例创建了一些测试。

文件夹布局说明

以下是新创建的jenkins-helloworld-example-plugin目录的屏幕截图:

src目录包含 Jenkins 插件的源文件以及插件的测试。

目标目录是通过mvn包生成的。还有一个pom.xml文件,当我们运行原型子命令时 Maven 创建了它。

项目对象模型POM)是 Maven 中的基本工作单元。它是一个包含有关项目和 Maven 用于构建项目的配置详细信息的 XML 文件。它包含大多数项目的默认值。其中包括构建目录,即target,源目录,即src/main/java,以及测试源目录,即src/test/java

Jenkins 插件源代码解释

正如我们之前提到的,src目录包含了 Jenkins 插件的源文件。为了在 Jenkins 中构建插件,您需要使用 Java 编程语言进行编写。教授 Java 编程语言超出了本书的范围,但我们将简要讨论 Maven 为我们创建的一些文件。

请注意,Maven 创建了一个相当长的目录结构,这是常见的,因此helloworld插件的目录结构为./src/main/java/io/jenkins/plugins/sample/HelloWorldBuilder.java。测试文件本身位于./src/test/java/io/jenkins/plugins/sample/HelloWorldBuilderTest.java

我在这里包含了HelloWorldBuild.java类的源代码:

package io.jenkins.plugins.sample;   import hudson.Launcher; /* More Import Statements Here */

public class HelloWorldBuilder extends Builder implements SimpleBuildStep {

    /* Rest of methods in Github Source */

    @Override
    public void perform(Run<?, ?> run, FilePath workspace, Launcher launcher, TaskListener listener) throws InterruptedException, IOException {
        if (useFrench) {
            listener.getLogger().println("Bonjour, " + name + "!");
        } else {
            listener.getLogger().println("Hello, " + name + "!");
        }
    }

    @Symbol("greet")
    @Extension
    public static final class DescriptorImpl extends BuildStepDescriptor<Builder> {

        /* Rest of the source in Github */
}

请注意,HelloWorldBuilder类扩展了Builder类,这是 Jenkins 核心类;还请注意,我们正在使用一个名为BuildStepDescriptor的类,这也是一个 Jenkins 类。此文件的源代码可以在我的 GitHub 存储库中的jenkins-plugin-example中的HelloWorldBuilder.javagithub.com/jbelmont/jenkins-plugin-example/blob/master/src/main/java/io/jenkins/plugins/sample/HelloWorldBuilder.java)文件中看到。

对于HelloWorldBuilderTest.java中的测试用例,我们使用了 Java 编程语言的流行单元测试库 JUnit。

package io.jenkins.plugins.sample;   import hudson.model.FreeStyleBuild; /* More Import Statements Here */   public class HelloWorldBuilderTest {

    @Rule
  public JenkinsRule jenkins = new JenkinsRule();   final String name = "Bobby";    @Test
  public void testConfigRoundtrip() throws Exception {
        FreeStyleProject project = jenkins.createFreeStyleProject();
  project.getBuildersList().add(new HelloWorldBuilder(name));
  project = jenkins.configRoundtrip(project);
  jenkins.assertEqualDataBoundBeans(new HelloWorldBuilder(name), project.getBuildersList().get(0));
  }

    /* More test Cases in this file. */

}

上述的 Java 测试文件具有诸如@Rule@Override@Test@DataBoundSetter等注解,这些注解是一种元数据,提供关于程序的数据,而不是程序本身的一部分。注解对其注释的代码的操作没有直接影响。此文件的源代码可以在我的 GitHub 存储库中的jenkins-plugin-example中的HelloWorldBuilderTest.java文件中看到(github.com/jbelmont/jenkins-plugin-example/blob/master/src/test/java/io/jenkins/plugins/sample/HelloWorldBuilderTest.java)。

构建 Jenkins 插件

为了构建 Jenkins 插件,您需要在插件目录中运行mvn install命令。

mvn install命令将构建和测试 Jenkins 插件,并且更重要的是,创建一个名为pluginname.hpi的文件;或者在我们的情况下,它将在target目录中创建一个名为jenkins-helloworld-example-plugin.hpi的文件,我们可以用它来部署到 Jenkins。

我已经在以下屏幕截图中附上了一个示例安装运行:

请注意,此运行通过将我们的 Jenkins 插件安装到多个位置来完成,我们将使用这些位置来安装我们的 Jenkins 插件。

安装 Jenkins 插件

现在,为了安装新构建和安装的HelloWorld示例插件,我们需要转到 Jenkins 仪表板|管理 Jenkins|管理插件视图,然后点击高级选项卡。如果需要更多细节,请参阅第六章,编写自由脚本EnvInject 插件部分。您还可以直接转到插件部分,方法是转到scheme://domain/pluginManager;或者,如果您正在本地运行 Jenkins,只需转到http://localhost:8080/pluginManager/

然后确保点击高级选项卡或转到http://localhost:8080/pluginManager/advanced

然后您需要转到上传插件部分:

点击选择文件,然后找到我们新创建的Helloworld Jenkins 插件,它应该在目录中:

jenkins-helloworld-example-plugin/target/jenkins-helloworld-example-plugin.hpi

然后确保点击上传按钮。

以下是新安装的Helloworld示例插件的屏幕截图:

Jenkins 插件生态系统

Jenkins 有大量可用的插件,您可以在 Jenkins 插件网站(plugins.jenkins.io/)上找到完整的列表。

可用插件列表

以下截图显示了与 Jenkins 中 JSON 相关的插件搜索:

请注意,Jenkins 插件网站除了默认视图之外,还有多个视图可供您使用:

请注意,我点击了中间的图标,但您也可以点击最右边的图标以获得一个小纲要视图。搜索默认为相关搜索项,但您可以选择不同的搜索条件,例如最常安装、趋势和发布日期。

摘要

在本章中,您了解了 Java 的 Maven 构建工具以及如何在 Windows、Linux 和 macOS 上安装它。您还学习了如何使用 Maven 构建工具创建 Jenkins 插件,我们简要讨论了一些 Java 语法以及如何使用 Jenkins 仪表板中的管理插件 UI 中的高级选项来安装 Jenkins 插件。我们还研究了 Jenkins 插件生态系统。

问题

  1. 我们用来创建 Jenkins 插件的构建工具是什么?

  2. 我们在 Windows 操作系统中使用了哪个软件包管理器来安装 Maven?

  3. 我们在 macOS 操作系统中使用了哪个软件包管理器来安装 Maven?

  4. 我们在HelloWorld插件中简要讨论的配置文件的名称是什么?

  5. 我们可以直接导航到哪个 URL 来管理 Jenkins 中的插件?

  6. 我们用来在 Maven 中构建和安装 Jenkins 插件的命令是什么?

  7. Maven 为我们创建了哪种类型的文件,以便我们可以安装 Jenkins 插件?

进一步阅读

请查看 Packt Publishing 出版的书籍扩展 Jenkinswww.amazon.com/dp/B015CYBP2A/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)了解更多关于 Jenkins 插件的信息。

第八章:使用 Jenkins 构建管道

本章将详细介绍如何使用现有的 Jenkins 实例设置 Jenkins 蓝海以及如何使用 Docker 进行设置。我们将详细了解蓝海用户界面(UI),并讨论 Jenkins 经典视图和蓝海视图之间的区别。我们还将详细讨论 Pipeline Syntax,并简要讨论其用途,并解释两种不同类型的 Pipeline Syntax。

本章将涵盖以下主题:

  • Jenkins 2.0

  • Jenkins 管道

  • 在 Jenkins 蓝海中导航

  • Pipeline Syntax

技术要求

本章需要基本了解如何与 Unix Shell 环境交互。我们还将简要讨论 Pipeline Syntax,因此如果您具有一些基本的编程技能,就能理解编程语言中关键字的用途。

Jenkins 2.0

Jenkins 2.0 的设计方法和流程与 Jenkins 1.0 相比有所不同。不再使用自由风格的作业,而是使用了一种新的“领域特定语言”(DSL),这是 Groovy 编程语言的缩写形式。

管道视图在 Jenkins 1.0 中的功能也有所不同。管道阶段视图还帮助我们可视化管道中的各个阶段。

为什么要转移到 Jenkins 2.0?

那么,首先,为什么要转移到 Jenkins 2.0,而不是继续使用 Jenkins 1.0?Jenkins 经典视图被认为是混乱的,并且没有考虑到易用性。Jenkins 2.0 在更直观的方式上使用 Docker 镜像上做出了很大的推动。此外,新的 UI 包括了 Jenkins 管道编辑器,并通过引入管道视图改变了您查找构建的方式。新 UI 的目标是减少混乱,增加对使用 Jenkins 的团队中每个成员的清晰度。新 UI 还具有 GitHub 和 Bitbucket 集成以及 Git 集成。Jenkins 2.0 UI 本质上是您安装的一组插件,称为蓝海。

在现有实例上安装蓝海插件

如果在大多数平台上安装 Jenkins,您将不会默认安装所有相关插件的蓝海插件。您需要确保您正在运行 Jenkins 版本 2.7.x 或更高版本才能安装蓝海插件。

为了在 Jenkins 实例上安装插件,您必须具有通过基于矩阵的安全设置的管理员权限,任何 Jenkins 管理员也可以配置系统中其他用户的权限。

安装蓝海插件的步骤如下:

  1. 确保您以具有管理员权限的用户登录

  2. 从 Jenkins 主页或 Jenkins 经典版的仪表板上,点击仪表板左侧的“管理 Jenkins”

  3. 接下来,在“管理 Jenkins”页面的中心,点击“管理插件”

  4. 点击“可用”选项卡,并在过滤文本框中输入“蓝海”,以过滤插件列表,只显示名称和/或描述中包含“蓝海”的插件

请阅读第七章,开发插件,特别是安装 Jenkins 插件部分,以获取更多信息。

通过 Jenkins Docker 镜像安装蓝海插件

您需要确保已安装 Docker 才能获取 Jenkins CI Docker 镜像。

Docker 先决条件

由于 Docker 利用操作系统的虚拟化技术,因此 Docker 的安装要求是特定的。

OS X 的要求是:

  • 2010 年或更新型号的 Mac,带有英特尔的 MMU 虚拟化

  • OS X El Capitan 10.11 或更新版本

Windows 的要求是:

  • 64 位 Windows

  • Windows 10 专业版、企业版或教育版(不是家庭版,也不是 Windows 7 或 8)来安装 Hyper-V

  • Windows 10 周年更新版或更高版本

  • 访问您机器的 BIOS 以打开虚拟化

在您的操作系统上安装 Docker,请访问 Docker 商店(store.docker.com/search?type=edition&offering=community)网站,并点击适合您的操作系统或云服务的 Docker 社区版框。按照他们网站上的安装说明进行安装。

通过使用 Windows 命令提示符或 OS X/Linux 终端应用程序检查 Docker 版本来确保 Docker 已安装。在命令行中运行以下命令:

注意这里我安装了 Docker 版本 18。

安装 Docker 镜像

要获取 Docker 镜像,您需要确保在 Docker Hub(hub.docker.com/)上有一个帐户。一旦您在 Docker Hub 上有了帐户并且安装了 Docker,获取最新的 Jenkins CI Docker 镜像就很简单。

在 Windows 命令提示符或终端中运行以下命令:

请注意,我已经拉取了jenkinsci/blueocean Docker 镜像,因此该命令没有从 Docker Hub 中拉取,而是打印出了一个 SHA 哈希校验和。这表明我已经拥有了jenkinsci/blueocean的最新 Docker 镜像。

接下来,您需要让 Jenkins Docker 容器运行起来,并且您需要在终端或命令提示符中运行以下命令:

您可以通过简单地创建一个为您执行此操作的 shell 脚本或创建一个别名来简化此过程。

以下是我在文本编辑器中创建的一个 shell 脚本:

我有一个个人的bin目录,我在~/bin中存储所有个人脚本,然后我确保将其添加到PATH变量中。脚本文件名为run-jenkinsci-blueocean。我们需要确保该脚本是可执行的,通过发出以下命令:

 chmod +x run-jenkinsci-blueocean

然后我只需要运行~/bin/run-jenkinsci-blueocean命令。

您也可以在 Unix 中创建类似的别名:

# inside ~/.zshrc

alias runJenkinsDockerImage='docker run -u root jenkins-blueocean --rm -d -p 8080:8080 -p 50000:50000 -v jenkins-data:/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock jenkinsci/blueocean'

请注意,我在我的.zshrc文件中添加了这个 shell 别名,但您也可以将其添加到.bashrc文件中。

Windows 用户可以创建一个批处理文件或找到其他方法来使运行 Docker 命令更容易。

为了停止 Docker 容器,您可以运行以下命令:

docker ps -a

此命令将显示系统中所有正在运行的容器;您需要查看Container IDNAMES列,并复制与 Docker 镜像jenkinsci/blueocean对应的 ID。最后,要停止容器,您需要运行以下命令:

docker stop jenkins-blueocean

请注意,因为我们在 shell 脚本中的docker run命令中使用了--name jenkins-blueocean选项,Docker 创建了一个名为jenkins-blueocean的容器;如果我们没有这样做,那么 Docker 将为我们创建一个容器的名称。当您在终端或命令提示符中发出docker ps -a命令时,您可以使用容器 ID 和名称来停止容器。

一旦 Jenkins 运行起来,您可以在这里访问:http://localhost:8080,您需要提供为管理员生成的默认密码来解锁 Jenkins。在第五章中,Jenkins 的安装和基础知识,我们跳过了安装建议插件的入门步骤,但这次我建议您在入门屏幕上安装建议的插件:

通过点击安装建议插件按钮,您将获得所有建议的插件和依赖插件,这将帮助您在新的 Jenkins 2.0 流程中使用流水线等功能。

访问 Blue Ocean Jenkins 视图

您需要确保点击打开 Blue Ocean 按钮,看起来类似于这样:

点击“打开蓝色海洋”按钮后,您将被重定向到此 URL:http://localhost:8080/blue/organizations/jenkins/pipelines。Jenkins UI 将看起来非常不同,并且行为也不同。

这是您将看到的初始屏幕,因为我们还没有创建任何流水线:

我们将在接下来的部分中探索流水线语法以及如何在 Jenkins 2.0 UI 中进行导航。

Jenkins 流水线

我们将使用 Jenkins 2.0 UI 创建我们的第一个流水线,并且还将使用内置到新 Jenkins 2.0 UI 中的流水线编辑器创建一个 Jenkinsfile。

创建 Jenkins 流水线

我们要做的第一步是点击“创建新流水线”按钮。您将被重定向到以下屏幕:

对于本章的目的,我们将使用我创建的现有 GitHub 存储库,但您也可以轻松使用 Bitbucket 和您自己托管在 GitHub 或 Bitbucket 上的代码。为了使其工作,您需要确保在 GitHub 上有一个帐户,如果没有,请确保注册 GitHub(github.com/)。

为 GitHub 提供个人访问令牌

如果您在 GitHub 上还没有个人访问令牌,您将需要创建一个。请注意,在以下截图中,有一个名为“在此处创建访问密钥”的链接:

点击“在此处创建访问密钥”链接后,您将被重定向到以下 GitHub 页面:

您可以保持默认选项勾选,然后点击标题为“生成令牌”的绿色按钮。确保将此个人访问令牌保存在安全的地方,因为它只会显示一次;复制它,因为我们将需要它。您需要将访问令牌粘贴到“连接到 Github”输入框中,然后点击蓝色的“连接”按钮:

选择您的 GitHub 组织

您需要选择您所属的 GitHub 组织。在接下来的截图中,我选择了 GitHub 用户名组织jbelmont

选择 GitHub 存储库

您需要做的最后一步是实际选择要创建 Jenkins 流水线的 GitHub 存储库。在这里的截图中,我输入了cucumber-examples并选择了下拉菜单。然后蓝色的“创建流水线”按钮被启用:

使用流水线编辑器创建流水线

在我们选择的 GitHub 存储库中,没有现有的 Jenkinsfile,因此我们被重定向到流水线编辑器屏幕,我们可以在其中创建我们的第一个 Jenkinsfile:

我们需要为 Node.js 和代理添加一个 Docker 镜像,看起来类似于这样:

请注意,我们使用-v选项为 Docker 挂载数据卷提供了一个图像和参数。

接下来,我们点击灰色的加号按钮,然后我们将看到以下更改:

接下来,在为阶段命名后,我们点击蓝色的“添加步骤”按钮。对于这个演示,我们将选择“构建”:

接下来,我们需要选择一个步骤选项。我们将选择标题为“Shell 脚本”的选项,这将安装我们所有的 Node.js 依赖项:

接下来,我们输入一些要在我们的 Shell 脚本中运行的命令:

接下来,我们将再次点击灰色的加号按钮,以向我们的流水线添加一个阶段,现在看起来类似于这样:

接下来,我们将为此阶段输入一个名称,并且在本章中,我们将选择Cucumber Tests

接下来,我们为此阶段添加一个步骤,并且我们将再次选择 Shell 脚本作为选项:

最后,我们将点击保存按钮并提供提交消息,以便将此更改推送到我们的 GitHub 存储库:

点击蓝色的“保存并运行”按钮后,Jenkinsfile 将合并到主分支中,并且管道将运行。

在 Jenkins Blue Ocean 中导航

在 Jenkins Blue Ocean 中,您习惯使用的一些视图在 Jenkins Blue Ocean 中不可用。Jenkins Blue Ocean 背后的主要概念是使 Jenkins 内部导航更加便捷,并通过更好的图标和页面导航改进 Jenkins UI。新 Jenkins UI 的许多灵感来自强调世界已经从功能性开发人员工具转向开发人员体验的《蓝海战略》一书,并且新 UI 致力于改善 Jenkins 的开发人员体验。

管道视图

以下屏幕截图描述了 Jenkins Blue Ocean 的管道视图。请注意,我们为两个不同的 GitHub 存储库创建了两个不同的管道。通过单击“新管道”按钮并添加个人 base64 (github.com/jbelmont/decode-jwt) Golang 库来解码 JSON Web 令牌通过命令行工具创建了第二个管道:

此列表将根据您在 Jenkins 实例中添加的管道数量而有所不同。请注意,您可以标记一个管道,并且有标有名称、健康、分支和 PR 的列。

管道详细视图

如果您点击实际管道,那么您将进入一个管道详细信息页面,其中包含有关特定管道中运行的所有阶段的所有详细信息。接下来的屏幕截图是 base64 管道:

管道构建视图

您可以单击管道视图中的每个节点,并查看该阶段完成的所有工作。在第一个屏幕截图中,我们点击“构建信息”节点,以查看在该特定阶段运行的命令,其中包括拉取 GitHub 存储库的最新副本并运行go versiongo fmt命令:

请注意,第二个节点标记为“运行测试”,当我们点击该特定节点时,我们只看到go test命令,该命令在 Golang 中运行我们的单元测试用例:

管道视图的一大优点是,您可以更清晰地查看持续集成构建中每个阶段的更好布局的可视化效果。

管道阶段视图

如果您点击管道中的实际阶段,由>符号表示,它将向您显示一个下拉视图,其中包含该特定阶段的详细信息:

请注意,这里我们点击了“运行测试”阶段,以查看报告,报告显示我们用 Golang 编写的单元测试用例已通过。

Jenkins 管道中的其他视图

还有其他视图可供使用,例如拉取请求视图,它会显示所有打开的拉取请求以及分支视图:

Jenkins Blue Ocean 视图仍在进行中,因此任何管理任务,例如添加插件和添加安全信息,仍然在 Jenkins 经典视图中完成。

管道语法

管道语法有两种形式(jenkins.io/doc/book/pipeline/syntax/#declarative-pipeline):

  • 声明性管道

  • 脚本管道

两种形式之间的区别在于,声明性管道语法旨在比脚本管道更简单。脚本管道语法是 DSL,遵循 Groovy 编程语言的语义。

管道编辑器

cucumber-examples存储库中,我们使用管道编辑器创建了一个 Jenkinsfile。您实际上可以在不使用管道编辑器的情况下编写 Jenkinsfile,尽管我建议在调试管道脚本时使用它,因为编辑器具有一些很好的功能。

Jenkinsfile

这里有管道编辑器为我们创建的实际管道语法。它使用了声明性管道语法,这个语法有几个要讨论的项目:

pipeline {
  agent {
    docker {
      args '-v /Users/jean-marcelbelmont/jenkins_data'
      image 'node:10-alpine'
    }

  }
  stages {
    stage('Build') {
      steps {
        sh '''node --version

npm install'''
      }
    }
    stage('Cucumber Tests') {
      steps {
        sh 'npm run acceptance:tests'
      }
    }
  }
}

管道关键字

所有有效的声明性管道必须被包含在管道块中,正如您在前面的 Jenkinsfile 中所看到的。

代理关键字

代理部分指定整个管道或特定阶段将在 Jenkins 环境中执行的位置,取决于代理部分的放置位置。该部分必须在管道块的顶层内定义,但阶段级别的使用是可选的。

阶段关键字

stages 关键字包含一个或多个阶段指令的序列;stages 部分是管道描述的大部分工作将被放置的地方。

管道语法文档

如果您有兴趣了解更多关于管道语法的信息,请查看文档(jenkins.io/doc/book/pipeline/syntax/)。

总结

本章讨论了如何在现有的 Jenkins 实例中使用 Jenkins Blue Ocean 视图以及如何使用 Docker 设置 Blue Ocean 视图。我们看了许多不同的 Jenkins Blue Ocean 视图,并讨论了它们与 Jenkins 经典视图之间的一些区别。我们还讨论了管道语法和 Jenkinsfile。下一章将介绍 Travis CI 的安装和基本用法。

问题

  1. 如果您通过 Docker 安装 Jenkins,您可以使用 Blue Ocean 视图吗?

  2. 在 Blue Ocean 视图中使用管道编辑器有什么用?

  3. Jenkins 经典视图和 Blue Ocean 视图之间有哪些区别?

  4. 您能详细查看管道的每个阶段吗?

  5. Blue Ocean 视图能处理 Jenkins 中的管理任务吗?

  6. 阶段语法是用来做什么的?

  7. 声明性管道语法需要包装在管道块中吗?

进一步阅读

请查看Packt Publishing的书籍扩展 Jenkins(www.amazon.com/dp/B015CYBP2A),以了解更多关于 Jenkins 插件的信息。

第九章:Travis CI 的安装和基础知识

本章将帮助您设置 Travis 持续集成CI)。我们将解释托管解决方案(如 Travis CI)的应用嵌入式配置的概念。我们还将解释什么是 YAML 配置以及如何使用它。我们将研究使用 Travis CI 的基础知识,并花一些时间探索 Travis CI 和 Jenkins 之间的一些区别,以及 Travis CI 的概念。我们将介绍 Travis CI 的不同部分,包括语法和构建生命周期,并查看一些实际示例。

本章将涵盖以下主题:

  • Travis CI 介绍

  • Travis CI 先决条件

  • 添加一个简单的 Travis CI YAML 配置脚本

  • Travis CI 脚本解析

技术要求

本章将需要一些基本的编程技能,并且我们在前几章讨论过的许多 CI 概念将在本章中得到应用。如果您尝试创建一个 GitHub 账户和 Travis CI 账户将会很有帮助。您可以按照Travis CI 先决条件部分中的步骤进行操作。一些示例使用了 Docker,这是一种容器技术,因此如果您对容器和 Docker 有一些了解将会很有帮助。您将在本章学习有关 YAML 语法的知识。本章中有一些命令使用了命令行应用程序,因此熟悉命令行应用程序或 CLI 将会很有帮助。

Travis CI 介绍

Travis CI 是一个托管和自动化的 CI 构建解决方案。Travis CI 使用一个应用内配置文件,使用了 YAML(yaml.org/spec/1.2/spec.html)语法,我们将在本章后面更详细地讨论。由于 Travis CI 托管在云端,因此它具有可以快速在其他环境和不同操作系统中使用的优势,而无需担心设置和安装。这意味着 Travis CI 的设置比 Jenkins 要快得多。

比较 Travis CI 和 Jenkins

Jenkins 是一个自包含的开源自动化服务器,可以在组织级别进行定制和配置。请记住,在 Jenkins CI 章节中,我们花了一些时间在 Windows、Linux 和 macOS 上安装 Jenkins。我们还有能力按照自己的意愿配置 Jenkins。虽然这对于在运营、DevOps 等方面有专门团队的软件公司来说非常好,但对于经常是孤独开发者为个人项目设置环境的开源项目来说就不那么好了。

Travis CI 是围绕开源开发原则和易用性而设计的。在 GitHub 中创建项目后,可以在几分钟内设置好 Travis CI。尽管在这方面 Travis CI 不像 Jenkins CI 那样可定制,但它具有快速设置和使用的明显优势。Travis CI 使用一个应用内配置文件来实现这一点,但目前必须与 GitHub(github.com/)开发平台一起使用,尽管也许将来可能会扩展到其他平台,如 Bitbucket(bitbucket.org/dashboard/overview),但这仍然是一个开放的讨论。

Travis CI 先决条件

要开始使用 Travis CI,您需要创建一个 GitHub 账户,您可以在 GitHub(github.com/)上进行操作。

创建 GitHub 账户

看一下截图——您所需要做的就是提供用户名、电子邮件和密码,然后点击“注册 GitHub”按钮:

为了本章的目的,我们将创建一个名为packtci的 GitHub 用户名。一旦您点击了“注册 GitHub”按钮,您将被带到以下页面:

请注意,您可以在 GitHub 中免费创建无限数量的公共存储库,而私有存储库需要支付月度订阅费。一旦单击“继续”按钮,您将被重定向到以下页面:

如果您愿意,您可以通过滚动到页面底部并单击“跳过此步骤”按钮来跳过所有这些选项。一旦单击“提交”按钮或“跳过此步骤”按钮,您将被带到这个页面:

您应该收到来自 GitHub 的一封电子邮件;请查看以下截图:

您需要单击该链接以验证您在 GitHub 中的帐户,然后您应该已经设置好了您的 GitHub 帐户。

创建 Travis CI 帐户

您需要创建一个 Travis CI 帐户才能开始使用 Travis CI。您需要使用您的 GitHub 登录凭据登录。在这里的截图中,请注意您可以单击“注册”按钮或“使用 GitHub 登录”按钮:

在本章中,我单击了“使用 GitHub 登录”按钮,然后输入了我在 GitHub 上创建的用户名为packtci的 GitHub 帐户的登录凭据。一旦输入您的 GitHub 凭据,您将被重定向到以下页面:

您需要单击“授权 travis-ci”按钮以完成 Travis CI 设置。一旦单击“授权 travis-ci”按钮,Travis CI 完成最终设置步骤后,您将被重定向到以下页面:

请注意,我们在 Travis CI 中有一个 API 令牌,我们将在以后使用。在这个新帐户中我们没有任何 GitHub 项目,所以没有显示。在本章中,我将创建一个运行一些基本测试的 GitHub 项目。

向您的新 GitHub 帐户添加 SSH 密钥

为了创建一个新的 GitHub 存储库,您需要将您的 SSH 密钥添加到您的用户帐户中。如果您的系统中没有任何 SSH 密钥,可以使用以下命令创建:

请注意,我提供了一个电子邮件地址并指定了一种 RSA 类型的加密算法。一旦运行此命令,它将在您的系统上创建一个公钥和私钥。

一旦您创建了 SSH 密钥,您只需要将公钥上传到 GitHub。您需要复制文件的内容;如果您使用 macOS,您可以运行以下命令将其复制到系统剪贴板:

ssh-keygen -t rsa -b 4096 -C "myemail@someemailaddress.com"
# This command generates a file in the path that you pick in the interactive prompt which in this case is ~/.ssh/id_rsa_example.pub

pbcopy < ~/.ssh/id_rsa_example.pub

您需要进入 GitHub 的设置页面:

然后您需要在设置页面中单击以下按钮:

接下来,您需要单击“新 SSH 密钥”按钮,然后提供一个名称并粘贴您的 SSH 密钥的内容。在下面的截图中,我提供了一个名为示例 SSH 密钥的名称,然后粘贴了我的公钥的内容:

您只需要单击“添加 SSH 密钥”按钮,然后您就可以准备好向您在 GitHub 中拥有的任何存储库提交更改。

添加一个简单的 Travis CI YAML 配置脚本

我创建了一个示例 GitHub 存储库,您可以在 Github 上的 functional summer 中看到(github.com/packtci/functional-summer)。该存储库是一个 Node.js 项目,其中有一个package.json脚本,一个名为summer.js的文件和一个名为summer_test.js的测试文件。我们将在存储库的根目录中的名为.travis.yml的文件中为 Travis CI 添加配置。此配置脚本将执行几件事。首先,它将通知 Travis CI 我们正在运行一个 Node.js 项目,然后它将安装项目的依赖项,最后将运行 CI 构建中指定的测试。

Travis CI YML 脚本内容

我们首先在存储库的根目录创建一个名为.travis.yml的文件,然后将以下内容复制到此文件中:

language: node_js node_js: - "6.14.1" install: - npm install script: npm test

我们将更详细地讨论 YML 脚本的每个条目,但基本上我们对 Travis CI 说的是这是一个 Node.js 项目,我们希望 Travis CI 在 CI 构建中使用 node 版本 6.14.1,然后使用 npm 软件包管理器安装项目所需的所有依赖项,最后使用npm test命令运行所有测试。我将提交此更改到functional-summer存储库,然后我们将看到如何为该项目切换 Travis CI。

将 GitHub 存储库添加到 Travis CI 帐户

第一步是您必须转到travis-ci.org/,然后提供 GitHub 帐户的登录凭据。然后点击屏幕右上角的头像,转到您的个人资料页面,看起来像这样:

接下来,我添加了一张截图,详细说明了将新存储库添加到 Travis CI 的步骤:

请注意,在截图中,我添加了一个文本块,说第一步是点击“同步帐户”按钮,这是必要的,这样 Travis CI 才能看到您已添加到 GitHub 帐户中的任何新存储库。一旦 Travis CI 同步了您的帐户,您就应该能够在您的帐户中看到您的存储库。根据您已经拥有多少项目,您可能需要按存储库名称筛选以找到您的项目。下一步是切换带有您的存储库名称的滑块,就像截图中所示的那样。

在下面的截图中,我们已经在 Travis UI 上切换了functional-summer存储库,现在我们只需点击行即可进入这个新添加的 Travis CI 构建作业:

一旦您点击行,您将被重定向到 Travis CI 中的以下页面:

我们还没有触发任何构建,但 Travis CI 有一些默认设置。如果您将更改提交到任何推送的分支,或者在 GitHub 上打开拉取请求,Travis CI 将启动构建。让我们对functional-summer存储库进行一些微小的更改,这将触发 Travis CI 中的构建。如果您点击“构建历史”选项卡,您会注意到已创建了一个带有 Git 提交更改的构建:

Travis CI 作业日志

您可以单击 Travis CI 屏幕左侧的构建作业项目,看起来像这样:

或者,您可以单击“当前”选项卡,以查看已配置存储库的当前执行作业。为了查看作业日志,您需要滚动到“作业日志”选项卡,并查看 Travis CI 构建中的运行命令,对于functional-summer存储库,看起来像这样:

请记住,在我们添加到 GitHub 的.travis.yml脚本中,我们指定了四件事:

  1. .yml脚本的顶部,我们指定了 Node.js 的语言

  2. 我们指定了 Node.js 的版本 6.14.1

  3. 我们运行了npm install命令来安装项目的所有依赖项

  4. 最后,我们运行了npm test命令

您可以在作业日志中确认这些步骤是否已运行。请注意,在上一张截图中,有右箭头链接,展开以获取 CI 构建中每个命令的更多详细信息。

Travis CI 脚本分解

现在我们已经了解了 YAML 语法,我们可以更详细地解释 Travis CI 脚本的各个部分。

选择一种编程语言

language: go

.travis.yml脚本的这一部分中,我们添加了我们将在持续集成构建中使用的编程语言。这通常是您添加到.travis.yml脚本中的第一个条目。

Travis CI 支持许多编程语言,例如:

  • C

  • C++

  • 使用 Node.js 的 JavaScript

  • Elixir

  • Go

  • Haskell

  • Ruby

您可以查看 Travis CI 文档中的语言(docs.travis-ci.com/user/languages),以获取支持的编程语言的完整列表。

选择基础设施

您可以通过在 YML 脚本中使用sudodist字段来设置 Travis CI 中更自定义的环境。

具有 Ubuntu Precise (12.04)基础设施的虚拟镜像

您可以在 Travis YML 脚本中使用以下条目来使用 Ubuntu Precise 基础设施:

sudo: enabled
dist: precise

默认基础设施

您可以通过添加此条目来显式设置默认基础设施,即基于容器的 Ubuntu 14.04 环境:

sudo: false

这并非必须,因为您可以只设置语言,默认基础设施将为您完成。

具有 Ubuntu Trusty (14.04)基础设施的虚拟镜像

您可以在 Travis YML 脚本中使用以下条目来使用 Ubuntu Trusty 基础设施:

sudo: enabled
dist: trusty

基于容器的基础设施

您可以在 Travis YML 脚本中使用以下条目来使用基于容器的基础设施:

sudo: false
dist: trusty

请注意,我们在此处明确将 sudo 权限设置为 false,并使用 Ubuntu Trusty。

macOS 基础设施

您可以在 Travis YML 脚本中使用以下条目来使用 macOS 基础设施:

os: osx

构建自定义

在 Travis CI 中,您可以以各种方式自定义构建,我们将从解释构建生命周期开始。

构建生命周期

Travis CI 中的构建由两个步骤组成:

  • 安装:安装所需的任何依赖项。我们在 YML 脚本的安装块中看到了这一步骤。

  • Script:运行构建脚本。这可以是一系列要运行的脚本。

before_install 步骤

这一步骤正式称为before_install步骤,您可以在此步骤中安装 CI 构建中的任何其他依赖项,并启动自定义服务。

install 步骤

我们已经在install步骤中看到了这一步骤,您可以在此步骤中安装 CI 构建所需的任何依赖项。

before_script 步骤

before_script步骤中,您可以指定在执行脚本块之前需要执行的任何命令。例如,您可能有一个 PostgreSQL (www.postgresql.org/) 数据库,并且需要在运行任何测试之前对数据库进行填充。

脚本步骤

script步骤中,您可以执行对健康代码库至关重要的任何命令。例如,通常会运行代码库中的任何测试,对代码库进行 lint。 一个 linter 或 lint 工具是分析代码库以查找任何与编程相关的错误、软件错误、样式错误或可能具有代码异味的代码的工具。

after_script 步骤

after_script步骤中,您可以执行任何有用的命令,例如报告和分析。您可能需要发布代码覆盖率报告或在代码库中创建指标报告。

构建生命周期列表

以下是 Travis CI 的完整生命周期:

  • 可选安装:apt 插件

  • 可选安装:缓存组件

  • before_install

  • install

  • before_script

  • script

  • 可选:before_cache

  • after_successafter_failure

  • before_deploy

  • deploy

  • after_deploy

  • after_script

构建失败行为

如果在 before_install、install 或 before_script 生命周期事件中发生错误,则 CI 构建将立即出错,CI 构建将停止。

如果在脚本生命周期事件中发生错误,则构建将失败,但 CI 构建将继续运行。

如果在 after_success、after_failure、after_script 和 after_deploy 生命周期事件中发生错误,则构建将不会标记为失败,但如果其中任何生命周期事件导致超时,则构建将标记为失败。

为 CI 构建安装第二种编程语言

您可以通过在before_install生命周期事件中添加条目来轻松安装另一种编程语言。最好指定您的主要语言,然后是次要语言。

带有多种语言的示例 Travis CI YML 脚本

在 Travis CI YML 脚本中,我们将 Go 版本 1.10 指定为主要编程语言,然后将 Node.js 指定为次要语言。我们在 before_install 生命周期事件中安装 Node.js 依赖项,然后运行 Golang 测试,然后是 Node.js 测试:

language: go go: - "1.10" env: - NODE_VERSION="6" before_install:
 - nvm install $NODE_VERSION install: - npm install script: - go test
 - npm test

如果您想进一步探索此示例,请查看multiple-languages存储库(github.com/packtci/multiple-languages)。

Travis CI 中的 Docker

Docker 可以在 Travis CI 中使用,启用 Docker 的唯一步骤是将以下条目添加到您的 Travis CI YML 脚本中:

sudo: required

services:
 - docker

请注意,我们在services块中添加了一个条目,并添加了 Docker 的列表条目。

带有 Dockerfile 的示例 Travis CI YML 脚本

在 Travis YML 脚本中,我们指定了sudo权限,Golang 编程语言,然后指定了 Docker 服务,下载了自定义 Docker 镜像jbelmont/print-average:1.0,然后运行 Docker 容器并将其删除:

sudo: required

language: go

services:
  - docker

before_install:
  - docker pull jbelmont/print-average:1.0

script:
  - docker run --rm jbelmont/print-average:1.0

我已经添加了 Travis CI 构建的截图供您参考:

请注意,Docker 正在 CI 构建中运行,因为我们在 Travis CI 中指定了 Docker 作为要运行的服务。当我们运行 Docker 容器时,它会打印出在docker-travis-ci-example存储库中的main.go中计算的平均值(github.com/packtci/docker-travis-ci-example)。您可以在 Docker Hub 上查看我的 Docker 镜像(hub.docker.com/r/jbelmont/print-average/)。

Travis CI 中的 GUI 和无头浏览器

您可以以几种方式在 Travis CI 中运行无头浏览器。您可以利用 X 虚拟帧缓冲,或简称为 XVFB,您可以在 XVFB 文档中了解更多信息(www.x.org/archive/X11R7.6/doc/man/man1/Xvfb.1.xhtml)。我们将使用 Puppeteer(pptr.dev/)来使用无头 Chrome,这是由 Google 开发的库,提供了一个高级 API 来使用无头 Chrome。

带有无头 Chrome、Puppeteer 和 Jest 测试库的 Travis yml 脚本示例

在 Travis YML 脚本中,我们在 Travis CI 构建中设置了许多不同的操作。首先,我们将语言设置为node_js,然后将node_js的版本设置为8.11,然后设置了一个名为dist: trusty的新属性,该属性将 Travis CI 环境设置为 Ubuntu 14.04,称为Trusty。然后我们使用 add-ons 块添加了最新的稳定版本的 Chrome。然后我们在 CI 构建上的端口9222上运行了稳定版本的 Google Chrome,然后我们使用cache块,以便在每次 CI 构建运行时缓存node_modules。然后我们安装我们的 Node.js 依赖项,最后我们使用 Jest 库运行 Node.js 测试:

language: node_js

node_js:
 - "8.11"

dist: trusty

sudo: false

addons:
 chrome: stable

before_install:
 - google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 http://localhost &

cache:
 directories:
 - node_modules

install:
 - npm install

script:
 - npm test

在第一张截图中,请注意我们在 Travis CI 构建中以headless模式运行google-chrome的部分,然后安装依赖项:

在第二个截图中,我们使用 Google Chrome Puppeteer 库运行测试。请注意,构建以0的退出状态运行,并成功完成:

您可以在travis-ci.org/packtci/puppeteer-headless-chrome-travis-yml-script/jobs/395882388#L70找到此 Travis CI 构建,并且您可以在github.com/packtci/puppeteer-headless-chrome-travis-yml-script和 GitHub 上找到此存储库的源代码。

摘要

在本章中,我们已经讨论了 Travis CI 的许多方面,包括 Travis CI 和 Jenkins 之间的区别。我们介绍了一些先决条件操作,以便设置 Travis CI 并学会将 SSH 密钥添加到您的 GitHub 帐户。然后我们详细解释了 Travis CI 构建作业,并详细介绍了 YAML 语法。然后我们探讨了许多 Travis YML 脚本的真实示例,并解释了 Travis CI 中的构建生命周期,并介绍了不同方面,例如启动 Docker 等服务及其在 Travis CI 中的用途。

在下一章中,我们将介绍所有 Travis CLI 命令,解释如何在 Linux、macOS 和 Windows 上安装 Travis CLI,并介绍如何使用 Travis CLI 命令自动化任务,如加密凭据。

问题

  1. Jenkins 和 Travis CI 之间的主要区别是什么?

  2. Travis CI 可以在 Bitbucket 中工作吗?

  3. 如何将新存储库添加到 Travis CI 中?

  4. YAML 中的标量变量是什么?

  5. YAML 中的列表是什么?

  6. YAML 中锚点的作用是什么?

  7. 在 Travis CI 构建中是否可以使用辅助编程语言?

  8. 如何在 Travis CI 构建中使用 docker?

进一步阅读

您可以通过查看 Travis CI 网站上的用户文档(docs.travis-ci.com/)来进一步探索 Travis CI 中的概念。

第十章:Travis CI CLI 命令和自动化

在上一章中,我们向您展示了如何在软件项目中配置 Travis CI,并解释了如何使用 Travis CI 的基础知识。本章将帮助您在操作系统上安装 Travis CLI,并且我们将介绍 Travis CI 中所有不同类型的命令,例如一般 API 命令,存储库命令等。我们将介绍 CLI 命令可以使用的不同选项,并且我们还将详细介绍每个命令的含义。我们还将通过使用我们的访问令牌和 curl REST 客户端直接查看使用 Travis API。我们将简要介绍 Travis Pro 和 Enterprise 版本。

本章将涵盖以下主题:

  • Travis CLI 安装

  • Travis CLI 命令

技术要求

本章将需要一些基本的 Unix 编程技能和关于使用命令行终端应用程序的知识。如果您使用的是 Windows 操作系统,则考虑使用命令提示符或 PowerShell 应用程序。如果您使用的是 macOS 操作系统,则使用默认安装的终端应用程序。如果您使用的是 Linux,则应该已经安装或可用终端。

Travis CLI 安装

安装 Travis CLI 的第一个先决条件是在您的操作系统上安装 Ruby(www.ruby-lang.org/en/documentation/installation/),并确保它是 1.9.3 或更高版本。

您可以通过在命令行或终端中运行以下命令来检查是否已安装 Ruby:

Windows 安装

Travis CLI 用户文档在github.com/travis-ci/travis.rb#windows上建议您使用 RubyInstaller (rubyinstaller.org/)在 Windows 操作系统上安装最新版本的 Ruby。

我们需要在 RubyInstaller 下载站点上选择 Ruby Devkit 版本 2.5.1,然后确保接受许可协议,然后选择适当的安装选项。确保安装开发工具链。

当安装程序窗口关闭时,将打开一个命令提示符,您需要选择一个选项;您可以只需按 Enter 键在系统上安装所有三个选项。安装过程可能需要一段时间才能完成。我的安装大约花了 20 分钟来更新 GPG 密钥并安装 Ruby 编程语言所需的其他依赖项:

在这里,我们已经在系统上安装了 Ruby 版本 2.5.1,正如我们所期望的那样:

在这一步中,我们在 Windows 命令提示符中安装 Travis RubyGems:

在最后一步中,我们验证 Travis CLI RubyGem 是否已安装在我们的系统上;它报告版本1.8.8

Linux 安装

Linux 操作系统有多个不同的软件包管理器,因此如何在系统上安装 Ruby 取决于您的特定 Linux 操作系统。我们将在 DigitalOcean 服务器上的 Ubuntu 14.04 上安装 Ruby 和 Travis CLI:

  1. 在 Ubuntu 上安装 Ruby,请运行以下命令:
sudo apt-get install python-software-properties
sudo apt-add-repository ppa:brightbox/ruby-ng
sudo apt-get update
sudo apt-get install ruby2.1 ruby-switch
sudo ruby-switch --set ruby2.1
  1. 接下来,通过运行以下命令确认 Ruby 已安装:

  1. 接下来,我们需要使用以下命令安装 Travis CLI RubyGem:
gem install travis -v 1.8.8 --no-rdoc --no-ri
  1. 最后一步是使用以下命令检查 Travis CLI 是否已安装:
travis version
1.8.8

macOS 安装

您需要安装 Xcode 命令行工具,可以使用以下命令来执行:

xcode-select --install

如果您已经安装了 Xcode 命令行工具,则在终端中会显示以下信息:

Ruby 已预安装在当前的 macOS 操作系统上,因此您只需要运行以下命令来安装 Travis CLI:

这里我使用了sudo,因为我需要提升的管理员权限来安装 RubyGem。

如果您在终端中看到此消息,您将知道 Travis CLI 已安装:

这里我使用的是 Travis CLI 版本1.8.8,但您的特定版本可能不同。

Travis CLI 命令

Travis CLI 功能齐全,与 GitHub 中的 Travis API(github.com/travis-ci/travis-api)一起使用,并且具有以下三种不同形式的 CLI 命令:

Travis CLI 库是用 Ruby 编程语言编写的,如果您想直接与其交互,请准备在 GitHub 的Ruby Library部分阅读更多内容(github.com/travis-ci/travis.rb#ruby-library)。

我们将使用我们在第九章中创建的packtci GitHub(github.com/packtci)用户,在创建 GitHub 帐户部分创建的packtci Travis CI 帐户。

非 API 命令

非 API Travis CLI 命令包括helpversion命令。这些命令不直接命中 Travis API,而是打印有关 Travis CLI 的有用信息。

打印帮助信息

help命令将显示特定命令接受的参数和选项。

在下面的截图中,我们在命令行终端中运行travis help命令:

如果您想获取特定命令的帮助,只需使用travis help COMMAND

以下是有关 Travis 中whoami命令的更多信息的截图:

打印版本信息

version命令显示安装在系统上的当前 Travis CLI 客户端。以下截图显示了 Travis CLI 的当前客户端版本为1.8.8

API 命令

API 命令直接命中 Travis API,有些需要您拥有适当的访问令牌,您可以使用travis login命令获取。

登录到 Travis CI

login命令通常是您需要使用的第一个命令,以便与 Travis API 一起工作,因为它会对您进行身份验证。

login命令将要求您输入 GitHub 用户名和密码,但不会将这些凭据发送到 Travis CI。相反,它使用您的用户名和密码创建一个 GitHub API 令牌,然后将令牌显示给 Travis API,然后运行一系列检查以确保您是您所说的人。然后它会给您一个 Travis API 的访问令牌,并最终 Travis 客户端将再次删除 GitHub 令牌。只要您成功运行travis login命令,所有这些步骤都会在幕后发生。

以下截图显示我们尝试运行travis accounts命令,并通知我们需要登录:

在下面的截图中,我们运行travis login命令并提供 GitHub 用户名和密码:

现在我们已成功登录到 Travis CI 系统,并且 Travis CI 已经为我们发放了一个访问令牌。

显示当前访问令牌

token命令用于显示当前访问令牌。截图中的访问令牌已经被隐藏,以确保安全:

注销 Travis CI

logout命令将注销您在 Travis CI 中的登录并删除您的访问令牌。

请注意,在下面的截图中,我们发出travis logout命令后,travis token命令显示我们需要重新登录:

我们需要重新登录到 Travis CI 以再次获取令牌。在下面的截图中,我们重新登录到 Travis,然后获取另一个访问令牌,以便我们可以向 Travis API 发出命令:

显示帐户信息

accounts命令用于列出您可以为其设置存储库的所有帐户。请记住,当我们之前运行此命令时,Travis 通知我们需要登录到 Travis 才能执行此命令。在下面的截图中,Travis 通知我们我们已经订阅了 Travis 中的四个不同存储库:

显示 Travis 命令的帮助信息

请记住,我们可以通过运行以下命令在 Travis 中找到特定命令的所有选项:

travis help

在下面的截图中,我们为accounts命令运行help命令:

有一个名为--debug的选项,我们将使用它来调试发送到 Travis API 的 HTTP 请求。在下面的截图中,我们获得了有关发送到 Travis 的请求的其他信息,例如命中的端点是GET "accounts/" {:all=>true}以及其他信息:

交互式控制台会话

console命令将您放入一个交互式的 Ruby 会话中,其中所有实体都被导入到全局命名空间中,并确保您已经通过 Travis 进行了身份验证,如果您正在设置正确。在下面的截图中,我按下了Tab并在控制台会话中获得了自动完成:

还要注意,当前登录的用户是packtci

打印 API 端点信息

endpoint命令打印出我们正在使用的 API 端点。请注意,在截图中,我们正在使用 Travis API 的免费开源版本:

Travis 的 PRO 版本使用以下端点:api.travis-ci.com/

进行实时监控,查看当前正在运行的所有 CI 构建

travis monitor命令将对已登录帐户中的所有 CI 构建进行实时监控。在下面的截图中,Travis CI 目前没有任何活动:

让我们为puppeteer-headless-chrome-travis-yml-script仓库(github.com/packtci/puppeteer-headless-chrome-travis-yml-script)添加一个单元测试用例,然后将此更改推送到 GitHub 版本控制系统。在以下截图中,我们将更改推送到存储库中:

现在,如果我们回到 Travis 监视器正在运行的终端会话中,我们将看到已启动构建,然后它被传递:

我们有一个2.1的构建作业;在.travis.yml文件中,我们没有指定任何其他构建作业,因此 Travis CI 将所有构建作业捆绑到一个构建作业中。

您可以在此网址阅读有关 Travis CI 构建阶段的更多信息:docs.travis-ci.com/user/build-stages/

发起 Travis CI API 调用

您可以通过使用travis raw RESOURCE命令直接对 Travis API 进行 API 调用。请记住,我们始终可以使用travis help COMMAND来查找如何在 Travis CLI 中使用特定命令。在以下截图中,我们对raw命令运行help命令:

现在我们知道如何运行raw命令,让我们向 Travis API 的此端点发出请求:

GET /config

如果您想查看 Travis API 的开发人员文档,则需要转到以下网址:developer.travis-ci.com/

确保登录并授权 Travis CI 作为 GitHub 的第三方应用程序。在以下截图中,我们为packtci GitHub 用户授权 Travis CI:

然后,您可以在以下网址查看 Travis CI 的 API 文档:developer.travis-ci.com/。在以下截图中,我们对/config端点进行 GET 请求,并在raw命令中使用以下两个不同的选项:

  • --json

  • --debug

在不久的将来,Travis API 计划废弃 V2 API,只有 V3 API 将得到官方支持。您可以使用 API 资源浏览器对 V3 API 进行 REST 调用:

GET /owner/{owner.login}

在以下截图中,我们使用API 资源浏览器对以下端点进行 REST 调用:

您可以转到此网址的 API 资源浏览器:developer.travis-ci.com/explore/。然后在输入框中输入资源,看起来像这样:

使用 curl 进行 API V3 REST 调用

我们将发出travis token命令,以便我们可以将访问令牌复制到系统剪贴板:

travis token

接下来,我们将运行travis endpoint命令并复制 URL:

travis endpoint
API endpoint: https://api.travis-ci.org/

我们将以以下方式发出curl请求:

curl -X GET \
 -H "Content-Type: application/json" \
 -H "Travis-API-Version: 3" \
 -H "Authorization: token $(travis token)" \
https://api.travis-ci.org/repos

请注意,在此 curl 请求中,我们使用了travis tokencli 命令,该命令将返回此特定 HTTP 标头的有效令牌。此 HTTP 请求将返回一个 JSON 响应有效负载,我们将使用它来复制特定的存储库 ID,以便进行以下 REST 调用以查找functional-summer存储库的所有环境变量(github.com/packtci/functional-summer):

curl -X GET \
 -H "Content-Type: application/json" \
 -H "Travis-API-Version: 3" \
 -H "Authorization: token $(travis token)" \
https://api.travis-ci.org/repo/19721247/env_vars

在此GET请求中,我们从functional-summer存储库获取所有环境变量,并收到以下 JSON 响应:

{
  "@type": "env_vars",
  "@href": "/repo/19721247/env_vars",
  "@representation": "standard",
  "env_vars": [

  ]
}

让我们发出POST请求,向functional-summer存储库添加一个环境变量:

curl -X POST \
  -H "Content-Type: application/json" \
  -H "Travis-API-Version: 3" \
  -H "Authorization: token $(travis token)" \
  -d '{ "env_var.name": "MOVIE", "env_var.value": "ROCKY", "env_var.public": false }' \
  https://api.travis-ci.org/repo/19721247/env_vars

现在,当我们对环境变量进行GET请求时,我们看到已设置名为MOVIE的环境变量:

curl -X GET \
 -H "Content-Type: application/json" \
 -H "Travis-API-Version: 3" \
 -H "Authorization: token $(travis token)" \
https://api.travis-ci.org/repo/19721247/env_vars
{
 "@type": "env_vars",
 "@href": "/repo/19721247/env_vars",
 "@representation": "standard",
 "env_vars": [
 {
 "@type": "env_var",
 "@href": "/repo/19721247/env_var/1f64fa82-2cad-4270-abdc-13d70fa8faeb",
 "@representation": "standard",
 "@permissions": {
 "read": true,
 "write": true
 },
 "id": "1f64fa82-2cad-4270-abdc-13d70fa8faeb",
 "name": "MOVIE",
 "public": false
 }
 ]
}

打印重要的系统配置信息

report命令打印出重要的系统配置信息,如下截图所示:

列出当前登录用户有权限访问的所有存储库

repos命令将列出存储库,无论它们是活动的还是不活动的,并且有各种可以使用的选项。在下面的截图中,我们使用了-m选项来匹配packtci GitHub 用户的所有存储库:

在 GitHub 中为任何新的或过时的存储库与 Travis CI 进行同步

sync命令帮助您更新 GitHub 用户的信息以及任何新的或修改的存储库。让我们在 GitHub 中添加另一个存储库,名为functional-patternsgithub.com/packtci/functional-patterns)。在下面的截图中,我们使用sync命令,以便 Travis CI 意识到新存储库,然后使用repos命令确认它显示在我们可以访问的存储库列表中:

sync命令可以替换我们在第九章中所采取的步骤,即单击同步帐户按钮以同步我们帐户中的所有存储库信息。

lint - 一个 Travis YML 脚本

lint命令非常有用,因为它检查您的 Travis YML 脚本中是否有正确的语法。让我们在functional-patterns存储库(github.com/packtci/functional-patterns)中创建一个 Travis YML 脚本。我们将为.travis.yml脚本添加以下条目:

language: blah node_js: 8.11

现在让我们运行lint命令来检查语法。在下面的截图中,Travis 通知我们正在使用blah的非法值,并且它将默认为ruby作为语言:

让我们修复语言条目以使用 Node.js,然后再次运行lint命令:

lint命令报告我们现在在.travis.yml脚本中有有效的语法。

获取组织或用户的当前构建信息

whatsup命令让您查看最近在 Travis 中发生的活动。当我们运行这个whatsup命令时,它给了我们 Travis CI 中最近的活动:

packtci Travis 帐户中,只有一个用户,但是您可以在 Travis CI 帐户中拥有许多用户,因此只查看您的存储库可能更有用。记住,我们可以使用help命令查找特定命令的更多选项。作为练习,使用help命令查找仅显示您自己的存储库的选项。

查找当前登录用户信息

whoami命令对于查找 Travis CI 帐户当前登录的用户非常有用:

whoami命令报告packtci,正如我们所期望的那样。

存储库命令

存储库命令具有 API 命令的所有选项,此外,您还可以使用--repo owner/name选项指定要使用的特定存储库。

显示 Git 版本控制中每个分支的最新构建信息

branches命令显示版本控制中每个分支的最新构建信息:

当您运行此命令时,可能会显示更多的分支。

列出所有存储库的缓存信息

cache命令可以列出存储库中的所有缓存:

删除给定存储库的缓存信息

cache命令还可以删除存储库中的缓存,如果您使用-d--delete选项:

我们收到了一条红色的警告消息,要求我们确认删除缓存。

在 Travis CI 中启用存储库

enable命令将在您的 GitHub 存储库中激活 Travis CI:

enable命令有助于替换我们在第九章中所采取的手动步骤,即激活 Travis CI 中的存储库,在那里我们在 Travis web 客户端中点击滑块按钮以激活存储库。

在 Travis CI 中禁用存储库

disable命令将使您的 GitHub 存储库中的 Travis CI 处于非活动状态:

取消 Travis CI 中的最新构建

让我们使用以下命令启用functional-patterns存储库:

travis enable

现在让我们使用以下命令向存储库推送提交:

git commit --amend --no-edit

先前的git命令允许您重用之前使用的git commit命令,但您需要发出以下命令:

git push -f

让我们查看 Travis CI 中存储库的当前状态;构建在 Travis CI 中正式创建可能需要一段时间:

在上一张屏幕截图中,我们发出了whatsup命令来查看构建的当前状态,并注意到packtci/functional-patterns开始了作业编号1。然后我们发出了travis cancel命令,并提供了一个参数1。这并不完全必要,因为这是当前的构建,所以我们可以只发出travis cancel命令。当我们运行travis whatsup命令时,构建被取消。

加密环境变量或部署密钥

encrypt命令允许您加密存储在环境变量和/或部署密钥中的秘密值,这些值您不希望公开显示:

在 Travis CI 中添加环境变量

我们将在我们的.travis.yml脚本的env块中添加这个条目。您可以在docs.travis-ci.com/user/environment-variables的文档中阅读有关在 Travis CI 中使用环境变量的更多信息。一般来说,您可以通过在您的.travis.yml脚本中添加一个名为env的块来添加环境变量。

我在.travis.yml脚本中添加了一个示例片段:

env:
    DB_URL=http://localhost:8078
    global:
        secure: "DeeYuU76cFvBIZTDTTE+o+lApUl5lY9JZ97pGOixyJ7MCCVQD26m+3iGLCcos0TbvjfAjE+IKTKZ96CLJkf6DNTeetl3+/VDp91Oa2891meWSgL6ZoDLwG8pCvLxaIg2tAkC26hIT64YKmzEim6ORQhLdUVUC1oz9BV8ygrcwtTo4Y9C0h7hMuYnrpcSlKsG9B8GfDdi7OSda4Ypn4aFOZ4/N3mQh/bMY7h6ai+tcAGzdCAzeoc1i0dw+xwIJ0P2Fg2KOy/d1CqoVBimWyHDxDoaXgmaaBeGIBTXM6birP09MHUs2REpEB9b8Z1Q+DzcA+u5EucLrqsm8BYHmyuPhAnUMqYdD4eHPQApQybY+kJP18qf/9/tFTyD5mH3Slk60ykc/bFaNCi7i4yAe7O8TI/Qyq3LPkHd1XEFDrHasmWwp/4k3m2p5ydDqsyyteJBHMO/wMDR7gb6T6jVVVmDn0bmurb4CTmiSuzslBS9N5C9QRd5k4XFUbpqTAHm+GtNYOOzRFTTyVH3wSKBj8xhjPLGZzCXeUxuW72deJ+ofxpTgKs7DM9pcfUShk+Ngykgy6VGhPcuMSTNXQv2w7Hw5/ZOZJt36ndUNXT0Mc9othq4bCVZBhRiDGoZuz9FSfXIK/kDKm2TjuVhmqZ7T//Y4AfNyQ/spaf8gjFZvW2u1Cg="

我们添加了一个名为DB_URL的公共环境变量,并使用global块添加了一个全局变量,然后将条目粘贴到其中。

如果您愿意,您可以使用--add选项自动添加条目,尽管您在.travis.yml脚本中的任何注释都将消失,间距也将消失,因此在运行--add选项时要注意这一点。

加密文件

encrypt-file命令将使用对称(AES-256)加密加密整个文件,并将秘密存储在文件中。让我们创建一个名为secret.txt的文件,并将以下条目添加到其中:

SECRET_VALUE=ABCDE12345
CLIENT_ID=rocky123
CLIENT_SECRET=abc222222!

现在让我们加密我们的秘密文件:

因此,现在我们将把这个条目添加到我们的.travis.yml脚本中:

before_install: - openssl aes-256-cbc -K $encrypted_74945c17fbe2_key -iv $encrypted_74945c17fbe2_iv -in secret.txt.enc -out secret.txt -d

然后它可以为我们解密秘密文本文件中的值。

列出环境信息

env命令可以列出为存储库设置的所有环境变量:

我们没有为此存储库设置任何环境变量。

设置环境变量

env命令还可以从存储库中设置环境变量:

我们设置了一个名为API_URL的环境变量,并且它现在显示为多语言存储库的环境变量。

删除环境变量

env命令还可以从存储库中删除环境变量:

travis env列表命令现在报告我们没有为多语言存储库设置任何环境变量,这是我们所期望的。

清除所有环境变量

env命令可用于清除存储库中设置的所有环境变量:

列出最近构建的历史信息

history命令显示存储库的构建历史:

history命令默认只显示最后 10 个构建,但您可以使用--limit选项来限制或扩展构建的数量。

在项目上初始化 Travis CLI

init命令将帮助您通过为您生成一个.travis.yml脚本来在项目中设置 Travis CI。我们在 GitHub 中设置了一个名为travis-init-command的新项目(github.com/packtci/travis-init-command)。我们将使用init命令在此存储库中设置 Golang:

该过程的步骤如下:

  1. 第一步是使用sync命令,以便 Travis CI 知道这个新存储库

  2. 接下来,我们将在 Travis CI 中启用这个新存储库。

  3. 接下来,我们将尝试使用 Golang 创建一个.travis.yml脚本,但请注意它没有被识别,因此我们再次尝试使用 Go,这次成功了

  4. 最后,我们打印出新文件的内容,并注意它将语言设置为 Go,并使用了两个不同版本的 Go

打印 CI 构建日志信息

logs命令将打印出存储库的 Travis CI 日志的内容,默认情况下它将打印出最新构建的第一个作业。在这里,我们在最近创建的存储库中运行logs命令;但是,它不会通过 CI 构建,因为存储库中还没有任何可构建的 Go 文件:

travis logs

displaying logs for packtci/travis-init-command#1.1
Worker information
hostname: fb102913-2cd8-41fb-b69b-7e8488a0aa0a@1.production-1-worker-org-03-packet
version: v3.8.2 https://github.com/travis-ci/worker/tree/c370f713bb4195cce20cdc6ce3e62f26b8cf3961
instance: 22589e2 travisci/ci-garnet:packer-1512502276-986baf0 (via amqp)
startup: 1.083854718s
Build system information
Build language: go
Build group: stable
Build dist: trusty
Build id: 399102978
Job id: 399102980
Runtime kernel version: 4.4.0-112-generic
...
The command "go get -v ./..." failed and exited with 1 during .

Your build has been stopped.

请注意,正如我们之前注意到的那样,build失败了,因为没有任何 Go 文件可以构建。 logs命令也可以给出一个特定的构建编号来运行,您还可以给它们一个特定的分支来运行。运行travis help logs命令以获取更多选项。

打开项目的 Travis web 界面

open命令将在 Travis CI web 客户端中打开存储库:

travis open

travis-init-command存储库(github.com/packtci/travis-init-command)中运行travis open将带我们转到以下 URL travis-ci.org/packtci/travis-init-command

您可以使用--print选项来打印出 URL,而不是默认情况下打开到特定项目视图。运行travis help open命令以获取更多选项。

打印存储库的公钥信息

pubkey命令将打印出存储库的公共 SSH 密钥:

出于安全原因,我删除了公钥信息。您还可以以不同格式显示密钥。例如,如果您使用--pem选项,您的密钥将显示如下:

运行travis help pubkey命令以显示此命令的更多选项:

在 Travis CI 中重新启动最新的 CI 构建

restart命令将重新启动最新的构建:

打印 Travis CI 中当前的构建请求

requests命令将列出 Travis CI 收到的任何构建请求。我们将在刚刚为travis-init-command存储库触发的构建上运行travis requests命令:

由于其中还没有任何可构建的 Go 文件,构建仍然失败。

打印特定存储库设置

settings命令将显示存储库的设置:

请注意,减号(-)表示已禁用,而加号(+)表示已启用。

travis settings命令也可用于启用、禁用和设置设置:

配置 Travis CI 附加功能

setup命令可帮助您配置 Travis 附加功能:

您可以在 Travis CLI 用户文档中查看更多可用的 Travis 附加功能(github.com/travis-ci/travis.rb#setup)。

显示当前 CI 构建的一般信息

show命令默认显示有关最近 CI 构建的一般信息:

第一个命令travis show显示了最近的构建,在下一次运行中,我们提供了一个特定的构建编号。

在 Travis CI 中列出 SSH 密钥

sshkey命令将检查是否设置了自定义 SSH 密钥:

travis sshkey

此命令仅适用于 Travis 的 Pro 版本,如果没有 SSH 密钥,它将报告未安装自定义 SSH 密钥。

您可以在用户文档中阅读有关此命令的更多选项(github.com/travis-ci/travis.rb#sshkey)。

显示当前构建的状态信息

status命令将输出有关项目最后构建的一行消息:

Travis CI 的 Pro 版本和企业版本选项

默认情况下,一般 API 命令会访问api.travis-ci.org端点。Travis Pro 版本具有一些常规 Travis 帐户没有的附加功能和选项,例如使用sshkey命令等。您可以在用户文档中阅读有关选项的更多信息(github.com/travis-ci/travis.rb#pro-and-enterprise)。

显示 Pro 版本的信息选项

如果您在一般 API 命令中使用--pro选项,则将访问 Travis Pro 端点https://api.travis-ci.com/。例如,如果我们使用--pro选项进行以下请求,我们将访问 Travis Pro API:

请注意,主机是travis-ci.com,这是 Travis PRO。

显示企业版本的信息选项

如果您已设置 Travis Enterprise,则可以使用--enterprise选项,以便访问您的企业域所在的位置:

我们没有设置 Travis Enterprise,但如果您设置了,则可以在此处输入您的域。

摘要

在本章中,我们已经介绍了如何在 Windows 操作系统、macOS 操作系统和 Linux 操作系统上安装 Ruby 和 Travis CLI RubyGem。我们详细介绍了每个 Travis CLI 命令,并讨论了使用每个命令的各种方式以及每个命令所接受的一些选项。我们还向您展示了如何使用 curl REST 客户端直接调用 Travis API。最后,我们还介绍了 Travis Pro 和 Enterprise 版本中的一些功能。

在下一章中,我们将介绍一些更高级的技术,以记录值并使用 Travis CI 进行调试。

问题

  1. 根据 Travis 文档,安装 Ruby 在 Windows 操作系统上的推荐方式是什么?

  2. 您应该使用哪个命令来打印已安装的 Travis 的当前版本?

  3. 您在 Travis CLI 中使用哪个命令来打印有用的信息?

  4. 如何获取访问令牌以便在 Travis CLI 中使用一般 API 命令?

  5. 您需要使用哪个 HTTP 标头来使用 Travis API 版本 3?

  6. 如何打印系统配置信息?

  7. 哪个命令检查您的 Travis YML 脚本的语法?

  8. 哪个命令可以帮助您在项目中设置 Travis?

进一步阅读

您可以在用户文档中进一步探索 Travis CLI 选项(github.com/travis-ci/travis.rb),您可以在 API 文档中阅读有关使用 Travis API 的更多信息(developer.travis-ci.com/)。

第十一章: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-languagesgithub.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 为我们设置了一些环境变量,包括GOPATHPATH环境变量。Travis CI 运行go version命令来验证 CI 构建中是否安装了 Go 版本 1.10:

在 CI 构建的这一步中,我们安装了 Node.js 作为我们的第二编程语言。这是可选的,但请注意,Travis CI 在before_install右侧有一个构建标签,这是我们在第九章中讨论的构建步骤之一,Travis CI 的安装和基础知识,在构建自定义部分。还要注意,在before_installinstall生命周期标签的右侧,有一个时间戳,显示了构建步骤实际花费的时间,以人类可读的格式显示为2.55秒和2.88秒,分别对应before_installinstall生命周期事件:

请注意,这里没有脚本构建生命周期的构建标签,因为这是 CI 构建的主要部分。

任何其他生命周期事件,如after_successafter_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/)。

  1. 拉取 Go Docker 镜像:
docker pull travisci/ci-garnet:packer-1512502276-986baf0

请注意,我们运行docker pull命令来实际拉取 Docker 镜像

  1. 启动交互式 Docker 会话:

请注意,我们在分离模式下运行了一个交互式 shell 会话

  1. 在正在运行的容器中打开登录 shell:
docker exec -it travis-debug bash -l

此命令使用 Bash shell 启动一个与正在运行的 Docker 容器的交互式 shell 会话

  1. 切换到 Travis 用户:
su - travis

在此命令中,我们切换到 Travis 用户,而不是默认的 root 用户

  1. multiple-languages Git 存储库克隆到主目录中:
git clone --depth=50 --branch=master https://github.com/packtci/multiple-languages
cd multiple-languages

此命令将我们的multiple-languages存储库克隆到本地的 Docker 容器中,然后切换到此目录

  1. 检出我们想要在本地测试的 Git 提交。

运行git log命令并找到我们想要在本地检出的提交。很可能是我们将要检查的顶级 Git 提交。

git log
git checkout 2a663fc233d3ae3986fd99efc510369ded92ba94

在这一步中,我们要确保只测试与我们想要测试的更改相对应的更改。

  1. 安装库依赖和第二编程语言:
NODE_VERSION="6"
nvm install $NODE_VERSION
npm install

在这一步中,我们使用node 版本管理器nvm)安装 Node.js 作为第二编程语言,然后运行npm install命令来安装所有库依赖项

  1. 运行脚本构建步骤。

在下面的截图中,我们在本地 Docker 容器中运行go testnpm 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 中用于部署:

有关支持的提供商的完整列表,请转到 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 的基础知识。

问题

  1. 当您在 GitHub 中合并拉取请求时,是否会触发另一个构建?

  2. 在运行脚本生命周期事件中,Travis 作业日志是否显示标签?

  3. 我们如何在 Travis CI 中本地调试构建?

  4. 调试构建模式是否适用于公共存储库?

  5. 您将如何使用 Travis API 获取作业 ID?

  6. 在运行调试模式构建时,您可以使用哪个方便的 bash 函数来进行 before_install 生命周期事件?

  7. 您会使用哪个 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 的一些更高级主题,例如工作流程。

问题

  1. Jenkins 和 Travis CI 之间的主要区别是什么?

  2. CircleCI 可以在 Bitbucket 和 GitHub 中都使用吗?

  3. 在 CircleCI 中如何设置存储库?

  4. 如何在 CircleCI 中查看构建作业?

  5. 我们在 Bitbucket 的java-summer存储库中使用了哪个构建工具?

  6. 应该使用 CircleCI 语法的版本 1 吗?

  7. 在 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 中使用六个命令,它们是buildconfighelpsteptestsversion,如果您从官方 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-cigithub.com/packtci/go-template-example-with-circle-ci)GitHub 存储库中查找所有 Go 测试文件:

在 CircleCI 中使用工作流程

CircleCI 中的工作流程是一种运行并行build作业的方式,可以用来定义一组作业并指定作业顺序。让我们在go-template-example-with-circle-cigithub.com/packtci/go-template-example-with-circle-ci)配置 YML 脚本中添加一个工作流字段:

version: 2
jobs:
    build:
        ...
    integration:
        ....
workflows:
    version: 2
    build_and_integration:
        jobs:
            - build
            - integration

在这个工作流程中,我们分别创建了两个并行作业,分别称为buildintegration。它们彼此独立,这将有助于加快构建过程。

CircleCI Web UI 中的工作流程

如果我们点击左侧导航窗格中的“工作流程”链接,我们可以在 CircleCI Web UI 中看到工作流程。然后您需要点击特定的项目,本例中是go-template-example-with-circle-ci,如下截图所示:

如果您点击“RUNNING”工作流程,您将看到以下页面:

构建作业运行了 2 秒,但集成测试运行的时间比构建作业长。最好将这两个作业分开,因为工作流程表明它们彼此不依赖。

顺序工作流程示例

我们之前展示的工作流程示例包含了两个独立运行的作业,但我们也可以有需要其他作业完成后才能运行的作业。假设我们有一个只有在构建运行时才运行的验收测试套件,然后我们的应用程序只有在验收测试套件通过后才会部署。

在我们的示例中,我们使用cypress.iowww.cypress.io/)运行端到端测试,这是一个端到端的 JavaScript 测试库。假设我们的验收测试在 CI 构建中通过,我们就可以将我们的应用程序部署到 Heroku。我们在第十一章中介绍了如何在 Travis CI 中设置 Heroku,Travis CI UI Logging and Debugging部分,所以如果您需要更多关于安装和设置 Heroku 以及在 Heroku 中创建可以部署的应用程序的信息,请阅读那部分。我们需要将 Heroku API 密钥和应用程序名称添加为环境变量。

向项目添加环境变量

在我们的 CircleCI 项目中,我们首先需要通过点击go-template-example-with-circle-cicircleci.com/gh/packtci/go-template-example-with-circle-ci)项目旁边的齿轮图标进入项目设置。确保您在“作业”或“工作流程”视图中,然后您应该看到一个齿轮图标:

一旦您点击齿轮图标,您将被重定向到“项目设置”页面,您需要点击“环境变量”链接。然后您的页面将看起来像以下截图:

我们将通过点击“添加变量”按钮向我们的项目添加两个环境变量,这将弹出一个如下的模态框:

出于安全目的,我已经删除了项目的应用程序名称和 API 令牌的内容,但是一旦您点击“添加变量”按钮,项目中就会有一个环境变量可用。我们现在有两个可以使用的环境变量,即HEROKU_API_KEYHEROKU_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 调用,并使用jqstedolan.github.io/jq/)JSON 命令行处理器来美化 JSON 输出,如下面的截图所示:

使用 jq 实用程序计算我们的 CircleCI 构建的一些指标

让我们使用jq命令行实用程序来计算 CircleCI API 提供的信息的一些指标。我们可能想要找到的一个是项目中所有已通过的构建。我们可以使用jq命令通过使用 jq 中的mapselect内置函数来实现这一点(stedolan.github.io/jq/manual/#Builtinoperatorsandfunctions)。

在下面的截图中,我们获取了最近 30 次构建的构建摘要,然后只显示实际通过的构建:

在这里,我们使用jq实用程序进行了两个不同的查询。

  • 第一个查询是jq 'map(select(.failed == false)) | length',它对对象数组进行映射,并在failedfalse时过滤掉顶级属性failed

  • 第二个查询是jq '. | length',它只是计算数组的长度,即5

我们运行了第二个命令,以确保第一个命令确实过滤了响应有效负载中的一些条目。从中我们可以得知,在最近的 30 次构建中,go-template-example-with-circle-cigithub.com/packtci/go-template-example-with-circle-ci)GitHub 存储库中有一次构建失败。

摘要

在本章中,我们介绍了如何在 macOS/Linux 环境中安装 CircleCI CLI,并向您展示了如何安装 CLI 的夜间构建。我们向您展示了如何使用 CircleCI CLI 中的每个命令,并向您展示了 CircleCI CLI 夜间构建中可用的一些命令功能。我们解释了工作流程为什么有用以及如何在 CircleCI 中使用它们。最后,我们向您展示了如何使用 CircleCI API 以及如何使用jq命令实用程序收集有用的指标。

问题

  1. 安装 CircleCI CLI 的主要先决条件是什么?

  2. 我们从哪里获取了 CircleCI CLI 的夜间构建?

  3. CLI 中有多少个命令存在?

  4. CLI 中的哪个命令对于了解特定命令的功能以及给定命令的选项很有用?

  5. 我们如何在 CircleCI 中运行并行作业?

  6. 我们使用哪个命令来验证我们的 CircleCI YML 脚本?

  7. 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-examplegithub.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

现在让我们使用jqstedolan.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-opengnome-openkde-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_KEYHEROKU_APP_NAME。我们在第十三章中介绍了如何设置项目级别的环境变量,CircleCI CLI 命令和自动化,但我们只需要复制项目环境级别的变量。如果环境变量相同,CircleCI 有一种简单的方法可以做到这一点:

点击导入变量按钮,然后输入要复制的项目,就像这样:

请注意,我只检查了HEROKU_API_KEY环境变量,并且我将手动设置HEROKU_APP_NAME,因为它对于circleci-jobs-examplegithub.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-circlecicircleci.com/workflow-run/533ee47a-a990-4679-826b-7b24221df2ca)的工作流示例:

特别要注意的是,集成作业花了一分钟多的时间才完成,部署作业也花了一分钟多的时间,这使得构建需要 3 分钟 20 秒才能完成。如果我们点击集成作业,我们会看到作业中的以下步骤:

请注意,npm install花了 1 分钟 3 秒才完成。让我们打开运行步骤调用npm install以获取更多细节:

我们唯一的依赖是cypress.io,但我们没有缓存这个依赖,所以它将每次运行这个步骤。CircleCI 有一种方法让我们通过利用称为save_cachecircleci.com/docs/2.0/configuration-reference/#save_cache)和restore_cachecircleci.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-examplegithub.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_cacherestore_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 时提供一些检查表。

问题

  1. 我们在 CircleCI 中用于关注新项目的 API 端点是什么?

  2. cat 实用程序可以用来创建新文件吗?

  3. 如何在 CircleCI 配置 YML 脚本中运行多行命令?

  4. 在 CircleCI 中使用set -x或脚本中的执行跟踪时是否存在安全漏洞?

  5. 我们用什么 CLI 命令来验证我们的配置 YML 脚本?

  6. 环境变量可以从 CircleCI 中的其他项目导入吗?

  7. 我们在 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-vuejsgithub.com/jbelmont/containerized-golang-and-vuejs),它展示了如何使用容器、Golang 和 Vue.js 作为参考。我们首先要做的是确保应用程序正在使用名为make devmakefile任务运行。此命令执行以下操作:

docker-compose up frontend backend db redis

总之,此命令会启动四个 Docker 容器,当它运行时,我们应该能够访问http://localhost:8080。现实中,烟雾测试会访问运行中的应用程序,但这只是用于演示烟雾测试的目的。我们将使用一个名为Cypresswww.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-examplegithub.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.jsgithub.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 库,最后是curlbash。您使用哪个库并不重要;我只是为了演示目的而使用这些库。

使用 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-examplesgithub.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 流水线中运行不同测试的最佳实践

我们在第三章中描述了以下阶段,持续交付的基础

  1. CI/CD 流水线的第一个阶段通常包括构建和提交阶段。这是您构建流水线其余部分所需的任何构件并在构建中运行单元测试套件的地方。第一阶段旨在运行非常快,因为开发人员需要有一个短的反馈循环,否则您可能会冒着开发人员绕过此阶段的风险。

  2. CI/CD 流水线的第二阶段通常会运行集成测试,因为它们是运行时间较长的测试类型,并且可以在流水线的第一阶段运行并通过后运行。第二阶段是对系统的任何新功能是否破坏了系统集成组件的一层保证。

  3. CI/CD 流水线的第三阶段可能包括一套负载测试、回归测试和安全测试,并且比 CI/CD 流水线的前两个阶段运行时间更长。

  4. 第四阶段可以是验收测试的运行阶段,尽管我个人见过一些公司同时运行验收测试套件和集成测试,因此它们的 CI/CD 流水线只有三个阶段。我们在本章中列出的阶段并不是硬性规定,而只是一些建议,因为每个应用程序在行为上都是独特的。

密码和秘密存储的最佳实践

正如我们在涵盖 Jenkins、Travis CI 和 CircleCI 的章节中所看到的,每个持续集成服务器都有一种存储安全信息(如密码、API 密钥和秘密)的方法。在 CI 服务器中运行某些操作是危险的,比如在 Bash 中使用set -x选项进行执行跟踪。最好是使用 CI 服务器的功能来安全地存储密码和秘密,比如 CircleCI 中每个项目的上下文设置,只有项目所有者才能看到。您也可以使用诸如Vaultwww.vaultproject.io/intro/index.html)这样的工具来安全地存储密码,并可以使用 RESTful API 检索,或者使用Amazon Key Management Serviceaws.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 脚本自动化该过程。一些公司可能使用Herokudevcenter.heroku.com/start)作为他们的提供商,或者一些公司可能使用AWS CodeDeployaws.amazon.com/codedeploy/),但最终,您希望自动化您的部署过程,以便有一个标准和自动化的方式来部署您的软件。还很好地拥有一个部署脚本,可以整理版本控制提交,并能够在每个软件发布中显示新功能和错误修复。

自动化的 GitHub 发布示例

我们将使用 GitHub API 中的以下端点来自动化发布策略:POST /repos/:owner/:repo/releases。此端点的文档可以在developer.github.com/v3/repos/releases/#create-a-release找到。我们将在multiple-languagesgithub.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-languagesgithub.com/packtci/multiple-languages/releases)存储库中的 Releases 选项卡,并单击我们的新发布,它看起来像这样:

部署脚本的最佳实践

在为消费者发布新软件时,最好自动化部署过程。不必像我们在这里所做的那样创建自定义部署脚本,因为有很多优秀的库可供使用,它们比我们编写的这个小脚本更结构化和功能丰富。例如,您可以使用GoReleasergoreleaser.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 的知识。

问题

  1. 为什么将集成测试与单元测试分开很重要?

  2. 提交阶段是什么?

  3. 提到一种系统测试类型。

  4. 我们使用的密码管理工具的名称是什么?

  5. 为什么在 shell 脚本中要小心执行跟踪?

  6. 提到部署清单中的一项内容。

  7. 我们提到的 Golang 的部署工具的名称是什么?

进一步阅读

您可以查看 Packt Publishing 出版的书籍Continuous Integration, Delivery, and Deploymentwww.packtpub.com/application-development/continuous-integration-delivery-and-deployment)了解更多有关 CI/CD 最佳实践的知识。

第十六章:评估

第一章:自动化测试的持续集成/持续交付

  1. 手动流程是任何重复且可以自动化的流程。

  2. 自动化是一种过程,通过脚本或某种操作使操作自动完成。

  3. 打开部门之间的沟通是为了找到手动流程。

  4. 持续集成/持续交付

  5. 自动化脚本很有用,因为它们有助于自动化手动任务。

  6. 公司内部网可以帮助其他部门共享信息并链接断开的信息。

  7. 其他部门应该共享数据,因为这有助于在部门之间桥接信息,并增加使用数据的可能性。

第二章:持续集成的基础知识

  1. 软件构建可以仅包括编译软件组件。构建可以包括编译和运行自动化测试,但一般来说,您在构建中添加的进程越多,反馈循环就会变得越慢。

  2. 分阶段构建是将构建分解为较小构建的构建。例如,在第一个构建中,您可以进行编译步骤并运行所有单元测试。可以使用第二个构建来运行更长时间的测试,例如端到端测试。

  3. Make 是一种广泛使用的脚本工具,可用于许多不同类型的编程语言。Maven 是 Java 社区使用的脚本工具。

  4. 最好遵循命名约定,因为它有助于更好地组织代码库,并帮助开发人员快速了解源文件的情况。遵循特定的文件夹结构可以帮助您快速设置新项目。

  5. CI 有很多有价值的东西,但特别是 CI 系统有助于将环境配置和设置解耦到一个隔离的环境中,开发人员可以在其中运行代码库中的所有测试,并执行重要任务,如报告和调用其他第三方服务。

第三章:持续交付的基础知识

  1. 我们所说的交付软件是指实际的软件产品已交付给预期的用户,而不仅仅是软件产品已经获得 QA 部门的批准。换句话说,预期的用户实际上正在使用软件。

  2. 手动部署软件是一种反模式,以及手动软件配置。

  3. 在交付软件时自动化的一些好处包括团队赋权(团队感到有权做决定)、减少错误(通过消除由手动流程引起的错误)以及减轻压力。

  4. 配置管理是指检索、存储、识别和修改与每个给定项目相关的所有软件工件以及软件工件之间的任何关系的过程。

  5. 撰写描述性和有意义的提交消息有助于开发人员快速跟踪正在处理的问题,并帮助开发人员了解您实际完成的工作。

  6. 部署管道可以被视为将开发人员编写的软件交付给用户手中的过程。

  7. 部署应该在每个环境中都是相同的,这样您就可以可靠地知道它在每个环境中都是相同的测试,并避免可能的配置不匹配。

第四章:持续集成/持续交付的商业价值

  1. 开发人员在没有所有要求的情况下很难开发新功能。没有所有必要的要求,开发人员完成分配的工作的能力会受到很大阻碍。

  2. 痛苦驱动开发是关于改进导致您痛苦的流程。主要观点是您感受到的痛苦将帮助您指出改进的方向。

  3. 如果开发人员被大量警报轰炸,他们最终会忽略消息。最好的方法是警报有意义,而不仅仅是噪音。

  4. 通过将团队成员轮换到不同的团队,您可以帮助塑造他们的视角,增加对开发实践的更广泛理解,并增加他们的产品知识。

  5. 这是有益的,因为并非所有的开发实践都有价值,可能是因为还没有想到更好的开发实践,有时询问为什么要做某事将有助于为组织带来必要的变化。

  6. 指标和报告是说服利益相关者接受 CI/CD 价值的好方法。记住,有时一张图片胜过千言万语。

  7. 领导层可能不理解自动化的含义,也不理解自动化对组织的影响。您可能需要通过午餐和学习或公司演示来对他们进行教育。

第五章:Jenkins 的安装和基础知识

  1. Chocolatey

  2. Java

  3. curl -X POST -u <user>:<password> http://<jenkins.server>/restart

  4. sudo ufw allow 8080

  5. Homebrew

  6. 在 Jenkins 仪表板中,单击“管理 Jenkins”,然后单击“管理插件”。

  7. 在 Jenkins 仪表板中,单击“管理 Jenkins”,然后单击“配置系统”,然后您需要向下滚动到全局属性和必要的环境变量。

第六章:编写自由脚本

  1. 问号符号在您需要了解有关构建配置选项的详细信息时非常方便。

  2. 这是一个 Crontab 语法,您可以用它来轮询您的版本控制系统。

  3. 是的,您可以使用多种语言,例如您可能有一个 go 脚本和一个 Node.js 脚本需要在您的环境中运行。

  4. 这是您正在操作的 Unix 环境,许多 Unix 命令都可以使用,如sedawk

  5. 全局属性将在您添加的所有构建作业中可用,而项目级环境变量只能在您添加它们的特定项目中可用。这是由 EnvInject 插件启用的。

  6. 查看执行的命令和/或命令的输出是有用的,这样您可以更轻松地调试在 CI 环境中运行的每个命令时出现的问题。

  7. 后构建操作对于报告和收集指标非常有用,也适用于您认为重要的任何额外操作,超出主要构建脚本操作之外。

第七章:开发插件

  1. 我们使用了 Maven 构建工具。

  2. 我们在 Windows 中使用了 Chocolatey 软件包管理器。

  3. 我们在 macOS 中使用了 Homebrew 软件包管理器。

  4. Maven 构建工具的settings.xml文件。

  5. 您可以转到 URL {{domain}}/pluginManager/advanced 来管理插件。

  6. mvn install命令用于构建和安装 Jenkins 插件。

  7. Maven 创建一个名为pluginname.hpi的文件,其中插件名称可以是您为实际插件指定的任何名称。

第八章:使用 Jenkins 构建流水线

  1. 是的,实际上您可以,当您使用 Jenkins 的 Docker 安装时,这是建议安装的插件之一。

  2. 通过使用 Pipeline 编辑器,您可以获得有用的调试和可视化管道中的阶段。

  3. 蓝色海洋视图仍在积极开发中,因此任何类型的管理任务都需要在经典视图中完成。

  4. 是的,如果您单击管道的节点,然后单击弹出窗口,您将获得该特定构建阶段的详细视图。

  5. 还没有。

  6. stages 关键字包含一个或多个 stage 指令的序列,stages 部分是 Pipeline 描述的“工作”大部分所在的地方。

  7. 是的,它确实需要被包装。

第九章:Travis CI 的安装和基础知识

  1. Jenkins 允许完全定制,因为它必须由 Jenkins 管理员安装和设置,而 Travis CI 则更容易设置,因为它使用应用程序 YML 脚本,并且仅在 GitHub 中使用,GitHub 是一个基于 Web 的版本控制托管服务,使用 Git。

  2. 不。

  3. 您需要转到 Travis 中的个人资料,然后单击同步按钮,然后切换您新同步的存储库。

  4. 标量是普通值,意味着它们可以是数字、字符串、布尔值。

  5. YAML 中的列表只是元素的集合。

  6. 锚点用作在 YML 文件中重用项目的方法。

  7. 是的,在before_install块中添加第二个编程语言。

  8. 通过在 YML 脚本的 services 块中添加 docker 来启用 docker。

第十章:Travis CI CLI 命令和自动化

  1. Travis CLI 用户文档(github.com/travis-ci/travis.rb#windows)建议您使用 RubyInstaller(rubyinstaller.org/)在 Windows 操作系统上安装最新版本的 Ruby。

  2. 您应该使用travis version命令。

  3. 您使用travis help命令。例如,要打印有关 token 命令的信息,运行以下命令:travis help token

  4. 您需要运行travis login命令,然后输入您的 GitHub 用户名和密码。

  5. 您需要传递以下 HTTP 标头:Travis-API-Version: 3

  6. travis report命令打印出系统配置信息。

  7. travis lint命令将检查您的 Travis yml 脚本的语法和有效性。

  8. travis init命令可帮助您在项目中设置 Travis,例如,在项目中设置 go,请运行以下命令:travis init go

第十一章:Travis CI UI 日志记录和调试

  1. 是的,每当您在 GitHub 中合并拉取请求时,Travis CI 将自动启动另一个构建。

  2. 不,但您将看到 before_install 和 install 生命周期事件的标签,以及其他一些生命周期事件。

  3. 您需要使用 Docker 来拉取一个镜像,您可以在这里找到完整的 Docker 镜像列表(docs.travis-ci.com/user/common-build-problems/#Troubleshooting-Locally-in-a-Docker-Image)。

  4. 是的,但您需要发送电子邮件至support@travis-ci.com,然后请求启用特定存储库的调试模式。此外,您还需要使用相应的作业 ID 调用 Travis API 来触发调试模式中的构建。

  5. 您需要通过对/builds 端点进行 GET 请求来对 Travis API 进行 API 调用。以下是使用 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
  1. travis_run_before_install是您将使用的便利 bash 函数。

  2. 您将使用 travis setup SERVICE cli 命令,以下是在 Travis CI 中设置 Heroku 的示例命令:travis setup heroku

第十二章:CircleCI 的安装和基础知识

  1. Jenkins 允许进行全面定制,因为必须由 Jenkins 管理员安装和设置,而 Circle CI 要容易得多,但不允许进行 Jenkins 可以实现的定制。话虽如此,您只需声明要使用的环境(默认为 Linux),并在 yml 脚本中声明要使用的构建语言,例如 Java。

  2. 是的,Circle CI 适用于 Bitbucket 和 GitHub。

  3. 您只需在 Circle CI 应用程序中点击“添加项目”按钮,然后点击要设置的存储库的“设置项目”按钮。

  4. 您点击左侧导航窗格中的 JOBS 链接,然后点击您正在使用的存储库,然后查看最近完成的作业。

  5. 我们使用了Maven 构建工具

  6. 不,您应该使用 Circle CI Syntax 的版本 2,因为版本 1 已被弃用。

  7. 您将构建语言放在^(image)字段中的 docker 字段内。以下是一个示例:

jobs:
   build:
     docker:
         # specify the version you desire here
         - image: circleci/node:7.10

第十三章:Circle CI CLI 命令和自动化

  1. 您需要安装 Docker 才能使用 Circle CI CLI。

  2. 我们从 GitHub Releases 获取了夜间构建(github.com/CircleCI-Public/circleci-cli/releases)。

  3. 目前 CLI 中有 6 个命令,但将来可能会添加更多命令。

  4. help命令很有用,因为它解释了如何使用每个命令以及命令的作用。

  5. workflows 字段是您可以在 Circle CI 中运行并行作业的方式。

  6. 我们使用了circleci config validate命令。

  7. API 端点是https://circleci.com/api/v1.1/

第十四章:Circle CI UI 日志和调试

  1. API 端点是POST https://circleci.com/api/v1.1/project/:vcs-type/:username/:project/follow?circle-token=:token

  2. 是的,cat 实用程序可用于创建新文件,您可以执行以下操作:

cat > somefile
# input
input
Press Control D
  1. 您可以使用竖线操作符(|)创建多行命令,例如:
- run: name: Run Tests and Run Code Coverage with NYC command: | echo "Generate Code Coverage" npm test echo "Show the coverage" npm run coverage
# or simply
- run: |
 echo "Generate Code Coverage" npm test echo "Show the coverage" npm run coverage
  1. 是的,如果在脚本中运行set -x选项并设置密码,它们可能会泄漏到标准输出,因此请将密码或密钥存储在 CircleCI 应用程序中的项目或上下文设置中。

  2. 使用circleci config validate命令验证您的配置 yml 脚本。

  3. 是的,如果您转到项目设置并查看环境变量,并使用导入变量按钮,它们可以。

  4. 我们使用了 save_cache 和 restore_cache 声明。

第十五章:最佳实践

  1. 这很重要,因为 CI/CD 流水线中的第一个阶段旨在快速运行。

  2. CI/CD 流水线中的提交阶段通常是流水线中的第一个阶段,在这个阶段中,您构建您的构件并运行单元测试。

  3. 负载测试。

  4. Vault

  5. 您应该小心,因为您可能会意外地暴露密码。

  6. 开发人员和运营之间的协作。

  7. goreleaser (goreleaser.com/)

posted @ 2024-05-20 12:04  绝不原创的飞龙  阅读(14)  评论(0编辑  收藏  举报