自分用RustプロジェクトのGitHub Actions CIワークフローを設定してみた

# 概要

自分用にRustのプロジェクトで使用するGitHub ActionsのCIワークフローを作成したので、まとめてみました。

# ワークフロー

まずはどのような[ワークフロー](https://docs.github.com/ja/actions/using-workflows/about-workflows)にしたかをご紹介します。

`.github/workflows/rust_test.yml`に以下の内容を書くことで、GitHub Actionsの設定を行うことができます。

GitHub Actionsの詳細は以下などをご参照ください。

https://docs.github.com/ja/actions/learn-github-actions/understanding-github-actions

```yml
name: Rust_CI

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

env:
  CARGO_TERM_COLOR: always
  RUSTFLAGS: "-Dwarnings"

jobs:
  test:

    runs-on: ubuntu-latest

    defaults:
      run:
        working-directory: test_rust

    steps:
    - name: Checkout
      uses: actions/checkout@v4

    - name: Cache cargo registry
      uses: actions/cache@v4
      with:
        path: |
          ~/.cargo/bin/
          ~/.cargo/registry/index/
          ~/.cargo/registry/cache/
          ~/.cargo/git/db/
          target/
        key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}

    - name: Build test
      run: cargo build --release --verbose
    - name: Run tests
      run: cargo test --verbose
    - name: Lint with clippy
      run: cargo clippy --all-targets --all-features
    - name: Check formatting
      run: cargo fmt --all --check

```

# 例

以下のリポジトリで実験を行っています。

https://github.com/kotabrog/test_github_actions

# 各項目の説明

ここからはそれぞれで何をしているかをできるだけ書いていきたいと思います。  
（間違っていたり補足などあればご指摘お願いします）

## name

`name: Rust_CI`はワークフローの名前です。

こんな感じで表示されます。

![スクリーンショット1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533384/da68e9f5-2a25-d188-c70e-c9d2e84221d6.png)

## on

`on`にはどのタイミングでこのワークフローが実施されるかを設定します。

私の場合は以下のようにしています。

```yml
on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]
```

つまり、mainブランチへのpush時と、mainブランチへのpull_request時のみ、ワークフローが走るようになっています。


以下で詳しく書いていきますが、結構厳しめの設定で作っているので、mainブランチに関する変更の場合のみ動かすようにしています。

また、各ブランチでは、もしかしたら一時的に動かないコードをあげたい……となるかもしれないですが、そのときにCIでエラーがでてしまうと煩わしいかなと思ったので、mainブランチのみ対象にしています。

## env

`env:`ではワークフロー全体の環境変数を設定しています。

https://docs.github.com/ja/actions/learn-github-actions/variables#1-%E3%81%A4%E3%81%AE%E3%83%AF%E3%83%BC%E3%82%AF%E3%83%95%E3%83%AD%E3%83%BC%E3%81%AB%E5%AF%BE%E3%81%99%E3%82%8B%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0%E3%81%AE%E5%AE%9A%E7%BE%A9

### CARGO_TERM_COLOR

ターミナルへの出力時に色をつけるかどうかを指定します。

色がないと見づらいので、`always`にして色をつける設定にしています。

https://doc.rust-lang.org/cargo/reference/config.html#termcolor

### RUSTFLAGS

`RUSTFLAGS`にはCargoが実行するコマンドに共通して渡すフラグを設定することができます。

今回は`RUSTFLAGS: -Dwarnings`としていて、warningが出たときにエラーになるようにしています。  
これでwarningをCIで見つけられるようにしています。

## jobs

ワークフローは1つ以上のjobで構成されます。

今回は一つのjob（testという名前）だけで構成されています。

```yml
jobs:
  test:
    # 以下でtest jobの設定
```

## runs-on

`runs-on: ubuntu-latest`ではjobを実行するOS（今回は`ubuntu-latest`）を指定しています。

ちなみにこの中にはRustの最低限のあれこれが入っているようなので、Cargoをinstallするなどといったことはする必要はないようです。

## defaults

`defaults:`ではそのjob内で共通の設定を行うことができます。

### working-directory

```yml
    defaults:
      run:
        working-directory: test_rust
```

`working-directory`にディレクトリを設定することで、そのディレクトリでコマンドを実行してくれるようになります。

私は基本的にリポジトリ直下にRustのプロジェクトを置かないので、この設定を入れています。

## steps

`steps:`で実行するタスクを設定します。  
基本的には設定した順番で実行されますが、場合によってはステップの実行後に後処理を自動で加えてくれる場合もあります。

例えば、今回使用しているワークフローの中だと`actions/checkout`と`actions/cache`については、jobのclean upをするステップを最後に追加します。

### Checkout

```yml
    - name: Checkout
      uses: actions/checkout@v4
```

このステップでは、GitHubリポジトリのコードをこの後のステップでアクセスできるようにしてくれています。

https://github.com/actions/checkout

ちなみにnameはステップの名前なので、好きな名前を設定することができます。

### Cache

```yml
    - name: Cache cargo registry
      uses: actions/cache@v4
      with:
        path: |
          ~/.cargo/bin/
          ~/.cargo/registry/index/
          ~/.cargo/registry/cache/
          ~/.cargo/git/db/
          target/
        key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
```

このステップでは依存関係のダウンロード時間を節約するために、Cargoの依存関係のキャッシュを設定しています。

詳細については省略しますが、以下などを参考にしました。

https://zenn.dev/kt3k/articles/d557cc874961ab

https://github.com/actions/cache/blob/main/examples.md#rust---cargo

参考にしたサイトに書いてあったことですが、私の設定だとリポジトリ内のコードのビルドはフルビルドになっていて、Cacheをうまく使えていないようです。

その原因や解決方法などは、上記の1つめのサイトをご参照ください。

※ もしかしたらこの問題は解決されていて、最近ではリポジトリ内のコードのビルドについてもキャッシュされるようになっているかもしれませんが、詳細は調べていません

### Build

```yml
    - name: Build test
      run: cargo build --release --verbose
```

buildがうまくいくかどうかをテストしています。

### Test

```yml
    - name: Run tests
      run: cargo test --verbose
```

テストを実行しています。

### Lint

```yml
    - name: Lint with clippy
      run: cargo clippy --all-targets --all-features
```

このステップでは、Lintツールであるclippyを使用して確認をしています。

clippyについての詳細は以下がわかりやすかったです。

https://zenn.dev/shinyay/articles/hello-rust-day068

こちらでは以下を参考に、`--all-targets`ですべてのビルドターゲット（binやlibだけでなく、テスト、exsamplesなども）に対して、`--all-features`ですべての機能（features）に対してLintの対象になるようにしています。

https://doc.rust-lang.org/nightly/clippy/continuous_integration/github_actions.html

### Format

```yml
    - name: Check formatting
      run: cargo fmt --all --check
```

このステップでは、コードフォーマットツールであるrustfmtを使用しています。

こちらもrustfmtについての詳細は以下がわかりやすかったです。

https://zenn.dev/shinyay/articles/hello-rust-day069

それぞれのフラグについてですが、`--all`ですべてのコードを対象にフォーマットを行っています。

また、cargo fmtのデフォルトでは実行するとコードを書き換えるようになっているのですが、今回のCIではコードの書き換えを許していないので、`--check`で書き換える代わりにエラーで知らせてくれるようにしています。

ちなみに書き換えることを許可する場合はGitHubでの設定がたしか必要になった気がします。

# おわりに

以前こちらでRustとWebAssemblyでWebサービスを行う際の設定の記事を書きました。

https://qiita.com/Kotabrog/items/19ef23a7a2aaf5d37fbb

こちらの記事の中ではテスト関連についてはあまり書いていませんでしたが、いずれ今回の内容もふまえてアップデート版を書きたいと思います。