CI workflow (Unstructured-IO/unstructured-api)
The CI workflow from Unstructured-IO/unstructured-api, explained and optimized by Latchkey.
CI health: C - fair
Run this on Latchkey for self-healing, caching, and up to 58% lower cost.
Grade your own workflow free or run it on Latchkey →What it does
This is the CI workflow from the Unstructured-IO/unstructured-api repository, a real project running GitHub Actions. It is shown here with attribution under its Apache-2.0 license.
Below, Latchkey shows a faster, safer version produced by its optimization engine.
The workflow
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
PIPELINE_FAMILY: "general"
jobs:
lint:
runs-on: opensource-linux-8core
steps:
- uses: actions/checkout@v5
- name: Read Python version from .python-version
run: echo "PYTHON_VERSION=$(cat .python-version)" >> $GITHUB_ENV
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true
cache-dependency-glob: "uv.lock"
- name: Set up Python ${{ env.PYTHON_VERSION }}
run: uv python install ${{ env.PYTHON_VERSION }}
- name: Install lint dependencies
run: uv sync --only-group lint --locked
- name: Lint
run: make check
shellcheck:
runs-on: opensource-linux-8core
steps:
- uses: actions/checkout@v5
- name: ShellCheck
uses: ludeeus/action-shellcheck@master
test:
runs-on: opensource-linux-8core
needs: lint
steps:
- uses: actions/checkout@v5
- name: Read Python version from .python-version
run: echo "PYTHON_VERSION=$(cat .python-version)" >> $GITHUB_ENV
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true
cache-dependency-glob: "uv.lock"
- name: Set up Python ${{ env.PYTHON_VERSION }}
run: uv python install ${{ env.PYTHON_VERSION }}
- name: Install dependencies and run core tests
run: |
sudo apt-get update && sudo apt-get install --yes poppler-utils libreoffice
uv sync --group test --locked
make install-pandoc
make install-nlp-models
sudo add-apt-repository -y ppa:alex-p/tesseract-ocr5
sudo apt-get install -y tesseract-ocr tesseract-ocr-kor
tesseract --version
make test
make check-coverage
changelog:
runs-on: opensource-linux-8core
steps:
- uses: actions/checkout@v5
- if: github.ref != 'refs/heads/main'
uses: dorny/paths-filter@v3
id: changes
with:
filters: |
src:
- 'doc_recipe/**'
- 'recipe-notebooks/**'
- if: steps.changes.outputs.src == 'true' && github.ref != 'refs/heads/main'
uses: dangoslen/changelog-enforcer@v3
# TODO - figure out best practice for caching docker images
test_dockerfile:
runs-on: opensource-linux-8core
needs: lint
steps:
- uses: actions/checkout@v5
- name: Read Python version from .python-version
run: echo "PYTHON_VERSION=$(cat .python-version)" >> $GITHUB_ENV
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true
cache-dependency-glob: "uv.lock"
- name: Set up Python ${{ env.PYTHON_VERSION }}
run: uv python install ${{ env.PYTHON_VERSION }}
- name: Free up disk space
run: |
# Clear some space (https://github.com/actions/runner-images/issues/2840)
echo "Disk usage before cleanup:"
df -h
# Remove unnecessary pre-installed software
sudo rm -rf /usr/share/dotnet
sudo rm -rf /opt/ghc
sudo rm -rf /usr/local/share/boost
sudo rm -rf /usr/local/lib/android
sudo rm -rf /opt/hostedtoolcache/CodeQL
sudo rm -rf /usr/local/.ghcup
sudo rm -rf /usr/share/swift
# Clean up docker to ensure we start fresh
docker system prune -af --volumes
echo "Disk usage after cleanup:"
df -h
- name: Test Dockerfile
run: |
uv sync --group test --locked
make docker-build
make docker-test
The same workflow, on Latchkey
Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.
name: CI on: push: branches: [ main ] pull_request: branches: [ main ] env: PIPELINE_FAMILY: "general" concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: lint: timeout-minutes: 30 runs-on: opensource-linux-8core steps: - uses: actions/checkout@v5 - name: Read Python version from .python-version run: echo "PYTHON_VERSION=$(cat .python-version)" >> $GITHUB_ENV - name: Install uv uses: astral-sh/setup-uv@v7 with: enable-cache: true cache-dependency-glob: "uv.lock" - name: Set up Python ${{ env.PYTHON_VERSION }} run: uv python install ${{ env.PYTHON_VERSION }} - name: Install lint dependencies run: uv sync --only-group lint --locked - name: Lint run: make check shellcheck: timeout-minutes: 30 runs-on: opensource-linux-8core steps: - uses: actions/checkout@v5 - name: ShellCheck uses: ludeeus/action-shellcheck@master test: timeout-minutes: 30 runs-on: opensource-linux-8core needs: lint steps: - uses: actions/checkout@v5 - name: Read Python version from .python-version run: echo "PYTHON_VERSION=$(cat .python-version)" >> $GITHUB_ENV - name: Install uv uses: astral-sh/setup-uv@v7 with: enable-cache: true cache-dependency-glob: "uv.lock" - name: Set up Python ${{ env.PYTHON_VERSION }} run: uv python install ${{ env.PYTHON_VERSION }} - name: Install dependencies and run core tests run: | sudo apt-get update && sudo apt-get install --yes poppler-utils libreoffice uv sync --group test --locked make install-pandoc make install-nlp-models sudo add-apt-repository -y ppa:alex-p/tesseract-ocr5 sudo apt-get install -y tesseract-ocr tesseract-ocr-kor tesseract --version make test make check-coverage changelog: timeout-minutes: 30 runs-on: opensource-linux-8core steps: - uses: actions/checkout@v5 - if: github.ref != 'refs/heads/main' uses: dorny/paths-filter@v3 id: changes with: filters: | src: - 'doc_recipe/**' - 'recipe-notebooks/**' - if: steps.changes.outputs.src == 'true' && github.ref != 'refs/heads/main' uses: dangoslen/changelog-enforcer@v3 # TODO - figure out best practice for caching docker images test_dockerfile: timeout-minutes: 30 runs-on: opensource-linux-8core needs: lint steps: - uses: actions/checkout@v5 - name: Read Python version from .python-version run: echo "PYTHON_VERSION=$(cat .python-version)" >> $GITHUB_ENV - name: Install uv uses: astral-sh/setup-uv@v7 with: enable-cache: true cache-dependency-glob: "uv.lock" - name: Set up Python ${{ env.PYTHON_VERSION }} run: uv python install ${{ env.PYTHON_VERSION }} - name: Free up disk space run: | # Clear some space (https://github.com/actions/runner-images/issues/2840) echo "Disk usage before cleanup:" df -h # Remove unnecessary pre-installed software sudo rm -rf /usr/share/dotnet sudo rm -rf /opt/ghc sudo rm -rf /usr/local/share/boost sudo rm -rf /usr/local/lib/android sudo rm -rf /opt/hostedtoolcache/CodeQL sudo rm -rf /usr/local/.ghcup sudo rm -rf /usr/share/swift # Clean up docker to ensure we start fresh docker system prune -af --volumes echo "Disk usage after cleanup:" df -h - name: Test Dockerfile run: | uv sync --group test --locked make docker-build make docker-test
What changed
- Cancel superseded runs when a branch or PR gets a newer push.
- Add a job timeout so a hung step cannot burn hours of runner time.
4 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:
- Dependency installs
This workflow runs 5 jobs per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.