Prerequisites
- Basic understanding of programming concepts 📝
- Python installation (3.8+) 🐍
- VS Code or preferred IDE 💻
What you'll learn
- Understand the concept fundamentals 🎯
- Apply the concept in real projects 🏗️
- Debug common issues 🐛
- Write clean, Pythonic code ✨
🎯 Introduction
Welcome to this exciting tutorial on GitHub Actions! 🎉 In this guide, we’ll explore how to automate your Python workflows with GitHub’s powerful CI/CD platform.
You’ll discover how GitHub Actions can transform your development process. Whether you’re testing code 🧪, deploying applications 🚀, or automating repetitive tasks 🔄, understanding GitHub Actions is essential for modern Python development.
By the end of this tutorial, you’ll feel confident creating and managing workflows for your Python projects! Let’s dive in! 🏊♂️
📚 Understanding GitHub Actions
🤔 What is GitHub Actions?
GitHub Actions is like having a robot assistant 🤖 that watches your repository and performs tasks automatically. Think of it as a smart butler 🎩 that springs into action whenever specific events occur in your project.
In technical terms, GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows you to automate your build, test, and deployment pipeline. This means you can:
- ✨ Run tests automatically on every push
- 🚀 Deploy code when merging to main
- 🛡️ Check code quality and security
- 📦 Publish packages automatically
- 🔄 Automate any repetitive task
💡 Why Use GitHub Actions?
Here’s why developers love GitHub Actions:
- Native Integration 🔗: Built into GitHub - no external services needed
- Matrix Builds 🎯: Test across multiple Python versions simultaneously
- Marketplace 🛒: Thousands of pre-built actions to use
- Free for Public Repos 💰: Generous free tier for open source
- Event-Driven ⚡: Trigger workflows on any GitHub event
Real-world example: Imagine maintaining a Python package 📦. With GitHub Actions, you can automatically run tests, build documentation, and publish to PyPI whenever you create a new release tag!
🔧 Basic Syntax and Usage
📝 Your First Workflow
Let’s start with a simple Python testing workflow:
# 👋 .github/workflows/python-tests.yml
name: Python Tests 🧪
# 🎯 When to run this workflow
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
# 💼 What jobs to run
jobs:
test:
# 🖥️ Which OS to use
runs-on: ubuntu-latest
steps:
# 📥 Check out the code
- uses: actions/checkout@v3
# 🐍 Set up Python
- name: Set up Python 3.11
uses: actions/setup-python@v4
with:
python-version: '3.11'
# 📦 Install dependencies
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest pytest-cov
# 🧪 Run tests
- name: Run tests with pytest
run: |
pytest tests/ --cov=myapp --cov-report=xml
💡 Explanation: This workflow runs your Python tests automatically whenever you push code or open a pull request!
🎯 Common Workflow Patterns
Here are patterns you’ll use daily:
# 🏗️ Pattern 1: Matrix testing across Python versions
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
os: [ubuntu-latest, windows-latest, macos-latest]
# 🎨 Pattern 2: Conditional steps
- name: Deploy to production 🚀
if: github.ref == 'refs/heads/main'
run: |
echo "Deploying to production!"
# 🔄 Pattern 3: Scheduled workflows
on:
schedule:
# ⏰ Run daily at 2 AM UTC
- cron: '0 2 * * *'
💡 Practical Examples
🛒 Example 1: Python Package CI/CD Pipeline
Let’s build a complete workflow for a Python package:
# 🎯 .github/workflows/python-package.yml
name: Python Package CI/CD 📦
on:
push:
branches: [ main ]
tags: [ 'v*' ]
pull_request:
jobs:
# 🧪 Test job
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.8', '3.9', '3.10', '3.11']
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }} 🐍
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies 📦
run: |
python -m pip install --upgrade pip
pip install -e .[dev]
- name: Lint with flake8 🔍
run: |
pip install flake8
flake8 . --count --select=E9,F63,F7,F82 --show-source
- name: Type check with mypy 🎯
run: |
pip install mypy
mypy src/
- name: Test with pytest 🧪
run: |
pytest tests/ -v --cov=src/ --cov-report=term
# 📚 Build documentation
docs:
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v3
- name: Set up Python 🐍
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Build docs with Sphinx 📖
run: |
pip install -e .[docs]
cd docs && make html
- name: Deploy to GitHub Pages 🌐
if: github.ref == 'refs/heads/main'
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs/_build/html
# 🚀 Publish to PyPI
publish:
runs-on: ubuntu-latest
needs: [test, docs]
if: startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/checkout@v3
- name: Set up Python 🐍
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Build package 📦
run: |
pip install build
python -m build
- name: Publish to PyPI 🚀
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
🎯 Try it yourself: Add a step to create a GitHub release with your package artifacts!
🎮 Example 2: Automated Code Quality Checks
Let’s create a workflow that maintains code quality:
# 🛡️ .github/workflows/code-quality.yml
name: Code Quality Guardian 🛡️
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
quality-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # 📊 Full history for better analysis
- name: Set up Python 🐍
uses: actions/setup-python@v4
with:
python-version: '3.11'
# 🎨 Code formatting check
- name: Check code formatting with Black
run: |
pip install black
black --check .
# 🔍 Import sorting
- name: Check import sorting with isort
run: |
pip install isort
isort --check-only .
# 🛡️ Security check
- name: Security scan with Bandit
run: |
pip install bandit[toml]
bandit -r src/ -f json -o bandit-report.json
# 📊 Code complexity
- name: Check complexity with Radon
run: |
pip install radon
radon cc src/ -a -nb
# 💯 Coverage report
- name: Generate coverage report
run: |
pip install pytest pytest-cov
pytest --cov=src/ --cov-report=xml
- name: Upload coverage to Codecov 📈
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
fail_ci_if_error: true
# 💬 Comment PR with results
- name: Comment PR with quality report
uses: actions/github-script@v6
if: always()
with:
script: |
const fs = require('fs');
const coverage = fs.readFileSync('coverage.xml', 'utf8');
const comment = `## 📊 Code Quality Report
✅ All quality checks passed!
📈 Coverage: 95%
🎨 Code style: Compliant
🛡️ Security: No issues found
📊 Complexity: Within limits
Great job! 🎉`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});
🚀 Advanced Concepts
🧙♂️ Advanced Topic 1: Custom Actions
Create your own reusable Python action:
# 🎯 action.yml for a custom Python action
name: 'Python Magic Linter ✨'
description: 'A magical Python linting action'
inputs:
python-version:
description: 'Python version to use'
required: false
default: '3.11'
strict-mode:
description: 'Enable strict linting'
required: false
default: 'false'
runs:
using: 'composite'
steps:
- name: Set up Python 🐍
uses: actions/setup-python@v4
with:
python-version: ${{ inputs.python-version }}
- name: Run magic linting ✨
shell: bash
run: |
echo "🪄 Starting magical linting..."
pip install pylint mypy black isort
if [[ "${{ inputs.strict-mode }}" == "true" ]]; then
echo "🔥 Strict mode activated!"
pylint src/ --fail-under=9.0
else
echo "😊 Regular mode"
pylint src/ --exit-zero
fi
🏗️ Advanced Topic 2: Matrix Strategies and Environments
Complex deployment with environments:
# 🚀 Advanced deployment workflow
name: Multi-Environment Deploy 🌍
on:
push:
branches: [main, develop]
jobs:
# 🧪 Test in multiple environments
test:
strategy:
matrix:
environment: [dev, staging, prod]
python-version: ['3.10', '3.11']
exclude:
# 🚫 Don't test prod with older Python
- environment: prod
python-version: '3.10'
runs-on: ubuntu-latest
environment: ${{ matrix.environment }}
steps:
- uses: actions/checkout@v3
- name: Run environment-specific tests 🧪
env:
ENV_NAME: ${{ matrix.environment }}
API_KEY: ${{ secrets[format('{0}_API_KEY', matrix.environment)] }}
run: |
echo "🌍 Testing in $ENV_NAME environment"
pytest tests/ -m $ENV_NAME
# 🚀 Deploy to environments
deploy:
needs: test
runs-on: ubuntu-latest
strategy:
matrix:
environment: [dev, staging]
environment:
name: ${{ matrix.environment }}
url: https://${{ matrix.environment }}.myapp.com
steps:
- name: Deploy to ${{ matrix.environment }} 🚀
run: |
echo "🎯 Deploying to ${{ matrix.environment }}!"
# Your deployment script here
⚠️ Common Pitfalls and Solutions
😱 Pitfall 1: Secrets in Logs
# ❌ Wrong way - secrets might leak!
- name: Deploy with credentials
run: |
echo "Deploying with key: ${{ secrets.API_KEY }}" # 💥 Don't log secrets!
deploy.py --key ${{ secrets.API_KEY }}
# ✅ Correct way - mask secrets!
- name: Deploy safely
run: |
echo "🔐 Deploying with secure credentials..."
deploy.py --key "${{ secrets.API_KEY }}"
env:
API_KEY: ${{ secrets.API_KEY }}
🤯 Pitfall 2: Forgetting Dependencies Cache
# ❌ Slow - reinstalls every time!
- name: Install dependencies
run: |
pip install -r requirements.txt # 🐌 Downloads everything again
# ✅ Fast - uses cache!
- name: Cache Python dependencies 📦
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install dependencies
run: |
pip install -r requirements.txt # ⚡ Lightning fast with cache!
🛠️ Best Practices
- 🎯 Keep Workflows Focused: One workflow per concern (test, deploy, etc.)
- 📝 Use Descriptive Names: Make workflow purpose clear
- 🛡️ Secure Your Secrets: Never hardcode sensitive data
- ⚡ Cache Dependencies: Speed up workflows with caching
- 🎨 Reuse with Actions: Create custom actions for repeated tasks
- 📊 Monitor Usage: Track your Actions minutes
- 🔄 Version Your Actions: Pin action versions for stability
🧪 Hands-On Exercise
🎯 Challenge: Build a Complete Python Project Workflow
Create a GitHub Actions workflow that:
📋 Requirements:
- ✅ Tests on Python 3.9, 3.10, and 3.11
- 🏷️ Checks code quality (linting, formatting)
- 📊 Generates test coverage reports
- 📚 Builds and tests documentation
- 🚀 Deploys to PyPI on version tags
- 🎨 Creates GitHub releases automatically
🚀 Bonus Points:
- Add dependency vulnerability scanning
- Implement automatic PR labeling
- Create deployment environments with approvals
💡 Solution
🔍 Click to see solution
# 🎯 Complete Python project workflow!
name: Python Project CI/CD 🚀
on:
push:
branches: [main, develop]
tags: ['v*']
pull_request:
schedule:
- cron: '0 0 * * 0' # 📅 Weekly security scan
env:
PYTHON_VERSION: '3.11'
jobs:
# 🏷️ Label PRs automatically
label:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v4
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
# 🧪 Test across Python versions
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.9', '3.10', '3.11']
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }} 🐍
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Cache dependencies 📦
uses: actions/cache@v3
with:
path: |
~/.cache/pip
.tox
key: ${{ runner.os }}-py${{ matrix.python-version }}-${{ hashFiles('**/requirements*.txt') }}
- name: Install dependencies 📥
run: |
python -m pip install --upgrade pip
pip install tox tox-gh-actions
- name: Test with tox 🧪
run: tox
- name: Upload coverage 📊
uses: codecov/codecov-action@v3
if: matrix.python-version == '3.11' && matrix.os == 'ubuntu-latest'
# 🎨 Code quality checks
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 🐍
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install quality tools 🛠️
run: |
pip install black isort mypy pylint bandit safety
- name: Format check with Black 🎨
run: black --check .
- name: Import sort check 📑
run: isort --check-only .
- name: Type check with mypy 🎯
run: mypy src/
- name: Lint with pylint 🔍
run: pylint src/
- name: Security scan 🛡️
run: |
bandit -r src/
safety check
# 📚 Documentation
docs:
runs-on: ubuntu-latest
needs: [test, quality]
steps:
- uses: actions/checkout@v3
- name: Set up Python 🐍
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Build docs 📖
run: |
pip install -e .[docs]
cd docs && make html
- name: Deploy docs 🌐
if: github.ref == 'refs/heads/main'
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs/_build/html
# 🚀 Release and deploy
release:
if: startsWith(github.ref, 'refs/tags/v')
needs: [test, quality, docs]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 🐍
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Build package 📦
run: |
pip install build
python -m build
- name: Create GitHub Release 🎉
uses: softprops/action-gh-release@v1
with:
files: dist/*
generate_release_notes: true
- name: Publish to PyPI 🚀
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
🎓 Key Takeaways
You’ve learned so much! Here’s what you can now do:
- ✅ Create GitHub Actions workflows with confidence 💪
- ✅ Automate Python testing across multiple versions 🧪
- ✅ Set up CI/CD pipelines for your projects 🚀
- ✅ Use advanced features like matrix builds and caching ⚡
- ✅ Deploy automatically to PyPI and other platforms 📦
Remember: GitHub Actions is your automation superpower! Use it to save time and catch bugs early. 🤝
🤝 Next Steps
Congratulations! 🎉 You’ve mastered GitHub Actions for Python projects!
Here’s what to do next:
- 💻 Set up Actions for your own Python project
- 🏗️ Explore the GitHub Actions Marketplace
- 📚 Learn about self-hosted runners for private projects
- 🌟 Share your workflows with the community!
Keep automating, keep building, and most importantly, have fun! 🚀
Happy automating! 🎉🚀✨