Skip to content
Latchkey

CI workflow (personalrobotics/ssik)

The CI workflow from personalrobotics/ssik, explained and optimized by Latchkey.

B

CI health: B - good

Point runs-on at Latchkey and get 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: personalrobotics/ssik.github/workflows/ci.ymlLicense BSD-3-ClauseView source

What it does

This is the CI workflow from the personalrobotics/ssik 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: CI

# PR CI runs full pytest on Linux only. macOS coverage is provided by:
#   * Local dev on macOS (the primary dev machine)
#   * Release workflow's cibuildwheel matrix on tag (full cross-platform)
#
# Local pre-push gate (scripts/check.sh, auto-installed via
# scripts/install-hooks.sh) runs ruff + format + mypy + pytest locally
# before push. CI then runs Linux pytest as the safety net for the one
# class of bug local-only can't catch: Linux-specific behavior (LAPACK
# backend, libc/manylinux quirks, #82-style variance).
#
# Cost per PR: target ~5-10 min wall, free (public repo). The suite had
# grown to 43-68 min (serial pytest + uncached robot_descriptions clones);
# #348 cached the description repos and parallelized pytest (-n auto).
# Doc-only PRs skip CI entirely via paths-ignore.

on:
  pull_request:
    paths-ignore:
      - "**.md"
      - "docs/**"
      - "LICENSE"
      - ".gitignore"
  push:
    branches: [main]
    paths-ignore:
      - "**.md"
      - "docs/**"
      - "LICENSE"
      - ".gitignore"

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

permissions:
  contents: read

jobs:
  test:
    name: Linux py${{ matrix.python-version }}
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.13"]
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # hatch-vcs needs git history

      - name: Set up uv
        uses: astral-sh/setup-uv@v4
        with:
          enable-cache: true
          cache-dependency-glob: "uv.lock"

      - name: Sync dependencies
        run: uv sync --dev --python ${{ matrix.python-version }}

      - name: Runner info
        # Surfaces the runner's real core count up front (a standalone step so it
        # logs immediately, unlike a line inside the long pytest step). Confirms
        # whether xdist parallelism can help here. (#348)
        run: |
          echo "nproc=$(nproc)"
          python3 -c "import os; print('cpu_count', os.cpu_count(), 'affinity', len(os.sched_getaffinity(0)))"

      # Cache the robot_descriptions clones (~16 repos, several with mesh
      # files). Uncached, these clones dominate CI wall-clock (~30-55 min) and
      # cause the run-to-run variance. Keyed on MANIFEST.toml so adding an arm
      # invalidates; restore-keys lets a manifest change reuse the prior cache
      # so the prewarm step only clones the new entry. (#348)
      - name: Cache robot_descriptions
        uses: actions/cache@v4
        with:
          path: ~/.cache/robot_descriptions
          key: rd-${{ hashFiles('src/ssik/prebuilt/MANIFEST.toml') }}
          restore-keys: rd-

      - name: Pre-warm robot_descriptions
        # Serial clone before pytest so the parallel (-n auto) workers only
        # read the cache -- no concurrent clones racing on the same dir.
        run: uv run python scripts/prewarm_descriptions.py

      - name: ruff check
        run: uv run ruff check

      - name: ruff format --check
        run: uv run ruff format --check

      - name: mypy
        run: uv run mypy

      - name: regen_docs --check
        # Fail if MANIFEST.toml and the AUTOGEN doc-table regions are
        # out of sync. The script regenerates anchored tables from
        # the manifest; this gate enforces "edit MANIFEST.toml + run
        # scripts/regen_docs.py" as the only correct workflow for
        # arm metadata changes.
        run: uv run python scripts/regen_docs.py --check

      - name: pytest (parallel)
        # -n 4 (ubuntu-latest = 4 vCPUs). The heavy redundant tests (full
        # snapshot roster, etc.) are marked slow and run on the macOS pre-push
        # hook + nightly, so the per-PR suite is lean. Descriptions are
        # pre-warmed above so workers never clone concurrently. perf gates are
        # excluded here and run serially below. (#348)
        run: uv run pytest -q -n 4 -m "not slow and not perf"

      - name: pytest (perf gates, serial)
        # Wall-clock performance gates: run serially (no -n) so timing is
        # measured without cross-worker CPU contention. Small, fast set. (#348)
        run: uv run pytest -q -m "not slow and perf"

The same workflow, on Latchkey

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

name: CI
 
# PR CI runs full pytest on Linux only. macOS coverage is provided by:
#   * Local dev on macOS (the primary dev machine)
#   * Release workflow's cibuildwheel matrix on tag (full cross-platform)
#
# Local pre-push gate (scripts/check.sh, auto-installed via
# scripts/install-hooks.sh) runs ruff + format + mypy + pytest locally
# before push. CI then runs Linux pytest as the safety net for the one
# class of bug local-only can't catch: Linux-specific behavior (LAPACK
# backend, libc/manylinux quirks, #82-style variance).
#
# Cost per PR: target ~5-10 min wall, free (public repo). The suite had
# grown to 43-68 min (serial pytest + uncached robot_descriptions clones);
# #348 cached the description repos and parallelized pytest (-n auto).
# Doc-only PRs skip CI entirely via paths-ignore.
 
on:
  pull_request:
    paths-ignore:
      - "**.md"
      - "docs/**"
      - "LICENSE"
      - ".gitignore"
  push:
    branches: [main]
    paths-ignore:
      - "**.md"
      - "docs/**"
      - "LICENSE"
      - ".gitignore"
 
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
 
permissions:
  contents: read
 
jobs:
  test:
    timeout-minutes: 30
    name: Linux py${{ matrix.python-version }}
    runs-on: latchkey-small
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.13"]
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # hatch-vcs needs git history
 
      - name: Set up uv
        uses: astral-sh/setup-uv@v4
        with:
          enable-cache: true
          cache-dependency-glob: "uv.lock"
 
      - name: Sync dependencies
        run: uv sync --dev --python ${{ matrix.python-version }}
 
      - name: Runner info
        # Surfaces the runner's real core count up front (a standalone step so it
        # logs immediately, unlike a line inside the long pytest step). Confirms
        # whether xdist parallelism can help here. (#348)
        run: |
          echo "nproc=$(nproc)"
          python3 -c "import os; print('cpu_count', os.cpu_count(), 'affinity', len(os.sched_getaffinity(0)))"
 
      # Cache the robot_descriptions clones (~16 repos, several with mesh
      # files). Uncached, these clones dominate CI wall-clock (~30-55 min) and
      # cause the run-to-run variance. Keyed on MANIFEST.toml so adding an arm
      # invalidates; restore-keys lets a manifest change reuse the prior cache
      # so the prewarm step only clones the new entry. (#348)
      - name: Cache robot_descriptions
        uses: actions/cache@v4
        with:
          path: ~/.cache/robot_descriptions
          key: rd-${{ hashFiles('src/ssik/prebuilt/MANIFEST.toml') }}
          restore-keys: rd-
 
      - name: Pre-warm robot_descriptions
        # Serial clone before pytest so the parallel (-n auto) workers only
        # read the cache -- no concurrent clones racing on the same dir.
        run: uv run python scripts/prewarm_descriptions.py
 
      - name: ruff check
        run: uv run ruff check
 
      - name: ruff format --check
        run: uv run ruff format --check
 
      - name: mypy
        run: uv run mypy
 
      - name: regen_docs --check
        # Fail if MANIFEST.toml and the AUTOGEN doc-table regions are
        # out of sync. The script regenerates anchored tables from
        # the manifest; this gate enforces "edit MANIFEST.toml + run
        # scripts/regen_docs.py" as the only correct workflow for
        # arm metadata changes.
        run: uv run python scripts/regen_docs.py --check
 
      - name: pytest (parallel)
        # -n 4 (ubuntu-latest = 4 vCPUs). The heavy redundant tests (full
        # snapshot roster, etc.) are marked slow and run on the macOS pre-push
        # hook + nightly, so the per-PR suite is lean. Descriptions are
        # pre-warmed above so workers never clone concurrently. perf gates are
        # excluded here and run serially below. (#348)
        run: uv run pytest -q -n 4 -m "not slow and not perf"
 
      - name: pytest (perf gates, serial)
        # Wall-clock performance gates: run serially (no -n) so timing is
        # measured without cross-worker CPU contention. Small, fast set. (#348)
        run: uv run pytest -q -m "not slow and perf"
 

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.

This workflow runs 1 job per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.

Actions used in this workflow