Code of the Day
AdvancedCI/CD for Automation

Running Python in CI

Write a GitHub Actions workflow that sets up Python, installs requirements with pip caching, and runs your pipeline script on every push and on a daily schedule.

WorkflowAdvanced8 min read
Recommended first
By the end of this lesson you will be able to:
  • Write a workflow that uses actions/setup-python to install a specific Python version
  • Add pip dependency caching to avoid reinstalling packages on every run
  • Run a pipeline script and handle its exit code correctly

A Python workflow in GitHub Actions follows the same three-step pattern on almost every project: check out the code, set up Python, install dependencies, run the script. The only thing that changes is what the script does.

A complete workflow

# .github/workflows/run-pipeline.yml

name: Run pipeline

on:
  push:
    branches: ["main"]
  schedule:
    - cron: "0 2 * * *"    # 02:00 UTC daily
  workflow_dispatch:        # manual trigger

jobs:
  pipeline:
    runs-on: ubuntu-latest

    steps:
      - name: Check out repository
        uses: actions/checkout@v4

      - name: Set up Python 3.12
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"
          cache: "pip"                # cache ~/.cache/pip between runs

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

      - name: Run pipeline
        run: python pipeline.py
        env:
          API_KEY: ${{ secrets.API_KEY }}
          OUTPUT_DIR: output

Dependency caching

cache: "pip" in actions/setup-python caches the pip download cache between workflow runs. The cache key is computed from the hash of requirements.txt. When dependencies have not changed, pip installs from the cache rather than downloading packages — cutting a typical install from 30–90 seconds to under 5 seconds.

The cache is stored per branch and per OS. It is automatically invalidated when requirements.txt changes (a new hash) and expires after 7 days of non-use.

cache: "pip" caches the pip download cache, not the installed packages themselves. pip install still runs on every workflow execution; it just finds the wheels already downloaded rather than fetching them from PyPI. If you need to skip pip install entirely, cache the virtual environment itself using actions/cache with the site-packages directory as the path.

Exit codes and failure detection

GitHub Actions marks a step as failed when its command exits with a non-zero code. Python scripts exit 0 by default even if they catch and discard exceptions. Ensure your pipeline propagates failures:

import sys

def main():
    try:
        run_pipeline()
    except Exception as exc:
        print(f"Pipeline failed: {exc}", file=sys.stderr)
        sys.exit(1)

if __name__ == "__main__":
    main()

A failed step stops the job and marks it red in the Actions UI. Without sys.exit(1), a pipeline that silently failed will show as a green check — the worst outcome.

Multi-Python version matrix

To verify your script works on multiple Python versions, use a matrix strategy:

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.10", "3.11", "3.12"]

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          cache: "pip"
      - run: pip install -r requirements.txt
      - run: pytest tests/

This runs three parallel jobs — one per Python version — and fails the whole workflow if any version fails.

Uploading output files as artifacts

      - name: Run pipeline
        run: python pipeline.py

      - name: Upload report
        uses: actions/upload-artifact@v4
        with:
          name: pipeline-report
          path: output/
          retention-days: 30

Artifacts are downloadable from the Actions run page for retention-days days. Useful for reports, logs, and any output you need to inspect after the run.

Where to go next

Next: secrets and environments — how GitHub Secrets differ from plain environment variables, and how deployment environments add approval gates for production runs.

Finished reading? Mark it complete to track your progress.

On this page