Github Actionsで、プライベートリポジトリのコンテナを使った自動テストを行う

# はじめに
世間には様々なCI/CDツールがありますが、GitHub Actionsは、GitHubを使用していれば気軽に導入できるため、使用している方は多いかと思います。
本記事では、DockerImageをプライベートリポジトリのGitHubコンテナレジストリに登録し、登録したDockerImage内で、GolangのUnitTestを動作させる方法を記載しています。

# 背景
筆者の開発環境は、VSCodeのDevContainerを使用しています。DevContainerはこちらの記事を参照してください。

https://qiita.com/kishibashi3/items/e20aecef45ed8341e739

開発には、ビルドに時間のかかるライブラリや、GitHubにコミットできない大容量の外部ライブラリが必要でした。DevContainerは、あらかじめ開発環境をDockerImage化することで、誰でも同じ環境で開発できます。
CIでも同様の環境を用意する必要があり、DevContainerを流用して、CI用の環境を作成することにしました。

# 手順
Dockerのインストール方法、DevContainerの導入方法は本記事では割愛します。
本記事のディレクトリ構成は以下の通りです。

```
├── .devcontainer
│   ├── devcontainer.json
│   ├── docker-compose.devcontainer.yml
│   └── Dockerfile
├── .github
│   └── workflows
│       └── main.yml
├── example-service
│   ├── hoge.go
│   ├── hoge_test.go
│   ├── go.mod
│   └── go.sum
├── Dockerfile.ci
└── docker-compose.ci.yml
```

### 1. CIで使用するDockerImageをGitHubコンテナレジストリにPushする
#### ログイン
コンテナレジストリにログインします。個人アクセストークンは事前に取得しておいてください。
「Login Succeeded」が表示されれば正常に認証が完了しています。

```shell
export GITHUB_USERNAME=YOUR_USERNAME
export ACCESS_TOKEN=個人アクセストークン
echo $ACCESS_TOKEN | docker login ghcr.io -u $GITHUB_USERNAME --password-stdin
 > Login Succeeded
```

https://docs.github.com/ja/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token

#### DockerImageのBuild, Push
CI用のDockerFileを作成し、DockerFileと同じディレクトリ階層上で以下のコマンドを実行します。
Pushが完了すれば、プロジェクト→PackagesタブよりPushしたイメージが表示されます。

```shell
DockerImage_Name=ghcr.io/Organization_Name/Image_Name
TAG=latest
docker build -t $(DockerImage_Name) .
docker push $(DockerImage_Name):$(TAG)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528033/67ba2173-506e-b2f7-90f2-1f2bb1da82d6.png)

### 2. Secretsにユーザー名、個人アクセストークンを設定する
GitHubActions内でプライベートリポジトリのコードやDockerImageをPullする場合、事前にユーザー名と個人アクセストークンを設定する必要があります。
リポジトリ→Settings→Secretsで下記ページへ移動し、①「New repository secret」でユーザー名、個人アクセストークンを設定します。
本記事では、②の様に、ユーザー名を「PRIVATE_REPO_USERNAME」、個人アクセストークンを「PRIVATE_REPO_ACCESS_TOKEN」としていますが、キー名は後述のワークフローと合っていれば任意の名前で構いません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528033/c6c86238-e84b-0fdf-0997-ad7ee3327d78.png)

### 3. ワークフローの記述
.github/workflows/main.ymlを書いていきます。本記事のワークフローではPush時に動作する様に設定しています。ワークフローは以下のブロックに分けて記載しています。

* コンテナレジストリへのログイン
* 登録したコンテナの起動
* テスト実行

```yaml:main.yml
name: UnitTest

on: push

jobs:
  unittest:
    name: go test with coverage
    runs-on: ubuntu-18.04
    steps:
      - name: Setup Go environment
        uses: actions/setup-go@v2.1.3
        with:
          go-version: 1.16
      
      # CI環境からghcr.ioへログイン
      - name: Login to GitHub container repository
        uses: docker/login-action@v1
        with:
          registry: ghcr.io
          username: ${{ secrets.PRIVATE_REPO_USERNAME }}
          password: ${{ secrets.PRIVATE_REPO_ACCESS_TOKEN }}

      # コンテナ起動
      - name: docker-compose up
        env:
          DOCKER_BUILDKIT: 1
          TOKEN: ${{ secrets.PRIVATE_REPO_ACCESS_TOKEN }}
        run: |
      # UnitTestを実行する際にプライベートリポジトリにアクセスするため、個人アクセストークンをコンテナに渡す
          echo $TOKEN >> .token
          docker build --secret id=token,src=.token -f Dockerfile.ci -t sample_app_for_ci .
          docker-compose -f docker-compose.ci.yml build
          docker-compose -f docker-compose.ci.yml up -d

      # example-serviceのテスト実行
      - name: Run example-service Unit Test
        run: docker-compose -f ../docker-compose.ci.yml exec -T -w コンテナ内のテスト対象ディレクトリ app（イメージ名） go test -coverprofile coverage.out -covermode atomic ./...
        working-directory: ./example-service
```

ワークフロー内で記載されているdocker-compose.ci.yml, Dockerfile.ciは以下の通りです。

```yaml:docker-compose.ci.yml
version: "3.1"
services:
  app:
    image: example_app_for_ci
    volumes:
      - ./:/app:cached
    # DevContainer作成時に内包した外部ライブラリの移動
    command: sleep infinity
```

```dockerfile:Dockerfile.ci
FROM DockerImage_Name:TAG
# 各種ライブラリのインストール
RUN apt-get update && apt-get install -y cmake g++ wget unzip libgtk2.0-dev pkg-config glib-2.0
# GitHubの個人アクセストークン設定
RUN --mount=type=secret,id=token TOKEN=$(cat /run/secrets/token) \
 && git config --global url."https://$TOKEN:x-oauth-basic@github.com/".insteadOf "https://github.com/"
```

# おわりに
GitHubActionsで、プライベートリポジトリのコンテナを使って自動テストを行いました。個人アクセストークンの取り回しや、actionsの誤選定等で苦労しましたが、ymlファイルを置くだけで気軽に導入できるのはとても魅力的に感じました。この記事がどなたかのお役に立てれば幸いです。