Continuous integration (CI) involves the 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), on the other hand, builds upon 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 take you through a CI/CD process with GitHub. We will use a Python application to demonstrate our CI pipeline.

Here are the steps we will take:

  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 our README with a badge
  7. Create a PR and see CircleCI in action

Creating a simple Python app

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

Building the app

For simplicity, we will create a Flask application. Flask is a microframework for Python. For our exercise, minimal knowledge of the framework is necessary. We will use the example found here

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. Here is how:

  • Create the virtual environment:
python3 -m venv venv

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

python3 -m venv Env
  • Activate this 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 our 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

You can see the application running on your browser at http://localhost:5000/.

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

Please see these references for more information about tests:

Now we can run our test. Open Terminal and run:

python3 tests.py

Nothing should be returned to our Terminal. Why? We structured our Flask app so that it does not output to Terminal on a pass. No terminal output means that our tests are passing. The resources mentioned earlier give better examples of how Terminal handles passing and failing tests, but this example will work for our tutorial.

CircleCI config file

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

version: 2
jobs:
  build:
    docker:
      - image: circleci/python:3.6
    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: https://circleci.com/docs/2.0/language-python/.

Pushing to GitHub

Using the philosophy of committing your code early and often, we should 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 it on hold until now.

Our current code structure looks like this:

2018-09-11-Stanley01.png

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/*

We can now commit our 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 site and create one. If you are new to GitHub, you may want to read this tutorial. Then, create a repository.

2018-09-11-Stanley02.png

2018-09-11-Stanley03.png

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

2018-09-11-Stanley04.png

We will go with the second option, …push an existing repository. Run:

git remote add origin https://github.com/NdagiStanley/python_app.git
git push -u origin master

Configuring CircleCI

Now that the repo is on GitHub, we can finalize the CI by configuring CircleCI. Head on over to the CircleCI sign up page. Sign up for CircleCI with your GitHub account.

2018-09-11-Stanley06.png

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 drop down menu (top left) and select your GitHub username. Then, click Add Projects. The most recent project, ‘python_app’, is listed there.

2018-09-11-Stanley07.png

Click Set up Project at the right side of the row that includes our project. On the redirected page, you will notice the Next Steps section. Had we not had our own .circleci/config.yml file, we would have started at No. 1. Because we do have the config.yml file, we can just scroll to No. 5 and click Start building.

2018-09-11-Stanley08.png

Within no time, the build passes. Success!

2018-09-11-Stanley09.png

In the top right corner, click the Settings cog. Then click Projects on the left, and finally, python_app.

2018-09-11-Stanley10.png

You will be on a path like this one: circleci.com/gh/<username>/python_app. Mine is https://circleci.com/gh/NdagiStanley/python_app. Click the settings cog next to the repo name: python_app.

2018-09-11-Stanley11.png

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 Advanced Settings, notice that Only build pull requests is turned off. This means that every push to GitHub will run on CircleCI, including PRs.

ReadME - status badge

On our 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:

README.md
# 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)

I added a title and a brief description to mine.

Now, run the following commands:

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

If you go to https://github.com//python_app you will notice that we have a new branch: `add_readme`. We can go ahead and click Compare and pull request.

2018-09-11-Stanley12.png

Opening a pull request

This is how I set up my PR:

2018-09-11-Stanley13.png

Click Create pull request and in no time, this is what we get:

2018-09-11-Stanley14.png

A successful build! Now, click Show all checks. Notice that the check is from CircleCI.

2018-09-11-Stanley15.png

Even the browser’s tab favicon shows a tick for the successful run:

2018-09-11-Stanley16.png

If you click Details, this will redirect you to the build on CircleCI:

2018-09-11-Stanley17.png

Notice that the favicon here also shows that the build is successful:

2018-09-11-Stanley18.png

At the top, click python_app.

2018-09-11-Stanley19.png

You will be redirected to the builds for this project:

2018-09-11-Stanley20.png

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.

If you followed through successfully, you can now set up your own project in GitHub and configure CI builds on CircleCI. The following links point to my references while writing this article.

https://circleci.com/docs/2.0/configuration-reference/#full-example https://circleci.com/docs/2.0/configuration-reference/


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). You can find more about him on his personal web profile at stanmd.tk.