What is GitHub Actions?

GitHub Actions is a CI/CD platform built directly into GitHub. It allows you to automate tasks like testing code, building applications, and deploying to production - all triggered by events in your repository.

Think of it as having a robot assistant that watches your repository. Whenever something happens (like pushing code), the robot performs tasks you've defined automatically.

Key Concepts

  • Workflow: An automated process defined in a YAML file
  • Event: Something that triggers a workflow (push, pull request, etc.)
  • Job: A set of steps that run on the same runner
  • Step: Individual task within a job
  • Action: Reusable unit of code (like a function)
  • Runner: Server that runs your workflows
Workflow Structure:
┌─────────────────────────────────────────┐
│  Workflow (.github/workflows/ci.yml)    │
│  ├── Event: push to main               │
│  ├── Job 1: test                        │
│  │   ├── Step 1: Checkout code         │
│  │   ├── Step 2: Setup Python          │
│  │   └── Step 3: Run tests             │
│  └── Job 2: deploy                      │
│      ├── Step 1: Build                  │
│      └── Step 2: Deploy                 │
└─────────────────────────────────────────┘

Your First Workflow

Create a file at .github/workflows/ci.yml:

# .github/workflows/ci.yml
name: CI

# When to run
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

# What to run
jobs:
  test:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Set up Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'

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

    - name: Run tests
      run: pytest

Common Triggers (Events)

# On push to specific branches
on:
  push:
    branches: [main, develop]

# On pull request
on:
  pull_request:
    branches: [main]

# On schedule (cron)
on:
  schedule:
    - cron: '0 0 * * *'  # Daily at midnight

# Manual trigger
on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Environment to deploy'
        required: true
        default: 'staging'

# Multiple triggers
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  release:
    types: [published]

Complete Python CI Workflow

# .github/workflows/python-ci.yml
name: Python CI

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

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

    - name: Set up Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'

    - name: Install linting tools
      run: pip install flake8 black isort

    - name: Run linters
      run: |
        flake8 .
        black --check .
        isort --check-only .

  test:
    runs-on: ubuntu-latest
    needs: lint  # Run after lint job

    strategy:
      matrix:
        python-version: ['3.9', '3.10', '3.11']

    steps:
    - uses: actions/checkout@v4

    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python-version }}

    - name: Cache pip dependencies
      uses: actions/cache@v3
      with:
        path: ~/.cache/pip
        key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}

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

    - name: Run tests with coverage
      run: pytest --cov=app --cov-report=xml

    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v3
      with:
        file: ./coverage.xml

  build:
    runs-on: ubuntu-latest
    needs: test
    if: github.ref == 'refs/heads/main'

    steps:
    - uses: actions/checkout@v4

    - name: Build Docker image
      run: docker build -t myapp:${{ github.sha }} .

    - name: Log in to Docker Hub
      uses: docker/login-action@v3
      with:
        username: ${{ secrets.DOCKER_USERNAME }}
        password: ${{ secrets.DOCKER_PASSWORD }}

    - name: Push to Docker Hub
      run: |
        docker tag myapp:${{ github.sha }} myuser/myapp:latest
        docker push myuser/myapp:latest

Using Secrets

Store sensitive data securely:

# 1. Add secrets in GitHub:
#    Repository Settings → Secrets → Actions → New secret

# 2. Use in workflow:
env:
  DATABASE_URL: ${{ secrets.DATABASE_URL }}
  API_KEY: ${{ secrets.API_KEY }}

steps:
- name: Deploy
  run: ./deploy.sh
  env:
    SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
    SERVER_HOST: ${{ secrets.SERVER_HOST }}

Matrix Builds

Test across multiple configurations:

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        python-version: ['3.9', '3.10', '3.11']
        exclude:
          - os: macos-latest
            python-version: '3.9'

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

Deploy to Cloud Platforms

# Deploy to AWS
- name: Configure AWS credentials
  uses: aws-actions/configure-aws-credentials@v4
  with:
    aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
    aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    aws-region: us-east-1

- name: Deploy to S3
  run: aws s3 sync ./build s3://my-bucket

# Deploy to Heroku
- name: Deploy to Heroku
  uses: akhileshns/heroku-deploy@v3.12.12
  with:
    heroku_api_key: ${{ secrets.HEROKU_API_KEY }}
    heroku_app_name: my-app
    heroku_email: me@example.com

# Deploy to DigitalOcean
- name: Deploy to DigitalOcean
  uses: appleboy/ssh-action@v1.0.0
  with:
    host: ${{ secrets.DO_HOST }}
    username: ${{ secrets.DO_USERNAME }}
    key: ${{ secrets.DO_SSH_KEY }}
    script: |
      cd /var/www/myapp
      git pull
      docker-compose up -d --build

Reusable Workflows

# .github/workflows/reusable-test.yml
name: Reusable Test Workflow

on:
  workflow_call:
    inputs:
      python-version:
        required: true
        type: string

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-python@v5
      with:
        python-version: ${{ inputs.python-version }}
    - run: pip install -r requirements.txt
    - run: pytest

# Use in another workflow
jobs:
  call-tests:
    uses: ./.github/workflows/reusable-test.yml
    with:
      python-version: '3.11'

Best Practices

  • Cache dependencies: Speed up builds by caching pip/npm packages
  • Use specific versions: Pin action versions (e.g., @v4 not @latest)
  • Fail fast: Run quick checks (lint) before slow ones (tests)
  • Use secrets: Never hardcode credentials
  • Parallel jobs: Run independent jobs concurrently
  • Limit permissions: Use least privilege for tokens
  • Add status badges: Show build status in README
# Add badge to README.md
![CI](https://github.com/username/repo/actions/workflows/ci.yml/badge.svg)

Master GitHub Actions with Expert Mentorship

Our Full Stack Python program covers CI/CD with GitHub Actions. Learn to automate testing, building, and deployment with personalized guidance.

Explore Full Stack Python Program

Related Articles