Github Workflows

Here’s everything you need to know about GitHub Actions workflows: how they work, how to structure them, reuse logic, and implement CI/CD pipelines for modern projects like Django, Node.js, React, or Docker.
Author

Benedict Thekkel

🧩 1. What Is a Workflow?

A workflow is a YAML file inside .github/workflows/ that defines an automated process.

Examples:

  • CI (test your code)
  • CD (deploy your app)
  • Format, lint, tag, release

🗂️ 2. Workflow File Structure

.github/
└── workflows/
    ├── ci.yml              # Code lint/test/build
    ├── deploy.yml          # Production deployment
    ├── frontend.yml        # React/Vite-specific steps
    └── nightly.yml         # Cron jobs or scheduled workflows

🔑 Each file is a standalone workflow. It can run independently or depend on others.


📄 3. Basic Workflow Template

name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Install dependencies
        run: pip install -r requirements.txt

      - name: Run tests
        run: pytest

⚡ 4. Workflow Triggers (on:)

Trigger Usage
push: On any push (you can filter branches/tags)
pull_request: On PR creation or update
workflow_dispatch Manual run from UI
schedule: Cron jobs (e.g. nightly builds)
release: On GitHub Release creation

Example:

on:
  push:
    branches: [main, dev]
  pull_request:
    types: [opened, synchronize]

🧱 5. Job Structure

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: echo "Run tests here"

  lint:
    runs-on: ubuntu-latest
    needs: test
    steps:
      - run: echo "Lint only if test passed"
Section Meaning
jobs Top-level workflow units
steps Actions or shell commands inside a job
uses Reuse community actions or your own
run Shell commands you want to execute
needs: Job dependencies (run after another job succeeds)

🔁 6. Matrix Strategy (e.g. Python 3.10 and 3.12)

strategy:
  matrix:
    python-version: [3.10, 3.12]

steps:
  - uses: actions/setup-python@v5
    with:
      python-version: ${{ matrix.python-version }}

📦 7. Using Secrets & Envs

env:
  DEBUG: false
  SECRET_KEY: ${{ secrets.DJANGO_SECRET_KEY }}

You can also write them to a .env file in the runner:

- run: |
    echo "SECRET_KEY=${{ secrets.DJANGO_SECRET_KEY }}" >> .env

🔄 8. Reusing Workflows (Reusable Workflow Calls)

Split complex logic across reusable files.

Create: .github/workflows/test-python.yml

name: Python Tests

on:
  workflow_call:
    inputs:
      python_version:
        required: true
        type: string

jobs:
  run-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ inputs.python_version }}

Use it in another file:

jobs:
  test:
    uses: ./.github/workflows/test-python.yml
    with:
      python_version: '3.12'

🔍 9. Best Practices

Practice Why it Matters
Use secrets for credentials Never store sensitive info in YAML
Split CI/CD into multiple workflows Cleaner logic, easier re-runs
Use reusable workflows for shared logic Reduces duplication
Cache dependencies (actions/cache) Speeds up installs
Use matrix builds for testing multiple versions Ensures compatibility
Define test/lint jobs independently So one failure doesn’t block others
Use artifacts for build outputs Share data between jobs

📦 10. Useful Community Actions

Action Purpose
actions/checkout@v4 Clone your repo
actions/setup-python@v5 Python env setup
actions/setup-node@v4 Node.js projects
actions/cache@v3 Cache pip/npm/uv
docker/build-push-action@v5 Build/push Docker image
github/codeql-action/init Security scanning

🧪 11. Testing Locally

Use act to run GitHub Actions workflows locally:

brew install act
act -j test

🧰 12. CD (Deploy) Examples

→ Docker + Render:

- name: Deploy to Render
  run: curl -X POST ${{ secrets.RENDER_DEPLOY_HOOK }}

→ Heroku Deploy:

- name: Deploy to Heroku
  uses: akhileshns/heroku-deploy@v3.13.15
  with:
    heroku_api_key: ${{ secrets.HEROKU_API_KEY }}
    heroku_app_name: "my-django-app"
    heroku_email: "your@email.com"

→ Fly.io Deploy:

- name: Deploy to Fly
  run: flyctl deploy --remote-only

🧭 Suggested Workflow Layout

File Description
ci.yml Run tests, lint, build
frontend.yml Build React/Vite frontend
deploy.yml Build Docker image + push
cron.yml Scheduled workflows
reusable/test-python.yml Shared logic across repos or jobs

Back to top