DEV Community

Cover image for Beginners Guide to GitHub Actions, Django and Docker
Zoo Codes
Zoo Codes

Posted on • Edited on

Beginners Guide to GitHub Actions, Django and Docker

My Workflow πŸš€ πŸš€

I have recently been working on several Django projects some for school, some as side projects and others as part of freelance work.

Overtime, I realized the importance of having a basic Devops workflow in place. This enables to tweak according to the project needs and to have a clear and consistent workflow.
This workflow can be thought of as a set of steps that are executed in order to ensure a project is built and deployed successfully.

I have also included alternative steps for docker and docker-compose files, however these are optional and can be removed if not needed.

GitHub logo KenMwaura1 / zoo_Django_Actions

Sample Django Web App for GitHub Actions

Zoo Django Actions Contributors Forks Stargazers Issues MIT License LinkedIn

<<<<<<< Updated upstream

=======

Stashed changes

Zoo django Actions

Sample Django App uilizing Github Actions and Docker for deployment and testing.

Accompanying Article

Read the accompanying article here

Table of Contents
  1. About The Project
  2. Getting Started
  3. Usage
  4. Roadmap
  5. Contributing
  6. License
  7. Contact
  8. Acknowledgments

About The Project

(back to top)

Built With

(back to top)

Getting Started

To get started, you'll need to install the following:

Prerequisites

This is an example of how to list things you need to use the software and how to install them.

Installation

  1. Clone the…

This repository contains a skeleton Django App that can be used as a starting point for a Django project, Dockerfile, Docker compose file and workflow file.
Installation instructions can be found in the README.md file.

Submission Category:

DIY Deployments

Yaml File or Link to Code πŸ†

Overview of workflow πŸ“‹

Let's start with a quick overview of the workflow. Our workflow is as follows:

  • Once code is pushed to the main branch of the repository, we run 4 parallel jobs against 4 different python versions.
    • Python 3.7, 3.8, 3.9, 3.10
  • We then install the requirements using pip3.
  • We run black to format the code.
  • We run flake8 to check for errors and warnings.
  • We run isort to sort the imports.
  • We set up a PostgreSQL database.
  • We run the migrations.
  • We run the tests(if any).
  • Finally, we run the docker-compose file to ensure no errors in container creation.

Enough Words Show Me the YAML πŸ’»

Let's take a look at the YAML file.

name: Zoo Django Actions

on: [push]

jobs:

  build:

    runs-on: ubuntu-latest
    strategy:
      max-parallel: 4
      matrix:
        python-version: [ 3.6, 3.7, 3.8, 3.9]
        database-name:
          - zoo_django_actions
        database-password:
          - postgres
        database-user:
          - postgres
        database-host:
          - 127.0.0.1
        database-port:
          - 5432

    services:
      postgres:
        image: postgres:latest
        env:
          POSTGRES_DB: ${{ matrix.database-name }}
          POSTGRES_USER: ${{ matrix.database-user }}
          POSTGRES_PASSWORD: ${{ matrix.database-password }}
        ports:
          - 5432:5432
        # Set health checks to wait until postgres has started
        options:
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5


    steps:
    - uses: actions/checkout@v2.4.0
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v2.3.1
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
    - name: Format with black
      run: |
        pip install black
        # format the files with black
        black .
    - name: Lint with flake8
      run: |
        pip install flake8
        # stop the build if there are Python syntax errors or undefined names
        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
        # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
        flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
    - name: Sort imports
      run: |
        pip install isort
        # stop the build if there are Python syntax errors or undefined names
        isort .
        isort --check --diff .
    - name: Setup test database
      env:
        POSTGRES_DB_NAME: ${{ matrix.database-name }}
        POSTGRES_USER: ${{ matrix.database-user }}
        POSTGRES_PASSWORD: ${{ matrix.database-password }}
        POSTGRES_DB_HOST: ${{ matrix.database-host }}
        POSTGRES_DB_PORT: ${{ matrix.database-port }}
        POSTGRES_DB: ${{ matrix.database-name }}
      run: |
        export DATABASE_URL=postgres://${{ matrix.database-user }}:${{ matrix.database-password }}@${{ matrix.database-host }}:${{ matrix.database-port }}/${{ matrix.database-name }}
        export SECRET_KEY=test-secret-key
        export DEBUG=1
    - name: Run migrations
      run: |
        export DATABASE_URL=postgres://${{ matrix.database-user }}:${{ matrix.database-password }}@${{ matrix.database-host }}:${{ matrix.database-port }}/${{ matrix.database-name }}
        export SECRET_KEY=test-secret-key
        export DEBUG=1
        export ALLOWED_HOSTS=localhost
        export GITHUB_WORKFLOW=True
        export MODE=workflow
        python manage.py makemigrations
        python manage.py migrate
        python manage.py migrate --run-syncdb
        python manage.py check
    - name: Run tests
      run: |
          python manage.py test
      env:
        DATABASE_URL: postgres://${{ matrix.database-user }}:${{ matrix.database-password }}@${{ matrix.database-host }}:${{ matrix.database-port }}/${{ matrix.database-name }}
        SECRET_KEY: test-secret-key
        DEBUG: 1
        ALLOWED_HOSTS: localhost
        GITHUB_WORKFLOW: True
        MODE: workflow
    - uses: actions/checkout@v2.4.0
    - name: Build the images and start the containers
      run: |
        export GITHUB_WORKFLOW=True
        export MODE="Test"
        docker-compose -f docker-compose.yml build
        docker-compose -f docker-compose.yml up -d
        # run: docker-compose up -d --build
    - name: Stop containers
      if: always()
      run: docker-compose -f "docker-compose.yml" down

Enter fullscreen mode Exit fullscreen mode

Each step is labelled with a name and a run block. The name is used to identify the step in the output. Also provides context for the step. It might seem daunting to understand all of this, but it's really simple. I recommend the GiHub Action documentation to get started.

First thing to note is workflow files are written in YAML. This is a simple way to structure your workflow. The YAML syntax is very similar to JSON. It's beyond the scope of this guide to explain the YAML syntax. However, to get started you can use the YAML Cheatsheet.

Now let's do a walk-through on adding this workflow to your project/repository:

  • Create a new directory called .github/. Note the . in the beginning.
  • Create a new directory called workflows inside the .github/ directory.
  • Create a new file called main.yml inside the workflows directory. This is the main workflow file. However, you can name it anything you want.
  • Copy the above code into the main.yml file.
  • Add and commit the .github/workflows/main.yml file to your project. This will allow you to run the workflow from the GitHub UI.
    *

    $ git add .github/workflows/main.yml
    $ git commit -m "Add workflow"
    $ git push
    
  • Now you can view your workflow on GitHub. You can also run the workflow from the GitHub UI.

Example workflow runs from the GitHub UI:

Actions screenshot

Bonus Points

GitHub Actions have a great feature, badges - which are small images that show up on your GitHub repository. You can use badges to show the status of your workflow.
To add a badge to your README file:

  1. Go to your repository.
  2. Click on the Actions tab.
  3. Select the specific workflow you want to add a badge to.
  4. Click on the Create status badge button. Action screenshot
  5. Click on the Copy status badge Markdown button. Badge screenshot
  6. Paste the markdown into your README file.
  7. Commit your changes.
  8. Push your changes to GitHub.
  9. View your badge on GitHub.

Additional Resources / Info 🏷️

Tools and resources I used to create this workflow:

Wrap Up βœ”οΈ βœ”οΈ

You've now have simple workflow that can be used to run tests, build images, run migrations, and run tests.
This is a great way to get started with GitHub Actions. Allows for so much extensibility. Go forth and automate your workflows!

This is the end of the guide. If you have any questions or comments, please feel free to reach out to me on Twitter @Ken_Mwaura1.

Buy me a coffee

Happy Coding! ✨

Top comments (7)

Collapse
 
xmclej profile image
xmclej

Hi, Could you use github secrets rather than an .env file?
The intent would be to pass the github secrets via the github actions into the django docker container.
This way your secrets are protected...is this possible?

Collapse
 
ken_mwaura1 profile image
Zoo Codes

Yes very much possible and preferable if the container was to be purely run in GitHub Actions. However, since we are also running the container locally. Using an .env allows for flexibility.

Collapse
 
xmclej profile image
xmclej

So I potentially read this as not possible if you are self-hosting and running the docker containers on a self-hosted linux machine rather than within Github Actions.

To be clear your workflow actions file would be like:

name: Deploy to Production
on: 
  workflow_dispatch:
jobs:
  build:
    runs-on: self-hosted 
...
Enter fullscreen mode Exit fullscreen mode

Would that be correct?
If so and using a .env file is required then you must add it to your github repo in order to be deployed...correct? Which in turn makes your secrets potentially viewable in plain text...

Thread Thread
 
ken_mwaura1 profile image
Zoo Codes

If the intention is to deploy then you should have a 'deploy/prod' branch of your code then have a specific workflow that triggers using GitHub Secrets. Then have the main/dev branch use either .env or placeholder values. Remember the goal is to validate that it would run in a different environment as well as pass any required integration tests.

Collapse
 
hartley94 profile image
Martin Thuo

πŸ‘πŸ‘πŸ‘.

Collapse
 
sevenearths profile image
Robert Johnstone

Why did you have the docker commands at the end of the script. I thought you'd want to build a docker container and then test your code in the container.

Collapse
 
ken_mwaura1 profile image
Zoo Codes

The goal was dockerize the app and run it in the context of GitHub Actions.