Github Actionsとは
メタップスアドベントカレンダー8日目の記事です。
私が携わっているプロジェクトでは、CIツールとしてGithub Actionsを利用しています。Github Actionsでは、Jobと呼ばれる小さな単位を組み合わせて、一連の自動化処理であるWorkflowを作成します。Workflowはリモート・リポジトリへのプッシュやプルリクエスト作成をトリガーとして起動します(cronのような書式設定で定期実行もできるようです)。Jobの処理順序や、起動トリガーはYAMLファイルで定義し、所定のディレクトリ(.github/workflows
)へ設置します。1つのリポジトリには複数のWorkflowを定義可能です。本記事では、プログラミング言語Goのプロジェクトで、ビルドやテストをGithub Actionsを使って自動化してみたので解説を行います。
設定ファイルの記述
プロジェクトで使っている設定ファイルを下記に示します。こちらを適当な名前で保存し、 .github/workflows
へ保存してpushを行うと、設定に従って処理が行われます。Github Actionsでは、処理が行われるマシンをRunnerと呼びます。Jobは自分で一から作成することができますが、「プロジェクトのチェックアウト」や「ビルド」など、よく使われる処理はGithubにあらかじめ用意されており、Actionと呼ばれます。
name: ci
on:
push:
branches: [ develop ]
pull_request:
branches: [ develop ]
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Go
uses: actions/setup-go@v3
with:
go-version: '>=1.19.0'
cache: true
cache-dependency-path: go.sum
- name: OS Version
run: cat /etc/os-release
- name: Go Version
run: go version
- name: Go Lint
uses: golangci/golangci-lint-action@v3
- name: Setup
run: |
go install github.com/golang/mock/mockgen@latest
go install github.com/onsi/ginkgo/v2/ginkgo@latest
go get github.com/onsi/gomega/...
- name: Regenerate mock file
run: |
rm -rf mock
go generate ./...
- name: Go Build
run: |
go build -v ./...
- name: Go Test
run: |
ginkgo -v --cover --coverprofile=coverage.out --race --coverpkg=./... --covermode=atomic --output-dir=/tmp -r test
- name: Exclude auto-generated files from coverage
run: |
cat /tmp/coverage.out | grep -v "_mock.go" | grep -v ".pb.go" > coverage.out
go tool cover -func coverage.out
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: coverage.out
fail_ci_if_error: true
verbose: true
チェックアウト
- uses: actions/checkout@v3
プロジェクトをRunnerへチェックアウトするには、専用のActionを利用します。
Goのインストール
- name: Setup Go
uses: actions/setup-go@v3
with:
go-version: '>=1.19.0'
cache: true
cache-dependency-path: go.sum
GoのセットアップをRunnerに行います。上記の例では、Goのバージョン1.19.0以上の最新版がインストールされます。オプションの詳しい説明は下記をご参照ください。
- name: Go Lint
uses: golangci/golangci-lint-action@v3
Goで書かれたソースコードの静的解析を行います。
必要なツールの追加
- name: Setup
run: |
go install github.com/golang/mock/mockgen@latest
go install github.com/onsi/ginkgo/v2/ginkgo@latest
go get github.com/onsi/gomega/...
以下のJob実行に必要なツールを、追加でインストールします。本プロジェクトでは、mockを使った単体テストを行なっています。また、BDDテスティングフレームワークであるginkgoを利用していますので、それぞれ追加しておきます。
モックファイルの生成
- name: Regenerate mock file
run: |
rm -rf mock
go generate ./...
mockファイルはinterfaceで定義された仕様に従って生成されますが、仕様を変更するとmockファイルも再生成する必要があります。そのため、仕様を変更したのにmockファイルの再生成を忘れる、といったことが起こりがちです。そちらを防ぐため、このJobでもう一度作成を行います。go generate
コマンドを利用したmockの作成方法については、こちらをご覧ください。
プロジェクトのビルド
- name: Go Build
run: |
go build -v ./...
プロジェクト全体をビルドします。Goは静的型付言語ですので、文法エラーのあるプログラムファイルがpushされた場合、この時点で気がつくことができます。
テスト実行
- name: Go Test
run: |
ginkgo -v --cover --coverprofile=coverage.out --race --coverpkg=./... --covermode=atomic --output-dir=/tmp -r test
テストを実行します。上記の例では、test
ディレクトリ配下に存在するファイルを、BDDテストフレームワークであるginkgoを使ってテスト実行しています。同時にテストカバレッジを計測し、結果を/tmp/coverage.out
に保存しています。
カバレッジ計測に不要な情報を除去
- name: Exclude auto-generated files from coverage
run: |
cat /tmp/coverage.out | grep -v "_mock.go" | grep -v ".pb.go" > coverage.out
go tool cover -func coverage.out
mockファイルはテストを行うためにあるので、そちらのカバレッジを計測するのは意味がありません。そこで、カバレッジ結果ファイルから、自動生成されたスタブを削除します。gRPC通信のためのスタブファイルのカバレッジ結果も同様に削除します。
カバレッジをCodecovへアップロード
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: coverage.out
fail_ci_if_error: true
verbose: true
テストカバレッジを可視化するCodecovを利用しています。カバレッジをCodecovへのアップロードをしてくれるActionが用意されていますので、そちらを利用します。Codecovを設定すればPull Requestのページ上にカバレッジ率の増減が表示されますので、非常に便利です。
まとめ
CIを使うと、コーディング規約違反やテスト漏れといったヒューマンエラーを早期に発見することができます。ソフトウェアの性質上、不具合やミスをできるだけ早く発見することが品質の良いプロダクトを作成する手助けになりますので、めんどくさくてもプロジェクトの開始時などに作成しておくと良いでしょう。