JavaによるCI導入ガイドも最終回の第6回目となる。これまで Spock によるユニットテストや jacoco によるカバレッジ計測、Checkstyle によるコードスタイル検証の紹介をした。紹介した継続的インテグレーション(CI)のツールを GitLabに導入する。
目次
1. Gitlab 設置と初期設定
Gitlab の説明は既存の記事にて。 記事 を参考に Gitlab を導入する。 実行環境の保存, Runner の登録 は操作が異なるので説明する。
実行環境の保存
今回のアプリケーションのビルドと検証に必要な環境を Docker で作成する。 Gitlab のホスト上で空のディレクトリを作り、以下の Dockerfile を保存する。
Dockerfile(on Gitlab)
FROM openjdk:10.0.2-slim
ENV DEBCONF_NOWARNINGS yes
# 必要なパッケージのインストール
RUN echo "deb http://httpredir.debian.org/debian jessie-backports main contrib non-free" >> /etc/apt/sources.list && \
apt-get update && \
mkdir -p /usr/share/man/man1 && \
apt-get install --yes --no-install-recommends \
postgresql-client-10 docker.io curl python2.7 psmisc wget firefox
# Docker compose のインストール
RUN curl -L "https://github.com/docker/compose/releases/download/1.12.0/docker-compose-$(uname -s)-$(uname -m)" \
-o /usr/bin/docker-compose && \
chmod +x /usr/bin/docker-compose
# Chrome のインストール
RUN wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb && \
apt-get install --yes --no-install-recommends \
fonts-liberation libappindicator3-1 libasound2 libxss1 lsb-release xdg-utils && \
dpkg -i google-chrome-stable_current_amd64.deb && \
rm -f google-chrome-stable_current_amd64.deb
# Infer のインストール
# ダウンロードが遅いので、何回も実行する場合は
# 以下のようにあらかじめダウンロードしたものを使うほうが良いかも
#
# COPY infer-linux64-v0.15.0.tar.xz /opt
# RUN cd /opt && \
# tar xJf infer-linux64-v0.15.0.tar.xz && \
# ...
RUN cd /opt && \
curl -sL \
https://github.com/facebook/infer/releases/download/v0.15.0/infer-linux64-v0.15.0.tar.xz | \
tar xJ && \
rm -f /infer && \
ln -s ${PWD}/infer-linux64-v0.15.0 /infer && \
rm -f /opt/infer-linux64-v0.15.0.tar.xz
ENV INFER_HOME /infer
ENV PATH ${INFER_HOME}/bin:${PATH}
CMD '/bin/bash'
Dockerfile を作ったディレクトリで、以下のコマンドを発行し。イメージをビルドする。
sudo docker build . -t jdk-infer-browser
jdk-infer-browser
というタグでイメージが保存される。
Dockerfile のコメントにもあるが、infer のイメージの取得に時間がかかるため、上記のビルドには結構な時間がかかる。 必要なら、予め infer のビルド済みファイルを取得して Dockerfile のあるディレクトリに置いておき、 コメントの指示のようにコマンドを書き換えるのが良いだろう。
Runner の登録
上記記事と行うことは殆ど変わらないが、以下の点を読み替える。
<環境に付けるタグ名> -> jdk,infer,browser
<実行するDockerのイメージ> -> jdk-infer-browser
プロジェクトの作成
GitLab のトップページに移動し、‘Create a projec’ から新しいプロジェクトを作る。
2. Gitlab ジョブの設定
開発環境に戻り、Gitlab のジョブファイルを作成していく。
以下のファイルを作成する。Infer の実行方法のみ開発環境と異なる。 (ビルド時にビルド処理を Infer にキャプチャさせておくようにする。解析時にはキャプチャしたビルド情報をもとに解析を行わせる)
.gitlab-ci.yml
# 利用する Docker image
image: jdk-infer-browser
# Runner 上の環境変数
variables:
# ストレージドライバの指定
DOCKER_DRIVER: overlay2
# 以下データベース接続設定
POSTGRES_HOST: messageboard-ci-postgres
POSTGRES_DB: java_ci
POSTGRES_USER: java_ci
POSTGRES_PASSWORD: "password"
POSTGRES_PORT: 5432
# タスクステージの設定
stages:
- build
- cleanup_build
- unit_test
- code_check
- e2e_test
- deploy
# 各タスクの処理前に実行するコマンド
# gradle のキャッシュを gitlab にキャッシュさせる
before_script:
- export GRADLE_USER_HOME=`pwd`/.gradle
# プロジェクト全体のキャッシュの設定
cache:
paths:
- build/
- infer-out/
- .gradle
# データベースが必要なタスクのための
# サービスの設定を YAML エイリアスにする
.services:
- &postgres-service
name: postgres:9.6
alias: messageboard-ci-postgres
# データベースの初期化処理を
# YAML エイリアスにする
.scripts:
- &init-postgres >
PGHOST=$POSTGRES_HOST
PGPORT=$POSTGRES_PORT
PGUSER=$POSTGRES_USER
PGPASSWORD=$POSTGRES_PASSWORD
psql $POSTGRES_DB < ./sql/scheme.sql
# 各タスクのタグは同じにするので
# YAML エイリアスにする
.tags:
- &runner-tags
- jdk
- infer
- browser
# ここからタスクの登録
# ビルドタスク
build:
stage: build
tags: *runner-tags
script:
# ビルド処理コマンド
# この時点で infer にキャプチャをさせておく
- infer capture -a checkers --eradicate -- ./gradlew build -x check
# ビルド生成物の指定
artifacts:
when: on_success
paths:
- build/libs
# ビルドが失敗したときのクリーンアップタスク
cleanup_build_job:
stage: cleanup_build
tags: *runner-tags
script:
- ./gradlew clean
- rm -fr ./infer-out
when: on_failure
# ユニットテスト実行タスク
unit_test:
stage: unit_test
tags: *runner-tags
services:
# データベースサービスを起動する
- *postgres-service
before_script:
# 初期化スクリプトを実行する
- *init-postgres
script:
# データベース環境を指定してユニットテストを走らせる
- >
./gradlew
-Pdburl=jdbc:postgresql://$POSTGRES_HOST:$POSTGRES_PORT/$POSTGRES_DB
-Pdbuser=$POSTGRES_USER
-Pdbpass=$POSTGRES_PASSWORD
test --info
artifacts:
when: always
paths:
- build/reports/tests/test
- build/jacoco
# build タスクに依存させる
dependencies:
- build
# スタイル解析処理タスク
checkstyle:
stage: code_check
tags: *runner-tags
script:
- ./gradlew checkstyleMain --info
artifacts:
when: always
paths:
- build/reports/checkstyle
# build タスクに依存させる
dependencies:
- build
# カバレッジ解析タスク
coverage:
stage: code_check
tags: *runner-tags
script:
- ./gradlew jacocoTestReport --info
artifacts:
when: always
paths:
- build/jacocoHtml
dependencies:
# unit_test タスクに依存させる
- unit_test
# バグ解析タスク
infer:
stage: code_check
tags: *runner-tags
script:
# キャプチャの結果をもとに解析する
- infer analyze --fail-on-issue --eradicate
after_script:
# HTML レポートを出力させる
- infer explore --html
artifacts:
when: always
paths:
- infer-out/report.html
dependencies:
- build
# e2e テストタスク
e2e_test:
stage: e2e_test
tags: *runner-tags
# Spring のテストが起動するため
# DB を容易しておく
services:
- *postgres-service
before_script:
# DB 初期化
- *init-postgres
# アプリケーションサーバをバックグラウンドで起動させる
- >
/usr/bin/java
-Dspring.datasource.url=jdbc:postgresql://$POSTGRES_HOST:$POSTGRES_PORT/$POSTGRES_DB
-Dspring.datasource.username=$POSTGRES_USER
-Dspring.datasource.password=$POSTGRES_PASSWORD
-jar ./build/libs/message-board-0.1.0.jar &
script:
# e2e テストを実行させる
- >
./gradlew
-Pdburl=jdbc:postgresql://$POSTGRES_HOST:$POSTGRES_PORT/$POSTGRES_DB
-Pdbuser=$POSTGRES_USER
-Pdbpass=$POSTGRES_PASSWORD
-Pci=yes e2e:e2eTest --info
after_script:
# アプリケーションサーバを止める
- fuser 8080/tcp -k || true
artifacts:
when: always
paths:
- e2e/build/reports/tests
# build タスクに依存させる
dependencies:
- build
# 各タスクの成果物(レポートなど)をまとめて
# ブラウザからのアクセスができるようにする
pages:
stage: deploy
tags: *runner-tags
script:
- mkdir -p public
- cp -r build/reports/checkstyle public/checkstyle
- cp -r build/reports/tests/test public/unit_test
- cp -r build/jacocoHtml public/coverage
- cp -r infer-out/report.html public/infer
artifacts:
paths:
- public
これで CI を回す準備ができた。 先程作ったプロジェクトに git push
を行う。 自動的に CI の処理が開始される。 すると、Gitlab の当該プロジェクト内 ‘CI/CD’ 画面で、以下のようにタスクの状態を確認できるようになる。
しばらくすると、以下のように実行が完了する。
終わりに
これでCIを回すことができた。このあとは、アプリケーションを改良していきながら CI の良さを実感しよう。
改良したい点
- リファクタリング
- POJO クラスを Lombok を使ってスッキリさせる
- MessageForm クラスは Immutable にできる
- MessageBoardController にビジネスロジックが入っているので、Facade クラスを作ってそちらに追い出す
- コマンドのパースをもう少しマシにする
- セキュリティ対策
- CSRF 対策
- 機能の追加
- 日付の表示、ハンドル名、削除機能…
- API化
- フロントエンドをリッチにする
- and something you wants.
社内サーバにリモートリポジトリを作るのも一つですが、「開発にまつわる面倒事」をこの際全部、tracpath(トラックパス)に任せてみませんか?
バージョン管理サービス・プロジェクト管理サービスの「tracpath(トラックパス)」では、
ユーザー5名、リポジトリ数3つまで、無料で利用可能です。
さっそく実務でも使って見ましょう。
自らも開発を行う会社が作ったからこそ、開発チームの「作る情熱」を支える、やるべきことに集中出来るサービスになっています。
エンタープライズ利用が前提のASPサービスなので、セキュリティも強固です。