我非常痛恨 GitHub Actions。
I Hate GitHub Actions with Passion

原始链接: https://xlii.space/eng/i-hate-github-actions-with-passion/

## GitHub Actions:开发者的沮丧 作者表达了对 GitHub Actions 的强烈沮丧,甚至超过了对之前厌恶的技术(如旧版本的 PHP)。核心问题在于他们的项目 `tmplr`(一个模板/脚手架工具)的 CI 构建失败,原因是难以在不同目标平台(Linux ARM、macOS ARM、Linux x86_64、macOS x86_64)上安装 CUE 二进制文件。 尽管 CUE 在大多数平台上都能成功安装和运行,但 Linux ARM 构建始终以“命令未找到”错误告终——这是由于 GitHub Actions 的隔离跨构建环境将 x86_64 二进制文件隐藏在 ARM 运行器之外所致。这引发了一个重复的、耗时的 CI 配置更改和重复构建尝试的调试循环(每次更改需要 2-3 分钟)。 最终,作者通过从 `build.rs` 中移除构建逻辑并将其移动到 GNU Makefile,然后由 GitHub Actions 直接调用来解决了这个问题。他们提倡将构建逻辑保留在 GitHub Actions 的 YAML 配置之外,以避免类似的麻烦,并得出结论,虽然 Actions 提供了易于构建 macOS 的好处,但它通常会带来大量的调试时间和开发者精神损失。

一个 Hacker News 的讨论显示出对 GitHub Actions 的强烈不满。发起者表达了强烈的厌恶之情,引发了评论者们的共鸣,他们认为 Actions 最适合用于简单的代码检出和执行本地测试过的脚本,而不是用于复杂的流程逻辑。 许多人认为理想的 Action 应该只是运行一个用户定义的单个命令。 几位用户指出 “act” (https://github.com/nektos/act) 作为一种潜在的解决方案,它提供了一个本地运行器来复制 Actions 环境,以进行更可靠的测试。 一个关键的结论是,人们渴望获得更大的控制权和可预测性,开发者们提倡将核心逻辑*置于* GitHub Actions 流程定义之外。 一些人指出,并非所有开发者都能直观地理解这种最佳实践。
相关文章

原文

I can’t overstate how much I hate GitHub Actions. I don’t even remember hating any other piece of technology I used. Sure, I still make fun of PHP that I remember from times of PHP4, but even then I didn’t hate it. Merely I found it subpar technology to other emerging at the time (like Ruby on Rails or Django). And yet I hate GitHub Actions.

With Passion.

Road to Hell

Day before writing these words I was implementing build.rs for my tmplr project. To save you a click - it is a file/project scaffold tool with human readable (and craftable) template files. I (personally) use it very often, given how easy it is to craft new templates, by hand or with aid of the tool, so check it out if you need a similar tool.

The build.rs used CUE to generate README.md, CHANGELOG.md and also a version/help file to guarantee consistency. It was fun thing to do, it took approx. 1.5h and I even wrote an article about it. For myself and future generations.

I was happy with the results and didn’t check CI output which, quite unsurprisingly, failed. I was using cue binary inside build.rs and without it build simply couldn’t progress. When I woke up next day and saw e-mail from CI notifying me about failed build I immediatelly knew my day isn’t going to start with puppies and rainbows.

It took couple attempts to search and push GitHub Action that would install CUE and then I got the worst of the worst results: One system in matrix failing to build.

A word of explanation. I’m building tmplr for 4 platforms:

  • Linux ARM
  • macOS ARM
  • Linux x86_64
  • macOS x86_64

Makes sense, right? Even though my user base can be counted on a fingers of one-arm-less and second-arm-hook-equipped pirate, it’s still a thing “One Should Do”.

And with all that - Linux ARM failed with “command can’t be found”. CUE installed and ran nicely for all other 3 targets, but for some reason it failed for Linux ARM.

In case you don’t care about why I hate GitHub but your mind started to wonder to “what went wrong” let me tell you; because I know.

So supposedly cross build that happens in matrix is heavily isolated. When I install CUE I install it only on x86_64 Linux host and macOS ARM host. macOS has zero issues running x86_64 binary and no issues are raised when Linux x86_64 tries to run x86_64 binary. But GitHub Actions is nice enough to hide x86_64 binary from arm64 runner, so that it won’t break.

Thank you GitHub Actions. What would’ve I done without you.

Broken Loop

And so my least favorite feedback loop started and went like this:

  1. Search for possible fix
  2. Change ci.yml
  3. jj squash --ignore-immutable && jj git push
  4. Open “Actions” tab
  5. Open latest run
  6. Open Linux ARM run
  7. Wait couple of seconds
  8. Hate Life
  9. Offer the Universe choice words it won’t soon forget
  10. Rinse & repeat

I got quite efficient when it comes to points 8 and 9 but otherwise the whole loop still took around 2-3 minutes to execute.

FOR. A. SINGLE. CHANGE.

Yes. For a single change. Like having an editor with 2 minute save lag, pushing commit using program running on cassette tapes or playing chess over snail-mail. It’s 2026 for Pete’s sake, and we won’t tolerate this behavior!

Now of course, in some Perfect World, GitHub could have a local runner with all the bells and whistles. Or maybe something that would allow me to quickly check for progress upon the push or even something like a “scratch commit”, i.e. a way that I could testbed different runs without polluting history of both Git and Action runs.

But no such perfect world exists and one is at the whim of heartless YAML-based system.

Breaking off

I suffered only 30 minutes of such loops. Could’ve done it for longer but I was out of colorful language to use and felt without it the process just isn’t the same.

There is a wise saying in the internet that goes like:

For the love of all that is holy, don’t let GitHub Actions manage your logic. Keep your scripts under your own damn control and just make the Actions call them!

This is what everyone should do. This is what I did.

I deleted build.rs (with a sliver of sadness because it was really nice - but sacrifices had to be made). I moved all the generation from build.rs to GNU Makefile, committed the darn files into repository, reverted changes to CI and called it a day. Problem solved.

Exit Code: 0

GitHub Actions, Friends & Gentlefolk, is the reason why we can’t have (some) nice things. I can’t count how many hours I’ve lost debugging the runners or trying to optimize the build process. It’s a sorry process every single time, a time that would be better spent elsewhere.

And yet there are some benefits, like macOS builds that would be quite hard to get otherwise. I don’t know any other system that would be easier to setup than GitHub Actions (if you know one, let me know) but it seems there’s no escape.

We are all doomed to GitHub Actions.

…but at least I dodged the bullet early.

联系我们 contact @ memedata.com