Setting Up Free CI for Python Web Apps on GitHub, GitLab and Bitbucket
In the past, if you wanted to set up a continuous integration (CI) and deployment pipeline for your team, you had to download and configure Jenkins on your local server. Nowadays, you can simply hop onto cloud-hosted CI/CD providers such as Buddy, CircleCI and Travis CI.
In this guide, we’ll look at repository hosting providers such as GitHub, GitLab and Bitbucket that provide integrated CI/CD services for free. Using these means we no longer need to manage a separate platform to build and run CI/CD pipelines. We’ll be using a Flask project to learn how to configure a simple “build and test” pipeline on each platform.
Prerequisites
If you’re new to the world of continuous integration and continuous deployment, you should be aware that Docker has become the foundation of modern CI/CD platforms. Docker is essential in creating clean environments suitable for running scripts used in building, testing and deploying code. Docker containers consume far less resources and spin up faster than virtual machines. If you’re new to this, please check out our articles on Docker.
GitHub Actions: CI/CD
In 2018, GitHub released a new feature called GitHub Actions that allowed developers to create “custom Software Development Life Cycle(SDLC) workflows” that are triggered by events such as a git push
, issue creation
or a new release
. With GitHub Actions, you can automate the building of a container, the deploying of a web service, or the onboarding of new users to your project.
Check out the GitHub Marketplace to see all the cool GitHub Actions you can use to automate tasks on your project. These actions are created by the GitHub community. A number of them have been created by verified organizations and will have a badge to indicate so.
In August 2019, CI/CD support was added. This means you can call GitHub Actions that can build, test and deploy your projects to any platform. In addition to building on Linux, you can also build on macOS and Windows. Performing matrix builds is supported, which is an extremely useful feature since there are very few CI/CD providers that support it. A matrix build allows you to test different versions of a software engine—such as Node or Python—on different operating systems. This helps maintainers ensure that code runs on different platforms and software versions they’ve declared support for.
Here’s an example of YAML config file for GitHub:
jobs: test: name: Test on node ${{ matrix.node_version }} and ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: matrix: node_version: [8, 10, 12] os: [ubuntu-latest, windows-latest, macos-latest]
steps: - uses: actions/checkout@v1
- name: Use Node.js ${{ matrix.node_version }} uses: actions/setup-node@v1 with: version: ${{ matrix.node_version }}
- name: npm install, build and test run: | npm install npm run build --if-present npm test
You can run your workflows on the GitHub platform, or you can run them on your own servers (for example, on containers or virtual machines). For open-source projects, CI/CD is completely free. For private repositories, you get 2,000 build minutes and over, depending which tier you’re on. Pricing for pipeline builds is separate and charged on build time, not number of users.
The best way to learn how to use GitHub’s CI/CD services is through a practical demo. I’ve prepared a Python Flask project you can use. Simply fork it to your account, then click the Actions tab:
You’ll see several templates you can use to define your workflow. On the first template, “Python application”, click the Set up this workflow button. You’ll be taken to the following view:
Take the time to read through the proposed pythonapp.yml
file that will be placed inside your project’s repo in the folder .github/workflows
. Basically, this is what’s happening:
- A Docker base image is defined—the latest version of Ubuntu where our pipeline scripts will run.
- Python 3.7 is installed in the container.
- Pip is upgraded and project dependencies are installed.
flake8
is installed, which is used to lint our code. Note that we need to define--exit-zero
in order to treat syntax issues as warnings instead of errors, which would cause our pipeline to stop running.- In the last bit,
pytest
is installed, which is used to run tests defined in thetests
folder.
Hit the Start commit button.
I recommend that you provide a better commit message than the one I used above. I also recommend that you commit these new files directly on the master branch. I’m just committing on a separate branch so that you can have a clean project to experiment with. Once you hit the Propose new file button, GitHub will run your new pipeline automatically. The build process should occur fast. However, when you reach the test stage, it will fail. You can look through the logs to find out why it failed.
To fix this, simply pull the latest version of your repo to your machine using the git pull
command. Next, update the last line of .github/workflows/pythonapp.yml
in your code editor to this:
PYTHONPATH=. pytest -v
This should fix the pytest
fail problem. Commit this new change and perform a git push
. This should trigger a new build to run. After a couple of minutes, the results should be in:
Now that we’ve fixed the testing stage, it’s probably a good idea to look into how we can do deployment. Simply take look at GitHub’s Actions Marketplace. You’ll find deployment workflows to popular platforms such as:
- Azure
- AWS
- Google Cloud
- Digital Ocean
- And many others …
Deployment is an extensive topic and we can’t cover it here. Let’s look at the next platform.
GitLab CI/CD
GitLab has been offering continuous integration services since 2015. I’ve provided the same project on GitLab so that you can follow along. Once you’ve forked it to your account, you’ll be met with an option to enable Auto DevOps.
Auto DevOps is a feature that automatically provides a pre-defined CI/CD configuration, which allows you to build, test, deploy and monitor your project. The goal is to simplify the CI/CD process. In theory, this sounds great. However, in practice it didn’t work out for the Flask project I had.
The first problem I came across was that some of the predefined tasks took too long to complete. Also, the test
stage was incorrectly defined. There’s also a requirement to set up a Kubernetes cluster on the Google Cloud Platform, which can be done via GitLab’s native Kubernetes integration.
If this all sounds like a hassle to you, there’s an easier way to perform CI/CD on GitLab. Simply create a .gitlab-ci-yml
file at the root of your project. Here’s some sample content you can use:
image: "python:3.7"
before_script: - python --version - pip install -r requirements.txt - pip install flake8 - pip install pytest
stages: - Static Analysis - Test
flake8: stage: Static Analysis script: - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=120 --statistics
pytest: stage: Test script: - PYTHONPATH=. pytest -v
Commit this file and push it to your GitLab repo. This should trigger a successful run.
At first glance, it appears that GitLab’s syntax is cleaner than GitHub’s YAML CI config file. However, GitHub’s Actions makes it easy to perform complex tasks in just a few lines of configuration code. GitLab doesn’t have an equivalent to GitHub’s marketplace, so you’ll need to read step-by-step articles on how to achieve specific CI tasks for different languages, frameworks and third-party platforms. You can learn more about GitLab’s CI/CD service here.
Bitbucket Pipelines
Bitbucket is another Git hosting provider that provides both public and free private repositories. They also provide a CI/CD service integrated into their platform called Pipelines. If your team uses Jira, you can benefit from having the best integration with Bitbucket’s pipelines. The integration helps teams track associated builds and deployment to Jira issues. If you’re using GitHub’s CI/CD services, you can use Jira’s GitHub Actions to integrate your GitHub repo with your Jira software.
As I said earlier, the fastest way to learn a new platform is to follow a practical tutorial. Fork this project on your Bitbucket account if you have one. Open the Pipelines tab and scroll down. You should see something similar to this:
As you can see, Bitbucket’s pipeline configuration is stored in the bitbucket-pipeline.yml
file. Update the proposed configuration with the following:
image: python:3.7.3
pipelines: default: - step: caches: - pip script: # Modify the commands below to build your repository. - pip install --upgrade pip - pip install -r requirements.txt - pip install flake8 - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=120 --statistics - pip install pytest - PYTHONPATH=. pytest -v
Just like GitHub and GitLab, Bitbucket also uses Docker to define environments for running CI/CD scripts. Do note that for each step
, a separate Docker container is used to run the script. Hit the Commit file button and watch the livestream logs as it runs. Once it completes, you should see the following result:
If you’d like to deploy your project, you’ll be glad to learn that Bitbucket provides what it calls Pipes, which is quite similar to GitHub Actions. You can simply add them to your config file to easily deploy your application to any hosting platform you use.
You can learn more about running Python projects in Bitbucket pipelines here. You can also find more information here. Let’s now conclude this guide.
Summary
Having evaluated the CI/CD services offered by each Git Hosting Provider, I feel that GitHub’s offering is the best for new and experienced developers. It’s very fast to set up and its GitHub Actions ecosystem makes it super easy to integrate with other SDLC service providers. Deployment to any cloud hosting provider is also a breeze. I find it quite surprising how mature their product is, considering how long the alternatives have been in existence:
- GitLab Continuous Integration: January 2015
- Bitbucket Pipelines: October 2016
- GitHub Actions with CI/CD Support (Beta): August 2019
GitLab’s onboarding to their continuous integration service is not well thought out, as one has to dig through their documentation to find out the best way to configure a basic pipeline. One wonders why it’s necessary to set up a Google Cloud Kubernetes cluster for Auto DevOps, which is supposed to make life easier for developers. Both GitHub and Bitbucket excel at onboarding, as users only need to interact with the web interface to have a basic pipeline set up and running in less than a minute.
Performing additional CI/CD configuration on GitLab requires you to read articles and tutorials, which isn’t a problem. But considering the alternatives—GitHub Actions and Bitbucket Pipes—new developers can use these “workflow templates” to configure advanced pipeline tasks without reading manuals.
I do, however, have several gripes with Bitbucket. First, it’s impossible to use multiple steps
in a Python project because each step uses a different Docker container. This means the results of one step are not automatically available to the next step. There are ways to pass artefacts with subsequent steps. Unfortunately, there’s no documentation on how to share pip packages with multiple steps. The solution is to bundle all your pipeline scripts into one step. On GitHub, steps
are simply independent processes, not containers. So multi-steps on GitHub and GitLab for Python projects are not an issue.
The second issue is that Bitbucket doesn’t offer custom runners, and you can’t run builds on Windows and macOS. At the time of writing, they only offer 50 minutes per month of free build time, while GitLab offers 2,000 minutes per month. GitHub is free for public projects and includes 2,000+ free minutes for private repositories, depending on which tier you’re on.
To conclude, GitHub Actions is by far the best free CI/CD service with unlimited build time for open-source projects. It also has the ability to perform matrix builds on Linux, Windows and macOS simultaneously. It has the largest community by far, so if you have a question or encounter a problem, you’ll most likely get a response within 24 hours. Please don’t abuse the free service, though, as excessive usage will be detected and you may get banned.
Now that you don’t have an excuse not to use pipelines, go ahead and CI/CD your projects living in GitHub.