Automating workflows is an essential step in helping you maintain code quality in your applications – especially when working on both frontend and backend code in a single repository.
In this guide, we’ll walk through setting up automated GitHub workflows for a Python backend (using Flask or Django) and a React frontend. These workflows help test and validate code changes automatically, making sure any issues are caught early.
We’ll assume:
-
You’ve already written unit tests for your React components and backend routes.
-
Your project is set up as a monorepo, with separate directories for frontend and backend.
-
You’re familiar with GitHub Actions, the platform we’ll use for automation, and that you’re using the
ubuntu-latest
environment provided by GitHub.
Step 1: Create GitHub Actions Workflows
In this step, we’ll define two GitHub Actions workflows, one for the frontend and another for the backend. These workflows will run tests automatically whenever changes are pushed to the main
branch.
What is a GitHub Action Workflow?
A GitHub Action workflow is a set of instructions that tell GitHub how to automatically execute tasks based on certain events.
Here, our workflows will run tests and deploy the app only if the tests pass. Workflows are triggered by events, such as a push to a branch, and consist of jobs that define the tasks we want to automate.
Frontend CI/CD Pipeline
Let’s start by creating a new file in your repository at .github/workflows/frontend.yml
. This file will set up an automated pipeline to handle the frontend testing and deployment. Then, define the workflow with the following content:
name: Frontend CI/CD Pipeline
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Cache Node.js modules
uses: actions/cache@v3
with:
path: ./frontend/node_modules
key: ${{ runner.os }}-node-${{ hashFiles('./frontend/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '20'
- name: Install frontend dependencies
run: yarn install
working-directory: ./frontend
- name: Run frontend tests
run: yarn test
working-directory: ./frontend
Here’s a breakdown of what each part does:
-
on: push
: This triggers the workflow whenever there’s a push to themain
branch. -
Checkout code: This step uses the GitHub Action to check out the repository code.
-
Cache Node.js modules: Caches
node_modules
to speed up workflow execution on subsequent runs. -
Set up Node.js: Sets up the Node.js environment for dependency installation and testing.
-
Install dependencies and run tests: Installs packages with Yarn and then runs the pre-written tests to verify that the frontend works as expected.
Backend CI/CD Pipeline
Now, let’s create a separate file for the backend workflow at .github/workflows/backend.yml
. This file will automate testing and deployment for the Python backend.
name: Backend CI/CD Pipeline
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Cache Python packages
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('./backend/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.8'
- name: Create Virtual Environment
run: python3 -m venv venv
working-directory: ./backend
- name: Install backend dependencies
run: |
source venv/bin/activate
pip install -r requirements.txt
working-directory: ./backend
- name: Configure DATABASE_URL securely
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: |
if [ -z "$DATABASE_URL" ]; then
echo "DATABASE_URL is missing" >&2
exit 1
fi
- name: Run tests with pytest
run: |
source venv/bin/activate
pytest tests/ --doctest-modules -q --disable-warnings
working-directory: ./backend
- name: Deploy to Production
if: ${{ success() }}
run: |
echo "Deploying to production..."
- name: Notify on Failure
if: ${{ failure() }}
run: |
echo "Build failed! Sending notification..."
Here’s what this workflow does:
-
Checks out code and caches Python packages for faster execution on repeated runs.
-
Sets up Python and creates a virtual environment to isolate dependencies.
-
Installs dependencies in the virtual environment from
requirements.txt
. -
Configures environment variables securely with GitHub Secrets. In this example, we’re using a database URL that’s stored in a GitHub secret for secure access.
-
Runs backend tests with
pytest
, which checks that the backend routes and functions work correctly.
Step 2: Configure Secrets
For security, let’s set up GitHub Secrets to store sensitive information, like database connection strings.
-
Go to your GitHub repository and select Settings.
-
In the sidebar, select “Secrets and variables” from the sidebar, then click on “Actions“.
-
Add a new repository secret:
-
Name:
DATABASE_URL
-
Value: Your actual database connection string.
-
Using GitHub Secrets keeps sensitive data safe and prevents it from appearing in your codebase.
Step 3: Commit and Push Changes
Once your workflow files are ready, commit and push the changes to the main
branch. Each time you push changes to main
, GitHub Actions will trigger these workflows automatically, ensuring your code is thoroughly tested.
Step 4: Monitor Workflow Runs
After pushing your changes, navigate to the Actions tab in your GitHub repository to monitor the workflow runs. Here’s what you’ll find:
-
Workflow runs: This page lists each time a workflow is triggered. You can see if the workflow succeeded, failed, or is in progress.
-
Logs: Click on a specific workflow run to view detailed logs. Logs are divided by steps, so you can see exactly where an issue occurred if something goes wrong.
Identifying Issues in Logs
Each step’s log provides insights into any problems:
-
If dependencies fail to install, you’ll see error messages specifying which package caused the issue.
-
If tests fail, logs will list the specific tests and reasons for the failure, helping you debug quickly.
-
For workflows that use secrets, errors related to missing secrets will appear in the environment setup steps, allowing you to fix any configuration issues.
By understanding how to interpret these logs, you can address issues proactively and ensure smooth, reliable deployments.
Conclusion
By following these steps, you’ve set up automated GitHub workflows for both the frontend and backend of your application.
This setup ensures your tests run automatically with each push to the main
branch, helping maintain high code quality and reliability.
With automated workflows, you can focus more on building features and less on manually testing code, knowing that your workflows will alert you to any issues early on.
Source: freeCodeCamp Programming Tutorials: Python, JavaScript, Git & MoreÂ