How to Use Ruff CLI: Essential Flags for Linting and Formatting
I installed ruff and ran ruff check . on my Python project. The output showed 47 violations across 12 files. But how do I fix them? What if I want to preview changes before applying them? How do I override my config for a specific run?
I spent the next hour reading ruff’s CLI documentation. Turns out ruff has a powerful command-line interface with flags that make my workflow significantly smoother. Let me show you the essential commands and flags I use daily.
The Two Main Commands: check and format
Ruff’s CLI has two primary commands:
ruff check → Linting (find code issues, violations)ruff format → Formatting (apply consistent code style)Everything else builds on these two. I used to think ruff was just a linter, but the ruff format command replaces black entirely. Here’s my typical workflow:
# First, check what issues existruff check src/
# Fix the issues ruff can auto-fixruff check --fix src/
# Then format the coderuff format src/I run these before every commit. The entire process takes about 0.5 seconds on my project with 200 files.
The —fix Flag: Your Best Friend
The --fix flag is the most useful CLI option. It automatically fixes violations that ruff can safely correct:
ruff check --fix src/What gets fixed automatically? Import sorting, unused imports, trailing whitespace, missing newlines at file end, and many more. Ruff can fix about 200 different rule violations automatically.
I made a mistake early on: I ran --fix without checking what would change first. This modified files I didn’t expect. The better approach:
# First, see what will be fixedruff check --diff src/
# Then apply the fixesruff check --fix src/The --diff flag shows what changes will be made without actually modifying files. I always run --diff before --fix on unfamiliar codebases.
The —diff Flag: Preview Before Commit
The --diff flag shows a git-style diff of what ruff would change:
ruff check --diff src/Output looks like this:
--- src/utils.py+++ src/utils.py@@ -1,4 +1,5 @@ import os+import sys # Added missing import from pathlib import Path
def process_file(path):- data = open(path).read()+ with open(path) as f: # Safe file handling+ data = f.read() return dataI use --diff in three scenarios:
- Before fixing unfamiliar code - See what changes will be made
- In code reviews - Show reviewers what ruff would change
- Debugging config issues - Verify my configuration applies correctly
The —unsafe-fixes Flag: When You Need More Fixes
Some fixes are marked as “unsafe” because they might change code behavior. Ruff won’t apply these by default:
ruff check --fix --unsafe-fixes src/What makes a fix “unsafe”? Examples include:
- Removing unused variables (might be intentional for debugging)
- Changing
== Nonetois None(affects some custom__eq__implementations) - Rewriting certain comprehensions
I avoid --unsafe-fixes in CI pipelines. But during local development, I sometimes use it to clean up code quickly, then review the changes carefully.
The —check Flag for Format: CI Integration
The ruff format command modifies files by default. For CI, I use --check to fail if formatting is needed:
ruff format --check src/This exits with code 1 if any files need formatting, failing the CI build. Combined with lint check:
ruff check src/ruff format --check src/Both commands must pass. No files are modified.
The —config Flag: Override Settings from CLI
The --config flag does two things: specify a config file path, or override specific settings:
ruff check --config custom-ruff.toml src/More useful for me: overriding settings without modifying my config file:
ruff check --config "lint.select=['E','F','B']" src/
ruff check --config "line-length=100" src/
ruff check --config "lint.ignore=['E501','F401']" src/Each --config flag accepts a TOML key=value pair. I use this when:
- Running a stricter check on critical modules
- Temporarily relaxing rules during debugging
- Testing different configurations before committing
Multiple overrides work by passing multiple --config flags:
ruff check \ --config "lint.select=['E','F','B','S']" \ --config "line-length=120" \ src/The —select and —ignore Flags: Quick Rule Control
Instead of editing pyproject.toml, I can control rules directly from CLI:
ruff check --select E,F,B src/
ruff check --select ALL src/The --ignore flag excludes rules:
ruff check --ignore E501,F401 src/These flags override any config file settings. I use them for:
- Running security-only checks:
ruff check --select S src/ - Testing a stricter configuration:
ruff check --select ALL --ignore D src/ - One-off checks without modifying config
The —output-format Flag: Machine-Readable Output
For CI integration or scripts, I use structured output formats:
ruff check --output-format=json src/Available formats:
json → JSON array of violationsjunit → JUnit XML (for test reporting tools)sarif → SARIF format (for GitHub code scanning)github → GitHub Actions annotationsgitlab → GitLab Code Quality reportconcise → Minimal human-readable outputfull → Detailed human-readable output (default)For GitHub Actions, the github format creates annotations directly in the PR:
name: Lint
on: [push, pull_request]
jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Install uv uses: astral-sh/setup-uv@v4
- name: Lint with ruff run: uvx ruff check --output-format=github src/
- name: Check format run: uvx ruff format --check src/The —watch Flag: Continuous Development
The --watch flag runs ruff continuously, re-checking on file changes:
ruff check --watch src/I use this during active development. As I edit files, ruff immediately shows new violations. The feedback loop is instant.
Combine with --fix for live fixing:
ruff check --watch --fix src/Warning: This modifies files as you work. I prefer watching without fixing, then running --fix manually when ready.
The —exit-non-zero-on-fix Flag: Catch CI Modifications
In CI, if ruff fixes files, I want the build to fail so I know there were issues:
ruff check --fix --exit-non-zero-on-fix src/This applies fixes but exits with code 1 if any fixes were made. In CI, this means:
- Files get fixed (committed back via CI automation)
- Build fails, notifying me that code needed cleanup
- I can review the fixes before merging
Without this flag, --fix exits with code 0 even if files were modified, which can silently change code in CI without notification.
Other Useful Commands
Ruff has several utility commands:
ruff rule E501
ruff rule F401
ruff rule S101This explains what a rule does and why it matters. I use this when I encounter unfamiliar error codes.
ruff config
ruff config lint.select
ruff config line-lengthThis shows your effective configuration after loading from all sources.
ruff cleanRuff caches parsed files for speed. ruff clean removes the cache. I rarely need this, but it helps when debugging strange behavior.
Common Mistakes I Made
Mistake 1: Using —fix Without —diff First
I ran ruff check --fix src/ on a shared repository. It modified files other developers were working on. Now I always run --diff first to preview changes.
Mistake 2: Not Understanding CLI Overrides Config
I spent 30 minutes debugging why my pyproject.toml config wasn’t working. Turns out I had a --select E,F flag in my shell alias that was overriding everything. CLI flags always override config files.
Mistake 3: Missing —exit-non-zero-on-fix in CI
My CI pipeline ran ruff check --fix but always passed, even when it modified files. I didn’t realize fixes were happening. Adding --exit-non-zero-on-fix made CI notify me of changes.
Mistake 4: Using —unsafe-fixes Carelessly
I ran --unsafe-fixes on a project and it removed variables I was using for debugging. The code still ran, but my debug output disappeared. Now I only use unsafe fixes after careful review.
Summary
In this post, I showed you the essential Ruff CLI commands and flags I use daily. The key points are:
ruff checkfor linting,ruff formatfor formatting--fixapplies automatic fixes, but use--difffirst to preview--unsafe-fixesincludes fixes that might change code behavior--configaccepts file paths or TOML key=value overrides--selectand--ignorecontrol rules directly from CLI--output-format=jsonenables CI integration--watchruns continuously during development--exit-non-zero-on-fixcatches modifications in CI
Start with the basics: ruff check . to find issues, ruff check --diff . to preview fixes, ruff check --fix . to apply fixes, and ruff format . to format. Add flags as your workflow needs them.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments