Skip to content
Latchkey

Tests workflow (justquick/django-activity-stream)

The Tests workflow from justquick/django-activity-stream, explained and optimized by Latchkey.

F

CI health: F - at risk

Point runs-on at Latchkey and get caching, 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: justquick/django-activity-stream.github/workflows/workflow.yamlLicense BSD-3-ClauseView source

What it does

This is the Tests workflow from the justquick/django-activity-stream repository, a real project running GitHub Actions. It is shown here with attribution under its BSD-3-Clause license.

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

The workflow

workflow (.yml)
name: Tests

on:
  push:
    branches:
      - main
    paths:
        - '**.py'
        - '**.txt'
        - '**.yaml'
        - '**.toml'
  pull_request:
    branches:
      - main
    paths:
        - '**.py'
        - '**.txt'
        - '**.toml'
        - '**.yaml'

env:
  GITHUB_WORKFLOW: true

jobs:
  test:
    name: Test
    runs-on: ubuntu-latest

    # test matrix
    strategy:
      fail-fast: false
      matrix:
        python:
          - 3.8
          - 3.9
          - "3.10"
          - 3.11
          - 3.12
        django:
          - 3.2
          - 4.0
          - 4.1
          - 4.2
          - 5.0
          - 5.1
        database:
          - sqlite
          - mysql
          - postgres
        exclude:
          # django 3.2
          - python: 3.11
            django: 3.2
          - python: 3.12
            django: 3.2
          # django 4.0
          - python: 3.11
            django: 4.0
          - python: 3.12
            django: 4.0
          # django 4.1
          - python: 3.12
            django: 4.1
          # django 5.0
          - python: 3.8
            django: 5.0
          - python: 3.9
            django: 5.0
          # django 5.1
          - python: 3.8
            django: 5.1
          - python: 3.9
            django: 5.1

    # additional service containers to run
    services:
      # postgres service
      postgres:
        # docker hub image
        image: postgres:13-alpine
        # configure the instance
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: postgres
        ports:
          - 5432:5432
        # set health checks to wait until service has started
        options: >-
          --health-cmd="pg_isready"
          --health-interval=10s
          --health-timeout=5s
          --health-retries=5

      # mysql service
      mysql:
        # docker hub image
        image: mysql:8
        # configure the instance
        env:
          MYSQL_ALLOW_EMPTY_PASSWORD: yes
          MYSQL_DATABASE: test
        ports:
          - 3306:3306
        # set health checks to wait until service has started
        options: >-
          --health-cmd="mysqladmin ping"
          --health-interval=10s
          --health-timeout=5s
          --health-retries=5

    steps:
      # check out revision to test
      - uses: actions/checkout@v4

      # install python
      - name: Set up Python ${{ matrix.python }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python }}

      # upgrade pip
      - name: Update pip
        run: python -m pip install --upgrade pip

      # install environment specific dependencies
      - name: Install coverage
        run: pip install coverage>=7.6.0 setuptools

      - name: Install Django 3.2
        if: matrix.django == 3.2
        run: pip install "Django>=3.2,<4.0"
      - name: Install Django 4.0
        if: matrix.django == 4.0
        run: pip install "Django>=4.0,<4.1"
      - name: Install Django 4.1
        if: matrix.django == 4.1
        run: pip install "Django>=4.1,<4.2"
      - name: Install Django 4.2
        if: matrix.django == 4.2
        run: pip install "Django>=4.2,<5.0"
      - name: Install Django 5.0
        if: matrix.django == 5.0
        run: pip install "Django>=5.0,<5.1"
      - name: Install Django 5.1
        if: matrix.django == 5.1
        run: pip install "Django>=5.1,<5.2"
      - name: Install MySQL libs
        if: matrix.database == 'mysql'
        run: pip install mysqlclient>=2.2.4 django-mysql>=4.14.0
      - name: Install postgres libs
        if: matrix.database == 'postgres'
        run: pip install psycopg2-binary>=2.9.9

      - name: Install Django ReST framework libraries
        run: pip install -U django-rest-framework rest-framework-generic-relations drf-spectacular

      # install our package
      - name: Install package
        run: pip install -e .

      # execute the tests
      - name: Run tests
        run: coverage run runtests/manage.py test -v3 --noinput actstream testapp testapp_nested
        env:
          DATABASE_ENGINE: ${{ matrix.database }}
          # COVERAGE_FILE: ".coverage.${{ matrix.python_version }}"

      - name: Store coverage file
        uses: actions/upload-artifact@v3
        with:
          name: coverage
          path: .coverage #.${{ matrix.python_version }}

      - name: Coveralls
        uses: AndreMiras/coveralls-python-action@develop
        with:
          parallel: true
          flag-name: Unit Test

  coveralls_finish:
    needs: test
    runs-on: ubuntu-latest
    steps:
    - name: Coveralls Finished
      uses: AndreMiras/coveralls-python-action@develop
      with:
        parallel-finished: true

The same workflow, on Latchkey

Estimated ~20% faster on cache hits, plus fewer wasted runs and a safer supply chain. Added and changed lines are highlighted.

name: Tests
 
on:
  push:
    branches:
      - main
    paths:
        - '**.py'
        - '**.txt'
        - '**.yaml'
        - '**.toml'
  pull_request:
    branches:
      - main
    paths:
        - '**.py'
        - '**.txt'
        - '**.toml'
        - '**.yaml'
 
env:
  GITHUB_WORKFLOW: true
 
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
 
jobs:
  test:
    timeout-minutes: 30
    name: Test
    runs-on: latchkey-small
 
    # test matrix
    strategy:
      fail-fast: false
      matrix:
        python:
          - 3.8
          - 3.9
          - "3.10"
          - 3.11
          - 3.12
        django:
          - 3.2
          - 4.0
          - 4.1
          - 4.2
          - 5.0
          - 5.1
        database:
          - sqlite
          - mysql
          - postgres
        exclude:
          # django 3.2
          - python: 3.11
            django: 3.2
          - python: 3.12
            django: 3.2
          # django 4.0
          - python: 3.11
            django: 4.0
          - python: 3.12
            django: 4.0
          # django 4.1
          - python: 3.12
            django: 4.1
          # django 5.0
          - python: 3.8
            django: 5.0
          - python: 3.9
            django: 5.0
          # django 5.1
          - python: 3.8
            django: 5.1
          - python: 3.9
            django: 5.1
 
    # additional service containers to run
    services:
      # postgres service
      postgres:
        # docker hub image
        image: postgres:13-alpine
        # configure the instance
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: postgres
        ports:
          - 5432:5432
        # set health checks to wait until service has started
        options: >-
          --health-cmd="pg_isready"
          --health-interval=10s
          --health-timeout=5s
          --health-retries=5
 
      # mysql service
      mysql:
        # docker hub image
        image: mysql:8
        # configure the instance
        env:
          MYSQL_ALLOW_EMPTY_PASSWORD: yes
          MYSQL_DATABASE: test
        ports:
          - 3306:3306
        # set health checks to wait until service has started
        options: >-
          --health-cmd="mysqladmin ping"
          --health-interval=10s
          --health-timeout=5s
          --health-retries=5
 
    steps:
      # check out revision to test
      - uses: actions/checkout@v4
 
      # install python
      - name: Set up Python ${{ matrix.python }}
        uses: actions/setup-python@v5
        with:
          cache: 'pip'
          python-version: ${{ matrix.python }}
 
      # upgrade pip
      - name: Update pip
        run: python -m pip install --upgrade pip
 
      # install environment specific dependencies
      - name: Install coverage
        run: pip install coverage>=7.6.0 setuptools
 
      - name: Install Django 3.2
        if: matrix.django == 3.2
        run: pip install "Django>=3.2,<4.0"
      - name: Install Django 4.0
        if: matrix.django == 4.0
        run: pip install "Django>=4.0,<4.1"
      - name: Install Django 4.1
        if: matrix.django == 4.1
        run: pip install "Django>=4.1,<4.2"
      - name: Install Django 4.2
        if: matrix.django == 4.2
        run: pip install "Django>=4.2,<5.0"
      - name: Install Django 5.0
        if: matrix.django == 5.0
        run: pip install "Django>=5.0,<5.1"
      - name: Install Django 5.1
        if: matrix.django == 5.1
        run: pip install "Django>=5.1,<5.2"
      - name: Install MySQL libs
        if: matrix.database == 'mysql'
        run: pip install mysqlclient>=2.2.4 django-mysql>=4.14.0
      - name: Install postgres libs
        if: matrix.database == 'postgres'
        run: pip install psycopg2-binary>=2.9.9
 
      - name: Install Django ReST framework libraries
        run: pip install -U django-rest-framework rest-framework-generic-relations drf-spectacular
 
      # install our package
      - name: Install package
        run: pip install -e .
 
      # execute the tests
      - name: Run tests
        run: coverage run runtests/manage.py test -v3 --noinput actstream testapp testapp_nested
        env:
          DATABASE_ENGINE: ${{ matrix.database }}
          # COVERAGE_FILE: ".coverage.${{ matrix.python_version }}"
 
      - name: Store coverage file
        uses: actions/upload-artifact@v3
        with:
          name: coverage
          path: .coverage #.${{ matrix.python_version }}
 
      - name: Coveralls
        uses: AndreMiras/coveralls-python-action@develop
        with:
          parallel: true
          flag-name: Unit Test
 
  coveralls_finish:
    timeout-minutes: 30
    needs: test
    runs-on: latchkey-small
    steps:
    - name: Coveralls Finished
      uses: AndreMiras/coveralls-python-action@develop
      with:
        parallel-finished: true
 

What changed

1 third-party action is referenced by a movable tag. Pin it 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 2 jobs (91 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