SECRET OF CSS

Setting Up CI/CD for a Django Project Using Github Actions | by Akshar Raaj | Aug, 2022


Getting started with Github Actions

1*YqEXIEvHIP9Tn2 glLjVCw
Github Actions

In this post, our objective is to learn and setup Github Actions.

We will work with a Django application, and will setup a Github Actions workflow which would run the tests for this application.
The tests would be executed on every push to Github.

In a follow-up post, we will enhance the workflow to get the code deployed to an EC2 instance on every merge to branch master.

Let’s use the Django polls application. We have built this application at [here].

Also we have setup pytest for this application. pytest is the test framework which runs the tests for this application.

Clone this application and install the requirements. As is the best practise, you should use a virtual environment.

Navigate to a temp directory.

$ cd /tmp

Activate relevant Python version, this assumes you are using pyenv. Feel free to use virtualenv or virtualenv-wrapper.

$ pyenv shell 3.10.4

Create a virtual environment

$ pyenv virtualenv learn-gh-actions

Activate the created virtual environment

$ pyenv activate learn-gh-actions

Clone the polls application.

$ git clone git@github.com:akshar-raaj/polls.git

Navigate to the project folder

$ cd polls

Checkout to the relevant tag. We have created a tag v1.0 in this project. Checkout to this tag.

$ git checkout v1.0$ pip install -r requirements.txt

Once the requirements are installed, attempt to invoke the tests using command pytest.

You would see an error complaining about DJANGO_SETTINGS_MODULE. The error would look similar to:

django.core.exceptions.ImproperlyConfigured: Requested setting INSTALLED_APPS, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings.

Export the django settings module

$ export DJANGO_SETTINGS_MODULE=mysite.settings

Invoke the tests again by issuing pytest command.

$ pytest

Tests should have been run and all tests should have passed. You should see an output similar to:

============================================================= test session starts =============================================================
platform darwin -- Python 3.10.4, pytest-7.1.2, pluggy-1.0.0
django: settings: mysite.settings (from env)
rootdir: /private/tmp/polls, configfile: pytest.ini
plugins: django-4.5.2
collected 7 items
polls/test_models.py .. [ 28%]
polls/test_views.py .. [ 100%]
======================================================== 7 passed, 4 warnings in 0.40s ========================================================

Github Actions allow us to do cool things in our project. It can be used as a CI/CD.

A major advantage with Github Actions is that you can keep your CI/CD contained right inside Github. Thus your CI/CD setup sits right next to your code.
The Build > Test > Deploy cycle is contained within Github. There is no need to have a separate CircleCI setup or a Jenkins setup.

Github Actions are managed using what Github terms as workflows. A workflow is triggered on events. git push to Github can be considered as an event. Similarly a merge of a pull request can be considered as an event.

A workflow consists of multiple jobs. Each job consists of one or more steps.

A **step** is essentially a command. In our Github workflow, one step would be **pytest**. Similarly a step could be as simple as invoking **ls** or **curl**.

A step can also invoke a Github action. Github actions can be thought of as some complex scripts which can accomplish a non-trivial task.
If we need to pull some dependency for building our project, it cannot be accomplished with a simple command like ls or curl, thus a simple step cannot suffice. For such cases, some scripts can be written to accomplish this. And these scripts are called Github actions. We will see aGithub action in action shortly 😉

Setting up a Github repository

We will add a GH action workflow on the repository you cloned.

To illustrate it’s working, we need to push this code to a repository you own. Thus we will setup this cloned repository as a new repository owned by you.

Remove .git folder from this repository.

$ rm -rf .git

Verify that it’s not being tracked as a Git repository now.

$ git status

You would notice the following output:

fatal: not a git repository (or any of the parent directories): .git

Initialize this project as a git repository.

$ git init

You should see the following output

Initialized empty Git repository in /private/tmp/polls/.git

You need to create a new repository on Github where you can push your local changes and configure Github action.

1*MDCFnl39NFZby Y1zo6yNA

Once this new repository is created, you can add it as origin for this project and push local changes to this GH repository.

$ git remote add origin git@github.com:akshar-raaj/polls-2.git
$ git add *
$ git commit -m "Polls application"
$ git push --set-upstream origin master

If your GH username is john and you have named your repository as django-polls, then the remote url would look like git@github.com:john/django-polls.git

Refresh the repository page on GH. You should see your code on GH.

1*QpCs27EHF3L58zQdhV72dQ

Navigate to Actions tab. It should be visible at the top, right next to Pull requests.

The page would say Get started with GitHub Actions. It’s because you haven’t defined any GH workflow for the project yet.

Let’s define a workflow next.

Adding a workflow

Workflows are defined in the .github/workflows directory.

$ mkdir -p .github/workflows

A workflow is defined in a yaml file. Create a .yml file for the same. Let’s call it run-tests.yml.

$ touch .github/workflows/run-tests.yml

A workflow needs the following three keys:

name is used to specify a name for the workflow.
on is used to specify the action on which this workflow should trigger.

Let’s add the following contents to run-tests.yml.

name: Run tests
on: push

Let’s add and commit this change.

$ git add .github/$ git commit -m "Basic workflow, without any job."

Push the changes to Github

$ git push

We have defined our first workflow. We also pushed code to GH, and because workflow describes that workflow should be triggered on push, hence the workflow should have been triggered.

Navigate to Actions and you would notice one run of the workflow.

1*JaZZHv4KmnOT9r5H6lPd9g

Navigate to the detail page of this run. You would notice the Status as Failure. You should notice something like:

Error: .github#L1
No jobs defined in `jobs`

Adding a job

Thus GH is complaining that there was an error because no jobs have been defined. This suggests that jobs is a required key in the workflow.

Let’s modify the workflow yaml file to add a job. The following content should be added to run-tests.yml.

jobs:
execute_tests:
name: Install requirements and execute tests
runs-on: ubuntu-latest

Thus, we added a jobs key. We provided a job_id which is execute_tests. This is not a keyword, so you could name it anything else, say trigger_tests. The value for the job_id is a map of job configuration.

This map has following two keys:
– name
– runs-on

name is used to provide a descriptive name for the job. runs-on is used to specify the runner. runner means the installation in which this job should execute.

We want our job to be executed on an ubuntu machine. Thus we have given value for runs-on as ubuntu-latest.

Let’s commit the code and push to GH.

$ git add .github/workflows/run-tests.yml
$ git commit -m "Added a job. The job has a 'name' and a 'runs-on'"
$ git push

Navigate to Github Actions page. You would notice that one more run of the workflow would have been trigerred. This would have failed too.

1*LLXaAqfiC86USNm1tIFN7g

Navigate to the detail page for this run. You would notice the Status as Failure

As the error suggests, we need to add a step for the added job.

Adding a step

Let’s add following contents:

steps:
- name: Update
run: sudo apt update

At this point run-tests.yml should look like the following:

name: Run tests
on: push
jobs:
execute_tests:
name: Install requirements and execute tests
runs-on: ubuntu-latest
steps:
- name: Update
run: sudo apt update

Let’s commit and push to GH.

$ git add .github/workflows/run-tests.yml
$ git commit -m "Added a job. The job has a 'name' and a 'runs-on'"
$ git push

Navigate to Github Actions page. You would notice that another run of the workflow would have been triggered. This would have passed.

1*KRSqEiOMlHAiXEpCU0wsPg

Step to execute the tests

We were successful in adding a valid workflow which was executed successfully. We saw how a job and a step are mandatory in a workflow file and also got familiarity with the different keys under job and step.

It’s time to add a step which can execute the tests.

Workflow jobs always run on a runner. The runner is specified by runs-on. In our case, our job runs on an ubuntu installation. A fresh installation is provided for every job run, thus this runner or machine doesn’t have any idea about our codebase.
Hence we need to pull our codebase on the runner, so that further steps can be taken on it.

Pulling the codebase would involve multiple commands. Some commands being:
– Installing git on the machine.
– Creating a public-private key pair and adding the key on github.
– Executing `git clone` from this machine.

This process involves multiple commands. That’s where Github actions kick-in. An action is a set of commands composed in a script file. Instead of us having to specify all these commands individually in the workflow file, we can just use a GH action and that would accomplish the desired thing.

Actions provided by Github can be checked at https://github.com/actions/.

We are particularly interested in an action, which can pull and checkout our code on the runner machine. The relevant action is at https://github.com/actions/checkout.

Let’s add a step to pull and checkout our code on the runner machine. It would look like

- name: Check out repository code
uses: actions/checkout@v3

You would notice in this step we haven’t used run, and have instead used uses. If we want to use a GH action in a step, then key uses must be used. If we want to run a command in a step, then key run should be used.

Now we should have two steps in run-tests.yml. The complete file should look like:

name: Run tests
on: push
jobs:
execute_tests:
name: Install requirements and execute tests
runs-on: ubuntu-latest
steps:
- name: Update
run: sudo apt update
- name: Check out repository code
uses: actions/checkout@v3

Let’s commit and push.

$ git add .github/workflows/run-tests.yml
$ git commit -m "Added a step which uses a Github action using key 'uses' instead of 'run'. This action will pull and checkout the code on runner."
$ git push

Navigate to Github Actions page. You would notice that one more run of the workflow would have been triggered. This would have passed.

Once the code is checked out, we need to install the requirements and run the tests. Let’s add the relevant steps.

- name: Install pip
run: sudo apt install python3-pip
- name: Install requirements
run: pip install -r requirements.txt
- name: Execute tests
env:
DJANGO_SETTINGS_MODULE: mysite.settings
run: pytest

We added the following three steps.
– A step to install pip on the runner machine.
– Install the requirements using pip.
– Execute the tests using pytest.

Key env allows us to set environment variables for any particular step. We need environment variable DJANGO_SETTINGS_MODULE before executing pytest. We were able to set it using key env.

Let’s commit and push.

$ git add .github/workflows/run-tests.yml
$ git commit -m " Added three steps corresponding to installing pip, installing requirements and executing tests"
$ git push

Navigate to Github Actions page. You would notice that one more run of the workflow would have been triggered. This would have passed.

Navigate to details page of this run and you would notice that all steps would have been executed.

1*VkZCNbTVa D0OrR0vJIuUA

Expand step Execute tests on the detail page. You would notice that pytest would have been invoked and all tests should have been executed.

1*q3S1TC8qQ4YuXVK3p8EMlA

Thus we were able to setup a Github Actions workflow which gets triggered on every push to Github.

The complete project can be seen here.



News Credit

%d bloggers like this: