First CI/CD Pipeline

Build and deploy your first automated pipeline in under 10 minutes.

GitHub Actions Setup

Step 1: Create Repository

# Create and clone repository
git clone https://github.com/yourusername/my-first-pipeline.git
cd my-first-pipeline

Step 2: Add Test Script

hello_world.py - Test script with proper error handling
#!/usr/bin/env python3
"""
Basic Python script demonstrating CI/CD pipeline testing.

This simple example shows proper function structure, error handling,
and exit codes that work well with automated testing.
"""
import sys
from typing import Optional


def greet(name: str, greeting: str = "Hello") -> str:
    """
    Generate a greeting message.
    
    Args:
        name: The name to greet
        greeting: The greeting word (default: "Hello")
        
    Returns:
        Formatted greeting string
    """
    if not name or not name.strip():                  # Validate input
        raise ValueError("Name cannot be empty")
    
    return f"{greeting}, {name.strip()}!"             # Clean and format


def main() -> int:
    """
    Main function with proper exit codes for CI/CD.
    
    Returns:
        0 for success, 1 for error (standard Unix conventions)
    """
    try:
        # Get name from command line args or use default
        name: Optional[str] = sys.argv[1] if len(sys.argv) > 1 else "World"
        
        if name is None:                              # Handle None case
            print("Error: No name provided")
            return 1
        
        message = greet(name)                         # Generate greeting
        print(message)                                # Output result
        return 0                                      # Success exit code
        
    except ValueError as e:                           # Handle validation errors
        print(f"Error: {e}", file=sys.stderr)
        return 1
    except Exception as e:                            # Handle unexpected errors
        print(f"Unexpected error: {e}", file=sys.stderr)
        return 1


if __name__ == "__main__":
    sys.exit(main())                                  # Proper exit code handling

Step 3: Create Workflow

# Create workflow directory
mkdir -p .github/workflows
.github/workflows/ci.yml - Basic CI pipeline
name: Basic CI Pipeline
# Simple pipeline demonstrating modern Python CI/CD with GitHub Actions

on:
  push:                                               # Run on every push
    branches: [ main, develop ]                       # Target branches
  pull_request:                                       # Run on PRs  
    branches: [ main ]                                # PR target branch

jobs:
  ci:
    runs-on: ubuntu-latest                            # Use Ubuntu runner
    strategy:
      matrix:                                         # Test multiple versions
        python-version: ["3.11", "3.12", "3.13"]
    
    steps:
      - name: Checkout code                           # Get repository code
        uses: actions/checkout@v4                     # Latest stable version
      
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5                 # Install Python
        with:
          python-version: ${{ matrix.python-version }}
      
      - name: Install uv (modern Python package manager)
        uses: astral-sh/setup-uv@v3                   # Ultra-fast package manager
        with:
          enable-cache: true                          # Cache dependencies
      
      - name: Install dependencies
        run: |
          uv venv                                     # Create virtual environment
          uv pip install pytest ruff mypy           # Install dev tools
      
      - name: Code formatting check
        run: |
          uv run ruff format --check .               # Check code formatting
      
      - name: Linting
        run: |
          uv run ruff check .                        # Run linter
      
      - name: Type checking
        run: |
          uv run mypy *.py                           # Static type checking
      
      - name: Run tests
        run: |
          uv run python hello_world.py               # Test our script
          echo "✅ All tests passed!"                # Success message

Step 4: Deploy

git add .
git commit -m "Add CI pipeline"
git push origin main

Result: Automated testing on every push with modern Python tools (uv, ruff, mypy).

Troubleshooting Common Issues

Pipeline Doesn’t Run

  • Check .github/workflows/ directory structure

  • Verify .yml file extension

  • Ensure correct branch in trigger configuration

YAML Syntax Errors

  • Use consistent 2-space indentation (no tabs)

  • Validate YAML at yamllint.com before committing

  • Common issue: mixing spaces and tabs

Action Version Errors

  • Use stable versions: actions/checkout@v4

  • Check GitHub marketplace for current versions

  • Pin to specific versions for reliability

Debug Environment Issues:

- name: Debug environment
  run: |
    pwd && ls -la
    python --version
    which python uv git

Advanced Workflow Patterns

Matrix Testing (Multiple Versions):

strategy:
  matrix:
    python-version: ["3.11", "3.12", "3.13"]
    os: [ubuntu-latest, windows-latest, macos-latest]

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

Conditional Execution:

- name: Deploy to production
  if: github.ref == 'refs/heads/main'
  run: echo "Deploying..."

Artifact Sharing:

- uses: actions/upload-artifact@v4
  with:
    name: test-results
    path: results/

Production Pipeline Example

Complete CI/CD with Security and Deployment:

Production-ready pipeline with multi-stage deployment
name: Production Python CI/CD Pipeline
# Comprehensive pipeline with modern tooling, security, and deployment

on:
  push:
    branches: [ main, develop ]                       # Deploy branches
    tags: [ 'v*' ]                                   # Release tags
  pull_request:
    branches: [ main ]                                # PR validation

env:
  PYTHON_VERSION: "3.12"                             # Default Python version

jobs:
  # Stage 1: Fast quality checks (fail fast principle)
  quality:
    runs-on: ubuntu-latest
    outputs:
      cache-hit: ${{ steps.cache.outputs.cache-hit }}
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4                     # Get repository code
      
      - name: Set up Python ${{ env.PYTHON_VERSION }}
        uses: actions/setup-python@v5                 # Install Python
        with:
          python-version: ${{ env.PYTHON_VERSION }}
      
      - name: Install uv (modern package manager)
        uses: astral-sh/setup-uv@v3                   # Fast dependency management
        with:
          enable-cache: true                          # Cache for speed
          cache-dependency-glob: "pyproject.toml"
      
      - name: Cache dependencies
        id: cache                                     # Cache Python packages
        uses: actions/cache@v4
        with:
          path: ~/.cache/uv
          key: ${{ runner.os }}-uv-${{ hashFiles('pyproject.toml') }}
          restore-keys: ${{ runner.os }}-uv-
      
      - name: Install dependencies
        run: |
          uv sync --dev                               # Install all dependencies
      
      - name: Code formatting check
        run: |
          uv run ruff format --check .               # Check code formatting
      
      - name: Linting with modern tools
        run: |
          uv run ruff check .                        # Fast linting
      
      - name: Type checking
        run: |
          uv run mypy src/ tests/                    # Static type analysis

  # Stage 2: Comprehensive testing across Python versions
  test:
    needs: quality                                    # Wait for quality checks
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false                               # Continue testing other versions
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        python-version: ["3.11", "3.12", "3.13"]
        exclude:                                      # Skip problematic combinations
          - os: windows-latest
            python-version: "3.13"                   # Example exclusion
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}

Key Features:

  • Multi-stage quality gates (lint → test → security → deploy)

  • Cross-platform testing (Ubuntu, Windows, macOS)

  • Security scanning (bandit, safety)

  • Environment-specific deployments

  • Automated artifact management

Modern Python Tooling

Essential Development Stack:

# Install modern Python tools
curl -LsSf https://astral.sh/uv/install.sh | sh

# Create project with modern tooling
uv init my-project
cd my-project
uv add --dev pytest ruff mypy bandit

Project Configuration:

Modern Python project configuration
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "weather-cli"
version = "1.0.0"
description = "Production-ready CLI weather application"
authors = [
    {name = "DevOps Team", email = "devops@example.com"}
]
readme = "README.md"
license = {text = "MIT"}
requires-python = ">=3.11"
keywords = ["cli", "weather", "api", "devops"]
classifiers = [
    "Development Status :: 5 - Production/Stable",
    "Environment :: Console",
    "Intended Audience :: Developers", 
    "License :: OSI Approved :: MIT License",
    "Operating System :: OS Independent",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Programming Language :: Python :: 3.13",
    "Topic :: Software Development :: Libraries :: Python Modules",
    "Topic :: Utilities",
]

# Runtime dependencies (what users need)
dependencies = [
    "typer>=0.9.0,<1.0",                    # Modern CLI framework
    "rich>=13.0.0,<14.0",                   # Beautiful terminal output  
    "requests>=2.28.0,<3.0",               # HTTP client for API calls
    "pydantic>=2.0.0,<3.0",                # Data validation
]

# Optional dependencies for extra features
[project.optional-dependencies]
dev = [

Complete examples available in source_code/pipelines/

Next Steps

Explore Complete Examples:

  • source_code/pipelines/basic/ - Simple getting started examples

  • source_code/pipelines/advanced/ - Production-ready patterns

  • source_code/pipelines/templates/ - Reusable configurations

  • source_code/pipelines/examples/ - Complete applications

Advanced Topics:

  • CLI applications with Typer framework

  • Advanced GitHub Actions patterns

  • Production deployment strategies

  • Security and compliance integration