Continuous integration (CI) involves build and test automation of feature branches before they are merged to the main Git branch in a project. This ensures that a codebase does not get updated with changes that could break something. Continuous delivery (CD) expands on CI by automating releases of these branches or the main branch. This allows small incremental updates that reach your users faster, in line with Agile software development philosophy.

In this article, I will guide you through establishing a CI/CD process using GitHub. We will use a Python application to demonstrate building a CI pipeline.

I will lead you through these steps:

  1. Create a simple Python application (with Flask)
  2. Create tests for this app
  3. Add the config.yml file
  4. Push to GitHub
  5. Configure CircleCI
  6. Update the README with a badge
  7. Create a PR and see CircleCI in action

Prerequisites

To follow along with the tutorial, a few things are required:

  1. Python installed on your local system
  2. A CircleCI account
  3. A GitHub account

Creating a simple Python app

To create the application, we will use Flask, a microframework for Python. For this exercise, minimal knowledge of the framework is necessary. You can use the example found here.

Building the app

First, create a project directory (folder) and cd into it. Type this into terminal:

mkdir python_app && cd $_/

Next, open your favorite editor and create a hello.py file. Then, copy the following lines into that file:

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

Virtual environments in Python

When working in Python, it is strongly advised that you use a virtual environment. This allows you to install Python packages in an abstracted environment that is not your entire local machine. Some common ways to do this are to use virtualenv or, even better, virtualenvwrapper. We will use the module venv that comes as a part of Python3.

Create the virtual environment:

python3 -m venv venv

You can use other names for your virtual environment, as shown in the next example.

python3 -m venv Env

Activate the environment:

source venv/bin/activate

If you used another name for the environment, replace venv with that name. You will notice the environment name just before the shell prompt, which tells you that the virtual environment is active. Any Python package that is installed will be installed within this environment. To deactivate this environment simply run:

deactivate

Running the app

Now it is time to create a requirements.txt file in your editor. Add the word Flask to the file and save it.

Flask

Then, within the virtual environment, install the package by running:

pip install -r requirements.txt

The final command to run this application is:

FLASK_APP=hello.py flask run

Go to http://localhost:5000/ on your browser to review the application.

Creating the tests

In your editor, create a tests.py file and paste these lines into it:

from hello import app
with app.test_client() as c:
    response = c.get('/')
    assert response.data == b'Hello World!'
    assert response.status_code == 200

For more information about tests, refer to these resources.

Now you can run the test. Open terminal and run:

python3 tests.py

Nothing will be returned to your terminal. Why? We structured the Flask app so that it does not output to the terminal on a pass. No terminal output means that your tests are successful. The resources mentioned earlier provide more detailed examples of how terminal handles passing and failing tests.

Creating a CircleCI config file

Create a .circleci folder. Inside that folder, create a config.yml file. Then, copy these lines into it:

version: 2.1
jobs:
  build:
    docker:
      - image: cimg/python:3.10.1
    steps:
      - checkout
      - restore_cache:
          key: deps1-{{ .Branch }}-{{ checksum "requirements.txt" }}
      - run:
          command: |
            python3 -m venv venv
            . venv/bin/activate
            pip install -r requirements.txt
      - save_cache:
          key: deps1-{{ .Branch }}-{{ checksum "requirements.txt" }}
          paths:
            - "venv"
      - run:
          name: Running tests
          command: |
            . venv/bin/activate
            python3 tests.py
      - store_artifacts:
          path: test-reports/
          destination: python_app

You can find more information about this config file here.

Pushing to GitHub

Using the philosophy of committing your code early and often, we would have initialized Git earlier in this process, and we would have atomic commits. Because this tutorial is about integration of CircleCI and GitHub, I intentionally put that on hold until now.

The current code structure looks like this:

.
├── .circleci
│   └── config.yml
├── hello.py
├── requirements.txt
└── tests.py

Open your editor and create a .gitignore file in the working directory. We will use this file to state which files and folders that we do not want to commit to Git. Copy the following lines into that file:

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

# Virtualenv
venv/*

Commit your code by running the following commands:

git add .
git commit -m "Initial commit"

If you do not already have a GitHub account, go to GitHub’s homepage and create one. If you are new to GitHub, you may want to read the tutorial on pushing a project to GitHub. Then, create a repository by going to github.com/new.

I’ll use the name python_app, leave the rest of the default settings intact, and click on the button Create repository. (Scroll down if it’s not visible.)

GitHub create repo

After creating your new repository, you will get to a page like this one.

Newly created repo

Select the second option, push an existing repository. Run:

git remote add origin git@github.com:NdagiStanley/python_app.git
git branch -M main
git push -u origin main

Configuring CircleCI

Now that the repo is on GitHub, we can finalize the CI by configuring CircleCI. Head on over to the CircleCI signup page. Sign up for CircleCI with your GitHub account, if you haven’t already.

CircleCI

Once you are logged in, make sure that your personal GitHub account is active. If you are in several GitHub organizations, one of them might be active. Just click the dropdown menu (top left) and select your GitHub username. Then, click Add Projects. The most recent project, ‘python_app’, is listed there.

CircleCI project

Click Set up Project at the right side of the row that includes your project.

On the redirected page, you will notice three options (Fastest, Faster and Fast), with Fastest selected as the default. We’ll use this default option. Take note of the descriptions of the other two.

Enter main in the input field for the GitHub branch (notice the text underneath the field confirming presence of the .circleci/config.yml file) and click Set Up Project.

2022-04-14-cci-config

Within no time, the build passes. Success!

Passing build

It is important that you become familiar with the settings that you can change for this project. I will touch on what is relevant to us now.

In the top right corner, click Project Settings (button with a cog icon). Then click Advanced on the left.

CircleCI settings

Find the Only build pull requests card (scroll down if you have to). It is turned off, by default. This means that every push to GitHub will run on CircleCI, including PRs. Feel free to toggle this if you deem it necessary. Sometimes, in a large team setting, this is advised to moderate the build minutes.

README - status badge

On your local machine, check out to another Git branch by running:

git checkout -b add_readme

Open your editor and create a README.md file. Copy and paste the following lines into this file, replacing the username NdagiStanley accordingly:

# PYTHON APPLICATION

This Python application repo was created to showcase the integration between GitHub and CircleCI.

[![CircleCI](https://circleci.com/gh/NdagiStanley/python_app.svg?style=svg)](https://circleci.com/gh/NdagiStanley/python_app)

This adds a title, a brief description and a status badge.

Now, run the following commands:

git add .
git commit -m "Add README"
git push -u origin add_readme

If you go to your GitHub repo, https://github.com/<< username >>/python_app, you will notice that we have a new branch: add_readme. Click Compare and pull request.

GitHub branch

Opening a pull request

This section covers how I set up my PR.

The PR’s title is autogenerated as the commit message. I entered the following in the description, but this is optional:

#### What does this PR do?

Adds README.md file and a CircleCI badge in it.

Click Create pull request and in no time, you have a successful build!

GitHub PR

Note that I clicked Show all checks to reveal that the successful check is from CircleCI. (It now shows the option to Hide all checks.) Even the browser’s tab favicon shows a green tick for the successful run.

If you click Details, below Hide all checks, you will be redirected to the build on CircleCI:

The build on CircleCI

At the top, click python_app. You will be redirected to the builds for this project.

CircleCI builds

Conclusion

There you have it! We have integrated GitHub with CircleCI.

In summary, we set up a Python application and created tests for it. We then created a CircleCI config file and pushed the codebase to GitHub. Finally, we connected the GitHub repository we created to CircleCI.

You can now set up your own project in GitHub and configure CI builds on CircleCI. Congratulations on starting a continuous integration practice! Get the rest of your team involved and level up by adding more applications, more tests, and automating deployments.



Stanley is a Software Engineer and Technical Copywriter who has worn several hats, including technical team leadership and community engagement. He describes himself as a digerati (literate in the digital space).

Read more posts by Stanley Ndagi