Cicada – Open-source cross-platform version of GitHub Actions and Gitlab CI
github.com
github.com
The config language is nice, but honestly I've never been bothered by YAML for pipelines. I want my pipelines to be declarative anyway. I'm a little disappointed that Cicada didn't go with one of the existing options such as Jsonnet, Cue, Dhall, etc.
Lastly, there's a feature I've always found useful and in many cases necessary, that many CI systems don't have – enforced serial execution. When you're doing continuous delivery it's often critical to make sure that only one release is going out at a time and that the version being released strictly increases. I've seen outages because of release jobs racing and an unintended downgrade happening. This requires CI system support to achieve. Last time I checked, Jenkins, Concourse, and GitHub Actions all provided mechanisms for this, Semaphore may have as well. Circle and GitLab did not (the former after endless discussion with our account manager over it!) and I found it hard to trust the platforms as a result. Cicada does not appear to have this, which is a shame. It suggests to me a lack of hands-on production experience with continuous delivery. Arguably this is not a CD system, but there's no reason why a CI system shouldn't be CD as well, it's not until a much larger team/product size that a dedicated CD system becomes truly necessary and they take a lot of work to set up well.
The first is merge trains, which merges requests one by one to prevent this exact outcome. You just have to force all deployments to be done via a merge request. That’s the downside.
The second being forcing a GitLab Runner to run one job at a time. Tag it as “deployer” then ensure all deployment jobs are marked as “deployer”. That runner will pick up deployment jobs one by one in order of first creation.
Having just one runner be the deployer is an option too. I think we used hosted runners so not sure if this is possible in that setup? This would also make pipelines harder to optimise. Often there are many parts in pipelines that are safe to do in parallel, and only a few "critical sections" around which you want locking. This would solve simultaneous releases, but not the general case of the problem (which at least Jenkins and GitHub Actions manage ok).
GitLab always felt to me like Travis++, whereas systems developed later felt like they were built on fundamentally better primitives. Jenkins is a weird one because it has all of the features, can do all of these things, really quite well in many cases, but has a pretty bad developer experience and required a lot of maintenance to run a performant and secure install.
[1] https://docs.gitlab.com/ee/ci/resource_groups/index.html
If you go with oldest first and two or more prod jobs want to run, you are gonna have to wait for all the old deployments to finish unless you go to each one and cancel them, leaving only the latest one. This does prevent unorderd deployments from overwriting each other, but it's a pita.
If you go with newest first, old deployments will overwrite new ones unless you toggle the setting to prevent outdated jobs from running, by which case you'll be locked out of doing a rollback unless you regenerate a whole old pipeline.
It would make sense if they had a "newest only", where if you have one deployment running and 10 pending, by the time the current deployment finishes, gitlab would cancel all deployments except for the latest one. This way you don't have to wait for old deployments and you're free to do a rollback at any time. Bonus points if the cancelled jobs display a link to the latest job that was chosen from the queue.
Hi, GitLab Team member here. This is exactly how GitLab functions when you use "newest first" process mode [1] with Prevent outdated deployment jobs enabled [2]. By default, pipeline job retries for deployment rollback is enabled, you can rollback to any of the failed old deployments, except where its disabled. [3]
[1] https://docs.gitlab.com/ee/ci/resource_groups/#process-modes
[2] https://docs.gitlab.com/ee/ci/environments/deployment_safety...
[3] https://docs.gitlab.com/ee/ci/environments/deployment_safety...
That's amazing, I always had this problem in my head about how to solve people stepping on each other's toes with high volume CD workflows.
This should solve it nicely, thanks again!
Is there any CI system that allows one to write declarative pipelines? What would that even mean? GitHub Actions, GitLab Pipelines, etc. are all effectively just shell scripts disguised as more or less verbose yaml, with some trigger conditions added.
I guess nix's hydra is the closest to a declarative CI system that exists, because nix does the hard work of abstracting the imperative build steps into a declarative interface. Even then, if you want to do anything outside of nix derivations you would be writing something imperative.
Sure, if you design your scripts carefully (e.g. make them idempotent, reproducible, make all inputs explicit, etc.) you can build an abstraction to what you want to achieve which can then be used declaratively in the yaml flavor of choice. I think that is a very good way to do it. The script is still imperative though.
I guess what I want to say is that a generic CI or even just job scheduling system cannot be declarative without limiting what it can do. You can build your own scripts which can then provide a declarative interface to the imperative world to your CI pipeline, but you will be limited to the scope of your script. You can call out to other such abstractions (nix for your packaging needs, terraform for infrastructure, etc.) as well, but again you will be limited to the scopes of those.
As long as the CI system retains the ability to run jobs in some order instead of just being able to declare what you want and let the CI system figure everything else out (e.g. build first, then test, then possibly release; even if I declared what I want in a different order) it is still an imperative interface.
To make this clear: I don't think declarative CI/CD pipelines are desirable. They should be an (ideally as short as possible) list of steps executed in order to achieve some goal, because that is what fits the problem domain best (interact with the imperative world in arbitrary ways). Parts of those steps can be pushed out to declarative abstractions though where that makes sense, e.g. building and testing in nix so you get caching, sandboxing and reproducibility of builds basically for free, or deploying infrastructure changes through terraform so you get idempotence on those changes mostly for free. A generic CI system can then be reduced to it's bare minimum: a way to run a short script when some event happens.
To your last point, I have experience with using CD in production, but not to the scale where I have builds stepping over each other and causing issues. I agree that serial builds are important in this case, and is something that I will need to look into (conceptually it sounds pretty simple).
I get the DSL desire, and I feel I've already lost the "YAML is fine" battle elsewhere so that's not a problem. I think a language like jsonnet or cue would have been a better choice simply because they don't involve users learning a new language or you from implementing a new language. Both would have allowed plugging in your own standard library of functions and abstractions.
1. It doesn’t seem it’s possible to include other .ci files? I have multiple projects that use the same ci config with their own augments and Cicada seemingly won’t work with that flow?
2. Self hosted non-docker runners are Python3.11. Some of us (albeit few of us) don’t have the luxury of being able to abandon ancient OS targets.
3. Doesn’t seem git.push allows branch to specified as “$DEFAULT_BRANCH” macro (or similar). Some projects use master, some use main, some use gold, whatever, it would be nice to not have to know.
The example CI repo is no more than a “hello world”. I don’t think people with simple CI requirements are interesting in switching from what they already have. Your target audience is likely someone like me who maintains 10k+ lines (merged) of GitLab YAML and wants to get out. I would be more encouraged to look deeper into this project if it could show me the value it adds, because right now it just seems like a different YAML that I’ll eventually loathe too.
Very neat project, I hope to see it mature.
0: Secrets are stored using Vault. Read this commit message [1] for a full breakdown.
1: Currently you cannot include other .ci files. I have been in feature creep mode for months now, and I've been forcing myself to stop adding features and start talking to users. The goal is to make Cicada more or less a general purpose programming language, but the first step is making it work well for defining CI/CD workflows.
2: If it is necessary, I could back-port the self-hosted runners to Python 3.10/3.9 or earlier. And, since the runner interface just uses websockets, I (or someone else) could make a runner in a different language, ie Rust or Go.
3: There is a "event" global variable that includes info like "event.branch", which is the branch that was pushed, but it does not include stuff like the default branch. Currently you could do `on git.push where event.branch is "main"`, but something like "event.branch is event.default_branch" would be even better. I'll work on adding that.
The value add currently is that Cicada is FOSS, platform agnostic (works with GitHub/Gitlab), and uses a language that consolidates the workflows and scripts into one manageable file format. Existing CI systems are already packed with features that people expect, so the current struggle is catching up to this and then adding more on top of that. I'm trying to focus on what sets Cicada apart: That it gives you more control over your workflows, while being expressive and easier to manage then YAML and shell scripts.
[1]: https://github.com/Cicada-Software/cicada/commit/2659f79b500...
I definitely understand the reluctance towards feature creep, but I can imagine most bigger customers mightn't want to rewrite their include-heavy CI definitions in this, knowing it will come later and they'll have to rewrite again.
The slow changing nature of pipelines makes then candidate for not being touch once the configuration is set.
So, to answer your question, in my opinion - is there a room for improvement - yes, but the value is minuscule for a customer to switch to a different provider in CI/CD is working fairly well.
The economics may appear bad for not using GitHub, but there’s still valid needs for code that isn’t hosted in the cloud.
For those cases it is reasonable to see or know what’s out there.
I therefore don't see the need for all that DSL stuff that's designed around not needing a bash script. I still need the batch script for testability purposes.
I'm fine with the DSL otherwise; switching from Jenkins to GitHub actions isn't a big deal especially when all you're going to do is run a bash script.
I want to take any .gitlab-ci.yml and magically translate it to a github workflow, and vice-versa. I know it isn't impossible, but it's a heck lot of work to get it right with all the hidden features behind declarative pipelines.
But I agree with your assessment, as much as the classic
script:
- chmod +x pipeline.sh
- ./pipeline.sh
makes me want to die.I mean, go for it if you want but I'm not sure why you'd need to maintain so many heterogeous pipelines that would warrant a tool like this.
It's really, really limiting, and very hard to do things like "run this pipeline if on this branch"
Like you said, there are lots of intricate details you need to get right, and each CI/CD provider has a different ethos about how CI should be done. What I'm trying to do with Cicada is create one workflow format that gives you the power of GitHub Actions with their numerous event triggers, but make it work for other providers like Gitlab. Having one format that works with many providers is better than converting multiple formats back and forth, IMO.
Short answer: No
https://github.com/Cicada-Software/cicada/blob/main/LICENSE
I.. wouldn't use to build commercial software.
Cicada, this CI tool, uses a DSL (domain specific language) to write configuration, and this DSL is referred to as "Cicada language", and blasted in marketing copy as a "real programming language" on https://cicada.sh/ . The Cicada DSL is documented in https://cicada.sh/docs/ci-lang/index.html
However, this is a completely different language from Cicada language, a programming language and theorem prover hosted at https://cicada-lang.org/ and https://github.com/cicada-lang/cicada .
This name collision is very confusing, and I wonder why Cicada the CI tool didn't just stick to python, since it is also a "real programming language."
Especially since, as far as I can tell, this is built on Python. So you already have a working Python installation at least.
Where Cicada shines though is that it makes higher level concepts like conditional execution and caching front end center, so you can describe more with less lines. The DSL itself isn't very mature at the moment, but I plan on adding stuff like allow/deny capabilities and more in the future.
1: Can't link, you can search "Cicada" and "Amazon" on the USPTO TESS
https://tsdr.uspto.gov/search.action?sn=98150978
> online non-downloadable software for operating and managing continuous integration and continuous delivery pipelines in computer programming environments
A cross platform version of Github Actions would allow you to run your actions using your own tooling.
which is conveniently packaged in a GH-a-like system by:
https://gitea.com/gitea/act_runner
in
I made an irreverent short on why your CI pipelines ought to be portable and runnable locally. Number one reason? Give your developers their time (and sanity) back. Pushing to Git in the inner loop disrupts flow and shatters attention.
Adding a CLI for Cicada has been on my todo list for a while, and is long overdue! This should be easy enough to do.
Just to add to what Tao was saying, our pipelines are not only portable but also "smart".
Instead of having to specify every step of a job—either in code or config—you instead tell Garden that it's e.g. a build of type container, or a deploy of type Helm, and our plugin system figures out the rest (of course with escape hatches when needed).
We also track the files that go "into" each job and cache the results, so the same job never has to run twice if the code doesn't change. So if you have a large distributed system and a change only affects a small part of it, you don't need to re-run everything. It can save _a lot_ of time.
Unrelated, but love that the Cicada team created a Treesitter grammar for Neovim!