介绍
Pine & Path 正在准备一次花园步行活动。一位编辑请求推迟开始时间,随后另一位又更改了集合地点。Jenkins 可以准备并发布任一通知,但等待批准的较早运行可能在较新通知已经传达给访客之后,又将它覆盖。
你将亲眼观察这种覆盖,然后修改 Pipeline,让新运行自动取代等待中的旧运行。你的成果是一份真实的访客通知和可下载的七文件资料包,其中路线、日程和须知都与最新请求一致。
前置要求是 Jenkins 基本页面操作,以及能看懂一段简短的声明式流水线。本实验会解释参数、审批暂停和并发设置。Jenkins、控制器的两个执行器、通知工具和本地 Notice Desk 都已准备好。你不需要安装软件或编写网站。两个界面运行在同一台训练 VM 上;这是受控的本地发布服务,不是生产部署平台。预计用时由作者估算,未经初学者实测。
观察迟到的批准覆盖通知
在这一步中,你将发布最新通知,然后观察等待中的旧运行将它覆盖。请从 Notice Desk 开始。保持此界面打开;切换界面不会重启构建或重置通知。
检查正常的访客体验
第一个活动是花园步行活动。选择它的 View notice 链接。提供的通知显示于 10:00 在 Community Gate 开始。打开 Open the route,检查三个站点。使用浏览器的 Back 操作返回通知。打开 Read visitor notes,阅读集合说明,然后再次使用 Back。Schedule table 是 CSV 文件:以逗号分隔字段的纯文本表格。Notice details 是 JSON:用带标签的值描述同一个活动。Preparation record 标识资料包的准备方式。此时它如实显示 PROVIDED BASELINE;环境准备并没有替你运行 Jenkins 任务。
使用浏览器的 Back 操作返回 Notice Desk 首页。为花园步行活动选择 Download notice pack,保存 ZIP 归档。ZIP 将多个文件放在一起。使用电脑上的归档工具,将整个归档解压到一个新文件夹,然后打开其中的 index.html。将相邻的全部七个文件保持在一起。路线、样式表、日程、须知、JSON 和准备记录都是真实的本地文件;仅移动 index.html 会破坏它指向相邻文件的链接。浏览器可能直接打开 CSV/文本文件,也可能下载它;两种行为都适合阅读已保存的字节内容。
返回 Notice Desk 首页。检查独立的 Seed Swap 通知及其下载。该活动于 11:00 在 Potting Shed 开始,必须保持不变。接下来的编辑请求只涉及花园步行活动。
选择 Read the editor requests。较早请求使用的 UPDATE 值为 later-start:10:30,地点仍为 Community Gate。最新请求是 new-meeting-point:保持 10:30,改在 Glasshouse Gate 集合。这里的“最新”指较后的编辑请求,而不是碰巧最后结束的构建。
阅读已准备好的 Pipeline
切换到 Jenkins。打开 publish-walk-notice,然后选择 Configure。找到 Pipeline 区域及其 Script 编辑器。这是一条声明式流水线:外层 pipeline { ... } 块描述工作,成对的花括号将设置和阶段组织起来。
任务使用 agent any,在可用的已准备节点上分配执行器和工作区。执行器是 Jenkins 运行工作的槽位;工作区是一次运行的工作目录。这个训练控制器有两个执行器,因此该任务的两次运行可以重叠。它们的工作目录不同,但都发布到同一个花园通知。
parameters 块定义每次构建选择的值。choice 将两个编辑请求作为 UPDATE 选项提供。booleanParam 将 HOLD_FOR_REVIEW 定义为复选框:勾选表示 true,未勾选表示 false。params.HOLD_FOR_REVIEW 读取该次运行选择的值。when 条件仅在表达式为真时运行相应阶段。
三个阶段各有不同用途:
- Prepare notice 使用
deleteDir()清空本次运行的工作区,通过sh调用提供的准备程序,并归档notice-pack.zip。archiveArtifacts将这个原样文件与原生构建一起保留。fingerprint: true记录其内容身份和关联构建。准备和归档不会改变访客通知。 - Review notice 使用
input在发布之前暂停勾选了复选框的运行。其 Proceed 链接允许进入下一阶段;Abort 取消该次运行。这是有意的暂停,不是连接失败。 - Publish notice 将准备好的资料包发送到本地 Notice Desk。
post { always { ... } }块在运行结束时清空工作区,包括中断时。工作区清理后,原生归档文件仍然可用。
提供的准备和发布程序读取实际构建的参数和归档。保留这些程序、全部阶段、审核条件、指纹记录和两次清理操作。下一步你只会更改重叠运行的处理方式。目前保持 Script 不变。选择 Save 返回任务。
启动较早运行并让它等待
在 publish-walk-notice 任务页面,选择 Build with Parameters。为 UPDATE 选择 later-start,点击 HOLD_FOR_REVIEW 标签或复选框将其勾选。选择一次 Build。
在 Build History 中打开刚启动的运行;未做过其他操作时,它是 #1。打开 Console Output。等待出现 Release this selected notice? 以及 Proceed 和 Abort 链接。让它继续等待。控制台已经报告了准备和归档。任务共享的访客通知仍是初始版本,因为 Publish notice 尚未运行。
使用顶部的 publish-walk-notice 面包屑导航返回任务页面。这是在 Jenkins 内部导航,与切换到 Notice Desk 界面不同。记下较早运行编号,以便返回其等待中的控制台。
再次选择 Build with Parameters。这次选择 new-meeting-point,并保持 HOLD_FOR_REVIEW 未勾选。选择一次 Build。打开这次新运行的 Console Output,等待 Finished: SUCCESS。未做过其他操作时,它是 #2。
切换到 Notice Desk,重新加载首页。打开花园通知:现在应显示 10:30 和 Glasshouse Gate。打开路线和访客须知,确认集合地点相同。最新编辑请求已真正发布。
继续较早运行
切换回 Jenkins。返回任务,打开记下的较早运行及其 Console Output,然后选择它的 Proceed 链接。等待 Finished: SUCCESS。如果打开的是过时控制台,请先重新加载该运行的控制台,再选择链接;不要只为刷新页面而启动另一次构建。
切换到 Notice Desk,重新加载并再次检查花园通知。现在它显示 10:30,地点为 Community Gate。这是预期失败:较早运行在后面才结束,覆盖了较新请求。两个 Jenkins 运行都成功了,但业务结果却变旧了。
将当前资料包下载到不同的文件夹。其中的 publication.txt 给出较早 Jenkins 运行;路线、日程和须知与较早请求一致。返回 Jenkins,打开该较早构建的 Build Artifacts 链接,下载其原生 notice-pack.zip。Notice Desk 交付的就是这个实际准备好的归档,而不是悄悄生成另一份通知。
如果某次运行意外处于等待状态,请检查所选的 Parameters 和控制台。你可以在 input 提示上选择 Abort,取消意外等待的运行,然后重新使用预期参数启动。保留全部构建历史。提示出现之前的失败消息与预期审批暂停不同;请阅读控制台,如果不慎编辑了阶段,请返回 Configure 恢复提供的阶段。
自检:你保留了两个真实成功构建、较早运行的实际批准和过时的访客结果。独立的 Seed Swap 通知及其下载仍正常工作。后续修复不会抹去这些证据。
让新运行取代等待中的旧运行
在这一步中,你将阻止旧批准覆盖最新请求。请从 Jenkins 中的 publish-walk-notice 任务页面开始。
添加同任务重叠运行策略
Pipeline 的 options 块控制运行行为。单独使用 disableConcurrentBuilds() 会让新运行排队,直到当前运行结束。这样仍由较早批准掌控发布。设置 abortPrevious: true 则会在同一任务的新运行开始时中断旧运行。true 是布尔值,不是带引号的文本。冒号将这个具名选项传给设置。
选择 Configure,找到 Pipeline → Script,将下面一行插入 agent any 之后,放在外层 pipeline 块内、parameters 之前:
options { disableConcurrentBuilds(abortPrevious: true) }
现在开头应为:
pipeline {
agent any
options { disableConcurrentBuilds(abortPrevious: true) }
parameters {
这个小片段仅展示放置位置;不要用它替换整个 Script。保留现有参数、阶段、归档和清理。选择 Save。保存会改变后续运行;你必须运行新的一对构建来展示效果。
观察自动取代
在任务页面选择 Build with Parameters,选择 later-start,勾选 HOLD_FOR_REVIEW,然后选择 Build。打开这次新启动的较早运行的 Console Output,等待真实的 Proceed / Abort 提示。没有额外操作时,它是 #3。让它等待;不要手动中止或批准继续。
通过面包屑导航返回任务。选择 Build with Parameters,选择 new-meeting-point,保持 HOLD_FOR_REVIEW 未勾选,然后选择 Build。打开这次较新运行的控制台,等待 Finished: SUCCESS。没有额外操作时,它是 #4。
打开较早等待运行的控制台并重新加载。在已准备的 Jenkins 版本上,该运行结果为 NOT_BUILT,并显示 **Superseded by #...**,指出取代它的较新运行。旧的 Publish notice 阶段被跳过,清理仍然运行。仅看到灰色状态还不够:请阅读取代原因,并比较较新构建编号。旧 input 已不再等待批准。历史控制台可能仍显示之前的 Proceed/Abort 文本;被取代后不要使用这些旧链接。

阅读 Superseded by #... 和被跳过的发布阶段,然后比较最终 NOT_BUILT 结果。
这里的 NOT_BUILT 并不意味着没有做过任何工作。较早运行在暂停之前已准备并归档资料包。打开其 Build Artifacts,确认 notice-pack.zip 仍可下载。策略阻止的是随后发布这份过时资料包。两个运行的工作区清理都不会破坏这些归档文件。
切换到 Notice Desk,重新加载并打开花园通知。它必须显示 10:30,地点为 Glasshouse Gate。阅读路线、须知和日程。

一起检查 10:30、Glasshouse Gate 和路线第一站。返回首页,确认 Seed Swap 仍于 11:00 在 Potting Shed 开始。
练习取消意外的审批暂停
切换到 Jenkins。再启动一次 later-start 构建,勾选 HOLD_FOR_REVIEW。等待控制台 input 提示,然后选择 Abort。这是一次有意的手动取消,最终结果为 ABORTED。它与之前观察到的自动取代不同,不能替代自动取代的证据。在线通知保持 Glasshouse Gate,因为这次运行从未发布。
启动最终的 new-meeting-point 构建,保持 HOLD_FOR_REVIEW 未勾选,等待 SUCCESS。如果你不慎暂停或取消了错误请求,就用这个操作恢复:查看编辑请求页面,选择最新请求,不勾选暂停并构建。保留较早运行;删除历史会移除策略行为的证据。
如果保存的 Script 在运行时出现 FAILURE 和语法消息,请检查花括号配对,确保 options 行位于 pipeline 内,而不是 stages 或某个阶段的 steps 内。在 Configure 中修正、保存,然后重新启动预期的一对运行。不要重置 VM 或修改提供的工具。额外失败尝试可以保留在历史中。
保留并检查最终交付成果
切换到 Notice Desk。重新加载,然后下载花园的 notice pack。保存到新文件夹,不要与较早下载的文件混在一起。解压整个 ZIP,打开 index.html。确认 Glasshouse Gate、10:30、路线插图和全部三个站点。打开 Read visitor notes、Schedule table、Notice details 和 Preparation record。CSV 和 JSON 必须与页面一致;publication.txt 给出最终成功的 Jenkins 任务和构建。整个文件夹可以在本地工作,不依赖 Jenkins 或 Notice Desk。在各本地文件视图间使用浏览器的 Back 操作。
在 Jenkins 中打开对应的成功构建和 Build Artifacts,然后下载 notice-pack.zip。解压后的七个文件必须与 Notice Desk 下载的一致。Jenkins 原生归档记录构建准备了什么;Notice Desk 展示实际发布了什么。
自检:已保存的策略处于生效状态,真实的较新运行自动取代了暂停在 input 的旧运行,访客收到最新请求的实际文件。这对暂停/较新运行必须使用当前保存的 Script。最新完成的运行本身必须是一次成功的最新请求发布;如果发生错误发布或跳过发布,请恢复 Script 并重新运行。如果再次改变运行行为,请演示新的一对暂停/较新运行。所有构建历史、以前的归档、准备/审核/发布/清理行为和无关活动都保持完整。
这个设置影响同一任务内的重叠运行。它无法撤销中断前已发生的发布,无法协调不同任务,也无法保证所有生产部署都能安全中止。这里,旧运行暂停在唯一的发布步骤之前,因此边界很明确。
总结
你将真实的访客结果与 Jenkins 运行行为关联起来。你复现了旧批准覆盖较新通知的情况,然后使用 disableConcurrentBuilds(abortPrevious: true),让新运行中断同一任务中等待的旧运行。你区分了准备与发布、原生自动取代与手动取消,以及归档文件与工作区文件。
最终成果是最新的花园通知,以及匹配的离线路线、日程和须知。保留的历史说明了为什么这次成功构建意味着预期更新确实已经传达给访客。



