FlutterでのCI

# はじめに
チームでFlutterの開発をする中で、コードの品質やテストコードによる保護をするため、CIについて学びましたので記事にしたいと思います。

個人的な話になりますが、現在、携わっているプロジェクトでは、コードレビューはありますが、テストコードはほとんど無しで開発を進めています。
コードの品質を保つのはコードレビューで行っていますが、細かくみているとかなり時間を取られます。

スケジュールが厳しくなってくると、開発速度が優先になり、仕様を満たしているかだけが要点になってしまい、コードレビューで細かく見る時間がなくなってしまうことがありました。
本来は、スケジュールが厳しくなることを防ぎたいのですが、100％防ぐのは難しいですし、CIのサービスを使って、開発環境を整備していくのが良さそうだと思っています。


# CI
## CIとは
CIは「Continuous Integration」の略で、継続的インテグレーションと訳します。
コードの変更をmain（master）ブランチに統合するプロセスを指すようです。
そう説明されてもいまいちピンと来ませんでしたが、このプロセスの目的は**ソフトウェア開発プロセスの効率化と品質の向上**です。
具体的には、**任意のタイミングで、コードチェックを自動で行うこと**です。

### 任意のタイミングとは
任意のタイミングとは例えば下記になります。
- GitにPush時
- PR作成時
- 定期実行（毎日9時とか）

### コードチェックとは
コードチェックとは例えば下記になります。
- 静的解析
- フォーマットチェック
- ビルドできるか確認
- テストコードの実行

## CIを実行するためのサービスについて
CIを実行するために、CIサービスを使用する必要があります。
例えば、下記のようなサービスがあります。
- Jenkins
- Travis CI
- CircleCI
- GitHub Actions

Githubでコード管理しているので、Github Actionsが使いやすそうかなと思ったので、本記事ではGithub Actionsを使用しています。
各サービスの特徴や比較については行っていないので、最適な判断ではないと思います。

# Github Actionsを使ったFlutterのCIの例
具体的にどういう手順で、どういうCIを実装したか例を書いていきます。

## Github Actionsについて
Github Actionsは、CIとCDが実行できるサービスです。
CDとは、「Continuous Deployment」または「Continuous Delivery」の略で、継続的デプロイメントまたは継続的デリバリーを意味します。目的としては、ソフトウェアのリリースプロセスを自動化し、効率化することです。
CDについては、この記事では解説しませんが、また別の記事を書きたいと思っています。

Github Actionsをきちんと理解したい方には以下の本がおすすめです。
Kindle Unlimittedに入っていたら無料で読めます。

[GitHub Actions 実践入門](https://amzn.asia/d/eMQHd33)

## 注意点
Github Actionsはpublicのブランチだと無料なのですが、privateのブランチだと有料になります。
とりあえず試してみたい方はpublicのブランチに対して行ってください。

https://docs.github.com/ja/billing/managing-billing-for-github-actions/about-billing-for-github-actions

## 導入手順
この記事の例では、PullRequest作成時にコードチェックを実行するように進めていきます。
この記事と全く同じように試される場合は、以下の手順はPRでベースにするブランチに対して行ってください。

### ファイル作成
まず、Flutterのプロジェクトに.github/workflowsというフォルダを作成します。
.githubのフォルダはlibと同階層です。
そして、その中に、実行したい内容を記述したワークフローをYAMLで作成します。
作成したファイルをGitにPushすると有効になり、定義内容に応じて実行されます。
下記の画像のci.ymlがワークフローを定義したファイルです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2475427/e24c906b-be87-bbe7-fc15-15d3fcbe4c48.png)

### ファイルの内容
ymlファイルの内容は下記です。

```yaml:ci.yml
name: Continuous Integration
on: pull_request

jobs:
  CI:
    name: CI
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Create flutter environment
        uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.13.9'
      - name: Install packages
        run: flutter pub get
      - name: Check format
        run: dart format lib --set-exit-if-changed -o none
      - name: Analyze
        run: flutter analyze
      - name: Test
        run: flutter test --update-goldens
```

### ファイルの解説
#### 実行タイミング
`on: pull_request`でPullRequest作成時、更新時にワークフローを実行するようにしています。
`on: push`にするとPush時に実行され、他にもトリガーできるイベントが定義されています。

https://docs.github.com/ja/actions/using-workflows/events-that-trigger-workflows

#### 実行環境
`runs-on: ubuntu-latest`で、実行するOSを定義しています。

他にもmacosやwindowsも指定できます。
OSによって、料金が変わるみたいで、macosが1番高いみたいです。

https://docs.github.com/ja/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources

#### 実行する内容
stepsに何をするのかを定義しています。
- Checkout
対象のブランチのコードをチェックアウトします。
`uses`を使うと、[Marketplace](https://github.com/marketplace?type=actions)という場所に公開されている外部のワークフローを使用できます。
- Create flutter environment
Flutterの環境を構築します。
- Install packages
`run: 〜`でコマンドを実行できます。
開発しているプロジェクトの依存パッケージをインストールします。
- Analyze
`flutter analyze`で静的解析を実施します
- Test
`flutter test --update-goldens`でテストを実施します
テストコードには、GoldenTestを含んでいるのですが、実行環境の違いでエラーになってしまうので、`--update-goldens`をつけて、エラーとならないようにしています。
本当はエラーを回避して、`flutter test`を行うことがベストだと思います。
:::note info
エラーを回避する方法は実行環境を合わせることみたいです。
例えば、macで開発しているなら、Github Actionsも`runs-on: ubuntu-latest`ではなく、`runs-on: macos-latest`とすると問題ないという記事を見ました。
ただ、私の方ではOSを同じにしてもテストに失敗しましたので、仕方なく`--update-goldens`をつけることにしました。
GoldenTestは実行環境によって変わる？みたいなので、開発チームで開発PCが異なる場合（macとwindowsを使っている人がいるみたいな）は、どうやって運用しているのでしょうか？
:::

# さいごに
今回のコードは下記にあります。

https://github.com/AppDeveloperMLLB/codecheck