GitHub Actions アクションやワークフローを再利用してコードの重複をなくそう！

GitHub Actions、便利ですよね。
いろいろ作っていく中で、いろんなRepositoryに同じようなアクションやワークフローを使いたいことがあると思います。
それらをどう共通化して、再利用していくかということについて、いくつかの方法をご紹介したいと思います！

# Custom Action
https://docs.github.com/ja/actions/creating-actions/about-custom-actions

これはDockerやJavaScriptを使用してWorkflowの中で行いたい処理を共通化できます。
GitHub Actionsを利用している人でこの機能を利用していない人はまずいないでしょう。
よく使われるCustomActionは [checkout](https://github.com/actions/checkout) でしょうか。

基本的には公開されているものを使用すると思いますが、自分で作ることも可能です。

ちなみに現時点ではPrivateRepositoryで作成したCustomActionは、公開されてるCustomActionのように利用することは出来ません。
しかしやりようはあります。

まず同じRepositoryの `.github/actions` ディレクトリにCustomActionのディレクトリを配置すると
そのRepositoryのWorkflowで参照することが出来ます。

```yaml
# https://docs.github.com/ja/actions/learn-github-actions/workflow-syntax-for-github-actions#
# Example: Using an action in the same repository as the workflow
jobs:
  my_first_job:
    steps:
      - name: Check out repository
        uses: actions/checkout@v2
      - name: Use local my-action
        uses: ./.github/actions/my-action
```

この仕組を利用し、`.github/actions` 配下にPrivateRepositoryにあるCustomActionsをチェックアウトしてくれば
CustomActionをさまざまなRepositoryで参照することが可能です。

```yaml
jobs:
  my_first_job:
    steps:
      - name: Check out repository
        uses: actions/checkout@v2
      - name: Check out private github action repository
        uses: actions/checkout@v2
        with:
          repository: organization/my-action
          path: ./github/actions
      - name: Use local my-action
        uses: ./.github/actions/my-action
```

# Composite Action
https://docs.github.com/ja/actions/creating-actions/creating-a-composite-action

これはCustomActionの一種で、いくつかのWorkflowのStepを1つのアクションにする機能です。
以下のように、コンテナイメージを作るための複数のStepを1つのアクションにまとめられ、非常に便利です。

```yaml
name: "Publish to Docker"
description: "Pushes built artifacts to Docker"
inputs:
  registry_username:
    description: “Username for image registry”
    required: true
  registry_password:
    description: “Password for image registry”
    required: true
runs:
  using: "composite"
  steps:
    - uses: docker/setup-buildx-action@v1

    - uses: docker/login-action@v1
      with:
        username: ${{inputs.registry_username}}
        password: ${{inputs.registry_password}}

    - uses: docker/build-push-action@v2
      with:
        context: .
        push: true
        tags: user/app:latest
```

ちなみに `if` が使えなかったりするので、Stepの結果によって次のStepをどうこうするとかいう処理は難しいです。

# Reuse Workflow
https://docs.github.com/en/actions/learn-github-actions/reusing-workflows
> こちらの機能はこの記事を書いてる段階ではまだBetaです。

この機能は、他のRepositoryで定義したWorkflowを自身のRepository上で実行する機能です。

例えば、以下のようなWorkflowがあったとします。

* mainブランチにマージしたらRepositoryに定義されてるDockerfileを参照してコンテナイメージをビルド
* ビルドしたコンテナイメージをContainerRepositoryにPush
* 上記処理が正常に終了したら、コンテナイメージが無事作成出来たことをSlackに通知する

このWorkflowは、DockerfileがあるRepositoryならどこでも使えると思います。
こういうときに便利なのがこの `Reuse Workflow`という機能です。
このようなWorkflowを1つのRepositoryに配置しておけば、他のRepositoryでこのWorkflowを実行できます。

サンプルをご紹介します。
まず以下のWorkflowを `organization/sample-repository` Repositoryの `.github/workflows` に配置します。

```yaml:organization/sample-repository/.github/workflows/sample.yaml
name: Reusable workflow example

on:
  workflow_call:
    inputs:
      username:
        required: true
        type: string
    secrets:
      token:
        required: true

jobs:
  example_job:
    name: Pass input and secrets to my-action
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: ./.github/actions/my-action@v1
        with:
          username: ${{ inputs.username }}
          token: ${{ secrets.token }}      
```

`workflow_call` トリガーを使用することで他のRepositoryからこのWorkflowを実行することが可能になります。
呼び出し方は以下のようにします。

```yaml:organization/my-repository/.github/workflows/call-sample.yaml
name: workflow call
on:
  pull_request:
jobs:
  call-workflow:
    uses: organization/sample-repository/.github/workflows/sample.yaml@main
    with:
      username: mona
    secrets:
      token: ${{ secrets.TOKEN }}
```

こうすることで、外部で定義されているWorkflowを自身のRepository上で実行することが可能です。

>外部で定義されているWorkflowを自身のRepository上で実行することが可能

ちょっとわかりにくいかもなのでもう少し説明します。
上記サンプルの呼び出したWorkflowでは `uses: actions/checkout@v2` を実行しています。
このWorkflowを他のRepositoryで実行した場合、チェックアウトするRepositoryは呼び出し元のRepository(上記サンプルでいうと`my-repository`)になります。

なのでWorkflowを定義する場合は、呼び出し元のRepositoryのことを考慮しなくてもよいのです。
これはめちゃくちゃ便利です・・・！

ちなみにこの機能を利用するためにはWorkflowを定義するRepositoryで以下の設定が必要です。(`Settings` -> `Actions`)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152303/e0089411-29c4-8a17-75a3-7e8e5a5353bd.png)