GitHub Actions のベストプラクティス

# はじめに

GitHub Actionsのベストプラクティス集です。

# 高速化

## キャッシュのkey選択の最適化

**プラクティス**

キャッシュのkeyとして以下のような値を使用し、キャッシュを最大限活用します。

1. lockファイルのハッシュ値
1. OSの名称
1. ランタイム(Node.js, java, python)のバージョン
1. ブランチ名

```yaml:OKの例
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/cache@v2
        id: poetry_cache_id
        with:
          path: .venv
          key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}
      - name: install dependencies
        if: steps.poetry_cache_id.outputs.cache-hit != 'true'
        run: |
          poetry install
```

```yaml:NGの例
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: install dependencies
        run: |
          poetry install
```

**効果**

.mvn , .venv等のパッケージマネージャのキャッシュフォルダが保存され、使い回されるようになります。

# デバッグのしやすさ

## ローカル実行

**プラクティス**

CI/CDで実行するコマンドやオプションはGitHub Actionsには書かずに、shellやnpm-script等に記述します。

```yaml:OKの例
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: build and package
        run: |
          . .venv/bin/activate
          ./bin/build.sh -t Release
          ./bin/package.sh
```

```yaml:NGの例
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: build and package
        run: |
          . .venv/bin/activate
          conan install .. --build=missing --generator cmake_multi --settings
          conan build ..
          conan install .. --build=missing --generator cmake_multi --settings build_type=Release
```

**効果**

GitHub Actions内の処理をローカルで動作確認したいとき、workflowではなく、shellやnpm-scriptsに記述していると容易に実行でき、便利です。

## 手動実行

**プラクティス**

手動実行用のイベント( [workflow_dispatch](https://docs.github.com/ja/actions/learn-github-actions/events-that-trigger-workflows#workflow_dispatch) )を設定します。

```yaml:OKの例
name: Manually triggered workflow
on:
  push:
  workflow_dispatch:
    inputs:
      name:
        description: 'Person to greet'
        required: true
        default: 'Mona the Octocat'
```

```yaml:NGの例
name: Manually triggered workflow
on:
  push:
```

**効果**

`Run workflow`ボタンが表示されて、手動で再ランできるようになり、CI/CDコードのデバッグがやりやすくなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114392/c187eff9-5f56-da04-5f76-9ef0ecae8e42.png)


# 安定性

## OSのバージョン指定

**プラクティス**

runs-onのOSのバージョンを指定します。

```yaml:OKの例
jobs:
  deploy:
    runs-on: ubuntu-20.04
```

```yaml:NGの例
jobs:
  deploy:
    runs-on: ubuntu-latest
```

**効果**

バージョンを指定しない場合、OSの破壊的変更によってCI/CDが動かくなることがあります。

バージョンを固定することでCI/CDが安定するようになります。

## アクションのバージョン指定

**プラクティス**

タグまたはSHAでアクションのバージョンを指定します。

```yaml:OKの例
steps:
    - uses: actions/javascript-action@v1.2.0
    - uses: actions/javascript-action@172239021f7ba04fe7327647b213799853a9eb89
```

```yaml:NGの例
steps:
    - uses: actions/javascript-action
    - uses: actions/javascript-action@latest
```

**効果**

バージョンを指定しない場合やブランチ名でバージョンを指定した場合、アクションの破壊的変更によってCI/CDが動かくなることがあります。

タグやSHAでバージョンを固定することでCI/CDが安定するようになります。

# 可読性

## 静的な環境変数

**プラクティス**

静的な環境変数は`env:`で定義して、参照しましょう。

```yaml:OKの例
env:
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: build
        run: docker build . --file Dockerfile --tag ${{ env.IMAGE_NAME }}:latest
```

```yaml:NGの例
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: build
        run: docker build . --file Dockerfile --tag ${{ github.repository }}:latest
```

**効果**

`steps`内で使う変数は、環境変数で設定しましょう。
変数の意味がわかりやすくなり、可読性が向上します。

## 動的な環境変数

**プラクティス**

実行時の日付を含むなどの、動的な環境変数は`run: echo ENV_NAME=value >> $GITHUB_ENV`で設定して、${ENV_NAME}で参照しましょう。

```yaml:OKの例
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: echo TAG_NAME=`date +%s` >> $GITHUB_ENV
      - name: build
        run: docker build . --file Dockerfile --tag ${{ env.IMAGE_NAME }}:${TAG_NAME}
```

```yaml:NGの例
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: build
        run: docker build . --file Dockerfile --tag ${{ env.IMAGE_NAME }}:`date +%s`
```

**効果**

動的な環境変数に名前をつけて管理できるようになり、可読性が向上します。

# テスト

## 複数プラットフォームの並列テスト

**プラクティス**

`strategy`を使って複数のプラットフォームで同時にテストを実行しましょう。

以下の例は、バージョン14, 16のNode.jsで同時にテストを実行する例です。

```yaml:OKの例
jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [14.x, 16.x]
    steps:
      - uses: actions/setup-node@v2
        with:
          node-version: ${{ matrix.python-version }}
      - run: npm run test
```

```yaml:NGの例
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-node@v2
        with:
          node-version: 16.x
      - run: npm run test
```

`steps`は、`strategy`の数だけ分岐して同時に実行されます。
GitHub Actions では次のように表示されます。

![Screenshot from 2021-12-03 23-44-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114392/cba3a5ca-cc78-f0b3-b23f-74f693c4ad5a.png)

**効果**

CI/CDの処理時間を変えずに、複数環境でのテストを簡単に実装できます。

マルチプラットフォームなアプリケーションの品質向上と生産性向上に寄与します。