Cirrus CLI is a tool for running containerized tasks reproducibly in any environment. Most commonly, Cirrus tasks are used as part of continuous integration workflows but can also be used as part of local development process as a hermetic replacement of helper scripts/Makefiles.
This is a statement from the makers of Cirrus CLI. You will write some YAML (yep, again) and Cirrus will do the hard work for you. Sounds pretty terrific.
Let's see how it works, how to install and (buzzwords incoming) how to test a small Ansible Playbook in a Podman Container with Cirrus CLI.
Cirrus CLI at a glance
Cirrus CLI allows you to write tasks, that can run on your local machine in the same way they are running on Cirrus CI. This allows to test your code on your local machine, before pushing it. There is no need to set up a local Jenkins or connect to a remote server that may be unavailable. Instead, you can run your compiling, testing and sanity checks in containers locally.
Cirrus supports Docker and Podman to execute the tasks and is very configurable. It can also replace other tooling like makefiles.
The installation of Cirrus CLI is pretty easy. Since I am on Fedora and Podman is the default container engine, I will stick to this setup. But, Cirrus supports Docker, too. You can also run it on Ubuntu, Debian, Arch Linux or whatever you like. You just need to ensure that you have Docker or Podman installed.
In most Fedora installations, you will find Podman already installed. Nevertheless, if it is missing you can install Podman with the below commands.
# Install Podman $ sudo dnf install podman
Yup, that's it. Done.
Installing Cirrus CLI is very easy, too. You just need to download the binary and copy it to a proper location. According to conventions, the Linux FHS and my personal experience, you should use one of two locations for this.
# Download cirrus $ wget https://github.com/cirruslabs/cirrus-cli/releases/download/v0.66.5/cirrus-linux-amd64 # Make available for current user only $ cp cirrus-linux-amd64 ~/.local/bin/cirrus # Make available for all users of the system $ sudo cp cirrus-linux-amd64 /usr/local/bin/cirrus
Afterwards, you can check if everything is working with the below command.
# Check cirrus version $ cirrus --version
Now that this is out of the way. Let's actually do something.
Playing with a test tool requires at least a bit of code. I opted for a very simple Ansible Playbook and a bunch of tests, I want to perform against it. Nothing fancy or special. Just some "first steps".
Example Code (Ansible)
The Ansible example code is a single playbook. You just need to create a directory and a playbook.yml with the below content. There is only one task, which should update the operating system.
That's already it. But, having valid code does not bring up issues or break our tests... OK, let's introduce some bugs to the playbook.
Can you already spot all problems? We will find out in a couple of minutes.
Writing the test
I will not dig into too many details for testing Ansible Playbooks for now. But, there are a couple of tests, that are quite common and almost mandatory in my development workflow. Cirrus CLI makes it also very easy to test things.
Let's start with some linting. Linting is something that can be done by many code editors and IDEs already. Linting YAML in general is a good idea. There is so much that can go wrong. So, how does this look like in Cirrus CLI?
The above file may need a bit more explanation. But let's run it first and see the result. Just run the below command.
# Run cirrus $ cirrus run ❌ 'Lint Yaml' Task 11s ✅ Preparing execution environment... 1.8s ✅ image pull 2.6s ✅ 'install' script 5.9s ❌ 'lint' script 1.0s yamllint playbook.yml playbook.yml 7:2 error syntax error: expected <block end>, but found '<block mapping start>' (syntax) 11:8 error wrong indentation: expected 9 but found 7 (indentation) 12:9 error wrong indentation: expected 7 but found 8 (indentation) Error: build failed: task Lint Yaml (0) failed 2021/10/27 01:14:06 build failed: task Lint Yaml (0) failed
Pretty awesome. We are getting our errors displayed. But what happened here? Let's go back to the Cirrus CLI configuration file.
We can see a line:
This is how you trigger a new task in cirrus. Every task can be prefixed with something. So,
task are all valid options for the first line.
name: "Lint Yaml"
The name should be clear. It's just a name.
container: image: "docker.io/library/python:latest"
These 2 lines are more interesting. This way, you will tell Cirrus to download an image. This can be a simple one as in our example or your own image. If the image is already present at the host, it will not be downloaded.
install_script: "pip install yamllint" lint_script: "yamllint playbook.yml"
script statement tell Cirrus to run a command in the container. Similar to the
task statement from above, you can prefix
script statements to make them unique. In our example, we want to install yamllint and run yamllint.
That's already it. The first test is done. Now you can fix the code or add more tests.
Adding more tests is pretty easy. You can just make a new task and write your tests similar to our example. Cirrus does not care how many tasks you want to have or how many scripts you need. But, for many test cases, you can find examples.
Furthermore, since the syntax is exactly the same as for Cirrus' hosted service, you can find examples in many Open Source repositories, that may have solved your tests already for you.
Links & Docs
Cirrus has a very nice documentation and there are also some articles on the web, that are worth a read.
Cirrus CLI brings the power of Cirrus CI to your local machine. You can also use Cirrus CLI on other providers to achieve the same results on Travis CI or GitHub Actions. This enables you to write tests, that are vendor independent.