Pre-requisite

  • Django
  • Git and Github (Check our blog to get started with git here)

Overview

In this blog, we will create a simple Django app. And then we will deploy it on Heroku. At last, we will use GitHub actions to create a workflow to auto-deploy our app every time we push a new change in our main branch (We will talk about it later).

What is GitHub action?

Github action is a Platform to automate developer workflow. But what are these workflows? The workflow involves coding, building, testing, releasing, and deploying a project. In development, all big projects have their workflow and Github action helps in automating these workflows with the help of world-class CI/CD.

What is the CI/CD pipeline?

CI/CD stands for continuous integration and continuous deployment or continuous delivery.

CI is a development process where the team members integrate their work in the shared repository using some version control like git. When the code is pushed in a repository on Github it is put under several automated tests. So every time someone from the team pushed into the master branch the code goes through this workflow. After the CI process, Every change that passes through automation tests is deployed to production automatically. This helps in fast execution while not compromising the quality of the production.

Setting up Heroku account

Next, we will first set up a Heroku account. Sign up on Heroku. Once you are signed up on Heroku, download Heroku CLI from here. Make sure you have git installed on your system before installing Heroku CLI. If you are unfamiliar with git here is a beginner guide.

Once you are done with installing Heroku CLI. Go to your command line and type :

Heroku --version

If the installation of Heroku CLI is done it will show you the version of the same.

Now, the first thing you need to do is login into your Heroku account through your command line. To do this type :

heroku login

It will ask you to type any key to continue. Press a key and you will be redirected to a page like this, login into your Heroku account.

Once you are authenticated you can continue with your deployment. Now let's first create our app on Heroku. To create an app you need to type the below command on your command line.

heroku create "NAME OF YOUR APP"

Now, on your command line type.

heroku open

It will open the URL for your created app on your default browser. But we haven't deployed anything yet, This is Heroku's default webpage. Now let's create a simple Django app and configure it so that we can deploy it on Heroku.

Creating a simple Django app

It's time to make a simple Django app that we can deploy on Heroku and later use GitHub actions to auto-deploy it. If you have a Django project that you already have created and you want to deploy it you can continue with it, or else you can check out a simple Django app that I have created to follow along with the tutorial. Here is a link to the project's GitHub repo. To deploy our app on Heroku we need to first update some settings and add some files to it.

Adding requirements.txt file

First, we need to add a file named requirements.txt in our root directory. If you are following along with this tutorial you can simply copy the content of requirements.txt from here. Otherwise, Run command:

pip freeze > requirements.txt

what this command does is it copies the environment all packages to the requirements.txt file. This is usually done when multiple people are working on the project or you are trying to copy a project to another location (Here we will be copying it to the Heroku server) where we want to recreate the same environment to make the project run.

Install gunicorn and adding Procfile

Install gunicorn and add it to your requirements.txt file.

pip install gunicorn

Now, add a file called "Procfile" in your root directory. This file has no extension and is case-sensitive. Do make sure that the name has capital 'P' in it. In this file, we declare our web process and entry points.

Open the Procfile and add the following line to it:

web: gunicorn djangoprojectname.wsgi

Here web is the process type, gunicorn is the command needed to run our web process ( gunicorn helps to communicate our python code with our web server). And it takes the help of the WSGI file inside our project to do that.

Note: Put your own Django project name in place of 'djangoprojectname'.

Setting path for static files for production :

Go to your project settings.py file and add a line to tell Heroku where to look for static files.

STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

We can manage static files of our project using a python library called whitenoise. To do that we need to first install it.

pip install whitenoise

After installing it add it to your requirements.txt file and also add it to the middleware list in your settings.py. The middleware should be below your "django.middleware.security.SecurityMiddleware" middleware and above all other middlewares.

'whitenoise.middleware.WhiteNoiseMiddleware'

Adding allowed host and DEBUG

In your settings.pyadd the Heroku app you created above in your allowed host and set DEBUG to False. When we are in development we want the DEBUG to be true to know the error and its traceback which comes in handy in debugging. But in production, this could help other people to find vulnerabilities in our site and we definitely do not want that.

Hiding the secret key

In your settings.py there is a secret key above your DEBUG setting. This key is used to sign session cookies. So we need to make it secure so that someone could not modify the cookies sent by our app.

We will store this secret in a variable in our Heroku app settings. To do that open your Heroku account. And go to the app and click on settings.

Then click on Config vars.

Here give a name to your variable and paste the value of the secret key from your settings.py to the value field.

Now, in your settings.py set your secret key as follows:

SECRET_KEY = os.environ['SECRET_KEY']

Now, the secret key is secure.

Adding .gitignore file

Lastly, add a .gitignore file in your root directory this file contains files that you intentionally do not want git to track. You can copy the content of the .gitignore file from our projects github repository directly. Here is link to it.

We are done with all the configurations now and our Django app is ready for deployment.

Deployment on Heroku

Now, open your commad line and in your django project root directory type:

git init

It initialize or create a new repository.

Next, add files to the staging area this is done using the command:

git add -A

Next, commit the files that are in the staging area, this is done using the command :

git commit -m "first version of site"

Now it is time to deploy.

Run the following command

git push heroku master

It pushes our app to the Heroku repository and runs the site after uploading the app.

Voilaa!We are done with the deployment.

Creating continuous deployment

Now, for continuous deployment, we need a GitHub account. Create a new repository on GitHub.

Run the following commands on terminal

git branch -M main

This will create a branch called main.

git remote add origin "url of your github repository"
git push -u origin main

This will push our Django project to our remote GitHub repository.

Continuous Deployment

Now, in our project's root directory create a directory called.github, and inside that create another directory called workflows. Inside workflow create a file called "django.yml". You can give a file with the name of your choice but it should have a .yml extension.

Now, let's write our workflow to auto-deploy any change that we make in our project. But before that let's first understand the flow and some keywords related to Github actions.

Events

An event is an activity that triggers our workflow. Like we can assign our workflow to trigger every time we push something in the main branch.

Jobs

Each workflow is made up of several jobs. A job further is a combination of several steps.

Steps

Steps are commands or actions that we want to execute in the runner.

Runner

A runner is a machine installed with GitHub Actions runner applications. The jobs we create are run on these servers and each job runs on a new virtual environment. These runners listen to a job from the workflow, runs them, and then send the progress or results back to Github.

Actions

Actions are the smallest building block of a workflow. These are commands that are combined with steps to create a job.

Now, since we are now little bit familiar with some basic concepts, copy the following code in your .yml file.

name: CD

# Controls when the workflow will run
on:
  # Triggers the workflow on push request events but only for the main branch
  push:
    branches: [ main ]

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v2

      # Runs a single command using the runners shell
      - name: Set up Python
        uses: actions/setup-python@v2
        with:
          python-version: 3.7

      - name: Install Dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt

  deploy:
    name: Deploy to Heroku
    # The type of runner that the job will run on
    runs-on: ubuntu-latest
    needs: build

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps: 
      - uses: actions/checkout@v2
      - uses: akhileshns/heroku-deploy@v3.12.12 # This is the action
        with:
          heroku_api_key: ${{secrets.HEROKU_API_KEY}}
          heroku_app_name: ${{secrets.HEROKU_APP}} #Must be unique in Heroku
          heroku_email: ${{secrets.HEROKU_EMAIL}}
      - name: Deployed successfully
        run: |
          echo Deployed!!

Let's try to understand what is happening in this workflow.

The first line is the name of our workflow I have given it the name CD.

The on keyword declares events that will trigger this workflow. Here our workflow will be triggered on a push request but only for the main branch.

A workflow run is made up of one or more jobs. Jobs define what to do when a particular event is been triggered. You can think of it collection of tasks that need to be performed when a particular event is triggered. Here we have two jobs in our workflow build and deploy.

The first job we have in our workflow is 'build'. It runs on an Ubuntu operating system. The keyword 'uses' tells the job to retrieve version2 of the action actions/checkout/@v2. This action checks out and downloads our repository to the runner(Here our runner is ubuntu that we defined earlier). The next step or action in our job uses the actions/setup-python@v2 action to install the specified version of the python in our environment. The run keyword executes a command on our runner. Here we are using python to install or upgrade pip and then installing the required packages for our Django App to deploy on Heroku..

After 'build' is executed successfully the second job in our workflow will run. It is the 'deploy' job. It also runs on Ubuntu operating system and it needs the 'build' job. If the build job fails to execute, the deploy job will not run.

In our deploy job, we will again use pre-defined action to check out our repository under $GITHUB_WORKSPACE, so our workflow can access it. And we use akhileshns/heroku-deploy@v3.12.12 actionwhich helps in deploying our repository to Heroku. To deploy our app to Heroku we need Heroku credentials to log in. But we cannot put our credentials in public. So we need to create a Heroku API token and use it to log in to our Heroku account through actions. To generate the token use the following Heroku command:

heroku authorizations:create

It will generate a token:

Now, you can put this in your workflow directly but your Github repository can be public and we do not want to expose it in public. So the way we put this in our GitHub is through GitHub secrets.

Go to your project repository and click on settings:

Then click on secrets:

Click on new repository secrets:

And now give your secret a name and value.

And now you are ready to use it in your Github actions workflow.

Similarly, you can also create secrets for your app name and email.

Now, we are done with our workflow. Pushthe YAML file in your Github repository, first by adding and committing and then push it.

git commit -am "added YAML file"
git push

Now, let's update some files in our local repository and push them to the Github repository and check whether our auto-deploy is working or not (Remember every time we will push into our repo our app will be automatically deployed to Heroku).

Change your home.html in your templates as follows:

home.html

{% extends 'blog/base.html' %}
{% block content%}
    <h1>Blog home</h1>
    <div class="jumbotron">
        <h2>Blog 1</h2>
        <strong>By xyz</strong>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, 
        sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 
        Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 
        Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
        Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>

          <h2>Blog 2</h2>
        <strong>By xyz</strong>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, 
        sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 
        Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 
        Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
        Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
    <div>
{% endblock content %}

Again, add and commit the new changes and push them in your Github repository. Once your workflow is executed successfully. Then go to your Heroku app URL and check whether your app is auto deployed or not.

Conclusion

Visit your Heroku app URL and you will see the updates are auto-deployed.

Happy Coding