目录
Martin Fowler 对持续集成(Continuous Integration)的定义是:持续集成是一种软件开发实践,团队开发成员经常集成他们的工作,通常每个成员每天至少集成一次,这意味着每天可能发生多次集成。每次集成都通过自动化构建(包括编译、发布、自动化测试)进行验证,从而尽快发现集成错误。许多团队发现该过程可以减少集成问题,让团队能够更快地开发内聚的软件。
图片来源:百度百科
在 BuildBot 中,可以通过三种方式触发构建:
BuildBot 有如下优点:
BuildBot 由单个 BuildMaster 和一或多个连接到 Master 的 Worker 组成。BuildMaster 决定构建什么,何时构建,以及如何构建。Worker 连接到 Master,执行交给它们执行的命令。
流程如下所示:
git clone
、make
、make check
)Worker 通常运行在各种不同的平台上,它们通过 TCP 协议连接到 BuildMaster,TCP 连接由 Worker 发起。BuildMaster 下发的 Command 以及 Worker 返回的 Result 通过 TCP 连接进行传输。BuildMaster 是集群的管理者,所有 Command 都由 BuildMaster 下发给 Worker。
BuildMaster 只提供用于执行构建的指令,不提供源代码,因此 Worker 需要去代码仓库获取源代码。
Buildbot Master 的核心组件如下:
ChangeSource:
ChangeSource 是用户可配置的组件,它与外部的版本控制系统进行交互,检索新代码。在内部,新代码表示为变更(Changes),大致相当于单个提交或变更集。Buildbot 的设计要求 Worker 拥有自己的源代码副本。因此,如果没有基于新代码提交事件创建新构建的调度器,那么 ChangeSource 是可选的组件。
Scheduler:
Scheduler 是用户可配置的组件,它决定何时开启构建。该决定可能基于时间、正在被提交的新代码,或类似的事件。
Builder:
Builder 是用户可配置的如何执行构建的描述。它定义新构建有哪些步骤,可以在哪些 Worker 上运行,以及其它属性。Builder 接收构建请求(Build Request),构建请求指定为特定版本的代码创建构建的意图,并且生成构建的具体描述,包括要执行的步骤列表,需要执行构建的 Worker 等。
Reporter:
Reporter 是用户可配置的组件,它将开始或完成的构建的信息发送到外部源。Buildbot 提供自己的 Web 应用程序观察这些数据,因此 Reporter 是可选的。但它们可以用于提供平台(比如 Github)上的最新构建状态,或者发送邮件。
第一步是为 Master 创建 virtualenv。我们创建单独的目录,展示 Master 和 Worker 之间的区别:
mkdir -p ~/buildbot-test/master_root
cd ~/buildbot-test/master_root
然后创建虚拟环境。在 Python 3 中:
python3 -m venv sandbox
source sandbox/bin/activate
接下来,需要安装若干构建依赖,以确保可以安装 Buildbot 及其支持包。这些构建依赖项是:
gcc
用于基于 RHEL/CentOS/Fedora 的发行版,或 build-essential
用于基于 Ubuntu/Debian 的发行版)。python3-devel
用于基于 RHEL/CentOS/Fedora 的发行版,或 python3-dev
用于基于 Ubuntu/Debian 的发行版)。openssl-devel
用于基于 RHEL/CentOS/Fedora 的发行版,或 libssl-dev
用于基于 Ubuntu/Debian 的发行版)。libffi-devel
用于基于 RHEL/CentOS/Fedora 的发行版,或 libffi-dev
用于基于 Ubuntu/Debian 的发行版)。安装这些构建依赖:
# if in Ubuntu/Debian based distributions:
sudo apt-get install build-essential python3-dev libssl-dev libffi-dev
# if in RHEL/CentOS/Fedora based distributions:
sudo yum install gcc python3-devel openssl-devel libffi-devel
或者参考你的发行版的关于如何安装这些包的文档。
现在已经准备就绪,开始安装 Buildbot:
pip install --upgrade pip
pip install 'buildbot[bundle]'
现在已成功安装 Buildbot,开始创建 Master。my_master
代表指向目录的路径,我们将在该目录中创建 Master:
buildbot create-master my_master
Buildbot 的活动由配置文件控制。Buildbot 默认使用 master.cfg
文件中的配置,但其安装附带一个名为 master.cfg.sample
的示例配置文件。我们将使用示例配置文件,但必须将其重命名为 master.cfg
:
mv my_master/master.cfg.sample my_master/master.cfg
最后,启动 Master:
buildbot start my_master
现在在该终端中将看到来自 Master 的日志信息。它应该以类似下面的行结束:
2014-11-01 15:52:55+0100 [-] BuildMaster is running
The buildmaster appears to have (re)started correctly.
从现在开始,可以访问运行在端口 8010 上的 Web 状态页面:http://localhost:8010/。
Master 至少需要一个 Worker,执行它的命令。为此,请转到下一节。
Worker 将执行 Master 发送的命令。在本教程中,我们使用 buildbot/hello-world 项目作为示例。因此,Worker 需要访问 git 命令,以便检出代码。确保已安装 git,否则构建将失败。
就像我们为 Master 所做的一样,我们将在 Master 的 virtualenv 旁边为 Worker 创建 virtualenv。当然,完全可以在另一台计算机上做这件事 — 只要 Worker 计算机能够连接到 Master 计算机。我们首先为 Worker 创建新目录:
mkdir -p ~/buildbot-test/worker_root
cd ~/buildbot-test/worker_root
再次创建虚拟环境。在 Python 3 中:
python3 -m venv sandbox
source sandbox/bin/activate
安装 buildbot-worker
命令:
pip install --upgrade pip
pip install buildbot-worker
# required for `runtests` build
pip install setuptools-trial
现在,创建 Worker:
buildbot-worker create-worker my_worker localhost example-worker pass
注意
如果你决定在另一台计算机上创建 Worker,那么应该将
localhost
替换为运行 Master 的计算机的名称。
用户名(example-worker
)和密码(pass
)应该与 my_master/master.cfg
中的相同;通过查看 c['workers']
部分,验证情况是否如此:
cat ../master_root/my_master/master.cfg
最后,启动 Worker:
buildbot-worker start my_worker
检查 Worker 的输出。它应该以类似下面的行结束:
2014-11-01 15:56:51+0100 [-] Connecting to localhost:9989
2014-11-01 15:56:51+0100 [Broker,client] message from master: attached
The worker appears to have (re)started correctly.
同时,在另一个终端,在 Master 的日志(在 Master 目录中的 twisted.log)中,应该看到类似这样的行:
2014-11-01 15:56:51+0100 [Broker,1,127.0.0.1] worker 'example-worker' attaching from
IPv4Address(TCP, '127.0.0.1', 54015)
2014-11-01 15:56:51+0100 [Broker,1,127.0.0.1] Got workerinfo from 'example-worker'
2014-11-01 15:56:51+0100 [-] bot attached
目录树现在应该类似这样:
~/buildbot-test/master_root/my_master # master base directory
~/buildbot-test/master_root/sandbox # virtualenv for master
~/buildbot-test/worker_root/my_worker # worker base directory
~/buildbot-test/worker_root/sandbox # virtualenv for worker
现在,应该可以访问 http://localhost:8010,在这里将看到类似下面的 Web 页面:
点击左侧的“Builds”,打开子菜单,然后点击 Builders,可以看到刚刚启动的 Worker(由绿色气泡标识)已经连接到 Master:
现在,Master 正在安静地等待新提交。
接下来讲述 Buildbot 中的基础概念。
在 Buildbot 中,以下概念用于描述正在构建的源代码:
Repository
存储库是版本控制系统跟踪的文件所在的位置。通常,由 URL 或磁盘上的位置标识它。它包含代码库历史的子集。
Codebase
代码库是相关文件及其历史的集合,它们被版本控制系统作为一个单元进行追踪。文件及其历史被存储在一或多个存储库中。比如,Buildbot 代码库的主存储库位于 https://github.com/buildbot/buildbot/
。Buildbot 还有上千个 Fork。这些存储库虽然可能存储非常旧版本的 Buildbot 代码,但仍然包含相同的代码库。
Project
项目是一或多个代码库的集合,它们被一起构建,造出最终制品。例如,应用程序可能由两个代码库组成 — 一个用于代码,一个用于测试数据,后者占用大量空间。构建和测试这样的应用程序需要从两个代码库中获取代码。
Revision
修订是大多数版本控制系统使用的文本标识符,用于唯一地指定特定代码库中源代码的特定版本。
Source stamp
Source stamp 是识别某些代码库中特定版本的代码所需的信息的集合。在大多数版本控制系统中,Source stamp 只存储修订版本。在其它版本控制系统中,也需要分支。
Source stamp set
Source stamp set 是一组 Source stamp,用于标识某些项目中特定版本的代码。像项目是代码库的集合一样,Source stamp set 是 Source stamp 的集合,项目中每个代码库一个 Source stamp。
为构建项目,Buildbot 只需知道与该项目对应的 Source stamp set。Source stamp set 对组成项目的每个代码库都有一个 Source stamp。反过来,每个 Source stamp 都有足够的信息,标识代码库中代码的特定版本。
ChangeSource 是用户可配置的组件,它与外部的版本控制系统进行交互,检索新代码。在内部,新代码表示为变更(Change),大致相当于单个提交或变更集。变更被发送到调度器,然后调度器决定是否为这些新代码变更创建新构建。
Buildbot 的设计要求 Worker 拥有自己的源代码副本。因此,如果没有基于新代码提交事件创建新构建的调度器,那么 ChangeSource 是可选组件。
ChangeSource 可以通过多种方式,监视存储库的变更。比如周期性地轮询存储库;或者通过配置(比如通过被 commit 触发的钩子脚本),使存储库将变更推送给 ChangeSource。
下面是一个示例:
c['change_source'] = []
c['change_source'].append(changes.GitPoller(
'https://github.com/tim-chow/buildbot-test.git',
workdir='git-poller-buildbot-test-dir', branch='master',
pollInterval=120))
该示例创建一个 GitPoller
类型的 ChangeSource
,它每隔 120 秒轮询一次存储库,获取 master
分支上的变化。
ChangeSource
必须被添加到 c["change_source"]
列表中。
通常情况下,ChangeSource
自动为其产生的任何 Change
提供正确的 repository
属性。对于操作类 URL 说明符的系统,是存储库 URL。其它 ChangeSource
按需适配该概念。
许多 ChangeSource
也允许指定项目。该属性在在同一个 BuildMaster 上从多个不同的代码库构建时非常有用:项目字符串可以用于区分不同的代码库。调度器可以依据项目进行过滤,因此可以为每个项目配置不同的构建器。
Change 是一种抽象方式,Buildbot 用其表示开发人员对源文件执行的单个变更。在支持原子检入概念的版本控制系统中,Change 表示变更集或提交。
Change 用于 ChangeSource 与 Scheduler 进行通信。
Change
包含如下信息:
调度器是决定何时开始构建的组件。该决定可以基于时间、正在被提交的新代码,或类似的事件。
调度器负责创建构建请求(Build Request),它标识在源代码的特定版本上启动构建的请求。
每个 BuildMaster 拥有一组调度器对象,每个调度器对象获得每个传入的 Change
的副本。调度器负责决定何时运行构建。一些 Buildbot 安装可能只有一个调度器,而另一些可能有多个,每个调度器用于不同的目的。
调度器主要包含两部分信息:
下面是一个示例:
# Configure the Schedulers, which decide how to react to incoming changes. In this
# case, just kick off a 'runtests' build
c['schedulers'] = []
c['schedulers'].append(schedulers.SingleBranchScheduler(
name="all",
change_filter=util.ChangeFilter(branch='master'),
treeStableTimer=None,
builderNames=["runtests"]))
c['schedulers'].append(schedulers.ForceScheduler(
name="force",
builderNames=["runtests"]))
在上面的例子中,第一个调度器接收所有存储库上的变更,但只关注 master
分支上的变更。换言之,它只对它感兴趣的变更作出反应。当检测到这样的变更时,它运行 runtests
。treeStableTimer
用于避免在频繁提交时,产生大量的构建请求。
使用第二个调度器时,可以通过 Web UI 强制构建 runtests
。
Scheduler
必须被添加到 c["schedulers"]
列表中。
ForceScheduler
调度器ForceScheduler
调度器是可以在 Web UI 中配置强制构建表单的方式。
在 /#/builders/:builderid
Web 页面,可以在页面的右上角看到,为该 Builder 配置的每个 ForceScheduler
调度器都有一个按钮。如果点击该按钮,那么将弹出一个对话框,让你选择请求新构建的各种参数。
Buildbot 框架允许精确地定制构建表单的外观,哪些构建器有强制构建表单(并非所有构建器都需要强制构建),以及允许哪些用户在哪些构建器上进行强制构建。
可以通过配置 ForceScheduler
,并且将其添加到 schedulers
列表的方式,实现这一点。
该调度器接受如下参数:
name
:
每个调度器必须有唯一的名称。该名称被用在状态展示中,在构建属性 scheduler
中也是可用的。在 UI 中,按照该属性排序强制构建按钮(因此可以通过添加前缀 01、02 等,精确控制其顺序)。
builderNames
:
强制构建按钮应出现的构建器列表。
reason
:
该参数允许用户指定构建原因。默认值是 “force build”。
reasonString
:
用于创建强制构建构建原因的字符串。该字符串可以包含 %(owner)s
和 %(reason)s
(代表输入到 reason
字段的值)。
username
:
该参数用于指定与构建关联的用户名(即 owner)。默认值是 username 参数。
codebases
:
用于指定应该呈现的代码库的字符串或 CodebaseParameter 列表。默认是没有名称的单个代码库(即 codebases=[‘’])。
properties
:
参数列表,每个属性一个参数。这些可以是任意参数,其中参数名称被视为属性名,或 AnyPropertyParameter
,它允许 Web 用户指定属性名。默认值是空列表。
buttonName
:
在生成的强制构建表单中,“提交”按钮的名称。默认为调度器名称。
例子可能比长篇解释更好。
from buildbot.plugins import schedulers, util
sch = schedulers.ForceScheduler(
name="force",
buttonName="pushMe!",
label="My nice Force form",
builderNames=["my-builder"],
codebases=[
util.CodebaseParameter(
"",
label="Main repository",
# will generate a combo box
branch=util.ChoiceStringParameter(
name="branch",
choices=["master", "hest"],
default="master"),
# will generate nothing in the form, but revision, repository,
# and project are needed by buildbot scheduling system so we
# need to pass a value ("")
revision=util.FixedParameter(name="revision", default=""),
repository=util.FixedParameter(name="repository", default=""),
project=util.FixedParameter(name="project", default=""),
),
],
# will generate a text input
reason=util.StringParameter(name="reason",
label="reason:",
required=True, size=80),
# in case you don't require authentication, this will display
# input for user to type their name
username=util.UserNameParameter(label="your name:",
size=80),
# A completely customized property list. The name of the
# property is the name of the parameter
properties=[
util.NestedParameter(name="options", label="Build Options",
layout="vertical", fields=[
util.StringParameter(name="pull_url",
label="optionally give a public Git pull url:",
default="", size=80),
util.BooleanParameter(name="force_build_clean",
label="force a make clean",
default=False)
])
])
这将生成如下 UI:
BuildRequest
是用于开始特定构建的请求。BuildRequest
包含如下信息:
Builder
的名称。SourceStamp
集合。表示相同版本源代码和相同构建器的两个构建请求可能被合并。用户可以配置额外的限制,以决定构建请求的可合并性。
Builder
负责从 BuildRequest
创建新构建。创建新构建实质上是确定后续构建的以下属性:
将要运行的步骤序列由用户可配置的 BuildFactory
定义,BuildFactory
由用户附加到每个 Builder
。
只要有可能,即只要相关联的 Worker 空闲,Builder
将尝试从构建请求创建构建。当 Worker 空闲时,BuildMaster 将选择可以在该 Worker 上运行的最老的 BuildRequest
,并且通知相应的 Builder
可能开始构建它。
默认情况下,每个 Builder
完全独立地运行。这意味着,如果 N 个 Builder
附加到一个 Worker,那么 Worker 可能尝试并发地运行这 N 个构建。该级别的并发可被各种各样的联锁(Interlocks)控制。
在底层,每个 Builder
在 BuildMaster
上有自己的独占目录,并且在它附加到的每个 Worker 上拥有一个独占目录。Master 上的目录用于保存状态信息。Worker 上的目录是实际的检出、编译和测试步骤所发生的位置。
下面是一个示例:
factory = util.BuildFactory()
# 检出源代码
factory.addStep(steps.Git(repourl='https://github.com/tim-chow/buildbot-test.git', mode='incremental'))
# 运行测试(注意需要安装 'python')
factory.addStep(steps.ShellCommand(command=['python', 'setup.py', 'test']))
c['builders'] = []
c['builders'].append(
util.BuilderConfig(name="runtests",
workernames=["example-worker"],
factory=factory))
该示例创建一个名为 runtests
的 Builder
,它能够在 example-worker
上运行。
Builder 的名称不能相同,并且必须被添加到 c['builders']
(其值是 BuilderConfig
对象的列表)。
Build
表示源代码特定版本的单次编译或测试运行。构建由一系列步骤组成。这些步骤可能是任意的。比如,对于编译型软件,构建通常由 checkout、configure、make 和 make check 序列组成。对于像 Python 模块之类的解释型项目,构建通常是 checkout,随后调用捆绑的测试套件。
构建是由 Builder
实例创建。
BuildSet
代表一组 Build
,它们编译和/或测试源树的相同版本。通常,这些构建由多个 Builder
创建,并且将执行不同的步骤。
BuildSet
被作为单个单元追踪,如果任意 Build
组件失败,那么 BuildSet
失败,因此仅当所有 Build
组件都成功时,BuildSet
才成功。BuildSet
可以发出两种状态通知消息:firstFailure
类型和 Finished
类型。
创建 BuildSet
时,需要提供包含一或多个 Source stamp 元组((branch, revision, changes, patch)
)的集合,其中某些元组可能为 None
,以及将被运行的 Builder
列表。然后将它们交给 BuildMaster
,BuildMaster
负责为每个 Builder
创建单独的 BuildRequest
。
Worker
对应于执行构建的环境。单台物理机必须至少运行一个 Worker
,以便 Buildbot 能够利用它运行构建。多个 Worker
可以在一台机器上运行,以提供不同的环境,这些环境可以通过容器或虚拟机的方式,重用相同的硬件。
每个 Builder
与一或多个 Worker
相关联。比如,用于执行 macOS 构建的 Builder
天然与 Mac Worker
相关联。
对于任意给定的 Builder
,如果有多个 Worker
可用,那么将拥有一定程度的冗余:如果一个 Worker
脱机,那么其它 Worker
仍然可以保持 Builder
工作。此外,对于同一个 Builder
,多个 Worker
将允许同时执行多个构建,如果发生很多强制构建或 try
构建,这可能很有用。
在理想情况下,为一个 Builder
配置的每个 Worker
应该相同。否则,构建或测试失败将依赖构建被运行在哪个 Worker 上,这将使失败的调查复杂化。
下面是一个示例:
# The 'workers' list defines the set of recognized workers. Each element is
# a Worker object, specifying a unique worker name and password. The same
# worker name and password must be configured on the worker.
c['workers'] = [worker.Worker("example-worker", "pass")]
Buildbot 对用户的认知有限。它假设世界由一组开发人员组成,可以用若干个简单属性描述开发人员。这些开发人员对源代码进行更改,引发可能成功或失败的构建。
在发出 Buildbot 命令时,用户也可能具有不同级别的授权,例如从 Web 接口或 IRC 通道强制执行构建。
主要通过源码控制系统了解开发者。到达的每个 Change
对象都带有 who
字段,该字段通常给出负责该变更的用户的帐户名称(在存储库机器上)。该字符串被展示在 HTML 状态页面和每个 Build 的责任列表中。
如果想使用 User 做更多事情,而不仅仅只引用它们,那么用户名需要映射到某种类型的地址。该映射的职责被留给需要地址的状态模块。将来,管理用户的职责将转移到用户对象。
git
变更中的 who
字段用于创建用户对象,它在 Buildbot 管理用户的方式上,允许更多的控制和灵活性。
每个构建拥有一组构建属性(Build Properties),其构建步骤可以使用这些构建属性,修改它们的行为。
属性被表示为一组键-值对。实际上,每个属性是一个变量,一旦设置,构建中的后续步骤可以使用该属性,修改它们的行为。属性值可以是数字、字符串、列表或字典。列表和字典可以包含其它列表或字典。因此,属性值可以是任意复杂的结构。
属性的工作原理与变量非常相似,因此它们可被用于实现各种功能。
下面是一些示例:
Worker
的名称被设置到 workername
属性。如果存在多个不同 Worker
,并且构建操作取决于确切的 Worker
,有些用户可能认为,根据 workername
属性改变操作比为每个 Worker
创建单独的 Builder
更方便。got_revision
属性中。构建属性是为构建步骤提供配置信息的常用方法。
一些构建属性来自于外部源,在构建开始前被设置;其它的则在构建过程中被设置,可用于后面的步骤。属性的来源如下:
全局配置(global configuration
)
这些属性应用到全部构建。
调度器(Schedulers)
调度器可以指定对它启动的所有构建可用的属性。
变更(changes)
变更可以拥有附加属性。提供变更源收集的额外信息。这通常与 sendchange
命令一起使用。
强制构建
强制构建表单允许用户指定属性。
Worker
Worker
可以向它执行的构建传递属性。
Build
Build
自动在它自己上设置许多属性。
Builder
Builder
可以在它运行的所有构建上设置属性。
步骤
构建的步骤可以设置属性,后续步骤可以使用这些属性。特别是,source step 设置 got_revision
属性。
在内部,以 JSON 格式存储属性,因此属性被限制为基本类型的数据:数值、字符串、列表和字典。
在大多数情况下,属性用于在构建期间更改构建步骤的行为。这可以通过使用可渲染对象(实现 IRenderable
接口的对象)作为步骤参数的方式实现。当启动步骤时,将使用构建属性的当前值渲染这些对象,并且使用渲染结果替换步骤参数的实际值。
Buildbot 提供覆盖常见情况的可渲染对象类型。也可以创建自定义的可渲染对象。
最简单的可渲染对象是 Property
,它渲染被其参数命名的属性的值:
from buildbot.plugins import steps, util
f.addStep(steps.ShellCommand(command=['echo', 'buildername:',
util.Property('buildername')]))
可以通过传递 default
关键字参数的方式,指定默认值:
f.addStep(steps.ShellCommand(command=['echo', 'warnings:',
util.Property('warnings', default='none')]))
当属性不存在或其值被 Python 视为 False
时,将使用默认值。defaultWhenFalse
参数可以设置为 False
,以便强制 Buildbot 仅在未设置参数时才使用默认参数:
f.addStep(steps.ShellCommand(command=['echo', 'warnings:',
util.Property('warnings', default='none',
defaultWhenFalse=False)]))
默认值自身也可以是可渲染对象,比如:
command=util.Property('command', default=util.Property('default-command'))
Property
仅用于替换整个参数:在上面的例子中,它替换 echo
的参数。通常,需要将属性插入到字符串中。完成该任务的工具是 Interpolate
。
更常见的模式是通过使用 %(prop:<propname>)s
语法的方式,使用 Python 字典风格的字符串插值。在这种形式中,属性名出现在括号中。一个常见的错误是忽略尾部的 “s”,导致 Python 出现相当晦涩的错误(“ValueError: unsupported format character”)。
from buildbot.plugins import steps, util
f.addStep(steps.ShellCommand(
command=['make',
util.Interpolate('REVISION=%(prop:got_revision)s'),
'dist']))
该示例将生成带有类似于 REVISION=12098
的参数的 make
命令。
字典风格插值的语法是 “选择器:选择器特定的键[:表示如何解释键生成的值的字符串]”。
支持的选择器如下。
prop
:
键是属性的名称。
src
:
键是代码库和 Source stamp 属性,用冒号分隔。注意,语法是 %(src:<codebase>:<ssattr>)s
,与其它选择器不同。
kw
:
键引用传递给 Interpolate
的关键字参数。这些关键字参数可能是普通的值或可渲染对象。
secret
:
键引用 secretsProviders
中声明的提供者提供的密钥。
workers
:
键引用 workers
提供的信息条目。
解释值的方式如下。
-replacement
:
如果键存在,那么替换其值;否则,替换 replacement
。replacement
可能为空(默认),%(prop:propname:-)s
。
~replacement
:
与 -replacement
类似,但仅当键的值被 Python 视为 True
时,才替换。Python 认为 None
、0、空列表和空字符串是 False
,因此这些值被 replacement
替换。
+replacement
:
如果键存在,那么替换 replacement
;否则,替换空字符串。
?|sub_if_exists|sub_if_missing
?#|sub_if_true|sub_if_false
:
三元替换,取决于键出现(带 ?
,与 +
类似)或为 True
(带 ?#
,与 ~
类似)。其中管道符号(|
)可以是除 (
外的任意字符。
示例:
from buildbot.plugins import steps, util
f.addStep(steps.ShellCommand(
command=[
'save-build-artifacts-script.sh',
util.Interpolate('-r %(prop:repository)s'),
util.Interpolate('-b %(src::branch)s'),
util.Interpolate('-d %(kw:data)s', data="some extra needed data")
]))
示例:
x
from buildbot.plugins import steps, util
f.addStep(steps.ShellCommand(
command=[
'make',
util.Interpolate('REVISION=%(prop:got_revision:-%(src::revision:-unknown)s)s'),
'dist'
]))
另外,Interpolate
支持使用位置字符串插值。这里,%s
是占位符,后续参数是替换(可以是可渲染对象):
f.addStep(steps.ShellCommand(
command=[
'echo',
util.Interpolate('%d warnings and %d errors',
util.Property('warnings'),
util.Property('errors'))
]))
BuildMaster 可以通过多种方式,将构建状态呈现给不同用户。每种传递方式是配置文件的 services
列表中的一个 Reporter Target 对象。只需向该列表追加更多对象,即可添加 Reporter Target:
c['services'] = []
m = reporters.MailNotifier(fromaddr="buildbot@localhost",
extraRecipients=["builds@lists.example.com"],
sendToInterestedUsers=False)
c['services'].append(m)
c['services'].append(reporters.irc.IRC(host="irc.example.com", nick="bb",
channels=[{"channel": "#example1"},
{"channel": "#example2",
"password": "somesecretpassword"}]))
默认情况下,用户无需认证,即可在 Web UI 中访问控制功能。为安全起见,应该配置认证插件。
认证插件被实现为类,并且作为 auth
参数传递给 www
。
比如:
from buildbot.plugins import util
c['www'] = {
# ...
'auth': util.UserPasswordAuth({"homer": "doh!"}),
}
下面讲述授权框架。
在 Buildbot 中,授权框架非常通用和灵活。缺点是对新手而言,不易理解。该“简单”示例通过实现管理员拥有全部权限的方式,使你可以轻松开始。
请认真阅读以下文档,以理解如何在 Buildbot 中设置授权。
授权框架与 REST API 紧密耦合。授权框架仅适用于 HTTP,不适用于其它交互方式,如 IRC 或 try
调度器。它根据规则允许或拒绝对 REST API 的访问。
角色(Role)是授予用户的标签。
它类似但不同于常见的组的概念:
ldap
、github
等)提供,并且并非总受 Buildbot 管理员的精确控制。端点匹配器将角色关联到 REST API 端点。默认策略是在没有匹配器匹配时,允许通过。
角色匹配器将认证用户关联到角色。
请注意,可以使用该框架拒绝对 REST API 的读访问,但在 Websocket 或 SSE API 中,没有访问控制。这意味着用户仍然可以在 UI 中,看到来自正在运行的构建的实时更新,因为它们来自于 Websocket。
只能在 REST API 中读取的唯一资源是日志数据(也称为日志块)。
从严格的安全角度来看,实际上不能使用 Buildbot 的授权框架安全地拒绝对 Bot 的读取访问。访问控制被设计为限制只能通过 REST API 访问的控制 API。为减少攻击面,建议将 Buildbot 放在受访问控制的代理之后,如 OAuth2Proxy。
端点匹配器负责创建规则,以匹配 REST 端点,并且要求相应的角色。Buildbot 按照配置端点匹配器的顺序,处理它们。匹配端点的第一个规则将阻止进一步检查其它规则。当结果为拒绝时,如果想继续检查其它规则,那么设置 defaultDeny=False。如果没有端点匹配器匹配,那么授予访问权限。
可以通过在列表的末尾放置带有不存在角色的 AnyEndpointMatcher
的方式,实现默认拒绝策略。请注意,这将拒绝所有 REST API,并且大多数 UI 在遇到此类错误的情况下,未实现正确的访问拒绝消息。
每个 EndpointMatcher 类实现如下序列:
当前已实现多个端点匹配器。如果需要复杂的机制,那么需要实现自己的端点匹配器。在这种情况下,可以查看源代码,获取关于如何编写端点匹配器的详细示例。
角色匹配器负责创建规则,以匹配人员,授予他们角色。可以从认证插件提供的组信息授予角色,或者直接授予给人员的 Email。
下面的例子允许管理员控制所有事情,但允许匿名用户查看构建结果:
from buildbot.plugins import *
authz = util.Authz(
allowRules=[
util.AnyControlEndpointMatcher(role="admins"),
],
roleMatchers=[
util.RolesFromEmails(admins=["my@email.com"])
]
)
auth=util.UserPasswordAuth({'my@email.com': 'mypass'})
c['www']['auth'] = auth
c['www']['authz'] = authz
更复杂的配置,按分支分开:
from buildbot.plugins import *
authz = util.Authz(
stringsMatcher=util.fnmatchStrMatcher, # simple matcher with '*' glob character
# stringsMatcher = util.reStrMatcher, # if you prefer regular expressions
allowRules=[
# admins can do anything,
# defaultDeny=False: if user does not have the admin role, we continue parsing rules
util.AnyEndpointMatcher(role="admins", defaultDeny=False),
util.StopBuildEndpointMatcher(role="owner"),
# *-try groups can start "try" builds
util.ForceBuildEndpointMatcher(builder="try", role="*-try"),
# *-mergers groups can start "merge" builds
util.ForceBuildEndpointMatcher(builder="merge", role="*-mergers"),
# *-releasers groups can start "release" builds
util.ForceBuildEndpointMatcher(builder="release", role="*-releasers"),
# if future Buildbot implement new control, we are safe with this last rule
util.AnyControlEndpointMatcher(role="admins")
],
roleMatchers=[
RolesFromGroups(groupPrefix="buildbot-"),
RolesFromEmails(admins=["homer@springfieldplant.com"],
reaper-try=["007@mi6.uk"]),
# role owner is granted when property owner matches the email of the user
RolesFromOwner(role="owner")
]
)
c['www']['authz'] = authz
使用 Github 认证,允许 “Buildbot” 组织内的用户对控制端点进行访问:
from buildbot.plugins import *
authz = util.Authz(
allowRules=[
util.AnyControlEndpointMatcher(role="BuildBot")
],
roleMatchers=[
util.RolesFromGroups()
]
)
auth=util.GitHubAuth('CLIENT_ID', 'CLIENT_SECRET')
c['www']['auth'] = auth
c['www']['authz'] = authz
c['protocols'] = {'pb': {'port': 9989}}
protocols
中包含与 Master 和 Worker 之间通信所使用的协议有关的信息。
xxxxxxxxxx
c['title'] = "Test Buildbot"
c['titleURL'] = "http://timd.cn/buildbot/"
title
字符串将出现在 HOME 页面的顶部(链接到 titleURL
)。
c['buildbotURL'] = "http://192.168.10.10:8010/"
buildbotURL
用于指出 Web 服务的地址,应该使用 www
中设置的端口。
c['www'] = dict(port=8010,
plugins=dict(waterfall_view={}, console_view={}, grid_view={}))
激活 Web UI 的最小化配置,其中:
port
:接收请求的 TCP 端口plugins
:要加载的 UI 插件及其配置,这些插件独立安装c['db'] = {
# This specifies what database buildbot uses to store its state. You can leave
# this at its default for all but the largest installations.
'db_url' : "sqlite:///state.sqlite",
}
db_url
指定用于保存 Buildbot 状态的数据库。