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 PHP41, 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 Passion2.
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:
- Search for possible fix
- Change
ci.yml jj squash --ignore-immutable && jj git push3- Open “Actions” tab
- Open latest run
- Open Linux ARM run
- Wait couple of seconds
- Hate Life
- Offer the Universe choice words it won’t soon forget
- 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 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 maybe something that would allow me to quickly check for progress upon the push6 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.