CircleCIで{maven,github}-release-pluginを使ってGitHub Releaseに自動登録
一発ネタやちょっとしたオモチャならばともかく、プログラムを継続的に維持したいとなると、CIやCDの導入を検討したくなりm(ry
まぁ、長ったらしい前置きはさておき、CircleCIを使い始めました。
今回は、CircleCIを使用してMavenビルドを行い、作成したjarをGitHub Releaseへ自動登録するというネタです。
ところで、CIサービスの有名所と言えばもう一つ、Travis CIがありますね。
このTravis CI、ビルドマトリクスというCircleCIには無い機能を備えていて、ざっくり言うとJDK7と8を同時にビルドするなどができるようです。
この機能には少し惹かれるものがあったんですが、まぁ、ライブラリ提供する訳でも無いし、最新のJDKでビルドができれば良いかな、複雑な物を管理したくないしー、という感じで今回はCircleCIにしてみました。
あとは、CircleCIのv2がちょっぱやと言う話を耳にしていて気になっていたという点もあったり。
しかし、後述しますがCircleCIには無いとある機能がTravis CIにありそうなのでちょっち失敗したかなぁと思っています…
さて、CI&CDやるぞと言ってもなんか漠然としていますし、ダレないためにもまずはどこまでやるかを決めるべきかなと思いました。
さしあたって、まず何がしたいんだっけと考えた結果、ざっくり以下の3点ができればいいかなとなりました。
- 前回記事でテスト書かねば、と宣言したので継続化できる環境がほしい。
- バージョンアップ作業はルーチンワークなので自動化したい、オペミスするとカッコ悪いので。
- Mavenなのでビルド成果物も作成される、バージョン毎に自動で保存しておきたいでもMavenレポジトリ持ってないとりまGitHub Releaseで。
ということで、まずはこの3点が達成されることを目的にします。
まぁ、1.はCIサービスを使い始めた時点でほぼ達成かなと、コードをGitHubにcommitすれば勝手にテストを実施してくれるので。後はテストを書くだけですね、書いていませんが…
2.と3.が今回の本題です。
試行錯誤の結果、maven-release-plugin
とgithub-release-plugin
を使用することによって達成することができました。
これからその詳細を載せるのですが、これまた長ったらしいので、先に出来上がったシステム概要を載っけます。
- CircleCI v2
- build docker imageは
circleci/openjdk:8-jdk
- ビルドツールはMaven
- 連携先はGitHub
master
以外のブランチにcommitすることをトリガーにmvn package
- 要するに、packagingできることの確認
master
ブランチにcommitすることをトリガーにmvn deploy
- つまり、
master
ブランチへのPull requestをmergeでmvn deploy
される - その際、バージョンアップ等が自動で行われる
- ついでにGitHub Releaseにdraftとして登録される
- なぜdraftかと言うと、単純に直公開にビビった
- つまり、
ちゃんとやろうとするとgit-flowで言うところのrelease
ブランチとdevelop
ブランチが最低限は欲しいかなと思いましたが、まぁ必要に迫られたときでいいやとしました。
ではでは、詳細に記していこうと思います。
目次。
- バージョンアップの自動化(
maven-release-plugin
)maven-release-plugin
の設定- CircleCIで
maven-release-plugin
を実行
- GitHub Releaseへのjar自動登録(
github-release-plugin
)github-release-plugin
の設定maven-release-plugin
とgithub-release-plugin
の連携- CircleCIで
github-release-plugin
を実行
- ブランチ毎のMavenフェーズ選択
バージョンアップの自動化(maven-release-plugin)
バージョンアップ作業って面倒くさいです。
Mavenの場合、SNAPSHOT
を外してテストを通してjarとかにパッケージングして出来上がったパッケージをどっかに登録してgitのtag打って次の開発用にversion
をインクリメントしつつSNAPSHOT
付けてみたいな。
ミスるともっと面倒くさいことになります。しかも、恥ずかしい><
例えばv1.2.3
にしたつもりがv12.3
にインフレしちゃったりとかあるあるネタっすね。ヤヴァイ…
なので自動化したいわけです。
Mavenの場合、maven-release-plugin
を使うことで大体解決することができます。
大体というかほぼ全て解決してくれます。逆にmaven-release-plugin
で解決できないことは、パッケージの登録だけかと思います。
そんでもってパッケージの登録は後述するgithub-release-plugin
の領分です。
さて、やりたいこととしてはよくある事例かなぁとおもむろにググるとCircleCIの公式ドキュメントがヒット!
これは勝つるとふんすふんすしますが、よく見るとv1のドキュメントやん…orz
しかもよくよく読むとCircleCIのREST APIでmvn release:prepare
を実行しているだけです。
これでは、maven-release-plugin
をローカルで実行するかCircleCIで実行するかぐらいの差しかありません。
まぁ、ビルド環境が統一されるのでビルドできないそれおまかん的な問題は回避できます。
が、そうじゃない。
やりたいことは、ある条件をトリガーに(ここでは、master
ブランチのmergeなどでcommitが発生したら)自動でバージョンアップ作業を開始したい、なので、テストのビルドが通ったことを自分で確認してからAPIを呼び出すというのはちょっと今回のケースには合わないなぁという感じです。
他にも色々とインターネッツを漁りますが良い記事が見つかりません…
と、いうことで自力で頑張ります。
maven-release-pluginの設定
まず、maven-release-plugin
の設定です。
公式ドキュメントを参考にpomファイルを編集します。
projectタグ直下にscm
タグを追加して、そこに連動させたいGitHubレポジトリ情報を記載。
こんな感じ。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <scm> <connection>scm:git:git@github.com:hoge/fuga.git</connection> <tag>HEAD</tag> </scm> : :
そして、project/build/plugins
タグ内にmaven-release-plugin
用のplugin
タグを追加します。
こんな感じ。
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-release-plugin</artifactId> <version>2.5.3</version> <configuration> <tagNameFormat>v@{project.version}</tagNameFormat> <scmCommentPrefix>'[maven-release-plugin][ci skip] '</scmCommentPrefix> </configuration> </plugin>
tagNameFormat
はお好みです。
上記の設定だとproject
タグ直下のversion
タグの値が参照されて、v1.0
みたいな感じになります。
注意すべきはscmCommentPrefix
です。
ここに[ci skip]
を含めたほうがいいと思います
表現が曖昧なのはちゃんと試していないからですすみません。
これが無いと無限ループします、たぶん。
pomファイルの編集が終わったらGitHubにpushする前にローカルで動作確認します。
masterのcommit履歴が汚れるのは嫌なので適当なブランチを切って動作確認を行います。
git checkout -b hoge mvn --batch-mode -DskipTests=true release:prepare release:perform
maven-release-plugin
の動作確認が目的なのでskipTests
を指定しています。
--batch-mode
を指定しないとインタラクティブにバージョン番号とかを聞かれます。
ローカルで実行する場合は対話式に実行しても良いのですが、CircleCIで実行することを想定している以上、対話式では無理なので--batch-mode
指定で実行します。
その場合、バージョン番号は最小位の値をひとつインクリメントするようです。
動作確認が済んだらおk。次はCircleCI上で動作させます。
CircleCIでmaven-release-pluginを実行
唐突、というか今更ですが、ここでmaven-release-plugin
の動作についてざっくり説明します。
maven-release-plugin
で使用するのはおおよそ2つのgoalだけです。1つはprepare
でもう1つはperform
。
それぞれざっくり何をしているかというと、prepare
はpomファイルのversion
タグのSNAPSHOT
を外してそれをcommitして(!)git tag
を打って(!)次の開発用にversion
タグをインクリメントしつつSNAPSHOT
付けてcommitし(!)、それをpushします(!)。
実際にprepare
で作成される1回目のcommitを貼っつけましょう。こんな感じです。
commit 0123456789abcdef........................ Author: circleci <circleci@circleci.com> Date: Sat Sep 2 00:00:00 2017 +0000 [maven-release-plugin][ci skip] prepare release v1.2 diff --git a/pom.xml b/pom.xml index 0123456..789abcd 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ <groupId>io.github.hoge</groupId> <artifactId>fuga</artifactId> - <version>1.2-SNAPSHOT</version> + <version>1.2</version> <packaging>jar</packaging> <name>fuga</name> @@ -11,7 +11,7 @@ <scm> <connection>scm:git:git@github.com:hoge/fuga.git</connection> - <tag>HEAD</tag> + <tag>v1.2</tag> </scm> <properties>
一方perform
は何をしているかというと、prepare
で作成されたtagでcheckoutして、(デフォルト設定の場合)mvn deploy
を実施します。
maven-release-plugin
の動作概要おわりっ!
まぁ、何が言いたいかというと、!したように自動でcommit&push等を行うということです。
CircleCIでmaven-release-plugin
を使う場合、当然ながらCircleCIから上記のcommit&push等を行います。ところが、CircleCIのGitHub連携ではRead-onlyの秘密鍵しか作成されません。
つまり、Writableな秘密鍵を新規作成してCircleCIに登録、そしてその公開鍵を連携するレポジトリに登録する必要があります。
詳細はCircleCIのドキュメントを参照。
これでCircleCIはWritableな秘密鍵を使用してGitHubにpushできるようになります。
最後にconfig.yml
を修正してprepare
とperform
を実行するようにします。
こんな感じ。
version: 2 jobs: build: docker: - image: circleci/openjdk:8-jdk steps: - checkout - add_ssh_keys: fingerprints: - "xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx" - run: git config --global user.email "circleci@circleci.com"; git config --global user.name "circleci"; mvn --batch-mode release:prepare release:perform
gitのuser設定はお好みです。
これをGitHubにpushしてCircleCIで動作確認。うまくいったらおk。
めでたしめでたし。
しかし、セキュリティしっかりしているとメンドイ…
GitHub Releaseへのjar自動登録(github-release-plugin)
Mavenはpackage
フェーズでjarを作成します。
せっかくなので、このjarをどこか手軽な場所に自動で保存しておきたいと思ったりします。
まぁ、CircleCIにもjarをArtifactsとして置いておくことは出来るのですが、ログインしないとArtifactsへのリンクを辿れないようなので手軽な場所とは言えません。
Mavenを使っているので、一般的にはどこかのMavenリポジトリに対してdeploy
フェーズでjarを登録するべきでしょうが、Mavenリポジトリなんて持ってないし、今回の対象は前回記事で作成した実行可能形式fat jarなのでMavenリポジトリに登録するメリットがそんな無いっす。
ということで、どうしよっかなーと考えていたらGitHub Releaseに置けばいいじゃんむふーとなりました。
maven-release-plugin
同様、やりたいこととしてはよくある事例かなぁとおもむろにググるとCircleCIの公式ブログがヒット!
これは勝つるとふんすふんすしますが、よく見るとpackagecloudにデプロイするやり方やん…orz(デジャヴ)
気を取り直して、そういえば、GitHub ReleaseにアップロードするMavenビルドプラグインがあったなぁということを思い出します。
ようするにGitHub APIを使用してGitHub Releaseへの登録をMavenビルドに組み込めるプラグインですね。
それが表題のgithub-release-plugin
です。
公式プラグインでは無く、おそらく個人が公開しているサードパーティプラグインなので、メンテナンスされなくなったりする点が怖いですが、コードをみた感じシンプルなのでいざという時は自分でなんとかできる範囲です。
ということで、github-release-plugin
を使うことにしました。
github-release-pluginの設定
以下の4つが必要です。
scm
タグの追加plugin
タグの追加maven-deploy-plugin
のskip設定settings.xml
の作成
scm
タグはデプロイ先のGitHubの解決に使用しているようです。こいつはmaven-release-plugin
を設定する際に既に記述してあるので割愛。
次のplugin
タグはこんな感じ。
<plugin> <groupId>de.jutzig</groupId> <artifactId>github-release-plugin</artifactId> <version>1.2.0</version> <executions> <execution> <id>github-upload</id> <phase>deploy</phase> <goals> <goal>release</goal> </goals> <configuration> <releaseName>v${project.version}</releaseName> <tag>v${project.version}</tag> <artifact>${project.build.directory}/${project.artifactId}-${project.version}.jar</artifact> </configuration> </execution> </executions> </plugin>
github-release-plugin
は自身を使用してGitHub Releaseに登録しているようなのでその設定をほぼ丸パクリです。
そしてmaven-deploy-plugin
のskip設定です。以下のplugin
タグを追記します。
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> <version>2.7</version> <configuration> <skip>true</skip> </configuration> </plugin>
こいつを設定しないとmvn deploy
実行時にこんな感じで怒られます。
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy (default-deploy) on project hoge: Deployment failed: repository element was not specified in the POM inside distributionManagement element or in -DaltDeploymentRepository=id::layout::url parameter -> [Help 1]
ようするにデプロイ先を示せということなんですが、前述の通りGitHub Releaseに登録できれば良いのでmaven-deploy-plugin
さんはskip設定にして黙っていてもらいますm(_ _)m
最後のsettings.xml
はこんな感じ。
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"> <servers> <server> <id>github</id> <privateKey>${env.GITHUB_API_TOKEN}</privateKey> </server> </servers> </settings>
こいつを適当なファイル名で保存します。自分は.circleci.settings.xml
にしました。
id
はgithub
決め打ちです。
privateKey
にはGitHubのPersonal access tokensを記述します。Personal access tokensの発行方法はGitHubのドキュメントを参照。
ただし、べた書き記述してしまうとGitHubにpushできなくなってしまいます。pushしちゃうとAccess tokenがバレてしまうので。なので環境変数経由で渡すようにします。
実は認証にユーザ名とパスワードを記述するやり方もあります。が、自分はGitHubの2段階認証を有効にしているのでユーザ名とパスワードを記述するやり方は使えないっす。
話を戻して、動作確認はこんな感じ。
export GITHUB_API_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx mvn -s ./.circleci.settings.xml deploy -Dgithub.draft=true -DskipTests=true
以下のようしてgithub-release-plugin
単独の動作確認もできます。
export GITHUB_API_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx mvn -s ./.circleci.settings.xml de.jutzig:github-release-plugin:release -Dgithub.draft=true -DskipTests=true
GitHub Releaseを確認して登録できていればおk。
maven-release-pluginとgithub-release-pluginの連携
今回はmaven-release-plugin
と連携させたいので、そのための設定をmaven-release-plugin
に1つ追加します。
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-release-plugin</artifactId> <version>2.5.3</version> <configuration> <tagNameFormat>v@{project.version}</tagNameFormat> <scmCommentPrefix>'[maven-release-plugin][ci skip] '</scmCommentPrefix> <!-- vvv ココ vvv --> <arguments>-Dgithub.draft=true</arguments> </configuration> </plugin>
maven-release-plugin
はその実行過程で別プロセスとしてmvn deploy
を実行します。arguments
タグはその引数を指定するタグです。こいつに-Dgithub.draft=true
を設定します。
先述のとおり、自分の場合はいきなりの公開にビビっているのでdraft公開です。
動作確認はこんな感じ。前述のようにcommitが発生するので、一時的なブランチを作成してそのブランチで確認すると良いと思います。
export GITHUB_API_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx mvn -s ./.circleci.settings.xml --batch-mode release:prepare release:perform
さっきと同様、GitHub Releaseを確認して登録できていればおk。
お次はCircleCIで動作させます。
CircleCIでgithub-release-pluginを実行
前述の通り、github-release-plugin
を利用するにあたりGitHubのPersonal access tokensを使用します。
こいつを環境変数経由で渡すように組んだので、CircleCIのビルドSettingsからEnvironment Variablesに今回の環境変数名であるGITHUB_API_TOKEN
を追加します。
やり方は察してください(雑w
疲れてきたンゴ…
だがあと少しなので頑張る。
最後に前述の.circleci.settings.xml
を読み込むようconfig.yml
を修正して完了。
version: 2 jobs: build: docker: - image: circleci/openjdk:8-jdk steps: - checkout - add_ssh_keys: fingerprints: - "xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx" # vvv ココ vvv - run: git config --global user.email "circleci@circleci.com"; git config --global user.name "circleci"; mvn -s ./.circleci.settings.xml --batch-mode release:prepare release:perform
めでたしめでたし。
ちなみにですが、Travis CIではGitHub Releaseへのアップロードは標準機能っぽいです。これがCircleCIでちょっち失敗したかなぁと思ったやつです。
隣の芝生は青い案件であることを祈るンゴ。
ブランチ毎のMavenフェーズ選択
CircleCIはどのブランチでcommitが生じてもビルドが開始します。
それ自体は良いのですが、GitHub Releaseの自動登録はmaster
ブランチのcommitをトリガーにしたビルド時のみに実施してほしいです。
ということで、master
以外のブランチのcommitをトリガーにしたビルドはpackage
フェーズまでに留めるようにします。
これは簡単。環境変数CIRCLE_BRANCH
に実行中ビルドのブランチが格納されるので、これを使って条件分岐するだけです。
version: 2 jobs: build: docker: - image: circleci/openjdk:8-jdk steps: - checkout - add_ssh_keys: fingerprints: - "xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx" # if文で分岐するだけ - run: if [ $CIRCLE_BRANCH = 'master' ]; then git config --global user.email "circleci@circleci.com"; git config --global user.name "circleci"; mvn -s ./.circleci.settings.xml --batch-mode release:prepare release:perform; else mvn package; fi
めでたしめでたし。
むすび
(ヽ´ω`)
記事書くのは大変ですね…
気を取り直して、実際にCircleCIを使っての感想です。
キャッシュはえー。ローカル実行よりずば抜けてはえーよ!
Maven Central Repositoryは日本からだと遠いんですかね。
逆にCircleCIへのアクセスはラグりまくりですw
CircleCIにはデバックビルド機能が有り、これを有効にしてビルドを実施するとビルドコンテナにssh接続できるようになります。が、遅いおw
何はともあれ、これでやっとacceptanceテスト書けるぞっと思ったら、initiatorは対応していなかったっていう…
継続的デリバリー 信頼できるソフトウェアリリースのためのビルド・テスト・デプロイメントの自動化
- 作者: Jez Humble,David Farley,和智右桂,高木正弘
- 出版社/メーカー: KADOKAWA
- 発売日: 2017/07/31
- メディア: 単行本
- この商品を含むブログを見る