GitHub Actions:YAML 编程荒原中的新希望

2025-06-09

GitHub Actions:YAML 编程荒原中的新希望

程序员最大的超能力在于能够自动化几乎所有事情。这就是 GitHub Actions 的优势所在……前提是你能自信地YAML编写一个GitHub 工作流来解决你的特定问题。这还只是个假设。但即使你不能,也还有希望

让我向你介绍一个由 Piotr Krzemiński 发起的开源项目,过去几个月我为该项目做出了很大贡献👨🏻‍💻

GitHub 徽标 typesafegithub / github-workflows-kt

使用 Kotlin 编写 GitHub Actions 工作流。你再也不需要用 YAML 了!

徽标白色 徽标白色

Maven 中心 超棒的 Kotlin 徽章 GitHub Repo 星标 执照 github-workflows-kt@kotlinlang.slack.com

github-workflows-kt是一个在类型安全脚本中生成GitHub Actions 工作流YAML 文件工具,可帮助您使用 Kotlin 愉快地为您的 GitHub 项目构建稳健工作流,而不会出现错误

您不会再回到 YAML!

💡 想法

我们经常被 YAML 配置所包围。它是一种功能强大的格式,提供了用于定义分层数据的简单语法,但有时它会被(滥用?)用于配置复杂的场景,从而导致文件复杂,难以编写和维护。

我们当中谁没有不小心使用了错误的缩进,错过了提取可重用代码的机会,或者被模糊的类型弄糊涂过?在这些情况下,通用编程语言的强大功能就会派上用场。

我们正在开发github-workflows-kt来解决这些问题和其他问题,以便您可以放心地创建 GitHub 工作流。

✨ 好处

  • 没有缩进混淆——Kotlin 的语法不依赖于它
  • 立即……

什么意思github-actions-kotlin-dsl

什么是 GitHub Action KTS

github-actions-kotlin-dsl(*) 是一个库,允许您以类型安全的方式为 Kotlin Actions 生成 GitHub 工作流。

(*) 我知道这有点拗口。我个人称之为github-actions.kts

1) 在免费且出色的 IntelliJ IDEA 社区版中编辑脚本
2) 将脚本放入文件夹中.github/workflows,使其可执行
3)安装 Kotlin
4)github-actions-kotlin-dsl是用于构建 GitHub Workflow 对象的库
5) 最后,调用workflow.writeToFile()
6) 生成.github/workflows/$NAME$.yaml可在 GitHub Actions 上运行的脚本。

您可以看到蓝色下划线表示 YAML 工作流的主要组件具有直接的 Kotlin 等效项:workflow() Push() job() uses() run() GithubActionNameVxxx()

要实际运行您的第一个 Kotlin 工作流,请阅读友好的文档

此时,您可能会问自己:

等等,但是为什么🤔?

GitHub Actions 是一项很棒的服务,但是......

图像

程序员的主要超能力是能够自动化几乎所有事情。

因为这正是 GitHub Actions 的用途,所以一切都很好,对吧?

不幸的是,大多数人都很难编写 GitHub 工作流程来解决他们的特定问题:这是因为 YAML 是一种糟糕的编程语言 - 见下文。

因此,我们转而采用复制/粘贴编程,试图找到已经花时间编写工作流程来解决与我们类似的问题的人。

别误会,复制/粘贴编程总比没有好,我已经为 Java/Kotlin 开发人员编写了一份指南,帮助他们快速启动并运行 CI

但如果我们不能自动化解决特定问题,我们就会错失良机。

那么问题是什么?

Github 的 YAML 是一种糟糕的编程语言

截图于 2022-07-08 16:47:03

普通的 YAML 开发人员试图完成任务,但每次迭代之间都会浪费十分钟或更长时间。

一般来说,YAML 只是编写 JSON 的另一种方式。

有一系列不同的问题...

但 GitHub 按照Duck Typing的原理,把 YAML 扩展成了我们应该认可的编程语言

* 🐥 GitHub YAML 作为编程语言:🐥 *

  • 它允许编写任意脚本
  • 它在 Azure 上的任意 VM 上运行
  • ...具有任意数量的作业和命令
  • ...用任意语言编写的大量插件,可以以各种方式进行配置
  • ...带有变量和秘密
  • ...使用 for 循环 - 又称策略矩阵
  • ...使用布尔逻辑 - 仅当前一个作业成功时才运行此作业,仅在出现错误时运行此操作
  • ...使用强大的 GitHib 表达式 - 又称为eval函数
  • ...

如果它走路像鸭子、叫声也像鸭子,那它一定是只鸭子。

这里的问题是 YAML 从来没有为此设计过

因此,IDE 的支持相当差。他们确实会尽力提供帮助,但远不如真正的编程语言。YAML 看起来比 JSON 更花哨,是因为它的规则不那么简单,但这意味着你更容易出错。

最重要的是:

GitHub Action 的编辑-编译-运行反馈循环非常慢

您在上图中看不到的是,GitHub Actions YAML 开发人员可能在脚本的每次迭代中浪费 10 分钟或更长时间!

那么,有没有办法走出 YAML 荒地呢?

类型安全的工作流

在 YAML 中,不正确的语法看起来很像正确的语法。

on:
  cron: '7 42 * * 7'
  schedule: '7 42 * * 7'  
  cron:
    schedule: '7 42 * * 7'
  schedule:
    cron:  '7 42 * * 7'    
  schedule:  
   - cron: '7 42 * * 7'
Enter fullscreen mode Exit fullscreen mode

哪种语法是正确的?

截图于 2022-07-09 16:01:56

这次没有争论了。类型系统的存在本身就让各种小错误消失了!

该库还保证其生成的所有内容都是有效的 YAML。无需再等待 CI 生成MalformedYamlError...

此外,还有一些运行时检查,它会立即且快速地失败,而不是稍后缓慢地失败。

name: Hello: World!  
on:  
  pr:  
  schedule:  
   - cron: '7 42 * * 7'  
jobs:  
  "git checkout":  
    runs-on: "ubuntu_latest"  
    steps:  
      - id: step-0  
        name: First: git checkout
        uses: actions/checkout@v3
Enter fullscreen mode Exit fullscreen mode

你能找到上述工作流程中的 7 个错误吗?

让我们看一下 Kotlin 的对应代码

val workflow = workflow(  
    name = "Hello: World",  // 1. runtime error: semicolon not allowed
    on = listOf(  
        Schedule(listOf(
            // 2. named arguments 07:42 instead of 42:07 in the YAML
            // 3. runtime error: Field 'dayWeek' outside of range 0..6
            Cron(minute = "42", hour = "7", dayWeek = "7"))
        ),  
        PR(), // 4. compile error: should be PullRequest() 
    ),  
    sourceFile = __FILE__.toPath(),  
) {
    job(
    id = "git checkout",  // 5. runtime error: space not allowed in a job id
    runsOn = ubuntu_latest // 6. compile error: should be UbuntuLatest
    ) {  
        // 7. runtime error: semicolon not allowed here
        uses(name = "First: Check out", action = CheckoutV3())  
    }  
}
Enter fullscreen mode Exit fullscreen mode

只需编译和运行脚本,我们就可以避免大量小错误,这些错误会浪费我们在 CI 上的每 10 分钟时间。

类型安全操作

看看这个动作是如何配置的:

steps:  
  - id: step-0  
    name: git check out  
    uses: actions/checkout@v4  
    with:  
      branch: main  
      fetch-depth: 0
Enter fullscreen mode Exit fullscreen mode

这里有三个问题:

  • branch看起来像一个有效参数,但实际上不是
  • 我不知道魔法值是什么fetch-depth意思
  • v4 尚不存在

我们来看看 Kotlin 版本:

截图于 2022-07-08 16:00:32

  • 在这里我们清楚地看到这branch是错误的,自动完成功能告诉我们ref使用
  • F1: Documentation允许我们查看文档,并且特殊值有一个名称。
  • CheckoutV4甚至无法编译

在我写下这些文字的时候,我们已经支持 89 个动作包装器

类型安全表达式

GitHub Actions 还具有强大的表达式语法。

我想你现在明白了:我们也为它们提供了一种类型安全的替代方案:

更多信息请阅读:https://krzema12.github.io/github-actions-kotlin-dsl/user-guide/type-safe-expressions/

自动迁移您现有的工作流程

现在我知道你在想什么了:

这太酷了,我刚开始的时候就希望有这个功能。
但现在我有一个可以运行的 YAML 工作流,
我不想从头开始。

我理解你的想法。
我也有同样的担忧。

然后一个邪恶的想法在我脑海里诞生了:

好的,所以我们可以编写一个 Kotlin 脚本来生成它的 YAML 版本。
但是,我们能否利用现有的 YAML 来生成一个可以自行生成的 Kotlin 脚本呢?这有点像孩子生下父母一样。

事实证明这是可行的,经过一番深入的思考,脚本生成器诞生了。它允许您自动迁移到 Kotlin。

你可以运行如下命令:

$ ./gradlew :script-generator:run --args /path/to/.github/workflows/build.yaml
Kotlin script written to build.main.kts 
Run it with: ./build.main.kts 
The resulting YAML file with be available at build.yaml
Enter fullscreen mode Exit fullscreen mode

然后,您可以通过使用https://yamldiff.com/进行语义差异来确保新的 YAML 与旧的 YAML 等效

我不会详细介绍:请阅读友好的文档

幕后:包装器生成器

当我发现这个项目时,图书馆支持的活动可能有十几个。

我主要关心的是:如何扩大规模?

GitHub Actions 有几十个,而且它们总是在发布新版本,添加或弃用参数。

答案:维护者 Piotr Krzemiński 和我引入了动作包装器的自动生成

  • 包装器生成器...采取如下操作Vampire/setup-wsl@1
  • 它下载其action.yml文件(GitHub 要求),其中包含所有输入和输出参数及其描述
  • 从这两个文件中,我们为该操作生成一个包装器,即文件vampire.SetupWslV1
  • 这次生成是使用KotlinPoet库完成的。这也是上一段中脚本生成器的动力所在。

创建这个包装器生成器需要做很多工作,但它是值得的:

在我写下这些文字的时候,我们已经支持 89 个动作包装器

幕后:GitHub Actions

另一件让我们能够在有限时间内应对挑战的事情是:GitHub Actions 本身

我们是我们库的首批用户,并利用了 GitHub Actions 的强大功能:

  • 检查是否需要更新一些现有的包装器(例如新参数)。
  • 检查现有包装器(V2 到 V3)是否有新的主要版本可用。
  • 自动发布我们的库。
  • 作为我们 PR 上的 CI

对于那些有动力的人来说,我们的行动可以在以下网址找到:https://github.com/krzema12/github-actions-kotlin-dsl/tree/main/.github/workflows

这确实让我深有体会:如果你有信心可以编写一个工作流程来解决你的特定问题,那么 GitHub Actions 将会是一笔多么宝贵的资产。

常问问题

如果我不知道 Kotlin 怎么办?

我认为编程语言的选择并不重要,只要它不是 Bash 就行。我的意思是,任何具有良好 IDE 支持的现代编程语言都可以胜任这项任务。

我们这里只做了一些基本的事情。我们Workflow通过调用workflow()then jobthenrun("my command")uses(MyActionWithParameters(....))

唯一的问题是您需要安装IntelliJ IDEA 社区版,但它是免费且非常棒。

有什么缺点吗?

如果您在提交之前未运行 Kotlin 脚本,或者忘记提交 YAML 文件,或者直接编辑 YAML 文件,YAML 和 Kotlin 代码将不同步。我们提供了一致性检查,以便在出现这种情况时提前失败。只需使用 即可workflow.writeToFile(addConsistencyCheck = true)。代价是持续集成 (CI) 速度会慢 15 秒左右。

GitHub 的 YAML 功能众多,您可能会遇到一些库尚未支持的功能。我们提供了一个类型不安全的替代方案来弥补这些缺陷。您也可以查看未解决的问题或创建自己的问题。

IntelliJ 不支持 Kotlin 脚本,但它支持常规的 Kotlin 编程。希望这种情况能随着时间的推移得到改善。

编译和运行 Kotlin 脚本比你习惯的 Python 或 JavaScript 等解释型语言要慢。但它仍然比在 CI 上提交、推送并等待 10 分钟要快两个数量级。

如果你在团队中使用它,你必须确保你的同事明白这个 YAML 文件是由 Kotlin 脚本生成的。请注意,第一行 YAML 代码如下:# This file was generated using Kotlin DSL (.github/workflows/hello_world_workflow.main.kts).

欢迎您向他们发送我的文章链接:)

我是 GitHub Action 维护者,我可以帮忙吗?

是的!

规范中缺少的action.yml是每个输入参数的类型。我们目前正在将这些信息添加到我们的代码库中,action-types.yml但如果能像一些优秀的早期采用者那样,将其放在您自己的代码库文件中,那就更好了

请参阅https://github.com/krzema12/github-actions-typing

链接

本文的标题取自 Michel Pardo 的演讲《Kotlin:Java 6 Wasteland 中的新希望》,该演讲当年曾让人想转向 Kotlin。

完结

朋友们,就这些了。非常感谢你们让我走到今天。

我特别高兴能够回来写文章,这是我近一年来一直没能做到的事情。

如果您尝试一下,我很乐意听到您的反馈。

鏂囩珷鏉ユ簮锛�https://dev.to/jmfayard/github-actions-a-new-hope-in​​-yaml-wasteland-1i9c
PREV
为什么你打开了这么多标签页?我和五岁侄子的对话几乎是虚构的
NEXT
Android 的十亿美元错误 十亿美元错误引言 Android 的十亿美元错误 💣 LeakCanary 🐤 Android 的 Burrito 设计模式是怎么诞生的?它的文档和示例至今仍然很糟糕 GenAI LIVE!| 2025 年 6 月 4 日