やりたかったこと
GitHubのPull Request時にGoプロジェクトのビルド前チェックを行いたかったのでCircleCIで実現しました。
要件としては下記です。
- errcheckやらlint等のチェックを実施
- migrationを実施
- DB処理を行うため、MySQLとRedisのイメージも利用
- 処理時間の最大限短縮
- 必要な外部ライブラリを取得済みのカスタムイメージを利用(イメージはプライベートリポジトリに保持)
- vendorはキャッシュを利用
- 処理結果をslackに通知
方針策定
migrationの確認はDBを立ち上げなくてはならず、なおかつDBの起動を待って処理を行わなくてはなりません。そのため、dockerizeコマンドをstepsに組み込みます。
今回は例としてMySQLとRedisのDockerイメージを使用しています。
また、チェック処理やmigrationには外部ライブラリの利用が必要となり、それらのインストールに時間がかかってしまいます。
処理時間の短縮を図るため、ライブラリを予めインストールしたカスタムイメージの利用とvendorのキャッシュ化を行います。
設定手順
GitHubをCircleCI連携するためにはプロジェクト直下に.circleciディレクトリを作成し、config.ymlファイルを配置する必要があります。
また、今回は必要なライブラリをインストール済みのカスタムイメージを利用するため、Dockerfileを用意する必要があります。このDockerfileの配置に関しても公式が配置箇所を指定しているのでそれに従います。
├── adapter
├── domain
├── usecase
├── hogehoge
└── .circleci/
├── config.yml
└── images
└── go
└── Dockerfile
1. Dockerfile用意
今回はGo言語での開発のため、予めライブラリをgo get
したカスタムイメージを作成して利用することにします。
depも利用しているのでバージョン指定をしてdepのインストールも行います。
CircleCi純正のDockerイメージをベースに、カスタムしました。
FROM circleci/golang:1.11
ENV GOPATH /go
ENV DEP_VERSION 0.5.0
RUN curl -L -s https://github.com/golang/dep/releases/download/v${DEP_VERSION}/dep-linux-amd64 -o $GOPATH/bin/dep
RUN chmod +x $GOPATH/bin/dep
RUN go get -u golang.org/x/tools/cmd/goimports
RUN go get -u github.com/kisielk/errcheck
RUN go get -u golang.org/x/lint/golint
RUN go get -u bitbucket.org/liamstask/goose/cmd/goose
RUN go get -u github.com/client9/misspell/cmd/misspell
2. DockerHubにアップロード
CircleCIからDockerfileを利用する場合、用意したDockerfileをDockerHubにアップロードする必要があります。
方法としては直接アップロードする方式とGitHub等と連携して自動で更新する方法があるようです。今回は直接アップロードを行います。
また、下記操作をする前に予めリポジトリは任意の名前で作成してください。
$ cd path-to-project # プロジェクト直下へ
$ docker build --tag=hogehoge/foo:1.0.0 .circleci/images/go # tagは作成した任意のもの&初回は1.0.0が無難
$ docker login
$ docker push hogehoge/foo # tagは作成した任意のもの
3. CircleCIプロジェクト設定
CircleCIをGitHubに連携する方法に関してはこちらの記事がとても参考になりました。
今回は割愛します。
サンプルのconfig.ymlを作成するところまで進めてください。
4. config.yml用意
config.ymlはCircleCIのyml書式に従って記述します。
作成したconfig.ymlはコミットしてmasterブランチまで反映させてください。
version: 2
jobs:
check:
environment:
- GOPATH: /go
- APP_ENV: circleci
working_directory: /go/src/github.com/path-to-project
docker:
- image: hogehoge/foo:1.0.0
auth:
username: hogeuser
password: $DOCKERHUB_PASSWORD
- image: circleci/mysql:5.7-ram
environment:
MYSQL_ROOT_PASSWORD: rootpw
MYSQL_DATABASE: test
MYSQL_USER: circleci
MYSQL_PASSWORD: circleci
- image: circleci/redis:5.0-alpine
steps:
- checkout
- run: dockerize -wait tcp://127.0.0.1:3306 -timeout 120s
- run: dockerize -wait tcp://127.0.0.1:6379 -timeout 120s
- restore_cache:
keys:
- v1-vendor-{{ checksum "Gopkg.lock" }}
- run:
name: ensure
command: |
if [ ! -d vendor ]; then
dep ensure -vendor-only
fi
- save_cache:
key: v1-vendor-{{ checksum "Gopkg.lock" }}
paths:
- vendor
- run: goose -env circleci up
- run: go test -v ./...
- run: find . -type f -name '*.go' -not -path "./vendor/*" -print0 | xargs -0 goimports -l | xargs -r false
- run: find . -type f -name '*.go' -not -path "./vendor/*" -print0 | xargs -0 misspell -error
- run: # その他もろもろの確認処理
workflows:
version: 2
setup-install-check:
jobs:
- check
5. CircleCIプロジェクト設定の調整
上記の設定のみでは要件が満たせないため、プロジェクトの設定を調整します。
実行タイミングの設定
初期設定ではすべてのコミットに対してCircleCIが実行されます。
今回はPull Request時のみにCircleCIを実行したいため、設定を変更します。
CircleCIプロジェクトからSetting > Advanced Settings に進み、「Only build pull requests」をOnに設定。
これによって、PR時にのみ実行されるようになりますが、defaultブランチに対してのコミットは常に実行される点、注意です。
※defaultブランチはリポジトリごとに設定可能です。GitHubのSettingsから確認をしてみてください。
Slack通知の設定
CircleCIの実行結果をSlackに通知する設定を入れます。
CircleCIプロジェクトからSetting > Chat Notifications に進み、Slackの「Webhook URL」を設定。
SlackのWebhook URLは画面内のCircleCI Integrationリンクから遷移して取得することが可能です。
環境変数の設定
DockerHubにカスタムイメージを置く際、セキュリティの関係でプライベートリポジトリを使う必要がある場合があります。カスタムイメージを取得する際に、CircleCIがユーザ情報を保持する必要があるのですが、config.ymlに記載するのは良くないでしょう。
そのため、環境変数にカスタムイメージ取得用アカウントのパスワードを持たせ、config.ymlから呼び出しています。
CircleCIプロジェクトからSetting > Environment Variables に進み、Add Variableをクリック。
出てきたダイアログに環境変数名とその値を記載してAdd Variableをクリック。
config.yml説明
ポイントだけ少し補足説明します。
Docker周り
今回はプライベートリポジトリにアップロードしているカスタムイメージを利用するため、最初のimage
(実体はGo用のイメージ)に対してauth
を設定しています。
$DOCKERHUB_PASSWORD
は前項で設定した環境変数の値が適応されます。
MySQLイメージのenvironmentに各設定値を入力することで、その設定で起動されます。MYSQL_DATABASE
を設定することでtestという名前のDBスキーマも作ることができます。
docker:
- image: hogehoge/foo:1.0.0
auth:
username: hogeuser
password: $DOCKERHUB_PASSWORD
- image: circleci/mysql:5.7-ram
environment:
MYSQL_ROOT_PASSWORD: rootpw
MYSQL_DATABASE: test
MYSQL_USER: circleci
MYSQL_PASSWORD: circleci
- image: circleci/redis:5.0-alpine
DB起動待機
今回はredisとmysqlのDockerイメージを利用するが、それらの起動を待つために下記の処理を行っています。
- run: dockerize -wait tcp://127.0.0.1:3306 -timeout 120s
- run: dockerize -wait tcp://127.0.0.1:6379 -timeout 120s
キャッシュの利用
depを利用して取得したライブラリはvendorに格納されるため、vendorをキャッシュ化しています。
depはライブラリの依存関係がGopkg.lockに反映されるため、Gopkg.lockのチェックサム値をキーとしてキャッシュを保存します。
処理としては下記の流れです。
- キャッシュが存在したら取得する(restore_cache)
- キャッシュが存在しなかった(vendorがリストアされなかった)場合、dep ensureを行う。
- vendorをキャッシュに保存する(save_cache)
なお、dep ensureの際にGopkg.lockが更新され、キャッシュ保存時にキー値が変わってしまう恐れがあるため -vendor-onlyをつけています。
- restore_cache:
keys:
- v1-vendor-{{ checksum "Gopkg.lock" }}
- run:
name: ensure
command: |
if [ ! -d vendor ]; then
dep ensure -vendor-only
fi
- save_cache:
key: v1-vendor-{{ checksum "Gopkg.lock" }}
paths:
- vendor
※ 保持しているキャッシュを削除したい場合は、v1-vendor-の数値を書き換えるらしいです。
GitHub側の設定(追記2020/04/14)
上記でCircleCIの設定は完了です。Pull Request生成時にCircleCIは起動します。
しかし、Pull Requestの画面には表示されないため、追加で設定をすると良いです。
GitHubのリポジトリのSettingタブ > Branches > Branch protection rules
でブランチのマージ前等の設定が可能です。
新規にルールを設定する場合はAdd rule
、既存の設定を変更する場合はEdit
をクリックします。
Require status checks to pass before merging
にチェックを入れると、直近にGitHubプロジェクトのステータスを更新した要素が表示されるため、自分が作成したものにチェックを入れて保存してください。
なお、作成したものが出てこない場合、CircleCIのProject Setting内のGitHub Status Updates
のチェックが外れている可能性があるので、チェックを付け、空コミット等をしてリポジトリ内でCIを起動しましょう。
すると、Pull Requestの画面でCIの結果が見れるようになるはずです。
おわりに
Goのサンプルが少なめだったので参考になれば嬉しいです。