I'm certainly willing to believe that yaml is not the ideal answer but unless we're comparing it to a concrete alternative, I feel like this is just a "grass is always greener" type take.
I am not sure you can do this whilst having the granular job reporting (i.e. either you need one YAML block per job or you have all your jobs in one single 'status' item?) Is it actually doable?
Some do just that: dagger.io. It is not all roses but debugging is certainly easier.
I don't think anybody serious has any argument in favor of CloudFormation templates.
You write a compiler that enforces stronger invariants above and beyond everything is an array/string/list/number/pointer.
Good general-purpose programming languages provide type systems that do just this. It is criminal that the industry simply ignores this and chooses to use blobs of YAML/JSON/XML with disastrous results---creating ad-hoc programming languages without a typesystem in their chosen poison.
YAML is used for the declarative part of structuring the job graph. The host (in this case, GitHub) would need to call into your code to build the job graph. Which means it would need to compile your code, which means it needs its own build step. This means it would need to run on a build machine that uses minutes because GitHub is not going to just run arbitrary code for free.
There's no guarantee that your arbitrary language is thread safe or idempotent so it can't really run in parallel like how a declarative file could be used.
So now you're in a situation where you add another spin up and tear down step even if your actual graph gen call is zero cost.
There's a reason it works the way it does.
https://www.reddit.com/r/funny/comments/eccj2/how_to_draw_an...
Note: mostly using Deno these days for this, though I will use .net/grate for db projects.
When something is written in a real programming language (that doesn't just compile down to YAML or some other data format), this becomes much more challenging. What should you do in that case? Attempt to parse the configuration into an AST and operate over the AST? But in many programming languages, the AST can become arbitrarily complex. Behavior can be implemented in such a way as to make it difficult to discover or introspect.
Of course, YAML can also become difficult to parse too. If the system consuming the YAML supports in-band signalling -- i.e. proprietary non-YAML directives -- then you would need to first normalize the YAML using that system to interpret and expand those signals. But in principal, that's still at least more tractable than trying to parse an AST.
cough CloudFormation cough
There are multiple ways to safely run untrusted code.
I for one enjoy how build.rs in rust does it: you have a rust code that controls the entire build system by just printing stuffs on stdout.
There are other ways of course
In any case, regardless whatever clever method you try to use, even if you're successful, it's not as straightforward and easily understood and extensible as OPA policy. Let's say you succeed in governing Rust code. OK, but now I have developers who are writing in Python and Java and TypeScript. What now? Develop a new, customized solution for each one? No thanks
You already don't have to use YAML. Use whatever language you want to define the configuration, and then dump it as YAML. By using your own language and outputting YAML, you get to implement any solution you want, and GitHub gets to spend more cycles building features.
Simple example:
1. Create a couple inherited Python classes
2. Write class functions to enable/disable GHA features and validate them
3. Have the functions store data in the class object
4. Use a library to output the class as YAML
5. Now craft your GHA config by simply calling a Python object
6. Run code, save output file, apply to your repo
I don't know why nobody has made this yet, but it wouldn't be hard. Read GHA docs, write Python classes to match, output as YAML.If you want more than GHA features support [via configuration], use the GHA API (https://docs.github.com/en/rest/actions) or scripted workflows feature (https://github.com/actions/github-script).
There are existing solutions around, but do miss out a bunch of things that are blatantly missing in the space:
- workflow visualisations (this is already working - you can see an example of workflow relationship and breakdowns on a non-trivial example at https://github.com/http4k/http4k/tree/master/.github/typeflo...);
- running workflows through an event simulator so you can tell cause and effect when it comes to what triggers what. Testing workflows anyone? :)
- security testing on workflows - to avoid the many footguns that there are in GHA around secrets etc;
- compliance tests around permitted Action versions;
- publishing of reusable repository files as binary dependencies that can be upgraded and compiled into your projects - including not just GHA actions and workflows but also things like version files, composable Copilot/Claude/Cursor instruction files;
- GitLab, CircleCI, Bitbucket, Azure DevOps support using the same approach and in multiple languages;
Early days yet, but am planning to make it free for OSS and paid for commercial users. I'm also dogfooding it on one of my other open source projects so to make sure that it can handle non-trivial cases. Lots to do - and hopefully it will be valuable enough for commercial companies to pay for!
Wish me luck!
Here's some fun examples to see why HCL sucks:
- Create an if/elseif/else statement
- Do anything remotely complex with a for loop (tip: you're probably going to have to use `flatten` a lot)
https://ant-contrib.sourceforge.net/tasks/tasks/if.html
<if>
<equals arg1="${foo}" arg2="bar" />
<then>
<echo message="The value of property foo is 'bar'" />
</then>
<elseif>
<equals arg1="${foo}" arg2="foo" />
<then>
<echo message="The value of property foo is 'foo'" />
</then>
</elseif>
<else>
<echo message="The value of property foo is not 'foo' or 'bar'" />
</else>
</if>https://ant-contrib.sourceforge.net/tasks/tasks/for.html
<for param="file">
<path>
<fileset dir="${test.dir}/mains" includes="*.cpp"/>
</path>
<sequential>
<propertyregex override="yes"
property="program" input="@{file}"
regexp=".*/([^\.]\*)\.cpp" replace="\1"/>
<mkdir dir="${obj.dir}/${program}"/>
<mkdir dir="${build.bin.dir}"/>
<cc link="executable" objdir="${obj.dir}/${program}"
outfile="${build.bin.dir}/${program}">
<compiler refid="compiler.options"/>
<fileset file="@{file}"/>
<linker refid="linker-libs"/>
</cc>
</sequential>
</for>
Yes, programming with them was as fun as you're imagining. (if (equals foo "bar")
(then (echo "The value of property foo is 'bar'"))
(elseif (equals foo "foo")
(then (echo "The value of property foo is 'bar'")))
(else (echo "The value of property foo is not 'foo' or 'bar'")))
That’s not a Lisp-like language I particularly like, but it’s not flat-out insane like Ant appears to be.Advocates for the inappropriate use of XML (basically, anywhere it was used as anything other than a markup language) have lot to answer for.
It takes your programming language version and turns it into github actions yaml, so you dont need to do any of that sort of thing.
Could I possibly ask you to reply with the model of your phone so can make sure it works ok after have fixed?
You're blog looks just perfect to me, and loads fine even on my extremely slow Redmi A5. And even the code examples look fine.
So sorry! I must've accidentally written my comment on the wrong page.
The post I thought I was responding to showed a website with an animated "breathing" background which was completely locking up my phone.
Edit: Just to drive this point home, if you're the author https://blog.yossarian.net you've made a perfect looking blog and I wouldn't change a thing.
I like the things I depend on to actually have a funding model, so that's actually more appealing to me than something fully free.
- workflow visualisations (this is already working - you can see an example of workflow relationship and breakdowns on a non-trivial example at https://github.com/http4k/http4k/tree/master/.github/typeflo...);
- running workflows through an event simulator so you can tell cause and effect when it comes to what triggers what; - security testing on workflows - to avoid the many footguns that there are in GHA around secrets etc;
- compliance tests around permitted Action versions;
- publishing of reusable repository files as binary dependencies that can be upgraded and compiled into your projects - including not just GHA actions and workflows but also things like version files, composable Copilot/Claude/Cursor instruction files;
- GitLab, CircleCI, Bitbucket, Azure DevOps support using the same approach and in multiple languages;
Lots to do - and hopefully it will be valuable enough for commercial companies to pay for!
:)
I really enjoyed working with the Earthfile format[1] used for Earthly CI, which unfortunately seems like a dead end now. It's a mix of Dockerfile and Makefile, which made it made very familiar to read and write. Best of all, it allowed running the pipeline locally exactly as it would run remotely, which made development and troubleshooting so much easier. The fact GH Actions doesn't have something equivalent is awful UX[2].
Honestly, I wish the industry hadn't settled on GitHub and GH Actions. We need better tooling and better stewards of open source than a giant corporation who has historically been hostile to open source.
[1]: https://earthly.dev/earthfile
[2]: Yes, I'm aware of `act`, but I've had nothing but issues with it.
That is the key function any serious CI platform needs to tackle to get me interested. FORCE me to write something that can run locally. I'll accept using containers, or maybe even VMs, but make sure that whatever I build for your server ALSO runs on my machine.
I absolutely detest working on GitHub Actions because all too often it ends up requiring that I create a new repo where I can commit to master (because for some reason everybody loves writing actions that only work on master). Which means I have to move all the fucking secrets too.
Solve that for me PLEASE. Don't give me more YAML features.
Working with ADO pipelines is painful.
- Make change locally
- Push change
- Run pipeline
- Wait forever because ADO is slow
- Debug the error caused by some syntax issue in their bastardized version of yaml
- Repeat
I've seen few thousands-line YAML files with anchors riddled all over the place. It was impossible to deal with. Rewriting it in Jsonnet paid off immediately.
Another example is Nixpkgs. It's quite pleasant to deal with despite the size of its codebase.
Jokes aside, I like proper yaml anchors. Other CI's do support these and it made writing yaml actions much easier, esp. complicated cross-building recipes with containers and qemu.
I say this as someone that built entire Jenkins Groovy frameworks for automating large Jenkins setups (think hundreds of nodes, thousands of Jenkins jobs, stuff like that).
Although, I think it is generally an accepted practice to use declarative configuration over imperative configuration? In part, maybe what the article is getting at, maybe?
We write Bash or Python, and our tool will produce the YAML pipeline reflecting it.
So we dont need to maintain YAML with over-complicated format.
The resulting YAML is not meant to be read by an actual human since its absolute garbage, but the code we want to run is running when we want, without having to maintain the YAML.
And we can easily test it locally.
Honestly, just having a linter should be enough. Ideally, anything complicated in your build should just be put into a script anyways - it minimizes the amount of lines in that massive YAML file and the potential for merge conflicts when making small changes.
I use CUE to read yamhell too
It's the workflow for developing pipelines that's the problem. If I had something I could run locally - even in a debug dry-run only form that would go a long way to debugging job dependencies, etc. Testing failure cases flow conditional logic in the expected manner etc.
Language implementations for yaml vary _wildly_.
What does the following parse as:
some_map:
key: value
no: cap
If I google "yaml online" and paste it in, one gives me:{'some_map': {False: 'cap', 'key': 'value'}}
The other gives me:
{'some_map': {'false': 'cap', 'key': 'value'}}
... and neither gives what a human probably intended, huh?
Most notably it only offers three base types (scalar string, array, object) and moves the work of parsing values to stronger types (such as int8 or boolean) to your codebase where you tend to wrap values parsed from YAML into other types anyway.
Less surprises and headaches, but very niche, unfortunately.
Plus it has exactly enough convenience-feature-related sharp edges to be risky to hand to a newbie, while wearing the dress of something that should be too bog-simple to have that problem. I, too, enjoy languages that arbitrarily decide the Norwegian TLD is actually a Boolean "false."
GitHub Actions have a lot of rules, logic and multiple sublanguages in lots of places (e.g. conditions, shell scripts, etc.) YAML is completely superficial, XML would be an improvement due to less whitespace sensitivity alone.