Table of Contents
What is Continuous Integration/Continuous Delivery?
When you finish a new version of your Python application, how do you build and deploy it? Do you change the version number, upload the new files to production, and be done with it? There is a better way to do this, called continuous integration and continuous delivery.
Instead of manually performing all the tedious steps to get your software to production, you can also automate this. Luckily, you donβt have to build this automation yourself. Instead, you can use one of the many Continuous Integration/Continuous Delivery (CI/CD) systems that are available.
CI/CD is the pinnacle of good software engineering practices. Itβs the point where all other good practices come together. CI/CD bridges the gap between development and operations because it automates and enforces important steps in building and deploying software. It ensures quality and takes human errors out of the loop.
We will take an existing application and create a CI/CD pipeline for it. Youβll see how you can set up a professional CI/CD pipeline in under 15 minutes!
Requirements
To set up a good CI/CD pipeline, we need to have some other practices in place first:
- We must use a code version management system like Git.
- Our application should have automated tests (unit tests, integration tests).
- We should have at least a development, test, and production location for our software.
- We need a CI/CD system.
Setting up CI/CD may seem like a daunting adventure, but it really isnβt. In fact, you can allow yourself some shortcuts when starting out. To get started quickly:
- Our test and development location will be our own PC.
- Weβll start with just one unit test.
- Weβll use GitHub to host our code, and weβll use Docker Hub to host our finished application.
That leaves us with the final choice: which CI/CD system to use?
Which CI/CD system to choose from?
Several vendors offer CI/CD systems. A well known and open-source system Iβve used in the past is Jenkins. Itβs been around for a long time. The downside: it requires knowledge and time to set up and run. Iβve also been forced to use Atlassian at a company I worked for in the past, but I never got to like the product.
To get up and running really fast and show you how easy this can be, Iβll use Buddy in this article. Itβs offered as a cloud service, and their free tier still allows you to create 5 projects. Their interface is absurdly intuitive; it even suggests suitable actions to you after initially analyzing your code.
The application
For this pipeline, Iβll use an existing βhello worldβ application. You can view the source on my GitHub account. It has the following features:
- Itβs a Python project that uses Pipenv.
- It has basic unit tests.
- It includes a Dockerfile to create a docker Python image that is published to my public Docker Hub account.
Step 1. Create an account and project
Letβs start! If you donβt have a GitHub account, create one first. Then use your Github login to create a Buddy account. This way, Buddy has access to your repositories directly.
Step 2. Add the source repository
In GitHub, you should fork my βdocker-python-exampleβ repository, so itβs available as one of your own repositories in the next step.
Once youβve done that, click the large βCreate a new projectβ button in Buddy. You can choose one of your GitHub repositories. If everything went well, the βdocker-python-exampleβ repo should be there too:

Click the project. You should end up with the following screen:

Buddy detected this to be a Python application. Sweet! Once youβve finished setting up the complete pipeline, you can download a buddy.yml file to store your pipeline with your code. I strongly recommend you do so for your own projects.
Step 3. Create a new pipeline and add actions
Weβre ready to build a new pipeline. Click the βAdd a new pipelineβ button and fill out the form as follows:

We will run this pipeline everything something is pushed to the master branch. Alternatively, you can build your application recurrently or manually as well. Click the big blue button to add the new pipeline. Buddy has already taken a look at our code and will come up with a few suggestions:

Choose the Python action, and fill out the next form. If you want to follow along, you can copy/paste this:
pip install pipenv pipenv install pipenv run python3 tests.py
These actions:
- install pipenv using the pip install command,
- install all the requirements,
- and finally run the unit tests within our newly created environment.

You can choose the Python version here as well. Buddy runs each step in a Docker container, so you can even use your own custom Docker container if needed. For us, the default Python containers are fine. I picked version 3.8.6 because thatβs what I know will work with this project. You can try the build step right now if you want, just to make sure it all works before you continue. As you can see below, I needed 4 tries because I forgot to check in some files to the Github repo. Thatβs no problem. We can keep trying until it works:

Now go back to your pipeline and click the little + below your first action. It allows you to add more actions. By default, the next action only runs if the previous one is finished without errors.
The next action to add is another one of the suggestions: the Dockerfile linter. It checks our Dockerfile for mistakes. Thatβs a nice-to-have extra. All the defaults are fine, so all we need to do is click βAdd this action.β
Now itβs time to build the Docker image and push it to Docker Hub. Add another action, and again pick from the suggested actions. This time, itβs the one called βBuild Imageβ (the one with the docker icon inside it).
The defaults on the βSetupβ tab are fine. Head over to the βOptionsβ tab and fill in your Docker Hub details. This is also the place where you can define your image tag.
Weβll do something straightforward: we always tag the image as latest
. To keep the version history as well, we add an additional tag that uses a Buddy variable.
A what?! Buddy automatically defines a list of environment variables you can use in your project. A full list and more details can be found here. One of them is the shortened git revision number, accessible under ${BUDDY_EXECUTION_REVISION_SHORT}
. We can use it to give our image a unique id:

The last action you want to add is a notification, so youβll get notified of successful builds. Iβll demonstrate with a good old e-mail, but there are many other options, like:
- Slack
- Telegram
- MS Teams
- Discord
- SMS
The process is simple again. Click the little plus button under the last action, scroll through the available actions, and click e-mail. Youβll get a screen like this:

As you can see, you can use all the available environment variables to customize the message to your liking. In a more advanced setup, you probably want to tag your git repo with a version number and create a build trigger that triggers on new tags. This is a nice way of separating dev builds from release builds since this tag can be used as a variable to tag the docker image as well.
Step 4. Run the complete pipeline
Youβre all set. When you push something to the master branch, your pipeline will start running. Alternatively, you can manually trigger a build as well. Letβs do that now:

If all goes well, you should end up with a green bar saying: βPassed.β
To make sure, you can go to your Docker Hub account and verify thereβs a new image available. As you can see, our image is there with the correct tags: latest and a short hash, based on the last git commit:

Congratulations, you build a CI/CD pipeline. That wasnβt too hard now, was it?