はじめに
今作っているプロダクトに、ちゃちゃっとTravisCI環境を導入したくなりました。
そこでパクる参考にするため、すでにTravisCIを利用しているプロダクトとして
mackerel-agentのリリースエンジニアリング周りのソースを読んでみたのでメモを残しておきます。
対象ソース/扱う範囲
この記事では執筆時点のmackerel-agent最新バージョンであるv0.41.1を題材にしています。
取り上げる範囲としては、主にTravisCI周りだけです。
実際にはTravisCIだけでなくAppVeyorを使っていたり、表に出てこないバックエンドのプロセス(例えばChatOps用のボットとか)などがあると思いますので、ここで取り上げるプロセスはリリースエンジニアリングの一部のみとなります。
また、リリース周りはコードが陳腐化するのが早いですので、当記事を参考にする際は最新のソースも合わせて読むことをオススメします。
Let's read リリースエンジニアリング!
全体像
まずはTravisCIの設定ファイルである.travis.yml
から読んでみます。
以下のようなことを行うようになっていました。
- lint/コード整形/テスト/カバレッジ測定/ビルドなど
- ビルドした成果物をrpm/debにパッケージング(dockerを利用)
- リリース用プルリクエストの作成
- GitHub Releaseへのアップロード(ステージング/本番リリース)
- 結果をSlackで通知
順番に見ていきます。
lint/コード整形/テスト/カバレッジ測定/ビルドなど
ビルド周り/テストなどは以下の記述がされています。
単純な記述なため、見るだけである程度処理の予測がつくかと思います。
language: go
go:
- 1.7
script:
- make lint
- make cover
- test `gofmt -l . | wc -l` = 0
- make crossbuild
after_script:
- goveralls -coverprofile=.profile.cov
golang:1.7
でビルドするために、language: go
とgo:
の記述がされています。
script:
にlintやカバレッジ測定などの実際のテスト/ビルド処理などが書かれています。
after_script:
はカバレッジ測定用です。詳細は以下のサイトに載っていました。
参考:複数パッケージ構成のgolangのプロジェクトのカバレッジを測定しcoverallsに投稿する
ビルドした成果物をrpm/debにパッケージング(dockerを利用)
続いてrpm/debの作成です。.travis.yml
では以下の部分です。
before_deploy:
- docker pull astj/mackerel-rpm-builder:c5
- docker pull astj/mackerel-rpm-builder:c7
- make rpm deb rpm-kcps deb-kcps rpm-stage deb-stage tgz
##以下デプロイ周りの処理が続く##
パッケージングの処理はbefore_deploy:
以下に記述されています。
RPMのビルドで使うdockerイメージをpullした上でmake rpm deb(以下略)
を実行するようになっています。
dockerイメージはrpmbuild
コマンドをエントリーポイントとしたもので、これでrpmの作成を行なっています。debについてはdockerではなく直接debuild
コマンドを実行しています。
なお、TravisCI上でdockerを利用するためには以下の記述が必要です。(もちろんmackerel-agentの.travis.yml
にも記載されています)
sudo: required
services:
- docker
リリース用プルリクエストの作成
特定の名前(bump-version-x.y.z
(x.y.zはバージョン番号))のブランチをGitHubへpushすることでリリース用のプルリクエストを自動生成するようになっています。
TravisCI上でGitHubの履歴を参照し、前回のリリース以降にマージしたプルリクエストの情報などを以下のCHANGELOGに埋め込む処理を行なっています。
これらのファイルの変更をpushされたbump-version-x.y.z
ブランチにコミットした上で
github用のCLIツールhub
を用いてプルリクエストを作成しています。
.travis.yml
では以下の部分です。
色々細かなことをやっているため、コメント(風)の説明を入れてみました。
before_deploy:
##省略:前述のパッケージング処理##
# GitHubの操作用コマンドのソース取得 & ビルド
- go get github.com/aktau/github-release
- mkdir -p ~/bin
- git clone https://github.com/github/hub.git && cd hub && script/build -o ~/bin/hub && cd ..
# CHANGELOGの作成などで利用するために、ビルド対象以外のブランチの情報も取得
- echo $TRAVIS_BRANCH
- git config --add remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'
- git fetch
- git checkout master || git checkout -b master origin/master
- git checkout $TRAVIS_BRANCH
- git fetch --depth=1000
- git fetch --tags --depth=1000 || true
deploy:
## 省略:GitHub Releaseへのアップロード処理(後述します)
# リリース用プルリクエストの作成処理
- provider: script
script: _tools/releng --task=create-pullrequest --current-branch=$TRAVIS_BRANCH -v
skip_cleanup: true
on:
all_branches: true
condition: "$TRAVIS_BRANCH =~ ^bump-version-.*$"
before_deploy:
でhub
コマンドのインストールなどの準備作業を行った上で、
deploy:
に書かれた作業を行うようになっています。
deploy:
ではprovider: script
と記載することで、任意のスクリプトファイルを実行するようにしています。ここでは_tools/releng
というスクリプトを実行しています。
また、on:
以下に条件を書いておくことで、bump-version-x.y.z
という名前のブランチの場合のみ処理が行われるように制限を行なっています。
デプロイ用スクリプト_tools/releng
プルリクエスト作成やGitHub Releaseへのアップロードを行うためのperlで書かれたスクリプトです。
hub
コマンドやgit
コマンド、github-release
コマンドを呼び出しています。
ポイント
CHANGELOG変更分をcommit/pushしたりプルリクエストを作成するにはGitHubのアクセストークンが必要です。
アクセストークンは.travis.yml
に暗号化された環境変数として設定しておくことでスクリプトに渡しています。
env:
global:
- secure: "B34YsRebWxyx/UAHNAL5YWOgx2RE13TVfDC8FCCc7gAlogJcyAcPnC88fe8ECChHQC3GNnsHSi+GqgxTxzfoVkV+LUAFgHWbcF5O4O/4qHztKj7//72RwO+gXVr4U1Laz45bBr2r68XW0llj5tIlaEYxROJNLSAVDE4yDzmAHgc="
この値はTravisCIのCLIツールであるtravis
コマンドで作成できるものです。
(今回はtravis
コマンドについての説明は割愛)
この設定を行っておくことで、deployスクリプトの中から環境変数を参照可能です。
(以下の例では環境変数GITHUB_TOKEN
)として渡しています。
git_without_stderr qw/push -u/, "https://$ENV{GITHUB_TOKEN}\@github.com/$REPO_NAME.git";
なお、GitHubのトークンは漏れるとやばい情報です。
(今回の場合、このトークンがあればリポジトリにpushできちゃいます、、怖い。。。)
トークン利用時にTravisCIのログに表示されないように、標準出力/エラーのリダイレクトなど適切な処理を行うことを忘れないようにしましょう。
参考:Travis CIのテストの中でGitHubのレポジトリへpushする
GitHub Releaseへのアップロード(ステージング/本番リリース)
リリース用のプルリクエストがマージされたらGitHub Releaseへバイナリやrpm/debなどのパッケージファイルがアップロードされます。
これは.travis.yml
の以下の記述で実現しています。
deploy:
- provider: script
script: _tools/releng --task=upload-to-github-release -v && _tools/releng --task=upload-master-to-github-release -v
skip_cleanup: true
on:
branch: master
on:
にbranch: master
という記述を行なっていますので、masterブランチに変更があった場合にこの処理が行われます。
運用ルールとしてmasterへの直接コミットを禁止しておくことで、プルリクエストのマージ時にのみこの処理が行われることになります。
ここでのデプロイスクリプトの動きとしては、
- 1) GitHub上に現在のバージョンのリリースがなければ作成 & ファイルアップロード
- 2)
staging
リリースへファイルアップロード
となっています。
新バージョンのリリース以外でmasterにマージされたコミットはstaging
リリースにアップロードされていくということですね。
slackへの通知
特に説明することはないでしょう。見たままです。
notifications:
slack:
secure: n9Hs5rIlb6MuFVsRp46ykd+CptnE3boI7Q9mNLuHwz77IQ/2mpUmOyBRDeW3BA2EXa4WEYAXu8q7/b7zVV+c+RcPdz8/h9N9EBrHUVeIkCP/Nyp+aUA8GvLNjGuxYC7nov3L+9GHBFXKX8FaV2lZ1/Eeb1Ovbtv6Oi39xNkHqTQ=
おまけ:バイナリへのバージョン情報埋め込み
デプロイで利用しているブランチ名bump-version-x.y.z
を利用して、goのバイナリのビルド時にバージョン情報を埋め込む処理をしてたりします。
これは、
- Makefile内でgitのログを検索&加工してバージョン情報を取得
- 取得したバージョンを
go build
時のオプション-ldflags
に指定
することで実現しています。
CURRENT_VERSION = $(shell git log --merges --oneline | perl -ne 'if(m/^.+Merge pull request \#[0-9]+ from .+\/bump-version-([0-9\.]+)/){print $$1;exit}')
BUILD_LDFLAGS = "\
-X github.com/mackerelio/mackerel-agent/version.GITCOMMIT=`git rev-parse --short HEAD` \
-X github.com/mackerelio/mackerel-agent/version.VERSION=$(CURRENT_VERSION) \
-X github.com/mackerelio/mackerel-agent/config.agentName=$(MACKEREL_AGENT_NAME) \
-X github.com/mackerelio/mackerel-agent/config.apibase=$(MACKEREL_API_BASE)"
定義を1箇所にまとめるテクニックの好例ですね。
終わりに
ざっと.travis.yml
を中心に見てきました。
リリースプロセスとしてはおそらくバージョンアップ時のブランチ(bump-version-x.y.z
)作成などにボットなどを利用してChatOpsしてたりするのだと思います。
リリースエンジニアリングには各開発者(会社/コミュニティ)の個性が出る部分だと思いますので、
読んでいくのは楽しいものです。
機会があれば別のプロダクトについても読んでみたいと思います。
以上です。Enjoy!