Mastering Django CI/CD using GitHub Actions
Tags: programming, tutorial, github, productivity
Published: 2023-07-02 03:23:10

Testing and delivery is a common repetitive cycle in application development. This cycle consumes more time and resources as we fix or add more features to our application. Automating this repetitive process will reduce the time spent in application development.

Testing and Deployment are part of the cycle discussed earlier. We build, deploy and test our applications a lot. Doing this every time is vulnerable to human error. Automating our tests guarantees that every commit undergoes testing.

This article covers how we can use GitHub Actions to automatically test our Django applications.

Let’s dive in.



## Prerequisites

To follow through with this article, we need the following:

- Basic knowledge of Django, Git, and GitHub
- A GitHub account
- Python3 and Django installed on our operating system


## Setup Demo Django Application

We need a Django application that has tests to understand how GitHub actions work. We can work with this [demo Django project](https://github.com/devvspaces/simple-django-testcase), which includes tests. 

GitHub actions run on GitHub repositories and not on our machines. Only the owner of the repository can manage workflow runs. Later in the article, we will cover what Workflows are.

We need to fork the repository and clone it.

```
git clone <REPOSITORY_URL>
```

We should set up a new virtual environment.

```bash

# linux and mac
cd <PROJECT_DIR>
python3 -m venv venv
source venv/bin/activate
```
```windows

# windows
cd <PROJECT_DIR>
python3 -m venv venv
venv/Scripts/activate  # <-- difference
```

Now we need to install the project dependencies. Run this command in the project directory. This will install all the dependencies specified in `requirements.txt` file.

```
pip install -r requirements.txt
```

Now that our application setup is complete. Let’s run our tests will. The tests confirm the reliability of our application.

```bash
cd project
python manage.py test
```

The tests are successful. We can now go ahead and automate this process using GitHub actions.😊

> Automated tests don’t only work when we use Django’s built-in test runners. We can write automation for any kind of test runners we are using. One popular test runner in the Python community is Pytest.



## What are GitHub Actions?

To get started, we need to define a [workflow](https://docs.github.com/en/actions/using-workflows/about-workflows). A workflow is a configurable automated process that will run one or more jobs. A step is a command or action that performs a basic task. An Action is a collection of steps, which makes an action reusable. A Job is an operation in a workflow that contains steps. Jobs can run in parallel or in sequence.

We define workflows by using a `YAML` file checked in the repository. A workflow will execute when triggered by an event in the same repository it exists. 

GitHub workflows listen to many events, examples are push and pull requests. The events configured into our `YAML` file trigger our workflow. For example, when we push a new commit to the master branch, this can trigger a testing workflow.


## Creating A Testing Workflow

Let’s create a workflow that executes our Django application tests.


> Workflows are not for testing but for executing jobs which can be anything. An example of this is running tests. Our tests are already defined. We configure workflows to execute them. Other use cases are deployments and closing pull requests.

Create a new directory called .github/workflows in the root directory. Create a new file called `test.yml` in the .github/workflows directory. This file will be our workflow `YAML` configuration.

```yml

# test.yml
name: Run Django Tests
on:
  push:
    branches: [master]
jobs:
  test-django-app:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.9"]
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v3
        with:
          python-version: ${{ matrix.python-version }}
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
      - name: Run Django test
        run: |
          cd project
          python manage.py test
```

Let’s break down what each syntax means and how this executes the application tests.

We start by defining the workflow name by using the name syntax.

```
name: Run Django Tests
```

The workflow name helps us identify the workflow in our repository’s GitHub Actions tab.


> The GitHub Actions tab helps us to manage workflow runs. See how to manage a workflow run on GitHub.

Next, we define the event that triggers the workflow.

```
on:
  push:
    branches: [master]
```

By using the on [syntax](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#on),  every commit we push to the master branch triggers the workflow run. We can specify many events and branches.

Next, we have the [jobs syntax](https://docs.github.com/en/actions/using-jobs/using-jobs-in-a-workflow).

```
jobs:
  test-django-app:
```

A scenario to use this is when we want to deploy a web application after successful tests. We can create two jobs, one for testing and the other for deployment. Then configure the deployment to run after when tests are complete and successful.

> The name of our job is test-django-app, we can name it anything. But make sure it’s descriptive, as it helps us identify the currently running job in the GitHub Actions tab.

Next, the [runs-on syntax](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on) defines what machine will run the job. Here we instructed it to use `ubuntu-latest` version.

```
runs-on: ubuntu-latest
```

Next, the [strategy syntax](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategy) helps us define what Python versions we will use to test. In our workflow, we have defined only one version. The job will use each Python version declared in the list to execute itself in parallel.

```
strategy:
  fail-fast: false
  matrix:
    python-version: ["3.9"]
```

`fail-fast` applies to the entire matrix. If `fail-fast` is set to `true`, GitHub will cancel all in-progress and queued jobs in the matrix if any job in the matrix fails. This property defaults to `true`.

Assuming that we built a Python package and set many Python versions. We can ensure the package undergoes testing across defined Python versions.

Jobs contain a sequence of tasks called [steps](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idsteps). Steps can run commands, set up docker, or run an action in our repository. These steps run one after the other based on their position in the `YAML` definition.

The following are the actions taken by each step in our workflow:

1. Checkout the repository on the machine defined for the job,
2. Setup Python using the current version from the strategy,
3. Install pip and the project dependencies,
4. Move into the project directory and run the tests.

We need to commit and push a new workflow to trigger our workflow.

```
git add .
git commit -m "Added tests automation using Github Actions"
git push
```

We should see a new running or completed workflow run on the GitHub actions tab in our forked repository.


![GitHub workflow actions running page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/721sg5m1i926rksaw22x.png)


This workflow would show us each job and the steps executed.


![GitHub workflow job management tab(https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5tlk6am6n0dhwjn7s2mn.png)

Now, any time we push a new commit to the master branch. Our workflow executes our Django application tests.

[Understanding GitHub Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) is a complete guide to learning actions, workflows, and jobs. Check it out to master project development automation.


## Conclusion

In this article, we have covered how GitHub actions execute Django application tests. Continuous Integration and Delivery is an important part of a modern development cycle. Automating development life cycles ensures we build reliable applications when working with collaborators.


## Resources
- [Understanding GitHub Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) by GitHub
- [Introduction to GitHub Actions Training](https://learn.microsoft.com/en-us/training/modules/introduction-to-github-actions/) by Microsoft
- [GitHub Actions Tutorial for Beginners](https://www.learnenough.com/blog/git-actions-tutorial) to Automate Your Workflow