As your Django project starts growing; the process of testing and deployment gets cumbersome with time. If we rely heavily upon manual work, there are high chances that we might forget to follow through a couple of steps and our application crashes. It’s always better to automate redundant work and minimise the chances of application failure in production. The process followed by the industry to achieve this is known as CI/CD. A CI/CD pipeline basically automates the software delivery process. CI (Continuous Integration) is where the pipeline will build and test the code. And, CD (Continuous Delivery) is the part where our application gets deployed on the server. The CI/CD approach shown here might not resonate with exactly what you might be using in your organisation. It is mostly focused on simplicity. To help you understand the basics of it. Once the basics are cleared you can easily modify your workflow as per your need. This article was heavily inspired by a CI/CD article by Raju Ahmed Shetu which can be accessed here. A simple article on CI/CD can be found here.
As always, the tutorial is divided into few small sections. Each section focuses on one tiny step at a time. This approach reduces the overall complexity of the entire tutorial and promotes ease of learning. The sections are mentioned below :
For our CI/CD pipeline to work, we need to follow some basic conventions which were primarily discussed in CI/CD article referred to above. We will do some changes to fit our use case. We will consider 3 branches; main, develop and feature.
A diagram is also provided below to show the CI/CD flow we are following.
You can use your existing Github repo or create a new repo. Your repository needs to have at least two branches namely main and develop. If it doesn’t exist, go ahead and create a new branch called develop. Now we need to protect our branches so that only after review by an authorised developer the branches can be merged. Go to Repository > Settings > Branches > Add Rule > Require pull request reviews before merging. The image shown below is how the rule would look like.
Simply go to Heroku Dashboard and press the button Create New App. Once the app has started go to addons and add Heroku Postgres. For now, we don’t need to do anything else.
There are two workflows present. We have dev.yaml and prod.yaml. Let us look at the source code of dev.yaml:
name: Dev Worflow - Only Test
on:
pull_request:
branches: [develop]
push:
branches: [develop]
jobs:
health-checkup-job: #Check the healthy by running tests
runs-on: ubuntu-latest
strategy:
max-parallel: 4
matrix:
python-version: [3.8]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run migrations
run: |
python manage.py makemigrations --noinput
python manage.py migrate --noinput
- name: Run Tests
run: |
python manage.py test
- name: Check Syntax #We are just testing the syntax in names app; pycodestyle uses pep8 conventions of max line length of 79 characters while Django recommends 119 characters
run: pycodestyle --statistics names
If you are familiar with Django most of the commands are self-explanatory. I have also added some comments on parts where it might be confusing. Every workflow file needs to have a ‘name’ then we define when it’s triggered with ‘on’. In our case, it’s getting triggered on pull_request and push on develop. Then we define the ‘jobs’ to be performed followed by ‘steps’ within the job. Learn more about the workflow and how to define them from here. Similarly, we will also have prod.yaml which will not only do the testing as in dev.yaml but will also deploy our application on Heroku. The source code is given below:
name: Main Workflow - Test and Deploy
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
health-checkup-job: #Check the healthy by running tests
runs-on: ubuntu-latest
strategy:
max-parallel: 4
matrix:
python-version: [3.8]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run migrations
run: |
python manage.py makemigrations --noinput
python manage.py migrate --noinput
- name: Run Tests
run: |
python manage.py test
- name: Check Syntax #We are just testing the syntax in names app; pycodestyle uses pep8 conventions of max line length of 79 characters while Django recommends 119 characters
run: pycodestyle --statistics names
#Before deploy job you would usually have the build job in case you are using docker images
deploy-job:
runs-on: ubuntu-latest
needs: [health-checkup-job]
if: ${{ github.event_name == 'push' }}
steps:
- uses: actions/checkout@v2
- uses: akhileshns/heroku-deploy@v3.12.12
with:
heroku_api_key: ${{secrets.HEROKU_API_KEY}}
heroku_app_name: ${{secrets.HEROKU_APP_NAME}}
heroku_email: ${{secrets.HEROKU_EMAIL}}
Notice the ‘needs: [health-checkup-job]’ action above, it basically means that to perform the ‘deploy-job’ first the ‘health-checkup-job’ needs to complete. Once your application is deployed you still need to migrate your models and create a superuser. This can be easily performed with Heroku run commands from your terminal or the Heroku Dashboard > Your App > More > Run Console.
Your Django project won’t deploy automatically unless you make some Heroku specific changes in your repository. It would be redundant work if I try to explain the process here. Instead, visit the link provided here to learn more about the deployment in Heroku.
If you have seen the ‘deploy-job’ under the prod.yaml file, you might have noticed three variables – ${{secrets.HEROKU_API_KEY}}, ${{secrets.HEROKU_APP_NAME}} and ${{secrets.HEROKU_EMAIL}}. These are special Github secret variables. To define them we need to go to Repository > Settings > Secrets > New repository secret.
HEROKU_API_KEY can be found in Heroku Account Settings. HEROKU_APP_NAME is the name of the app you created in Section B and HEROKU_EMAIL is the Email ID of your Heroku account.
If you don’t have any active repository, I would encourage you to clone my Github Repo. Feel free to clone and upload it in your own repository.
Make some changes in your feature branch and create a pull request to develop branch. Once the Health Checkup Job passes go ahead and merge it with develop. Now, you can create a pull request to main and again Health Checkup Job will run. Once it passes; merge the develop branch with main. Once the merge is successful; prod.yaml will run and first conduct the Health Check Job and when the job succeeds it will run the Deploy Job. When the Deploy Job gets completed our application gets successfully deployed in Heroku.
Our output should look something like this:
Now that our pipeline is working flawlessly we could consider improving our CI/CD pipeline and adding more steps as required. We can also work towards improving it’s performance by introducing cache. This is beyond the scope of this article as our main aim is to have a simple CI/CD pipeline. Since, our deployment is automatically handled by Github we can sit back and relax. Afterall, the machines are working for us 🙂
If you are interested you can also read another article written by me about form validations in Django.
Md. Saifur Rahman is a Full Stack Django and Laravel Developer. Additionally, he loves to spend time learning and researching about the Internet of Things (IoT). He loves to share his work and contribute to helping fellow developers.
When Neeraj Chopra bagged India's only gold medal in Tokyo 2020 Olympics, the whole nation…
Htmx is short for high power tools for HTML. It simplifies tedious work for developers.…
What is Biomechanics? We know, mechanics is the branch of physics dealing with the motion…
Django Channels enables a developer to use WebSockets and other non-HTTP protocols in any Django…
This tutorial will help you perform form validation for your model fields. An example is…
Django is an open-source Python web framework. It follows the model-template-views architectural pattern. If you…
View Comments
This is a good informative article. Can I please fork the project?
Thanks! Sure go ahead.