【Python】LinterとFormatterがオールインワンのRuffを使ってGithubActionsを組んでみる！

## はじめに

最近Ruffっていうpython用のリンタ兼フォーマッタを教えてもらいました。

https://docs.astral.sh/ruff/

以下の特徴があるらしいです。

- Rust製なのでさっくさく
  - HPのベンチマークがえぐいことになってますね
- flake8とblackを内包したオールインワンライブラリ
- モノレポフレンドリー
  - 各階層の設定ファイルが参照される

今回はpoetryとRuffを組み合わせてpythonのフォーマットとリントを試してみようと思います！
GithubActionsのテンプレートも作成します！:fire:

## 1. Install

https://docs.astral.sh/ruff/installation/

今回はMacで構築します。
公式ではpipで入れてますが、せっかくなのでasdf + poetryで構築します！

### 1. asdf

今回はasdf経由でpoetryをinstallするのでasdfを入れます。

https://qiita.com/ysmb-wtsg/items/ec5fbc29a04323052ae4

```bash
brew install git curl  #インストール済であれば不要
brew install asdf
echo -e "\n. $(brew --prefix asdf)/libexec/asdf.sh" >> ${ZDOTDIR:-~}/.zshrc
. ~/.zshrc
```

### 2. poetry

適当な作業用ディレクトリを作ってpoetryの設定をします。

```bash
mkdir ruff_sample
cd ruff_sample
asdf plugin add poetry
asdf install poetry latest
asdf local poetry latest
```

### 3. Ruff

poetry経由でRuffをinstallします。

```bash
poetry init -q
poetry add -D ruff
```

これでinstallは完了です！！:tada:

## 2. Usage

https://docs.astral.sh/ruff/tutorial/

上述の通り、Ruffはリンタ兼フォーマッタなので適当なpyファイルを用意します。
スクリプトの内容は公式から持ってきました。

```bash
touch a.py
```

```a.py
from typing import Iterable

import os


def sum_even_numbers(numbers: Iterable[int]) -> int:
    """Given an iterable of integers, return the sum of all even numbers in the iterable."""
    return sum(
        num for num in numbers
        if num % 2 == 0
    )
```

このスクリプトはflake8のリンタエラーが含まれていて、blackのフォーマット規定に従っていません。
(`os`未使用と、　88文字に満たない行が改行されている点)

リンタは以下のように実行します。

```bash
poetry run ruff chack a.py
```

すると結果が表示されます。

```bash
❯ poetry run ruff check a.py
a.py:3:8: F401 [*] `os` imported but unused
  |
1 | from typing import Iterable
2 |
3 | import os
  |        ^^ F401
  |
  = help: Remove unused import: `os`

Found 1 error.
[*] 1 fixable with the `--fix` option.
```

これを勝手に直してもらうには先ほどのコマンドに`--fix`オプションをつけます。

```bash
❯ poetry run ruff check --fix a.py
Found 1 error (1 fixed, 0 remaining).
```

するとファイルが勝手に書き変わります。

```a.py
from typing import Iterable



def sum_even_numbers(numbers: Iterable[int]) -> int:
    """Given an iterable of integers, return the sum of all even numbers in the iterable."""
    return sum(
        num for num in numbers
        if num % 2 == 0
    )
```

次にフォーマットをかけてみます。

```bash
❯ poetry run ruff format a.py
1 file reformatted
```

するとファイルが勝手にフォーマットされます。

```a.py
from typing import Iterable


def sum_even_numbers(numbers: Iterable[int]) -> int:
    """Given an iterable of integers, return the sum of all even numbers in the iterable."""
    return sum(num for num in numbers if num % 2 == 0)

```

基本的な使い方はこんな感じです！

## 3. Settings

https://docs.astral.sh/ruff/settings/

設定は以下のいずれかのファイルに記載します。

- pyproject.toml
- ruff.toml

内容についてはdoc読むなりググるなりしてカスタムしてみてください。
ちなみに筆者は以下で使ってます。

```toml
[tool.ruff]
line-length = 120
indent-width = 4
target-version = "py39"
exclude = ["path/to/lambdaLayerLibs"]

[tool.ruff.lint]
select = ["E", "F", "W", "B", "I"]
ignore = []
fixable = ["ALL"]

[tool.ruff.lint.isort]
force-single-line = true

[tool.ruff.lint.mccabe]
max-complexity = 5

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
skip-magic-trailing-comma = false
line-ending = "auto"
```

## 4. GithubActions

ではこれをGithubActionsに組み込んでみます。
早速テンプレートです。

```format_and_lint.yml
name: Format and Lint
on:
  push:
    branches:
      - 'main'
  pull_request:
    types:
      - opened
      - synchronize
      - closed
    branches:
      - main
      - develop
jobs:
  format_and_lint:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4
      - name: Set up Python 3.9
        uses: actions/setup-python@v5
        with:
          python-version-file: ".python-version"
      - name: Install poetry
        run: |
          curl -sSL https://install.python-poetry.org | python3 -
          echo "$HOME/.poetry/bin" >> $GITHUB_PATH
      - name: Confirm Poetry
        run: |
          poetry --version
      - name: Poetry install only devDependencies
        run: |
          poetry install --only dev
      - name: Format
        run: |
          poetry run ruff format .
      - name: Lint
        run: |
          poetry run ruff check --fix --output-format=github .
      - name: Auto commit
        uses: stefanzweifel/git-auto-commit-action@v5
        with:
          commit_message: Commit by Workflow
```

:::note info
setup-pythonでは、asdfで使われている`.tool-versions`でのバージョン指定ができないので`.python-version`をtouchしてください。。
https://github.com/actions/setup-python
:::

このワークフローがあるリポジトリに先ほどのa.pyをコミットしてみると、以下のようにワークフローによる自動コミットが適用されます！:tada:

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2947418/cb437d25-a400-8502-9c0a-0d09e1638ebb.png)


## 最後に

以上がRuffの紹介からGithubActionsに組み込むまでの説明になります！
個人的に気に入った点は

- オールインワンなところ
- 爆速なところ

でした！(普通)
ただやはり開発体験において速さは正義なので、爆速なだけで導入価値あるなと感じました！:fist_tone2: