RustのLinux/Windows/macOS向け64bitバイナリをGitHub Actionsで生成する

# はじめに

前回の記事「[RustのLinux/Windows/macOS向け64bitバイナリをAzure Pipelinesで生成する](https://qiita.com/dalance/items/5c6414ce48eee1407e69)」にてRustプロジェクトのCI環境をAzure Pipelinesに移行しましたが、GitHub Actionsが出てきたので今度はこちらに移行しました。（さすがにそろそろ打ち止めですかね…）

実際に適用したリポジトリは以下になります。

https://github.com/dalance/procs

# できること

- プッシュ・プルリクエスト毎に3プラットフォームでテストを実行
- プッシュ・プルリクエスト毎にカバレッジを取得
- 定期的にRustのstable/beta/nightlyでテストを実行
- タグを打ったらGitHubにリリースする

## ターゲットプラットフォーム
- Linux: x86_64-unknown-linux-musl
- Windows: x86_64-pc-windows-msvc
- macOS: x86_64-apple-darwin

# スクリプト

GitHub Actionsのスクリプトは`.github/workflows`以下に配置します。他のCIと違ってこのディレクトリに複数のYAMLファイルを置けるので、今回は4つに分割しました。

- regression.yml
- coverage.yml
- periodic.yml
- release.yml

## regression.yml

プッシュ・プルリクエスト毎に3プラットフォームでテストを実行するスクリプトです。
ポイントは`hecrj/setup-rust-action@v1`で、3プラットフォームに対応したRustのセットアップ用アクションになります。
Github Actionsはこのようなアクションを組み合わせて書くようになっていて、素のスクリプトを書く部分は結構少なくなっています。（複雑なスクリプトを書く必要があるなら、そこを抜き出してアクションとして公開すればいいと思います）

```regression.yml
name: Regression

on: [push, pull_request]

jobs:
  build:
    strategy:
      matrix:
        os: [ubuntu-latest, macOS-latest, windows-latest]
        rust: [stable]

    runs-on: ${{ matrix.os }}

    steps:
    - name: Setup Rust
      uses: hecrj/setup-rust-action@v1
      with:
        rust-version: ${{ matrix.rust }}
    - name: Checkout
      uses: actions/checkout@v1
    - name: Run tests
      run: cargo test
```

## coverage.yml

プッシュ・プルリクエスト毎にカバレッジを取得するスクリプトです。カバレッジ取得にはcargo-tarpaulinを使っています。
カバレッジは[codecov.io](https://codecov.io)にアップロードしていますが、例によってアクションがあるのでそれを使います。
`CODECOV_TOKEN`はcodecovのページから取得したトークンで、GitHubのプロジェクトページで"Settings" -> "Secrets" -> "Add a new secret"から追加します。

```coverage.yml
name: Coverage

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest

    container:
      image: xd009642/tarpaulin
      volumes:
        - $GITHUB_WORKSPACE:/volume
      options: --security-opt seccomp=unconfined

    steps:
    - name: Checkout
      uses: actions/checkout@v1
    - name: Run tests
      run: cargo tarpaulin -v --out Xml
    - name: Upload coverage
      uses: codecov/codecov-action@v1.0.3
      with:
        token: ${{secrets.CODECOV_TOKEN}}
```

## periodic.yml

定期的にRustのstable/beta/nightlyでテストを実行するスクリプトです。これはRustコンパイラのバージョンアップによってビルドが壊れないかどうかの確認用です。

```periodic.yml
name: Periodic

on:
  schedule:
  - cron: 0 0 * * SUN

jobs:
  build:
    strategy:
      matrix:
        os: [ubuntu-latest]
        rust: [stable, beta, nightly]

    runs-on: ${{ matrix.os }}

    steps:
    - name: Setup Rust
      uses: hecrj/setup-rust-action@v1
      with:
        rust-version: ${{ matrix.rust }}
    - name: Checkout
      uses: actions/checkout@v1
    - name: Run tests
      run: cargo test
```

## release.yml

タグを打ったらGitHubにリリースするスクリプトです。`make release_*`の部分は`cargo build --release`とzipアーカイブの作成をしています。リリースするためのトークン`secrets.GITHUB_TOKEN`はGitHub側で勝手に設定してくれるので設定不要です。

```release.yml
name: Release

on:
  push:
    tags:
      - 'v*.*.*'

jobs:
  build:
    strategy:
      matrix:
        os: [ubuntu-latest, macOS-latest, windows-latest]
        rust: [stable]

    runs-on: ${{ matrix.os }}

    steps:
    - name: Setup Rust
      uses: hecrj/setup-rust-action@v1
      with:
        rust-version: ${{ matrix.rust }}
    - name: Checkout
      uses: actions/checkout@v1
    - name: Setup MUSL
      if: matrix.os == 'ubuntu-latest'
      run: |
        rustup target add x86_64-unknown-linux-musl
        sudo apt-get -qq install musl-tools
    - name: Build for linux
      if: matrix.os == 'ubuntu-latest'
      run: make release_lnx
    - name: Build for macOS
      if: matrix.os == 'macOS-latest'
      run: make release_mac
    - name: Build for Windows
      if: matrix.os == 'windows-latest'
      run: make release_win
    - name: Release
      uses: softprops/action-gh-release@v1
      with:
        files: '*.zip'
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

# まとめ

GitHub Actionsはまだパブリックベータの段階ですが、すでにかなりの数のアクションが公開されていて使いやすいです。
バックエンドは前回と同じAzure Pipelinesのはずですが、特に問題も起きず「前回の苦労は何だったのか…」という感じです。
CI結果がGitHubのページで完結するのもいいですね。