Skip to content
Latchkey

CI/CD Pipeline workflow (JiayuXu0/FastAPI-Template)

The CI/CD Pipeline workflow from JiayuXu0/FastAPI-Template, explained and optimized by Latchkey.

C

CI health: C - fair

Point runs-on at Latchkey and get run de-duplication, job timeouts, SHA-pinned actions, self-healing for flaky steps, and up to 58% lower cost, applied automatically.

Grade your own workflow free or run it on Latchkey →
Source: JiayuXu0/FastAPI-Template.github/workflows/ci.ymlLicense MITView source

What it does

This is the CI/CD Pipeline workflow from the JiayuXu0/FastAPI-Template repository, a real project running GitHub Actions. It is shown here with attribution under its MIT license.

Below, Latchkey shows a faster, safer version produced by its optimization engine.

The workflow

workflow (.yml)
name: CI/CD Pipeline

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

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Install uv
        uses: astral-sh/setup-uv@v2
        with:
          version: "latest"

      - name: Set up Python
        run: uv python install 3.11

      - name: Install dependencies
        run: uv sync --dev

      # 真红线:ruff lint 与格式都必须通过;不再 || true
      - name: Lint with ruff
        run: uv run ruff check src/

      - name: Format check with ruff
        run: uv run ruff format --check src/

      # mypy 当前还有积压错误(issue 跟踪),跑出来作为 informational artifact,
      # 不阻断流水线;后续逐步清理后再改为真红线。
      - name: Type check with mypy (informational)
        env:
          SECRET_KEY: test_secret_key_for_testing_only_32_chars_long
          SWAGGER_UI_PASSWORD: test_password_123
          DB_PASSWORD: test_db_password
          APP_ENV: testing
          DEBUG: "True"
        run: uv run mypy src/ --no-error-summary > mypy_report.txt 2>&1 || true

      - name: Upload mypy report
        uses: actions/upload-artifact@v4
        with:
          name: mypy-report
          path: mypy_report.txt

  test:
    runs-on: ubuntu-latest
    needs: [lint]
    services:
      redis:
        image: redis:7-alpine
        ports: ["6379:6379"]
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 5s
          --health-timeout 3s
          --health-retries 5

    strategy:
      matrix:
        python-version: ["3.11", "3.12"]

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Install uv
        uses: astral-sh/setup-uv@v2
        with:
          version: "latest"

      - name: Set up Python ${{ matrix.python-version }}
        run: uv python install ${{ matrix.python-version }}

      - name: Install dependencies
        run: uv sync --dev

      # 真红线:跑 tests/ 全集
      - name: Run tests with coverage
        env:
          SECRET_KEY: test_secret_key_for_testing_only_32_chars_long
          SWAGGER_UI_PASSWORD: test_password_123
          DB_PASSWORD: test_db_password
          APP_ENV: testing
          DEBUG: "True"
          REDIS_URL: redis://localhost:6379/0
        run: |
          uv run pytest tests/ -v \
            --cov=src --cov-report=xml --cov-report=html --cov-report=term-missing \
            --cov-fail-under=30

      - name: Upload coverage reports
        if: matrix.python-version == '3.11'
        uses: actions/upload-artifact@v4
        with:
          name: coverage-reports
          path: |
            coverage.xml
            htmlcov/

      - name: Upload coverage to Codecov
        if: matrix.python-version == '3.11'
        uses: codecov/codecov-action@v4
        with:
          file: ./coverage.xml
          flags: unittests
          name: codecov-umbrella

  security:
    runs-on: ubuntu-latest
    needs: [lint]
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Install uv
        uses: astral-sh/setup-uv@v2

      - name: Set up Python
        run: uv python install 3.11

      - name: Install dependencies
        run: uv sync --dev

      # 真红线:bandit 中/高严重度 → 必须修;low 通过 -ll 过滤
      - name: Run security checks with bandit (medium+ severity)
        env:
          SECRET_KEY: test_secret_key_for_testing_only_32_chars_long
          SWAGGER_UI_PASSWORD: test_password_123
          DB_PASSWORD: test_db_password
          APP_ENV: testing
          DEBUG: "True"
        run: uv run bandit -r src/ -ll

      # safety 现在需要登录账号,改用 pip-audit(PyPA 维护、无需登录)
      - name: Run dependency vulnerability scan (informational)
        run: |
          uv pip install pip-audit
          uv run pip-audit --strict --skip-editable || true

  build:
    runs-on: ubuntu-latest
    needs: [test, security]
    if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Install uv
        uses: astral-sh/setup-uv@v2

      - name: Set up Python
        run: uv python install 3.11

      - name: Build package
        run: |
          uv sync
          uv build

      - name: Upload build artifacts
        uses: actions/upload-artifact@v4
        with:
          name: python-package-distributions
          path: dist/

The same workflow, on Latchkey

Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.

name: CI/CD Pipeline
 
on:
  push:
    branches: [ main, master, develop ]
  pull_request:
    branches: [ main, master, develop ]
 
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
 
jobs:
  lint:
    timeout-minutes: 30
    runs-on: latchkey-small
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
 
      - name: Install uv
        uses: astral-sh/setup-uv@v2
        with:
          version: "latest"
 
      - name: Set up Python
        run: uv python install 3.11
 
      - name: Install dependencies
        run: uv sync --dev
 
      # 真红线:ruff lint 与格式都必须通过;不再 || true
      - name: Lint with ruff
        run: uv run ruff check src/
 
      - name: Format check with ruff
        run: uv run ruff format --check src/
 
      # mypy 当前还有积压错误(issue 跟踪),跑出来作为 informational artifact,
      # 不阻断流水线;后续逐步清理后再改为真红线。
      - name: Type check with mypy (informational)
        env:
          SECRET_KEY: test_secret_key_for_testing_only_32_chars_long
          SWAGGER_UI_PASSWORD: test_password_123
          DB_PASSWORD: test_db_password
          APP_ENV: testing
          DEBUG: "True"
        run: uv run mypy src/ --no-error-summary > mypy_report.txt 2>&1 || true
 
      - name: Upload mypy report
        uses: actions/upload-artifact@v4
        with:
          name: mypy-report
          path: mypy_report.txt
 
  test:
    timeout-minutes: 30
    runs-on: latchkey-small
    needs: [lint]
    services:
      redis:
        image: redis:7-alpine
        ports: ["6379:6379"]
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 5s
          --health-timeout 3s
          --health-retries 5
 
    strategy:
      matrix:
        python-version: ["3.11", "3.12"]
 
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
 
      - name: Install uv
        uses: astral-sh/setup-uv@v2
        with:
          version: "latest"
 
      - name: Set up Python ${{ matrix.python-version }}
        run: uv python install ${{ matrix.python-version }}
 
      - name: Install dependencies
        run: uv sync --dev
 
      # 真红线:跑 tests/ 全集
      - name: Run tests with coverage
        env:
          SECRET_KEY: test_secret_key_for_testing_only_32_chars_long
          SWAGGER_UI_PASSWORD: test_password_123
          DB_PASSWORD: test_db_password
          APP_ENV: testing
          DEBUG: "True"
          REDIS_URL: redis://localhost:6379/0
        run: |
          uv run pytest tests/ -v \
            --cov=src --cov-report=xml --cov-report=html --cov-report=term-missing \
            --cov-fail-under=30
 
      - name: Upload coverage reports
        if: matrix.python-version == '3.11'
        uses: actions/upload-artifact@v4
        with:
          name: coverage-reports
          path: |
            coverage.xml
            htmlcov/
 
      - name: Upload coverage to Codecov
        if: matrix.python-version == '3.11'
        uses: codecov/codecov-action@v4
        with:
          file: ./coverage.xml
          flags: unittests
          name: codecov-umbrella
 
  security:
    timeout-minutes: 30
    runs-on: latchkey-small
    needs: [lint]
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
 
      - name: Install uv
        uses: astral-sh/setup-uv@v2
 
      - name: Set up Python
        run: uv python install 3.11
 
      - name: Install dependencies
        run: uv sync --dev
 
      # 真红线:bandit 中/高严重度 → 必须修;low 通过 -ll 过滤
      - name: Run security checks with bandit (medium+ severity)
        env:
          SECRET_KEY: test_secret_key_for_testing_only_32_chars_long
          SWAGGER_UI_PASSWORD: test_password_123
          DB_PASSWORD: test_db_password
          APP_ENV: testing
          DEBUG: "True"
        run: uv run bandit -r src/ -ll
 
      # safety 现在需要登录账号,改用 pip-audit(PyPA 维护、无需登录)
      - name: Run dependency vulnerability scan (informational)
        run: |
          uv pip install pip-audit
          uv run pip-audit --strict --skip-editable || true
 
  build:
    timeout-minutes: 30
    runs-on: latchkey-small
    needs: [test, security]
    if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')
 
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
 
      - name: Install uv
        uses: astral-sh/setup-uv@v2
 
      - name: Set up Python
        run: uv python install 3.11
 
      - name: Build package
        run: |
          uv sync
          uv build
 
      - name: Upload build artifacts
        uses: actions/upload-artifact@v4
        with:
          name: python-package-distributions
          path: dist/
 

What changed

2 third-party actions are referenced by a movable tag. Pin them to the commit SHA (Latchkey resolves and applies this automatically) so a repointed tag cannot change what runs.

What Latchkey heals here

This workflow has steps that commonly fail on transient issues (network, registries, flaky browsers). On Latchkey managed runners they are detected, retried, and self-healed instead of failing your build:

This workflow runs 4 jobs (5 with the matrix expanded) per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.

Actions used in this workflow