github actionを手軽に作成して気軽に使う

🎄メリクリ！🎅

# Github Actions導入事例と飛び道具の紹介

### Github Actions使ってますか？

皆さんはGithub Actions好きですよね？
Github Actions 使いましょう！！

#### とはいえ……
##### Github Actions難しそうって最初に名前を聞いたときに思ってた
だからここではそのあたりの話をします。

ぜひ読んでってね！

- このドキュメントの対象者
  - Github Actions is 何？って思ってる人
  - Github Actionsやってみたいけど何から入れればいいかわからないよという人
  - Github Actions知ってるけど、やり始めるなら飛び道具を知って楽したいって人
- 話さないこと
  - Github Actions使うのに決済決議が必要なので助けて？
  - チームメンバーで賛意取れてないんだけどCIって導入していい？


# Github Actions導入事例

## Github Actionsを導入した経緯と概要

#### `Github Actionsって何？` ←　わかるわかる（去年の自分もそうだった）

![44036562.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529854/1459acf7-544f-46f8-a547-29dc15dc8c38.png)

一言でいうと **確認作業自動化(CI)ツールの一種** ！！


まあ、CIということはわかっていましたけどもね！！！
敷居が高く感じてね！！！

書いてなかったんですよ！！！！

使ってみたら便利だったんすよ！！！！

これは「そんな魅力を知らなかった去年の自分に向けた説明書き」を羅列したものになります。


#### 何がどう便利なの？

- 課金体系や導入が便利
  - public repositoryなら無料。
  - private repositoryでも無料枠の有る時間従属課金体系。
  - 外部連携ツールじゃないからgithubさえ導入していれば比較的導入が楽。
- コンテナ環境だからの利点
  - container実行するからローカル実行するとソースコードに副作用が出るはずのものも影響なく実行可能。
  - 書いてしまえば container 実行ツールだから別のCIサービスに移植しやすい
  - ローカル実行環境も有る https://github.com/nektos/act
- PRの時間短縮にひたすら便利。
  - よくある本質ではないが忘れやすい確認事項について自分で気づく事ができる雑案内を設置できる
    - 雑案内の例：![Screen Shot 2020-12-09 at 16.48.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529854/8d06f947-b1bf-7aed-0b81-5cf3a74516da.png)




#### 便利な点を知らなかったのにGithub Actionsを使ったきっかけとかある？
使ったきっかけは、
「[PHP Kaigi 2020 の Github Actionssで始めるPHPアプリケーションのCI実践入門  2020/02/11 15:00~](https://fortee.jp/phperkaigi-2020/proposal/f4a44588-487c-412a-bcf4-a9d709bca67c)」
のトークを見たからでしたね。

これわかりやすくて楽しいのでみんな見てね。


## 導入事例
### 事例1
#### まずはPHP Coding Standards Fixer(php系Linter)をCI化しました

- チームメンバーが導入してくれていたPHP Coding Standards FixerをCI化した
  - ![Screen Shot 2020-12-25 at 20.48.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529854/858b7593-ce74-cef5-1f25-7042f02612ca.png)

- ソースコード


```yml
name: PHP-CS

on: [push]

jobs:
  php-cs:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@master
      - name: Install Dependencies
        run: composer install -q --no-ansi --no-interaction --no-scripts --no-suggest --no-progress --prefer-dist
      - name: Execute PHP-CS
        run: composer run cs
```

##### PHP Coding Standards FixerのCI化のときに注意した点
- CIをチームに導入することが目的だったので現時点の状態でgreenなresultを返すように設定ファイルを修正
  - 具体的には `.php_cs.dist` で notPathを使って既存ファイルを無視するようにした
    - 既存ファイル全てに対してfix掛けるのが理想だけど、まずはCI導入を急いだ
- 運用メンバーの集まる会議で下記の点について説明し確認した
  - PHP Coding Standards FixerのCI化を導入するのに懸念があれば真摯に回答してメンバーからの賛意をもらう
  - 本質でないコメント往復を減らしてPRの回転率向上する目的を共有
    - プロジェクトコードをきれいにするのは副次目的でCI化が目的であることを伝える
  - Fixerで自動的にコミットしないことは明言
    - PullRequestでCI検知し各人が気がつけるようにしただけにとどめた
    - mergeにPHP Coding Standards Fixerによる修正は必須ではない設定
      - 手動運用なので各メンバーにローカルでFixerの実行をしてもらって運用することにした

- 下記にCS-fix導入時の `.php_cs.dist` 一部抜粋

```php
<?php

return \PhpCsFixer\Config::create()
     //    ... (前略) ...
    ->setFinder(
        // 対象は カレントワーキングディレクトリー 配下の *.php ファイルだけ
        // 一時フォルダや外部ソース、保守されていないコードを取り除いて実行する
        PhpCsFixer\Finder::create()
            ->exclude('vendor')
            ->in(__DIR__)
            // Github Actionssのprを通すためにすべて検査しない
            // todo: 下記項目の削除
            // {{{
            ->notPath('xxxxx/yyyyy/zzzzz1.php')
            ->notPath('xxxxx/yyyyy/zzzzz2.php')
            ->notPath('xxxxx/yyyyy/zzzzz3.php')
            // ...(中略)...
            // }}} 
            ->name('*.php')
            ->ignoreDotFiles(true)
            ->ignoreVCS(true)
    );
```


### 事例2
#### 「base branchが更新されてるのでmergeしてから再度レビュー依頼ください！」という指摘を自動化

~~脳内の僕：「なんで次がunit testじゃないんですか？」~~
~~現実の僕：「あ、石投げないでね、やめてね」~~

よくあるPRの指摘で本質じゃないやつがよくあるのでこの辺から先に自動化したいと思った


- 「indentが崩れてるのでspace足して整えてください！」
- 「base branchが更新されてるので取り込んでから再度レビュー依頼ください！」
- ~~「unit testを手元実行して確認したらredだから直してから再度レビュー依頼ください」~~
  - ~~事前条件がめんどいので後回し中~~

#### ソースコード

作ってみたけど意外と面倒くさいので、ソースコードは下部に付記

便利なので使ってね。


```yml
name: IS-PR-MERGED-BASE-BRANCH

on:
  pull_request:
    types: [synchronize, opened, reopened]
    branches:
      - '**'

jobs:
  check-to-be-merged-base-branch:
    runs-on: ubuntu-latest
    steps:
      # clone repository to be able to merge master
      - name: init
        uses: actions/checkout@master
        with:
          ref: ${{ github.event.pull_request.head.ref }}
          fetch-depth: 0
      - name: check
        # 1.2. setting for git merge command (require user.email and user.name)
        # 3. run no-commit merge ( maybe exit(1) if base branch is changed )
        # 4. check merged branch and current difference line is zero
        run: |
          git config --global user.email "xxxxxx.xx@users.noreply.github.com" && \
          git config --global user.name "xxxxxx.xx" && \
          git merge --no-commit --no-ff origin/${{ github.event.pull_request.base.ref }} && \
          test -z "$(git diff-index  --name-only HEAD --)"
```

- 処理の内容
  1. 対象ブランチを取得する
  1. pull requestをmergeした場合の状態をcommit無しに再現する
  1. このとき、対象ブランチと差分が有るならmerge先のブランチを取り込んでいないのでexit(1)する
    - きちんとmerge先ブランチを取り込んでからpushしたならばmergeしても差分は発生しないから
- 読みにくい部分
  - 特殊な変数展開表記 (spaceも重要)と特殊な変数を使うとできる
    - `${{ github.event.pull_request.base.ref }}` ※ドキュメントは多分無い
      - pullrequestとして宣言されているhookでのみ有効な特殊変数でpull requestのmerge先を表す
    - `${{ github.event.pull_request.head.ref }}` ※ドキュメントは多分無い
      - pullrequestとして宣言されているhookでのみ有効な特殊変数でpull requestのブランチを表す
  - actionに対する特殊な引数が重要
    - `fetch-depth: 0`がないとcheckoutしたときにgitの履歴が消えます ※ドキュメントは多分無い
      - 別branchもtagもないのでgit mergeできません(1敗)
- 注意点
  - この内容はlocalでの再現コマンドのactでは再現できてないです←なのでgithubでテストしてました
    - actコマンドでは上記特殊変数は明示的に指定して渡して上げる必要があるかもしれません。
    - actコマンドでは`fetch-depth: 0`を指定してないのにそれっぽくなるので注意


###### 導入編終わり
こんな感じでチームに導入したよ。
便利だったよ。
導入事例は以上！

-----------------------------


# 飛び道具紹介
## Github Actionsでやりたいことをやろうとすると敷居が高くて……って方に

### Q「Github Actionsって専用にリポジトリ作らないといけないんでしょ？公式ドキュメントのhello world面倒だったけど？」
わかる。わかる。
[公式のhello world](https://docs.github.com/ja/enterprise-server@2.22/actions/creating-actions/creating-a-docker-container-action)を読みに行くとただhello worldしたいだけなのにリポジトリ作ってそれを別リポジトリからactionとして呼び出してみよう！

みたいなこと言ってるからね。


いやー、hello worldしたいだけなのに別リポジトリに
Dockerfile書いてaction.ymlを作って

workflowから呼び出すのめんどいよね。5ファイルを用意するのだるいのわかる。

```text
.
├── .github
│   └── workflows
│       └── main.yml
├── Dockerfile
├── LICENSE
├── README.md
├── action.yml
└── entrypoint.sh
```


#### A. 「飛び道具 no.1 run and env」
##### hello world short version（1ファイルのみ）を用意した



run shellコマンドが意外と便利なので使ってみてね

- run って書くと複数行が実行できるよ便利だよ
  - `&& \` の利用はご計画的に
- `action.yml`を省いて実行する関係上、`set-output`が`set-env`に置き換わってるよ
  - 公式ドキュメントを実行してみて何が変わったのか比較してみてね。
- github上での実行方法
  - `vim .github/workflow/hello-world.yml && git add .github/workflow/hello-world.yml && git commit -m 'add Github Actions hello world' && git push origin`


```.github/workflow/hello-world.yml
on: [push]
env:
  SCRIPT_ARGV_1: 'Mona the Octocat'
jobs:
  hello_world_job:
    runs-on: ubuntu-latest
    name: A job to say hello
    steps:
      - name: run entryscript
        id: hello_and_return_time
        run: |
          echo "Hello ${{ env.SCRIPT_ARGV_1 }}" && \
          time=$(date) && \
          echo "::set-env name=TIME::$time"
      # Use the output from the `hello` step
      - name: Get the output time
        run: echo "The time was ${{ env.TIME }}"
```

#### A. 「飛び道具 No.2 act command」でローカル実行できるから実はリポジトリは不要
上記は Github Actionsでの実行方法であり、commit, pushしないと動作確認ができないのは面倒なので
いい感じのローカル実行環境を利用してデバックしよう！！


- ローカルでの実行方法(commit push不要)
  - `vim .github/workflow/hello-world.yml && brew install act && act -j hello_world_job`


### Q「Github Actionsで指定バージョンのphpが必要なのでDockerfile書かなきゃだよね？」

わかるわかる。
ubuntuやcentosにlatestで用意されているのとバージョン合わないとかあるある。
そもそも、古すぎるバージョンだとそもそもpackage managerが対応してないうぎゃーってなる。
あと、毎回apt installすると実行時間が嵩むからねぇ。
（従属課金的なコストも考えたりしなきゃだし）
めんどいよねー。


#### A. 「飛び道具 No.3 dockerhubのcontainerを利用する」
##### くっそおすすめな飛び道具。覚えたら１ファイルで大体なんでもできる。


- uses: docker://xxxxx:tag という有能オプションがあります。
  - これを使うことでdockerhubから落としてきたcontainerを使って実行することができます。
    - apt commandで実行環境を用意する必要がありません。
    - しかもtagも指定できるので古いバージョンの環境を実行することが容易です
- またstepsでの実行内容はvolume containerの中で引き継がれます。
  - (composer installでvendor配下に生成されるファイル群、env等)
- これらが合わさることでcomposer v1でinstallを行いつつ、php 7.2の環境でphp-csを実行するというきめ細やかな実行内容を自作Dockerfileやaction.yml無しに実行することが可能です。


```yml

jobs:
  check-by-cs-fix-dry-run:
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@v2
      - name: Install Dependencies
        uses: docker://composer:1
        with:
          entrypoint: /usr/bin/composer
          args: install -q --no-ansi --no-interaction --no-scripts --no-suggest --no-progress --prefer-dist
          path: ${{ github.workspace }}
      - name: php cs
        uses: docker://php:7.2-cli
        with:
          entrypoint: /usr/local/bin/php
          args: vendor/bin/php-cs-fixer fix -v --dry-run
          path: ${{ github.workspace }}
```

※ 最初に書いてあったPHP-CSとの違いはcomposerのversion指定と実行対象のphpのversion指定できていること。


## その他 Q & A

### Q 「Hello worldの後にメンテしやすいGithub Actions作りたいんだけど圧倒的成長できる参考コンテンツ無いの？」
初学者のコンテンツって充実してるけど、中級者向けになるととたんに減りだすよね。
どこ見ればいいかわかりにくいよねー。

#### A 「pipeline-componentsを見て！！！」

- [pipeline-components](https://github.com/pipeline-components)がくそ便利だよ。
  - Linterが複数言語網羅されている
  - Github Actionsを覗いてみると中で下記を行っていて自分で保守したいときに見るコンテンツとして最適
     - hadolintでDockerfileをチェック
     - 自己testの実行
     - 自己deployの実行


### Q「Github Actionsってblack boxでしょ？dev環境っぽくテストできないのはちょっと……」
#### A 「調べるとGithub Actionsで実行中コンテナにsshする方法が有るっぽいよ（しらんけど〜）」

### Q 「DB立ててテストのCI走らせたいんだけどできる？」
#### A「serviceでできるっぽい！！(しらんけど〜)」

### Q 「飛び道具のcontainer面白いけどdocker pullが非効率だからcacheしてみたい」
#### A「github action専用の保存ボリューム的な某でできるとかなんとか！！(しらんけど〜)」


## あとがき

めっちゃ便利だからGithub Actions使ってみてね。

楽しい楽しいGithub Actions lifeを君も過ごそう。