Module 7: CI Integration with GitHub Actions

Module Project: A Working CI Pipeline

Module Project: A Working CI Pipeline

Project overview

You have tests that run locally. Now you're going to automate them. You'll configure a complete CI pipeline with GitHub Actions that runs your whole test suite on every push and PR, generates coverage reports, uses matrix testing for multiple Python versions, and blocks unsafe merges.

This project takes an existing repository (you can use any project from the previous modules) and adds professional CI to it. Claude Code helps you generate the workflow YAML based on your project's context.


Project Objective

Configure a working CI pipeline with GitHub Actions for a repository with tests.

By completing this project:

  • ✅ Your repository will have a working .github/workflows/tests.yml
  • ✅ The tests will run automatically on push and PR
  • ✅ Matrix testing will validate on Python 3.10, 3.11, 3.12
  • ✅ A coverage report will be published as an artifact
  • ✅ pip caching will reduce build times
  • ✅ Branch protection will block merges if the tests fail

Technical Specifications

Prerequisites

  • A GitHub account
  • A repository with at least 10 tests (you can use the project from any previous module)
  • A requirements.txt with the dependencies

The Target Structure

your-project/
├── .github/
│   └── workflows/
│       └── tests.yml          ← The pipeline (you create it)
├── src/ or module/            ← Your code
├── tests/                     ← Your existing test suite
├── requirements.txt
├── pyproject.toml             ← pytest and coverage configuration
└── README.md                  ← The CI badge

The Pipeline Step by Step

Step 1: A basic workflow

Create .github/workflows/tests.yml:

name: Tests

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

jobs:
  test:
    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 dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
      
      - name: Run tests
        run: pytest tests/ -v

Commit, push, and verify that it runs on GitHub.

Step 2: Add matrix testing

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.10", "3.11", "3.12"]
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
      
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
      
      - name: Run tests
        run: pytest tests/ -v

Step 3: Add caching

      - name: Cache pip packages
        uses: actions/cache@v4
        with:
          path: ~/.cache/pip
          key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
          restore-keys: |
            ${{ runner.os }}-pip-

Step 4: Add coverage

      - name: Run tests with coverage
        run: pytest tests/ -v --cov=src --cov-report=term-missing --cov-report=html
      
      - name: Upload coverage report
        uses: actions/upload-artifact@v4
        with:
          name: coverage-report-${{ matrix.python-version }}
          path: htmlcov/

Step 5: pyproject.toml

[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-v --tb=short"

[tool.coverage.run]
source = ["src"]
omit = ["tests/*"]

[tool.coverage.report]
fail_under = 85
show_missing = true

Step 6: A badge in the README

![Tests](https://github.com/YOUR-USERNAME/YOUR-REPO/actions/workflows/tests.yml/badge.svg)

Step 7: Branch protection (manual on GitHub)

  1. Settings → Branches → Add rule
  2. Branch name pattern: main
  3. ✅ Require status checks to pass before merging
  4. ✅ Select the "test" job
  5. Save changes

Generating it with Claude Code

A prompt to generate the complete workflow:

Generate a GitHub Actions workflow (.github/workflows/tests.yml) 
for my Python project.

Context:
- Tests in tests/ (unit, integration)
- Dependencies in requirements.txt
- Coverage with pytest-cov, target: ≥85%
- Python versions: 3.10, 3.11, 3.12

Include: matrix testing, pip caching, a coverage report as an artifact.

Success Criteria

Your project is complete when:

  • ✅ git push triggers the workflow automatically
  • ✅ The tests run on 3 Python versions (matrix)
  • ✅ The pipeline uses caching (verifiable in the logs: "Cache restored")
  • ✅ A coverage report is generated as a downloadable artifact
  • ✅ A push with a failing test → the pipeline fails (verifiable)
  • ✅ The README has the CI badge

Evaluation Rubric (100 points)

A Working Workflow (35 points)

  • (15 pts) The workflow runs pytest successfully in CI
  • (10 pts) The triggers are configured (push and pull_request)
  • (10 pts) Valid workflow YAML with no errors

Matrix and Caching (25 points)

  • (15 pts) Matrix testing with at least 2 Python versions
  • (10 pts) pip caching implemented

Coverage (20 points)

  • (10 pts) A coverage report generated in CI
  • (10 pts) The coverage report published as an artifact

Protection (10 points)

  • (5 pts) The pipeline fails if the tests fail (verifiable with an intentional push)
  • (5 pts) A CI badge in the README

Documentation (10 points)

  • (5 pts) pyproject.toml with the pytest and coverage configuration
  • (5 pts) The README explains how to run the tests locally and in CI

Extra Credit (up to +10 points)

  • (+5 pts) Branch protection configured on GitHub
  • (+5 pts) Separate jobs for unit, integration, and e2e tests

Common Mistakes

Mistake 1: The workflow doesn't run

Cause: The file isn't in .github/workflows/ exactly, or the YAML has indentation errors.

Solution: Verify the exact path and use a YAML validator. Check the "Actions" tab on GitHub to see the errors.

Mistake 2: The tests fail in CI but pass locally

Cause: Missing dependencies in requirements.txt, hardcoded paths, or missing environment variables.

Solution: Review the CI logs. Make sure requirements.txt has ALL the dependencies (including pytest, pytest-cov).

Mistake 3: The cache doesn't work

Cause: The cache key doesn't match (requirements.txt changed or isn't hashed correctly).

Solution: Verify that hashFiles('requirements.txt') points to the correct file. Check the logs: "Cache hit" vs "Cache miss".

Mistake 4: The coverage report isn't generated

Cause: pytest-cov isn't in requirements.txt or the --cov flag doesn't point to the correct module.

Solution: Add pytest-cov to requirements.txt and verify that --cov=src points to your source code.


Resources for the Project

  1. GitHub Actions Quickstart - A quick start
  2. actions/setup-python - Setting up Python in CI
  3. actions/cache - Caching dependencies
  4. actions/upload-artifact - Publishing artifacts
  5. GitHub Branch Protection - Branch protection

Connection with the Next Module

The CI you configured today is part of the final project:

  • Module 8 (Capstone Project): The final application includes CI as part of the delivery
  • The pipeline you built here gets reused directly for the final project
  • You'll only need to adjust the paths and dependencies for the new application

You have professional CI. Every push, every PR, every merge is protected by your test suite. This is the industry standard.


Module 7, Capsule 06 — Testing with Claude Code Guide Your first professional CI pipeline