I hate Github Actions with Passion
Source: Dev.to
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 the times of PHP 41, but even then I didn’t hate it—I merely found it sub‑par compared to emerging frameworks like Ruby on Rails or Django. And yet I hate GitHub Actions.
With Passion2.

Road to Hell
The 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 the aid of the tool, so check it out if you need a similar utility.
The build.rs used CUE to generate README.md, CHANGELOG.md, and a version/help file to guarantee consistency. It was a fun thing to do; it took approximately 1.5 h and I even wrote an article about it—for myself and future generations.
I was happy with the results and didn’t check the CI output, which, quite unsurprisingly, failed. I was using the cue binary inside build.rs, and without it the build simply couldn’t progress. When I woke up the next day and saw an e‑mail from CI notifying me about the failed build, I immediately knew my day wasn’t going to start with puppies and rainbows.
It took a couple of attempts to search for and push a GitHub Action that would install CUE, and then I got the worst of the worst results: one system in the matrix failing to build.
A word of explanation
I’m building tmplr for four platforms:
- Linux ARM
- macOS ARM
- Linux x86_64
- macOS x86_64
Makes sense, right? Even though my user base can be counted on the fingers of a one‑arm‑less, second‑arm‑hook‑equipped pirate, it’s still a thing “One Should Do”.
With all that, the Linux ARM job failed with “command can’t be found”. CUE installed and ran nicely for the other three targets, but for some reason it failed on Linux ARM.
In case you don’t care why I hate GitHub but your mind started to wonder “what went wrong”, let me tell you—because I know.
The cross‑build that happens in the matrix is heavily isolated. When I install CUE I install it only on the x86_64 Linux host and the macOS ARM host. macOS has zero issues running an x86_64 binary, and no issues are raised when Linux x86_64 tries to run an x86_64 binary. But GitHub Actions is nice enough to hide the x86_64 binary from the arm64 runner, so that it won’t break.
Thank you, GitHub Actions. What would I have done without you?
Broken Loop
And so my least‑favorite feedback loop started and went like this:
- Search for a possible fix
- Change
ci.yml jj squash --ignore-immutable && jj git push3- Open the Actions tab
- Open the latest run
- Open the Linux ARM run
- Wait a couple of seconds
- Hate life
- Offer the universe choice words it won’t soon forget
- Rinse & repeat
I got quite efficient at points 8 and 9, but 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 a 2‑minute save lag, pushing a commit using a program running on cassette tapes4, or playing chess over snail‑mail. It’s 2026, for Pete’s sake, and we5 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 something that would allow me to quickly check progress upon push6, or even a “scratch commit”—a way to test different runs without polluting the history of both Git and Action runs.
But no such perfect world exists, and we are at the whim of a heartless YAML‑based system.
Breaking Off
I suffered only 30 minutes of such loops. I could have done it longer, but I ran out of colorful language, and without it the process just isn’t the same.
There is a wise saying on the internet:
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 a GNU Makefile, committed the new files into the repository, reverted the CI changes, and called it a day. Problem solved.
Exit Code: 0
GitHub Actions, friends, and gentlefolk, are the reason why we can’t have (some) nice things. I can’t count how many hours I’ve lost debugging runners or trying to optimise 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 set up 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.
Footnotes
Footnotes
-
PHP 4 era. ↩
-
“With Passion” is a reference to the song Passion by [artist]. ↩
-
jjis a fictional version‑control helper used for illustration. ↩ -
A tongue‑in‑cheek reference to extremely slow commit tools. ↩
-
“We” refers to the modern developer community. ↩
-
Immediate feedback mechanisms such as “scratch commits” or “preview runs”. ↩
