feat: Use autofix.ci to apply Ruff formatting #17
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |