GitHub Actionsの基本的な使い方を学んだので整理した

# :one: はじめに
会社のGitHub Enterpriseがつい最近更新されてGitHub Actionsが使えるようになり、ちまちまと触り始めておりました。
地道に公式ドキュメントを読み進めていく中で少しずつ使い方がわかってきはじめたので、忘れないように基本的な部分を読書メモとして残しました。
※間違って認識している箇所もあると思うので、気づいたら逐次更新していきます。

# :two: GitHub Actionsの簡単な説明
- GitHubに標準搭載の機能です。
- GitHubと連携してCI/CDを実現し、作成したワークフローを動かして自動化・効率化ができる機能です。
    - 具体的には、ブランチへのpushなどといったGitHubへのアクションをトリガーとして任意のスクリプトを実行できます。
    - cronみたいに特定の時刻でスクリプトを動かすこともできます。
    - 古くなったissue・PRを自動で閉じたりもできます。PRを立てたら自動でレビュワー登録とかもできるはず。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414936/c5fd6981-35a2-ceb0-6664-9cf4ed3524f7.png)
最初の画面はこんな感じ。

# :three: GitHub Actionsの下準備
GitHub Actionsを動かすためには環境準備などの下準備が必要です。
以下に簡単な概要をまとめます。

## GitHubホストランナー or セルフホストランナーの準備
GitHub Actionsを動かすには、まずGitHub Actionsを動かすための環境が必要です。
GitHub Actionsを動かすための環境は、**GitHubホストランナー**と**セルフホストランナー**の2つに分類されています。
とても簡単にいえば、 自前の環境か自前の環境ではないかです。

**GitHubホストランナー**は 「GitHub Actions実行時にGitHub側で自動で作成される仮想的な実行環境」で、
**セルフホストランナー**は「自前で用意したGitHub Actionsの実行環境」です。（AWS EC2インスタンスみたいな）

以下、簡単な違いです。

||メリット|デメリット|
|---|---|---|
|GitHub-hostedランナー| 無料枠内なら自由に使える。 <br> 動かすOSなどはGitHubが用意してくれる常に最新なものとなる。|無料枠を超えると停止する。 <br> 自前の環境よりは自由度は低い。|
|Self-hostedランナー|自前の環境なので無料枠制限などはない <br> OSなどは自分で設定できるので自由度が高い。|自前なのでお金がかかる。 <br> 自前の環境なので常に最新版とはいかない。<br> 環境構築や設定の手間がかかる。 |

自前の環境（セルフホストランナー）を利用する際には、GitHubと連携する作業が必要なので以下の公式ドキュメントに従って設定しましょう。[^1]

[^1]: 「Runners / Create self-managed runner」の画面あたりで一瞬不安になりますが、基本的にコピペしてEnterをしていけばよいです。

- https://docs.github.com/en/actions/hosting-your-own-runners/adding-self-hosted-runners

画面構成は変わるかもしれないですが、基本的には設定画面からできるはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414936/0c10b608-d03f-eea1-4fdf-8a172af5fc56.png)

セルフホストランナーの場合、バックグラウンドで実行させ続けたい（環境からexitしていても動き続けてほしい）ので、公式に従ってサービス化もしておくと良いと思います。

- https://docs.github.com/en/actions/hosting-your-own-runners/configuring-the-self-hosted-runner-application-as-a-service

# :four: GitHub Actionsのワークフローの書き方の基本
GitHub Actionsのジョブを書く際に必要な基本事項をまとめておきます。
公式にも「GitHubアクションを学ぶ」以下にコンセプトからコンテキスト・構文などたくさんまとまっています。
たくさんありすぎて混乱しますが頑張りましょう......。

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

## GitHub Actionsのワークフローを書く場所
- リポジトリ配下に `.github/workflow/hoge.yml` のようにyamlファイルを置くことで実行できるようになります。
    - たとえば以下のようなyamlを置けば、GitHibへのpushをトリガーとして `npm install -g bats` と `bats -v` が順次実行されるcheck-bats-versionというジョブが動くようになります。（公式[^2]より引用）

[^2]: https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions

```
name: learn-github-actions
on: [push]
jobs:
  check-bats-version:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: '14'
      - run: npm install -g bats
      - run: bats -v
```

## 基本用語：name, on, jobs, steps, run
### name
- 実際に動かしたいワークフローの名前を記載します。
- Jenkinsでいうジョブ名みたいなものです。
  - ......複数ジョブを記述できるので「パイプライン名」の方が近いかもしれない。

### on
- 何をトリガーとしてGitHubアクションを動かすかを指定します。
- トリガーとしてはcommitやtagのpush／PR関係／Issue関係など、いろいろ設定できます。
- pushをトリガーにする際にはbranch名なども指定できます。

### jobs
- 動かしたいジョブ名らを記載します。
  - 特に指定がなければ、記載した各ジョブは実行順を意識せずに動くらしいので、処理順を意識して実行したい際には `needs` を書いて依存関係を定義しましょう。
- `runs-on` ではジョブを動かす実行環境を指定します。
  - `ubuntu-latest` などをGitHubが用意しているタグを用いた場合はGitHubホストランナーを、セルフホストランナー作成時につけた自分でつけたタグを指定するとセルフホストランナーで動かせます。

### steps
- ジョブ中で実際に叩くコマンドを記載します。
- `run` で１行ずつ書けるし、複数行の記載もできます。
- ちゃんとGitHubアクションの出力ログを見たり、エラーを確認したいときは１行ずつの方が良さそうです。
  - run単位でエラーが出るので、複数行だとエラー箇所が分かりづらい......。

# :five: もう少し具体例
上の基本事項をおさえた上で、もう少しワークフローを書いてみます。以下の例ではセルフホストランナーにて、

- 「release/event/○○という名前のtagのpushをトリガーとして、ジョブhogeとジョブfugaを動かす」

というワークフロー「GitHubActionsTest」が起動することになります。

```
name: GitHubAcionsTest                 #任意のアクション名を記載する
on:                                    #GitHubアクションを動かすトリガー条件を記載する
  push:                                  #例：「release/event/**」という名前の「tag」が「push」されたら動かす
    tags:                             
      - 'release/event/**'             
jobs:                                  #各ジョブの詳細を記載する
  hoge:                                  #ジョブ名１
    runs-on: [self-hosted]                 #実行したい環境のタグ名。（ubuntu-latestならGitHubホストランナーが起動して動く）
    steps:                                 #各stepをrunで記載
      - uses: actions/checkout@v2          #利用する外部ライブラリのimportみたいなもの。
        with:                              #usesで呼び出すacthions/checkout@v2に渡す引数みたいなもの
          node-version: '14'
      - name: bats_install                 #step単位で名前もつけれるよ
        run: npm install -g bats           #実際に叩くコマンド
      - run: bats -v                       #実際に叩くコマンド

   fuga:                                 #ジョブ名２
    runs-on: [self-hosted]                 #実行したい環境のタグ名
    needs: hoge                            #ジョブhogeが完了していれば処理開始
    if: ${{ needs.hoge.result == 'success' }} #if文も書ける（ジョブhogeが成功した場合に動かす）
    steps:                                 #各stepをrunで記載
      - uses: actions/checkout@v2
        with:
          node-version: '14'
      - name: bats_install_2               #nameつけない場合はrunの内容がstep名になるっぽい。複数行の場合にnameつけた方がよいかも。
        run: npm install -g bats           #実際に叩くコマンド
      - run: bats -v                       #実際に叩くコマンド
......（以下略）

```

## jobsやstepsを分ける意味
`run: |` という形で、シェルスクリプトを書くようにrunを複数行書くようにもできるのですが、ジョブ名やstep名をしっかりわけておくと進捗状況が可視化されて:sunny: **楽しい** :sunny:です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414936/4d9f0867-a336-223d-109f-183c52fd9c78.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414936/50b56491-18d0-bd1b-3265-5b058fed0bf2.png)

あとはエラーがrun単位で吐かれるため、複数行で書くとエラー箇所がわかりにくくなります。
なので書くのは少し大変になりますが、処理単位でrunを分けるのがいいのかなぁと思っています。
なお、ベストプラクティスが全然よくわかってないので、詳しい人がいらっしゃればぜひ......。

（もしかすると「スクリプトは別途書いて、GitHubActionsではそのスクリプトを叩くだけ」という形でも良いのかもしれない。）

# :six: 注意点
GitHubActionsでワークフローを書く際に躓いたことを残しておきます。

### run間での変数渡し
- run内の処理の書き方は普通のシェルスクリプトのような書き方です。
- しかし、run間ではシェルスクリプトのように素直な変数の受け渡しができませんでした。
- 同一ホストランナーのジョブ内であれば `$GITHUB_ENV` という環境変数で変数のやり取りができるのでそちらを使いましょう。

```
steps:
  - run: echo "var1=hoge" >> $GITHUB_ENV
  - run: echo ${{env.var1}} # 出力: hoge。うまくhogeが渡った！
  - run: $var2="fuga"
  - run: echo $var2         # 出力error。うまく$var2が渡せないっぽい......。
```

### job間での変数渡し
- ジョブ間でも変数の受け渡しができませんでした。（ジョブ毎に実行環境が立ち上がるため？）
- 上述した `$GITHUB_ENV` でも渡せません。
- その場合は、`outputs` でメタデータファイル出力を駆使することで変数のやり取りを実現できました。

```
jobs:
  hoge                                          # ジョブ名hoge
  runs-on: [self-hosted]
  outputs:
    year:  ${{ step.hoge_date.outputs.year }}    # step.id名.outputs.変数名 で記述する。
    month: ${{ step.hoge_date.outputs.month }}
    day:   ${{ step.hoge_date.outputs.day }}
  steps:
    - id: hoge_date                             # outputsと紐付けるid名を記述する
      run: |
        echo "::set-output name=year::2021"     # echo "::set-output name=(outputsの変数名)::(格納したい値)"
        echo "::set-output name=month::12"
        echo "::set-output name=date::22"

  fuga                                          # ジョブ名fuga
  runs-on: [self-hosted]
  needs: hoge                                   # 依存関係の定義：hogeというジョブが終了したら動かす
  steps:
    - run: echo ${{needs.hoge.outputs.year}}      # 出力：2021。ジョブhogeで格納した値が出力される。
    - run: echo ${{needs.hoge.outputs.month}}     # 出力：12 が出るはず。
    - run: echo ${{needs.hoge.outputs.date}}      # 出力：22 が出るはず。
```

GitHubAction側で仕様が変更される可能性があるのですが、私が調べていた時点ではこのような形で渡せていました。（少し前まで渡し方が違っていたらしい。）
随時、GitHubActionsはアップデートされていくと思いますので、公式ドキュメントには常に当たるようにしましょう。

### ローカルでの動作確認
GitHubAciontsではデフォルトでは動作確認がやりづらかったです。
なぜならGitHubへのアクションをトリガーにする仕様のため、動作確認する毎に毎回pushなどをする必要があるためです。
（なので動作確認のためのcommitが増える増える。）

そこで、ローカルで動作確認をするための `act` というツールがあるらしいのでありがたく使いましょう。
これにより簡単にローカルで動作確認ができるようになるらしいです。
ちなみに私は知らなかったので全然使っていませんでした......悲しい。

- https://github.com/nektos/act
- https://qiita.com/wwalpha/items/6c303dcf04e236238315

そのうち、GitHub公式側でも動作確認できる何かしらを用意してくれないだろうか。

# :seven: おわりに
GitHub Actionsに対してノー知識だったので導入時に少しハードルが高く感じていたのですが、それなりに使えるようになると色々と「あれもできるのでは、これもできるのでは」と妄想が広がるので楽しいです。

もちろんCI/CDでメジャーどころのJenkinsで十分事足りるものもあるのですが、IssueやPRをトリガーにする場合などGitHub Actionsを使った方が簡単に行えるものもあるので、これから少しずつ移行していこうかなと思っています。 [^3]

[^3]: あとJenkinsバージョンの管理が大変だったり、ツールはなるべく少なくまとめたい気持ちもあるので。