前回の記事で、TravisCIからCircleCIに移行したことを書いた。
patorash.hatenablog.com
今回は、テストが通ったら自動的にrake release
する仕組みを作ったので、それについて書く。
ただし、今回のやつは結構面倒であった。
gemをリリースするorbsは既にある
実はもう既にある模様だが、なんとなく実装が好きになれなかった。
https://circleci.com/orbs/registry/orb/doximity/gem-publisher
- gem/nameとgem/versionというファイルにgemの名前とバージョン情報を書かなければならないこと
- rake releaseを使わずにgemをrubygemsにpushしていること
- たぶん、gemの名前が部分一致してしまうものだと機能しない
そこで、とりあえず自分でやってみることにした。が、実装は参考にさせてもらった。
実装
先に実装を載せておく。結構面倒です。何をやっているのかを書いていく。
- チェックアウト
- GitHubにアクセスするためのSSH鍵の登録
- bundle install
- RubygemsにgemをリリースするためのAPI KEYを設定
- gitのセットアップ(githubにタグを打つために必要)
- 同バージョンがまだリリースされていなければ、rake releaseする
- RubygemsにリリースするためのAPI KEY情報を消す
このうち、4,6,7は先ほどのorbsをかなり参考にさせてもらった。
jobs:
release:
executor:
name: ruby
parallelism: 1
steps:
- checkout
- add_ssh_keys:
fingerprints:
- "e7:e8:17:c5:03:05:fd:0c:0e:9b:3b:d3:90:53:c6:5c"
- run:
name: bundle install
command: bundle check || bundle install --jobs=4 --retry=3
- run:
name: RubyGems.org | Set credential
command: |
mkdir -p ~/.gem
echo ":rubygems_api_key: $RUBYGEMS_API_KEY" > ~/.gem/credentials
chmod 0600 ~/.gem/credentials
- run:
name: Setup git
command: |
git config push.default current
git config user.email "chariderpato@gmail.com"
git config user.name "patorash"
- run:
name: rake release
command: |
gem list --prerelease --all --remote imyou \
| grep -E "^imyou" \
| sed -e "s/^.*(\(.*\)).*\$/\1/" \
| grep -q -v "`bundle exec ruby -e "print Imyou::VERSION"`"
result=$?
if [ $result = 0 ]; then
bundle exec rake build
bundle exec rake release
fi
- run:
name: Delete credentials
command: |
shred -u ~/.gem/credentials
rale releaseでタグを打つには、読み書き可能なSSH鍵が必要なため、以下のサイトを参考にして作業した。
sue445.hatenablog.com
qiita.com
まず、秘密鍵と公開鍵を作る。
$ cd ~/.ssh
$ ssh-keygen -t rsa -b 4096 -m pem -C "CircleCI" -f id_rsa_circleci -N ""
次に、https://github.com/[username]/[reponame]/settings/keys にアクセスして、公開鍵を登録する。
id_rsa_circleci.pubの内容を貼り付けて保存する。このとき、Allow write accessにチェックを入れるのを忘れないこと。
保存できたら、もともと登録されていたであろうread onlyの鍵を削除しておく。
CircleCIに秘密鍵を登録
次に、https://circleci.com/gh/[username]/[reponame]/edit#ssh にアクセスして、秘密鍵を登録する。
id_rsa_circleciの内容を貼り付けて保存する。
その後、https://circleci.com/gh/[username]/[reponame]/edit#checkout にアクセスして、デプロイキーを削除する(これがread onlyのやつの秘密鍵)。
RubygemsのAPI KEYはRubygemsにログインしてプロフィールの編集画面にいったら書いてある。
この値を、CircleCIの環境変数に登録する。変数名は、RUBYGEMS_API_KEY
にする。
gitの設定
タグを打つ時にgitの設定が必要なので行なっておく。
rake releaseする
テストが通ったらrake releaseしたいが、もしgemのバージョンアップを含まない変更だった場合、実行されてしまうと既にリリース済みというメッセージと共にCIが落ちるので、バージョンアップしていない場合は無視したい。
先のorbsの実装のコマンドを実行したところ、gem list 〜
の結果に自分のgemとは異なるgemまで出てきた。
$ gem list --prerelease --all --remote imyou 16:12:18
*** REMOTE GEMS ***
bimyou_segmenter (1.2.0, 1.1.1, 1.0.1)
imyou (1.3.1, 1.3.0, 1.2.0, 1.1.3, 1.1.2, 1.1.1, 1.1.0, 1.0.0)
imyouというgem名なのだが、bimyou_segmenterまで引っかかった。bimyou_segmenterという名前なので仕方がない…。なので、imyouで先頭一致する行を抽出した。
$ gem list --prerelease --all --remote imyou | grep -E "^imyou"
imyou (1.3.1, 1.3.0, 1.2.0, 1.1.3, 1.1.2, 1.1.1, 1.1.0, 1.0.0)
あとは、バージョン情報だけ抽出し(括弧の中のみ)、現在のgemのバージョンの文字列があるかどうかチェックした。
gem list --prerelease --all --remote imyou \
| grep -E "^imyou" \
| sed -e "s/^.*(\(.*\)).*\$/\1/" \
| grep -q -v "`bundle exec ruby -e "print Imyou::VERSION"`"
result=$?
if [ $result = 0 ]; then
bundle exec rake build
bundle exec rake release
fi
細かいことを言うと、1.3.1をリリースする前に1.3.1.preとかがあると、ヒットしてしまって正式リリースがこのワークフローでは行えない。しかし、文字列を分割して配列に入れて完全一致するバージョンの有無を調べるのがbashだと面倒そうだったので、こうしている。簡単な方法があれば採用したい…。
あと、gem名とバージョン情報がハードコーディングになっている。多分、これがうまく取れないからorbsの作者も、gem/nameとgem/versionというファイルを保存するという苦肉の策をしているのだろう…。ここをエレガントに取得する方法があったら知りたい。
ワークフローに組み込む
テストが通ったらrake releaseする(ただしmasterブランチのみ)とした。
workflows:
build:
jobs:
- test:
name: 'Ruby 2.4.5-Rails 4.2'
ruby_version: '2.4.5'
rails_version: '4.2'
- test:
name: 'Ruby 2.5.5-Rails 4.2'
ruby_version: '2.5.5'
rails_version: '4.2'
- test:
name: 'Ruby 2.6.2-Rails 4.2'
ruby_version: '2.6.2'
rails_version: '4.2'
- release:
requires:
- 'Ruby 2.4.5-Rails 4.2'
- 'Ruby 2.5.5-Rails 4.2'
- 'Ruby 2.6.2-Rails 4.2'
filters:
branches:
only: master
実行したところ、成功!
releaseジョブの中身を確認しても、ちゃんとpushできていた。
gem list --prerelease --all --remote imyou \
| grep -E "^imyou" \
| sed -e "s/^.*(\(.*\)).*\$/\1/" \
| grep -q -v "`bundle exec ruby -e "print Imyou::VERSION"`"
result=$?
if [ $result = 0 ]; then
bundle exec rake build
bundle exec rake release
fi
imyou 1.3.1 built to pkg/imyou-1.3.1.gem.
imyou 1.3.1 built to pkg/imyou-1.3.1.gem.
Tagged v1.3.1.
Pushed git commits and tags.
Pushed imyou 1.3.1 to rubygems.org
課題
ハードコーディングをなんとかしたい(できればorbs化ができそう)
- SSH鍵の登録が作業のハードルが高いと思う。API Tokenにできればいいが、rake releaseで使われるのがSSH鍵のほうだろうから、難しそうにも思う。
追記
ハードコーディングは、なんとかなった。
Before
gem list --prerelease --all --remote imyou \
| grep -E "^imyou" \
| sed -e "s/^.*(\(.*\)).*\$/\1/" \
| grep -q -v "`bundle exec ruby -e "print Imyou::VERSION"`"
result=$?
if [ $result = 0 ]; then
bundle exec rake build
bundle exec rake release
fi
After
- gemspecファイルを探し出す
ruby -e
内で*.gemspecをロードして、gem名とバージョンを出力して変数に入れる
- ハードコーディングしていた箇所をそれらの変数で置き換える
set +e
filename=$(for n in *; do printf '%s\n' "$n"; done | grep gemspec)
gem_name=`ruby -e "require 'rubygems'; spec = Gem::Specification::load('${filename}'); puts spec.name"`
gem_version=`ruby -e "require 'rubygems'; spec = Gem::Specification::load('${filename}'); puts spec.version"`
gem list --prerelease --all --remote $gem_name \
| grep -E "^${gem_name}" \
| sed -e "s/^.*(\(.*\)).*\$/\1/" \
| grep -q -v $gem_version
result=$?
if [ $result = 0 ]; then
bundle exec rake build
bundle exec rake release
fi
ということで、これをOrb化したいなぁ〜。