Django with GitHub actions: A beginner guide

5 months, 1 week ago | 22 min Read | 158

hey folks, if you want to host your website or are already hosted on a virtual machine and are tired to push-pull code every time this blog post is for you where you learn about GitHub actions and how we can use it to automate the process once you push the code that Github action automatically Pull the code on Virtual machine and restart the services.

What is GitHub Actions?

GitHub Actions is a CI/CD (Continuous Integration and Continuous Deployment) tool offered by GitHub. It enables you to automate your software development workflows directly in your GitHub repository. With Actions, you can create workflows that perform tasks based on specific events—such as pushing code to a repository, creating pull requests, or scheduling tasks at specific times.

In the context of a Django project, GitHub Actions can be used to automate tasks such as running tests, building your application, and deploying code to your virtual machine (VM). This way, you can keep your website or app up-to-date without manually logging into the server and pulling the latest code every time you make a change.

Setting Up GitHub Actions for Django Deployment

Let’s set up a basic workflow to automate the deployment of your Django project to your virtual machine.

Step 1: Create a GitHub Actions Workflow

In your Django project repository on GitHub, go to Actions and click on New workflow. GitHub offers several templates, but for now, let’s create a custom workflow. Follow these steps:

  1. Create a .github/workflows directory in the root of your repository if it doesn't already exist.
  2. Inside this directory, create a new file named deploy.yml (or any name you prefer).

2. Workflow Name and Trigger Event

The name defines the workflow’s name, and the on section specifies when it will run. Here, the workflow runs whenever code is pushed to the main branch.

name: Django Deployment on VM

on:
  push:
    branches:
      - main  # Specify the branch to deploy, e.g., "main"

This part of the workflow listens for pushes to the main branch, which will automatically trigger the deployment process whenever you update this branch.

3. Define the Job and Virtual Environment

The jobs section specifies what the workflow will do. Here, we define a job named deploy, which runs on an ubuntu-latest GitHub-hosted virtual environment.

jobs:
  deploy:
    runs-on: ubuntu-latest  # Runs on a GitHub-hosted Ubuntu server

This line means our job will run on the latest version of Ubuntu, which is often compatible with most Django project requirements.

4. Checkout the Code

The checkout step pulls the latest code from the GitHub repository so that we can work with it in the virtual environment.

  	steps:
      - name: Checkout code
        uses: actions/checkout@v3  # Checks out your code

Using actions/checkout@v3, we access the repository files and ensure they’re ready for the next steps.

5. Set Up SSH Key

To securely connect to the VM, we add a private SSH key as an environment variable (SSH_PRIVATE_KEY). The workflow creates an .ssh directory, saves the SSH key in ~/.ssh/id_rsa, and sets the necessary permissions. The StrictHostKeyChecking no setting bypasses prompts about host authenticity.


      - name: Setup SSH key
        env:
          SSH_PRIVATE_KEY: ${{ secrets.DO_SSH_PRIVATE_KEY }}
        run: |
          mkdir -p ~/.ssh
          echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          echo "StrictHostKeyChecking no" >> ~/.ssh/config

6. Add Known Hosts

Here, we add the VM’s IP address to the known_hosts file, allowing the GitHub Actions runner to verify the host's authenticity on SSH.


      - name: Add Known Hosts
        run: |
          ssh-keyscan -H ${{ secrets.DO_SERVER_IP }} >> ~/.ssh/known_hosts

7. Test SSH Connection

This step tests the SSH connection to ensure the VM is accessible before deployment. It uses the SSH command to print "SSH connection successful" if the connection is valid.


      - name: Test SSH Connection
        run: |
          echo "Testing SSH connection..."
          ssh -v root@${{ secrets.DO_SERVER_IP }} 'echo "SSH connection successful"'
        env:
          DO_SERVER_IP: ${{ secrets.DO_SERVER_IP }}

8. Deploy to VM

This step initiates the actual deployment. It runs commands on the VM using SSH, updating the project and restarting services as needed.


      - name: Deploy to VM
        env:
          DO_SERVER_IP: ${{ secrets.DO_SERVER_IP }}

Let’s break down each part inside this Deploy to VM step:

Connect to VM and Check for Project Directory

  1. Check if Directory Exists: If the directory /path/to/your/project doesn’t exist, it creates it.
  2. Initialize Git Repository: Inside the directory, it initializes a git repository and adds the remote URL (replace YOUR_USERNAME and YOUR_REPO with your GitHub information).

          ssh root@$DO_SERVER_IP << 'ENDSSH'
            set -e
            echo "Starting deployment..."

            # Check if directory exists
            if [ ! -d "/path/to/your/project" ]; then
              echo "Creating directory..."
              mkdir -p /path/to/your/project
              cd /path/to/your/project
              git init
              git remote add origin <https://github.com/YOUR_USERNAME/YOUR_REPO.git>
            else
              cd /path/to/your/project
            fi

Fetch Latest Changes and Check for Updates

  1. Fetch Changes: Fetches the latest code from the main branch.
  2. Check for Changes: If there are updates in the branch, it pulls the latest code.

            # Fetch latest changes
            echo "Fetching latest changes..."
            git fetch origin main || {
              echo "Failed to fetch from origin"
              exit 1
            }

            if ! git diff --quiet HEAD..origin/main 2>/dev/null; then
              echo "Changes detected, updating code..."
              git pull origin main

Activate Virtual Environment and Install Dependencies

  1. Activate Virtual Environment: Checks if the virtual environment (venv) exists and activates it.
  2. Install Dependencies: Installs any new or updated dependencies from requirements.txt.

              # Activate virtual environment if it exists
              if [ -f "venv/bin/activate" ]; then
                source venv/bin/activate
                pip install -r requirements.txt
              fi

Run Django Commands

  1. Apply Migrations: If manage.py exists, runs Django migrations to update the database.
  2. Collect Static Files: Gather static files in a central location for easy access in production.

              # Run Django commands if manage.py exists
              if [ -f "manage.py" ]; then
                python manage.py migrate --noinput
                python manage.py collectstatic --noinput
              fi

Restart Services

  1. Restart Gunicorn: Checks if Gunicorn (Django’s application server) is active and restarts it.
  2. Restart Nginx: Similarly, checks if Nginx (the web server) is active and restarts it.

              echo "Restarting services..."
              # Restart Gunicorn
              if systemctl is-active --quiet gunicorn; then
                sudo systemctl restart gunicorn || {
                  echo "Failed to restart Gunicorn"
                  exit 1
                }
                echo "Gunicorn restarted successfully"
              else
                echo "Warning: Gunicorn service not active"
              fi

              # Restart Nginx
              if systemctl is-active --quiet nginx; then
                sudo systemctl restart nginx || {
                  echo "Failed to restart Nginx"
                  exit 1
                }
                echo "Nginx restarted successfully"
              else
                echo "Warning: Nginx service not active"
              fi

              echo "Deployment completed successfully"
            else
              echo "No changes detected. Skipping deployment."
            fi
          ENDSSH

If no changes are detected, the script skips the deployment, saving time and resources.

Complete workflow file:

name: Django Deployment on VM

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v3

      - name: Setup SSH key
        env:
          SSH_PRIVATE_KEY: ${{ secrets.DO_SSH_PRIVATE_KEY }}
        run: |
          mkdir -p ~/.ssh
          echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          echo "StrictHostKeyChecking no" >> ~/.ssh/config
          
      - name: Add Known Hosts
        run: |
          ssh-keyscan -H ${{ secrets.DO_SERVER_IP }} >> ~/.ssh/known_hosts

      - name: Test SSH Connection
        run: |
          echo "Testing SSH connection..."
          ssh -v root@${{ secrets.DO_SERVER_IP }} 'echo "SSH connection successful"'
        env:
          DO_SERVER_IP: ${{ secrets.DO_SERVER_IP }}

      - name: Deploy to VM
        env:
          DO_SERVER_IP: ${{ secrets.DO_SERVER_IP }}
        run: |
          ssh root@$DO_SERVER_IP << 'ENDSSH'
            set -e
            echo "Starting deployment..."
            
            # Check if directory exists
            if [ ! -d "/path/to/your/projectz" ]; then
              echo "Creating directory..."
              mkdir -p /path/to/your/project
              cd /path/to/your/project
              git init
              git remote add origin <https://github.com/YOUR_USERNAME/YOUR_REPO.git>
            else
              cd /path/to/your/project
            fi
            
            # Fetch latest changes
            echo "Fetching latest changes..."
            git fetch origin main || {
              echo "Failed to fetch from origin"
              exit 1
            }
            
            if ! git diff --quiet HEAD..origin/main 2>/dev/null; then
              echo "Changes detected, updating code..."
              git pull origin main
              
              # Activate virtual environment if it exists
              if [ -f "venv/bin/activate" ]; then
                source venv/bin/activate
                pip install -r requirements.txt
              fi
              
              # Run Django commands if manage.py exists
              if [ -f "manage.py" ]; then
                python manage.py migrate --noinput
                python manage.py collectstatic --noinput
              fi
              
              echo "Restarting services..."
              # Restart Gunicorn
              if systemctl is-active --quiet gunicorn; then
                sudo systemctl restart gunicorn || {
                  echo "Failed to restart Gunicorn"
                  exit 1
                }
                echo "Gunicorn restarted successfully"
              else
                echo "Warning: Gunicorn service not active"
              fi
              
              # Restart Nginx
              if systemctl is-active --quiet nginx; then
                sudo systemctl restart nginx || {
                  echo "Failed to restart Nginx"
                  exit 1
                }
                echo "Nginx restarted successfully"
              else
                echo "Warning: Nginx service not active"
              fi
              
              echo "Deployment completed successfully"
            else
              echo "No changes detected. Skipping deployment."
            fi
          ENDSSH

Final Step: Add Your Secrets

To securely connect to your virtual machine, add an SSH key to your repository secrets:

  1. In your GitHub repository, go to Settings > Secrets > Actions.
  2. Click New Repository secret.
  3. Add a name (e.g., SSH_KEY) and paste the content of your private SSH key.

like here I use do_server_ip and do_ssh_private_key (make sure you want to copy the complete private key )

-----BEGIN OPENSSH PRIVATE KEY-----
[key content]
-----END OPENSSH PRIVATE KEY-----

The "server_ip" refers to your virtual machine's IP address. It's similar to when you run the SSH command locally using "ssh username@host" — in this case, we use the host (IP address) for the connection.

Test Your Workflow

Push a change to your main branch (or whichever branch you specify). GitHub Actions should now trigger the workflow, deploy your changes, and restart your Django services on your virtual machine!

About Me

...

Hello! My name is Jatin Yadav and I enjoy creating websites I completed my graduation on june ,2018 and I want to be a professional web developer. The word which

Read More >
Cinque Terre

Make Changes | © 2024 Makechanges.xyz