Skip to content

feat: Use autofix.ci to apply Ruff formatting #17

feat: Use autofix.ci to apply Ruff formatting

feat: Use autofix.ci to apply Ruff formatting #17

Workflow file for this run

name: Lint Backend
on:
pull_request:
paths:
- 'src/**/*.py'
- 'tests/**/*.py'
- 'pyproject.toml'
- 'uv.lock'
- '.github/workflows/lint-backend.yml'
# Cancel in-flight runs on the same PR when a new push lands.
concurrency:
group: lint-backend-${{ github.ref }}
cancel-in-progress: true
# autofix.ci's GitHub App handles writes to the PR branch independently of
# the workflow token, so the job itself only needs read access. Setup:
# install https://github.com/apps/autofix-ci on the repo (free for public
# repos) and enable auto-apply in the autofix.ci dashboard.
permissions:
contents: read
jobs:
lint:
name: Ruff and mypy on changed files
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
# Need the full base ref locally so we can compute the diff
# against the PR's merge base.
fetch-depth: 0
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
enable-cache: true
- name: Set up Python
run: uv python install 3.13
- name: Install dev dependencies
run: uv sync --group dev
- name: Compute changed Python files
id: changed
run: |
base="${{ github.event.pull_request.base.sha }}"
head="${{ github.event.pull_request.head.sha }}"
# Diff against the merge base so we only see files touched in the PR.
merge_base=$(git merge-base "$base" "$head")
mapfile -t files < <(git diff --name-only --diff-filter=ACMR "$merge_base" "$head" -- '*.py')
if [ "${#files[@]}" -eq 0 ]; then
echo "No Python files changed."
echo "files=" >> "$GITHUB_OUTPUT"
exit 0
fi
printf '%s\n' "${files[@]}"
# Pass as a single space-separated string to subsequent steps.
echo "files=${files[*]}" >> "$GITHUB_OUTPUT"
- name: Ruff format (in-place)
if: steps.changed.outputs.files != ''
run: uv run ruff format ${{ steps.changed.outputs.files }}
# Hands any working-tree changes from the previous step to autofix.ci.
# In auto-apply mode the App pushes a commit back to the PR branch;
# the action exits non-zero on the run that produced the diff, then
# the App's commit triggers a fresh CI cycle that passes.
- name: Apply ruff format autofix via autofix.ci
if: steps.changed.outputs.files != ''
uses: autofix-ci/action@v1.3.1
with:
commit-message: "style: ruff format (auto)"
- name: Ruff lint (no autofix)
if: steps.changed.outputs.files != ''
run: |
# Lint violations still fail the build. Auto-fixing lint
# ("ruff check --fix") is intentionally NOT enabled here —
# safe-classified fixes still include things like F401 (unused
# imports), which can break side-effect imports used by the
# connector registration pattern.
uv run ruff check --no-fix --output-format=github ${{ steps.changed.outputs.files }}
- name: Mypy
if: steps.changed.outputs.files != ''
run: |
# Run mypy from src/ so module paths resolve, dropping the
# leading "src/" from each path. Tests files are checked from
# the repo root.
src_files=()
test_files=()
for f in ${{ steps.changed.outputs.files }}; do
case "$f" in
src/*) src_files+=("${f#src/}") ;;
tests/*) test_files+=("$f") ;;
esac
done
if [ "${#src_files[@]}" -gt 0 ]; then
(cd src && uv run mypy "${src_files[@]}")
fi
if [ "${#test_files[@]}" -gt 0 ]; then
uv run mypy "${test_files[@]}"
fi