利用AI实现Jenkins到GitHub Actions的自动化迁移

本文详细介绍了Slack如何开发自动化工具将Jenkins流水线迁移到GitHub Actions,通过结合GitHub官方导入工具和AI技术,预计可节省1300小时迁移时间,并分享了技术架构和实施细节。

在过去的几个月里,Slack的CI基础设施经历了激动人心的变革。由于开发人员对Jenkins的诸多不满(从安全问题、停机时间到普遍较差的用户体验),内部压力促使我们将大部分CI任务从Jenkins迁移到GitHub Actions。

今年夏天我在Slack的实习项目涉及创建一个转换工具,能够自动将Jenkins流水线迁移到GitHub Actions,从而节省开发人员时间并加速迁移进程。该项目取得了成功,预计可将迁移时间减半,节省超过1300小时。本篇博客将重点介绍这个转换工具,以及为期7周的设计、实施和改进过程。

在开始之前,您需要了解一些术语:

  • 流水线:一组用于组织Jenkins作业的Jenkins插件。在本项目开始时,我们有242个Jenkins流水线需要迁移
  • 实例:托管Jenkins流水线的Jenkins实例。我们需要从3个Jenkins实例迁移
  • 工作流:GitHub Actions的CI作业
  • GHA:GitHub Actions的缩写

项目规划

问题思考

项目的成功标准如下:

  • 创建工具,自动化手动迁移Jenkins流水线到GHA的困难
  • 在现有Jenkins作业的大量子集上运行此工具
  • 创建关于转换过程的文档

工具选择

经过研究,我们确定了一些有用的工具来从Jenkins转换到GHA:

  • GitHub Actions导入器:GitHub发布的工具,可以审计Jenkins实例,并将所有流水线完全转换、部分转换或无法转换为GitHub Actions
  • Python脚本:预计可以使用正则表达式脚本修复导入器的部分错误
  • 大型语言模型(LLM):用于修复过于复杂而无法使用正则表达式处理的错误

解决方案

第一部分 - 导入器

在一周的时间内,我们在多个Jenkins实例上运行了导入器。该工具使用简单:从GitHub CLI安装,配置后运行类似命令:

1
gh actions-importer audit jenkins --output-dir <output-dir>

导入器会为被审计Jenkins实例上的每个Jenkins流水线输出GitHub Actions工作流文件。

运行导入器后,我们对它的性能表现感到好奇。幸运的是,导入器在完成Jenkins实例审计后会创建审计摘要。审计文件包含关于有多少Jenkins作业被完全转换、部分转换和转换失败的信息。

大约50%的情况下,导入器能够无错误地将流水线从Jenkins迁移到GHA。5%的情况下导入器完全失败,其余作业被部分导入 - 它创建了工作流YAML文件,但无法完全填充它。

第二部分 - 研究

在项目的这个阶段,我们知道导入器的工作流还有很大的改进空间。大约一半的流水线没有完全转换,因此一个重要目标是研究导致它们无法完全转换的不支持的Jenkins步骤。

在研究导入器创建的几十个YAML文件后,很明显我们需要对工作流YAML文件进行4种主要类型的修正:

  1. 用内部镜像替换受速率限制的操作
  2. 用其他操作替换操作
  3. 为最终用户添加有用的注释
  4. 删除不必要的注释

在两周的时间里,我们研究了每一项:

1 - 用内部镜像替换受速率限制的操作

投资回报率:非常高

默认情况下,如果您使用GitHub Actions市场中的任意操作,任何由该操作发出的请求都会命中github.com API。对此有速率限制。然而,命中我们自托管的GitHub Enterprise实例的操作具有更高的速率限制。因此,我们需要用我们在GitHub Enterprise实例上拥有的这些操作的内部镜像替换受速率限制的操作。

2 - 用其他操作替换操作

投资回报率:高

在某些情况下,为了执行特定任务,导入器会使用一个操作,而我们更希望它使用另一个操作。例如,对于发送Slack消息,导入器通常选择使用rtCamp/action-slack-notify操作。自然,Slack有用于发送Slack消息的内部操作,我们更愿意为此目的使用内部库。

这类任务似乎适合LLM处理,因为两个操作通常具有不同的语法,仅使用Python的字符串方法很难在它们之间转换。初步测试也显示LLM在此任务上的完美表现,这令人放心。

3 - 为最终用户添加有用的注释

投资回报率:中等

工作流文件中的某些操作项只能手动完成。例如,工作流文件中用于访问机密的语法假定机密将存储在GitHub上。然而,我们不将机密存储在GitHub上,将它们移动到GitHub既耗时又违反我们的安全策略。更好的做法是让最终用户编辑工作流文件,以便从我们存储它们的位置读取机密,而不是从GitHub读取。

4 - 删除不必要的注释

投资回报率:低至中等

回想一下,大约45%的流水线只是部分转换。这意味着导入器找不到Jenkins环境或构建步骤的GHA等效项。发生这种情况时,导入器会在工作流中留下类似"X Jenkins项目不受支持"的注释。

事实证明,许多Jenkins流水线不受支持的原因是因为该功能在GHA中已经包含或不需要。因此,大多数那些"X不受支持"的注释并不太重要,最好删除。

第三部分 - 修正工具

概述

实施的最后部分是修正工具,它将修正导入器的工作流。我们从一开始就知道该工具将平衡使用Python的字符串方法和使用LLM。从上一节注意,在4类修正工具的操作项中,唯一需要AI的是"用其他操作替换操作"。因此修正工具主要是非AI的。制作修正工具花了大约3周时间。

实施架构

以下是鸟瞰图。修正工具的输入是包含工作流yaml文件的目录路径。然后发生以下事件链:

  1. 该目录中每个yaml文件的路径被写入数组
  2. 然后,对于该数组中的每个元素:
    • 读取该路径处yaml文件的内容
    • 对这些文件内容进行每个相关的非AI修正,并写回原始文件
    • 对这些文件内容进行每个相关的AI修正,并写回原始文件

最终结果是,输入到修正工具的目录中的每个yaml文件都被原地编辑。

实施非AI修正

如前所述,对导入器的yaml文件需要完成的大部分工作归结为"在包含特定字符串的行后添加注释"或"用一个字符串替换另一个字符串"。Python的String.replace()方法在这里被广泛使用。

实施AI修正

提示工程既是科学也是艺术。它是科学,因为有一个有用的公式:

使用以下结构:

  • 上下文设置(让LLM知道任务是什么以及为什么这样做)
  • 具体步骤(几个指令,每个都是关于LLM必须完成的非常具体的单独任务。过长的提示比过于模糊的提示危险小得多)
  • 输出指令(关于LLM应如何呈现其输出的指令)

在提示的上下文设置部分,包括任何相关的语法和文档。

提示工程也是艺术,因为尝试提示并查看您对AI输出的感受也是关键部分。这个过程有几轮:

  1. 从非常基本的提示开始
  2. 查看LLM的响应
  3. 向提示添加细节和上下文以防止LLM犯任何错误

我们最终得到的一个提示示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
我将向您提供一个GitHub Actions工作流yaml文件。在文件中,使用了一个名为rtCamp/action-slack-notify@v2.2.1的操作。您的工作是用slack/message-action@v1.0操作替换每个rtCamp/action-slack-notify@v2.2.1操作,并输出结果工作流yaml文件。作为参考,我将向您提供slack/message-action@v1.0操作的语法。我将把它放在<syntax></syntax> XML标签之间:

<syntax>
- uses: slack/message-action@v1.0
  with:
   channel: channel-name
   text: text-message
</syntax>

现在,执行以下步骤:
1. 找到使用rtCamp/action-slack-notify@v2.2.1操作的每个实例
2. 用具有上述语法的slack/message-action@v1.0操作替换每个实例
3. 用rtCamp/action-slack-notify@v2.2.1操作中SLACK_CHANNEL字段的值替换channel-name
4. 用rtCamp/action-slack-notify@v2.2.1操作中SLACK_MESSAGE字段的值替换text-message

此外,有几件事您绝对不应该做:
- 不要从文件中删除任何注释
- 除了rtCamp/action-slack-notify@v2.2.1之外,不要更改文件中的任何其他操作

我现在将向您提供您将进行编辑的工作流文件,放在<workflow></workflow> XML标签之间。在提供输出时,只提供YAML文件的内容。不要描述您做了什么。不要为您的决定提供任何理由。只提供更正后的GitHub Actions yaml文件,在输出中yaml文件前后不要有任何其他内容。

我们检查了大约24个LLM的输出,并注意到令人惊讶的事情 - 执行请求任务的准确率达到100%。要求LLM进行这些修正的一个主要担忧是幻觉和其他意外的副作用,因此看到LLM的表现令人放心。

影响和最终用户反馈

影响

首先,考虑手动将流水线从Jenkins转换为GHA的过程:

  1. 参考您的Jenkins流水线,编写GHA工作流yaml文件
  2. 测试该工作流yaml并根据需要进行调试
  3. 为工作流编写文档
  4. 从其Jenkins实例中删除流水线

请注意这些步骤按难度和时间成本的降序排列。

转换工具让最终用户完全跳过第1步,并在第2步中更加轻松。最终用户获得的工作流文件几乎没有缺陷。他们可以直接跳到调试,这将加速进行,因为他们获得的工作流具有高度准确性。

该工具尝试将大约242个流水线从Jenkins转换为GitHub Actions。开发人员手动将一个Jenkins流水线转换为GHA所需的时间各不相同。以下是根据开发人员类型的一些平均估计:

  • 对Jenkins和GHA经验丰富:2小时(基本上是大部分工作日)
  • 对Jenkins经验丰富但对GHA不熟悉:5小时(这些人需要学习GHA语法,所以是大部分工作日)
  • 对两者都不熟悉:10小时(对于刚接触Jenkins和GHA的人来说,这可能需要超过一天的时间。这个数字包括其他人帮助他们的时间)

假设10%的最终用户是类型1,40%是类型2,50%是类型3,将所有流水线转换为GHA的总时间约为1700小时。

假设使用此工具,一半的流水线无需编辑即可运行(这是现实的,因为一半的流水线完全转换),另一半平均需要3小时来调试。那么将所有流水线转换为GHA将需要约360小时。

因此,该工具预计将节省超过1300小时,即将每个Jenkins流水线移动到GHA所需时间的80%。

最终评论

在撰写本文时,距离转换工具的输出发布已经过去两周,最终用户的反馈现在开始到达。很明显,Slack的开发人员发现该工具有用,但我也听说生成的工作流中存在一些错误。即使从实习一开始,我就预计会发生这种情况,但担心可能很难对这些事件进行分类。

事实证明,没有问题。我为这个转换制作了一个Canvas文档,每当工具的最终用户遇到问题时,我都会用问题的详细信息及其解决方案更新canvas。随着更多用户使用该工具,我获得了更多反馈,canvas变得更加全面。

致谢

感谢所有贡献知识并帮助这个项目取得进展的每个人。

现在比以往任何时候,Slack都需要GHA专家。这听起来像您吗?如果是这样,我们正在招聘!

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计