The Ultimate GitHub Actions CI/CD Pipeline in 5 Minutes

· · ~1,100 words

A good CI/CD pipeline catches bugs before they reach production, enforces code quality, and automates the boring stuff. But setting one up from scratch? That's an afternoon of YAML debugging you'll never get back.

This guide shows you how to build a complete GitHub Actions pipeline for Python in five minutes — using three purpose-built GitHub Actions from Kryptorious Tools: Python Guardian (code quality), PR Sentinel (pull request checks), and Release Forge (automated releases). Pair them with the companion CLI tools available in the $9 lifetime bundle on Gumroad.

What We're Building

Here's the pipeline that fires on every push and PR:

  1. Lint & Type Check — Python Guardian runs ruff, mypy, and bandit
  2. Test — pytest with coverage reporting
  3. Secret Scan — Catch leaked credentials before they hit the repo
  4. PR Validation — PR Sentinel enforces branch rules, commit hygiene, and required checks
  5. Auto-Release — Release Forge builds and publishes when you push a version tag

Step 1: The Foundation — Python Guardian

Python Guardian is a unified code-quality action. Instead of wiring up six separate actions for linting, formatting, type checking, and security scanning, you get one action that runs them all:

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

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

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

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

      - name: Python Guardian — Lint & Security
        uses: codegero/python-guardian@v1
        with:
          ruff: true
          mypy: true
          bandit: true
          fail-on: error

Python Guardian handles dependency caching automatically and produces a single unified report. Set fail-on: error and your PR gets blocked on any lint violations or type errors. Set it to warning during early-stage projects where you're still cleaning up legacy code.

Step 2: Test & Coverage

Add a test job to the same workflow:

  test:
    needs: quality
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ['3.11', '3.12', '3.13']

    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: |
          pip install -e ".[dev]"

      - name: Run tests with coverage
        run: |
          pytest --cov=src --cov-report=xml --cov-report=term-missing

      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          file: ./coverage.xml

The strategy.matrix runs tests across Python 3.11, 3.12, and 3.13 in parallel. Combined with Python Guardian's lint step, any push to main is fully validated in under 90 seconds (most projects).

Step 3: Secret Scanning — Don't Be a Headline

The Secret Scanner GitHub Action scans every commit and PR for leaked credentials. It catches the obvious (AWS keys, GitHub tokens) and the subtle (database URLs with embedded passwords, hardcoded JWT secrets):

  secrets:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # scan full history

      - name: Secret Scanner
        uses: codegero/secret-scanner@v1
        with:
          scan-history: true
          fail-on-detection: true

If it finds a secret, the PR is blocked. You can add custom patterns in .secret-scanner.yaml at the repo root — useful for internal API keys or project-specific credential formats.

Step 4: PR Sentinel — Enforce Branch Discipline

PR Sentinel adds a layer of automation on top of GitHub's branch protection rules. It enforces things GitHub's built-in rules can't easily do:

  pr-check:
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request'
    steps:
      - uses: actions/checkout@v4

      - name: PR Sentinel
        uses: codegero/pr-sentinel@v1
        with:
          require-conventional-commits: true
          max-commits: 15
          require-pr-description: true
          enforce-linear-history: true
          label-on-conflict: true

Key features:

Step 5: Release Forge — Ship on Tag

The final piece: Release Forge automates the entire release process. Push a version tag, and it:

  release:
    needs: [quality, test, secrets]
    if: startsWith(github.ref, 'refs/tags/v')
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4

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

      - name: Release Forge
        uses: codegero/release-forge@v1
        with:
          generate-changelog: true
          pypi-publish: true
        env:
          PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}

Now your release workflow is: git tag v1.2.0 && git push --tags. Everything else — build, changelog, GitHub Release, PyPI upload — happens automatically.

The Complete Workflow (All Together)

Here's the full .github/workflows/ci.yml with all five jobs wired together. Copy, paste, adjust your Python versions, and you're done:

name: CI/CD Pipeline

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

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: { python-version: '3.12' }
      - uses: codegero/python-guardian@v1
        with: { ruff: true, mypy: true, bandit: true, fail-on: error }

  test:
    needs: quality
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ['3.11', '3.12', '3.13']
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: { python-version: '${{ matrix.python-version }}' }
      - run: pip install -e ".[dev]"
      - run: pytest --cov=src --cov-report=xml
      - uses: codecov/codecov-action@v4
        with: { file: ./coverage.xml }

  secrets:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }
      - uses: codegero/secret-scanner@v1
        with: { scan-history: true, fail-on-detection: true }

  pr-check:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: codegero/pr-sentinel@v1
        with:
          require-conventional-commits: true
          max-commits: 15
          require-pr-description: true

  release:
    needs: [quality, test, secrets]
    if: startsWith(github.ref, 'refs/tags/v')
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: { python-version: '3.12' }
      - uses: codegero/release-forge@v1
        with: { generate-changelog: true, pypi-publish: true }
        env:
          PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
🔧 All four GitHub Actions + seven CLI tools in one bundle.
Python Guardian, Release Forge, PR Sentinel, Secret Scanner, plus DevFlow, GitSweep, EnvGuard, and more — $9 lifetime on Gumroad. Drop them into your workflow today.
Explore all tools at codegero.github.io.

Why This Matters

CI/CD isn't optional in 2026 — it's table stakes. A pipeline that lints, tests, scans for secrets, validates PRs, and auto-releases isn't just about catching bugs. It's about velocity: every minute your team spends manually checking code style, writing changelogs, or building releases is a minute not spent shipping features.

The five jobs above run in under three minutes on free GitHub Actions runners for most Python projects. That's less time than it takes to get coffee — and it runs on every single push, catching problems when they're cheapest to fix.

Keep reading:

← All posts