[Flutter] 静的解析の設定を入れる (flutter_lints)

# はじめに

しばらく、以下のCodelabのlint設定を使っていましたがそろそろ見直したいと思います。

https://codelabs.developers.google.com/codelabs/flutter-codelab-first?hl=ja#2

# やること

- `flutter_lints` の導入
- GitHub Actions での自動実行

# 使用する Linter

https://pub.dev/packages/flutter_lints

`flutter_lints` を使います。

>This package contains a recommended set of lints for Flutter apps, packages, and plugins to encourage good coding practices.
>
> This package is built on top of Dart's recommended.yaml set of lints from package:lints.

公式で推奨される lint セットが入っているとのこと。
`flutter create` で作った時に含まれているものでもあるので、おそらくこれがデファクトスタンダードでしょう。

# インストール

https://pub.dev/packages/flutter_lints/install

公式手順に従い、以下のコマンドを実行します。

```bash
flutter pub add dev:flutter_lints
```


# 設定変更

プロジェクトのルートディレクトリに `analysis_options.yaml` を作成します。
(`flutter create` で追加した場合は、特に修正しなくてOK)

```diff_yaml
include: package:flutter_lints/flutter.yaml
```

ルールの除外も設定できますが、極力デフォルトの状態で使う派なので、必要になるまでこの状態とします。

# 実行

以下のコマンドを実行します。

```bash
flutter analyze
```

実行結果

```bash
# flutter analyze
Analyzing media_manager_app...                                          

   info • Use 'const' with the constructor to improve performance • lib/main.dart:32:10 • prefer_const_constructors
   info • Constructors for public widgets should have a named 'key' parameter • lib/main.dart:128:7 • use_key_in_widget_constructors

...

prefer_const_constructors
   info • Use 'const' with the constructor to improve performance • lib/main.dart:380:29 • prefer_const_constructors

30 issues found. (ran in 5.4s)
```

すごい怒られてますね。
早めに導入しておいて良かったです。

# CIへの組み込み

一旦、エラーはそのままにしておき、CIに組み込んで行きます。

`.github/workflows/check.yaml` を以下の内容で作成します。

```yaml
name: Check Process

on:
  # main ブランチ への Pull Request を対象にする
  pull_request:
    branches:
      - main

permissions:
  contents: read
  # Lint失敗時にコメントを残すための権限を追加
  pull-requests: write

jobs:
  lint:
    name: Run Flutter Lints
    runs-on: ubuntu-latest
    timeout-minutes: 10
    env:
      # LintのPRコメントのヘッダ
      LINT_COMMENT_HEADER: "### :robot: Flutter Lint Results"
    steps:
      # Flutter SDKをセットアップ
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Flutter
        uses: subosito/flutter-action@v2
        with:
          channel: 'stable'
          cache: true

      # 必要なパッケージをインストール
      - name: Install dependencies
        run: flutter pub get

      # flutter analyze を実行し、結果をログに出力
      - name: Run Flutter Analyze
        run: |
          flutter analyze --no-pub > lint-report.txt || true

      # 既存のLintコメントを削除
      - name: Remove Old Lint Comments
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          COMMENTS=$(gh api \
            -H "Accept: application/vnd.github+json" \
            -H "X-GitHub-Api-Version: 2022-11-28" \
            repos/${{ github.repository }}/issues/${{ github.event.number }}/comments --jq '.[].id')
          for ID in $COMMENTS; do
            BODY=$(gh api \
              -H "Accept: application/vnd.github+json" \
              -H "X-GitHub-Api-Version: 2022-11-28" \
              repos/${{ github.repository }}/issues/comments/$ID --jq '.body')
            if [[ $BODY == *"${{ env.LINT_COMMENT_HEADER }}"* ]]; then
              gh api \
                --method DELETE \
                -H "Accept: application/vnd.github+json" \
                -H "X-GitHub-Api-Version: 2022-11-28" \
                repos/${{ github.repository }}/issues/comments/$ID
            fi
          done

      # Lintエラーがある場合にPRコメントを投稿
      - name: Post Lint Results to PR
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          # issueがあるか確認(error,warning,info全て検出)
          if grep -qE 'error|warning|info' lint-report.txt; then
            # 結果を整形してコメント用に保存
            echo "${{ env.LINT_COMMENT_HEADER }}" > comment.md
            echo '```' >> comment.md
            cat lint-report.txt >> comment.md
            echo '```' >> comment.md

            # PRにコメントを投稿
            gh pr comment ${{ github.event.number }} --body-file comment.md

            # CIを失敗させる
            exit 1
          else
            echo "${{ env.LINT_COMMENT_HEADER }}" > comment.md
            echo '```' >> comment.md
            echo 'No issues found!' >> comment.md
            echo '```' >> comment.md

            # PRにコメントを投稿
            gh pr comment ${{ github.event.number }} --body-file comment.md
          fi
```

`lint` 実行だけでは寂しいので、失敗時にPRコメントを残すようにしてみました。
使用しているGitHubのコマンドについては以下を参照してください。

https://cli.github.com/manual/gh_pr_comment

https://docs.github.com/ja/rest/issues/comments?apiVersion=2022-11-28

では、この状態で別ブランチにPushし、main ブランチに対して、PullRequest を作成してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3294382/e47cb926-47ee-4478-9c70-ac25403a30f3.png)

こんな感じでエラー内容が表示されます。

修正して、再度 commit+push してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3294382/dc93f291-471f-10ac-3758-2a5d3a274da7.png)

このようにエラーのコメントが削除され、`No issues found!` のコメントが残され、CIが成功します。

CIも出来ました！

:::note

最初レビュー (`gh pr review`) で残そうとしたのですが、それだとCI上で削除することができなかったため、コメント(`gh pr comment`) で残しています

:::



# 終わりに

今回は、lintの導入とCIでの自動実行を入れてみました。
特にチーム開発では、個人の環境で実行していると実行ミスや環境差分による結果差分が発生する可能性があるため、早めに入れておいた方が良いですね。
少しでも参考になれば幸いです。