Adding Ruff to Your Python Project With GitHub Actions
By Gary Worthington, More Than Monkeys

If you’ve spent any time linting Python code, you’ve probably wrestled with flake8, black, pylint, isort, or some combination of the above. And while each tool does its job well enough, managing them together is a pain.
Ruff aims to replace most of them with one fast, opinionated linter. It handles formatting, style, and static analysis in a single package, and it’s fast enough to run on every commit, pull request, and even pre-commit hook.
In this post, I’ll walk through how to add Ruff to a Python project and wire it into GitHub Actions so your CI fails on style and formatting violations.
Why Ruff Exists
Ruff was created by Charlie Marsh, founder of Astral, to solve a simple problem: Python tooling was too slow, fragmented, and annoying to manage. You needed one tool to catch style violations (flake8), another to sort imports (isort), another to format code (black), and another to check for unused code (pyflakes, pylint, etc). On large codebases, this stack gets slow and painful to configure.
Ruff replaces almost all of that with one tool. It’s written in Rust, so it’s super fast. You can lint a full Django project in under a second. It supports hundreds of rules from tools you’re already using, but without the overhead.
You don’t need five separate config files or three different pip install commands. Ruff gives you a single, unified linter with sensible defaults, extensible rulesets, and support for auto-fixing common issues.
You’ll want to switch if:
- You’re sick of juggling multiple tools and configs
- Your CI pipeline is too slow
- You want consistent linting and formatting across your team
- You want fast feedback without sacrificing rule coverage
If you’re already using black, flake8, and isort, Ruff will likely give you the same results with faster execution and fewer moving parts.
Step 1: Install Ruff Locally
Add Ruff to your project dependencies. If you use requirements.txt:
ruff==0.4.6
With Poetry:
poetry add --group dev ruff
Test it locally:
ruff check .
To auto-fix violations:
ruff check . --fix
Step 2: Configure Ruff in pyproject.toml
Create or update your pyproject.toml:
[tool.ruff]
line-length = 100
target-version = "py311"
[tool.ruff.lint]
select = ["E", "F", "I"] # E: pycodestyle, F: pyflakes, I: isort (import sorting)
ignore = ["E501"] # Ignore line length errors (optional, e.g. if Black handles this)
This enables a solid baseline:
- Syntax and formatting rules from pycodestyle
- Logical errors from pyflakes
- Import sorting via isort rules
Adjust as needed for your project. You can find a whole bunch of options at https://docs.astral.sh/ruff/settings/#lint_select
Step 3: Add Ruff to GitHub Actions
Create a workflow at .github/workflows/lint.yml:
on:
pull_request:
push:
branches:
- main
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.13'
- name: Install Ruff
run: pip install ruff
- name: Run Ruff
run: ruff check .
This runs Ruff on every push and pull request to main. It fails the workflow if violations are found.
If you want Ruff to attempt fixes in CI (not recommended unless you’re auto-committing changes), use:
- name: Run Ruff with Fix
run: ruff check . --fix
Step 4: Enforce Status Checks on Pull Requests
To prevent issues reaching main, require the lint job to pass before merges:
- Go to GitHub > Settings > Branches
- Add or edit your main branch protection rule
- Enable “Require status checks to pass before merging”
- Add Lint to the list of required checks
This ensures contributors see violations early and fix them before merge.
Step 5: Clean Up Old Tools
Now that Ruff is handling your linting and formatting, you can simplify your setup:
- Remove .flake8, .isort.cfg, .pylintrc if not used elsewhere
- Consolidate config into pyproject.toml
- Align editor tooling (e.g. VS Code or PyCharm) to use Ruff
Bonus: Format With Ruff
Ruff can also act as a code formatter. To enable formatting checks:
ruff format . --check
To add it to GitHub Actions:
- name: Check formatting
run: ruff format . --check
To see differences:
run: ruff format . --check --diff
This replaces or supplements black depending on your preference.
Final Thoughts
Ruff is fast, easy to configure, and removes the need to glue multiple tools together. Adding it to your project and CI flow means less time arguing about code style and more time shipping useful software.
It’s simple enough to get started in under 10 minutes and powerful enough to grow with your codebase.