Running pre-commit in a reusable GitHub Actions workflow with caching
The pre-commit framework is an awesome utility to enforce coding standards and run sanity checks, such as preventing large files from being committed.
It's not just convenient to run locally on the developers' machines. By using pre-commit to run checks, linters, and formatters in our CI/CD workflows, it's straightfoward to ensure consistency, with our .pre-commit-config.yaml
becoming the single source of truth.
The official GitHub action for pre-commit is no longer actively maintained, and I often find that it is not versatile enough. Particularly in a repository that contains several components, each covered by a dedicated workflow, I'd like to
define the pre-commit setup and configuration in a single place,
cache dependencies and environments to the maximum degree possible,
and flexibly determine which files are processed by a given workflow run.
Here's a GitHub Actions workflow that meets these three requirements:
name: Lint
on:
push:
# exclude files covered by dedicated workflows
paths-ignore:
- 'component/**'
workflow_call:
inputs:
working-directory:
type: string
required: false
default: '.'
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
id: setup-python
uses: actions/setup-python@v5
with:
python-version: '3.10'
cache: 'pip'
- uses: actions/cache@v4
with:
path: ~/.cache/pre-commit
key: >
${{ format('pre-commit-{0}-{1}',
steps.setup-python.outputs.python-version,
hashFiles('.pre-commit-config.yaml')
) }}
- name: Install pre-commit
run: |
pip install --upgrade pip
pip install pre-commit
pre-commit install
- name: Run pre-commit hooks
working-directory: ${{ inputs.working-directory }}
run: |
git ls-files | xargs pre-commit run \
--show-diff-on-failure \
--color=always \
--files
This runs as a standalone workflow but can also be called by other workflows. If the main pre-commit workflow is stored as .github/workflows/lint.yml
, you can integrate it into other workflows as follows:
name: Component
on:
push:
paths:
- 'component/**'
jobs:
lint:
uses: ./.github/workflows/lint.yml
with:
working-directory: component
Subscribe to my newsletter
Read articles from Kilian Kluge directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Kilian Kluge
Kilian Kluge
My journey into software and infrastructure engineering started in a physics research lab, where I discovered the merits of loose coupling and adherence to standards the hard way. I like automated testing, concise documentation, and hunting complex bugs.