約 4 ヶ月ぐらい前に、アプリにバージョンを付けて運用してみました
不具合の際に、ユーザーとの認識も合わせやすくなり開発体験も向上したので、共有したいと思います
やりたいと思った背景については長くなったので、最後にしました
最終的に人間がやることは、コミットメッセージにプレフィクスつけるだけっす
アプリのバージョニングとは
git のコード管理とは違い、アプリ自体のバージョニングです
ある区切りにおいて、バージョニング(1.0.0 → 1.0.1)を行うわけですが、アプリのバージョンを区切るのはどこが適切か迷いました
自分が担当しているプロジェクトでは、master(origin)が staging、production ブランチにマージされたタイミングで
各環境にデプロイがはしるので、master ブランチに PR がマージされた直後にしました
アプリのバージョニングをしたいと思った背景
ユーザーとの認識合わせ
ユーザーからアプリの不具合が問い合わせであった場合、事象の再現ができなかったりしないですか?
どういった状況・現象の把握も大事ですが、バージョンの共有で、更に再現性が高くしたかった
どのブランチが、ステージング環境にデプロイされているんだっけ
複数人で複数環境(dev, stg, prod 等)を同時に開発していると、本番にデプロイされる前に
ステージング環境で試して、本番にデプロイするのがほとんどだと思います
しかし、どの環境にどのブランチがデプロイされているのか Git 管理をしていても把握が面倒だと思います
そこで、web アプリなら、バージョンを画面のどこかに記載しておけば
どのバージョンが動いているのかが一目瞭然になりますね
とはいっても、バージョン管理面倒だよね
バージョンをつけようとなった時に壁になるのが下記だと思います
- バージョン上げの判断基準どうしよう
→ 何が major で、何があると patch? - 1を毎回やるの面倒
・・・・
そんなのは、判断基準を作っちゃって、全部 CI で行いましょう
やりたいこと
- アプリにバージョンを表示したい
- 区切り方どうしよう
- 意味のある何かで、バージョンを区切りたい
- 自動でバージョンを上げたい
こうしよう!
- アプリにバージョンを表示したい
package.json の version 使おう!
- 区切り方どうしよう
npm version hoge
を使用しました
- 意味のある何かで、バージョンを区切りたい
コミットメッセージのプレフィクスを判断基準
- 自動でバージョンを上げたい
- 毎回人間が区切るの面倒!機械的にやりたい → CircleCI で実行
1. アプリにバージョンを表示したい
package.json を引っ張ってくるだけです
あくまでサンプルで、どんな引っ張りかたでも構いません
AngularJS ではコントローラー(コンポーネント) → view に表示です
const packageJson = require('./package.json');
const version = packageJson.json
{
"name": "sample",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
2. 区切り方どうしよう
npm コマンドで package.json の version 区切れるって知ってました?
npm version コマンドで出来ること。
ドキュメント
例:
npm version major
# 1.0.0 => 2.0.0
npm version minor
# 1.0.0 => 1.1.0
npm version patch
# 1.0.0 => 1.0.1
他にも、premajor とかたくさんありますが、上記 3 つのシンプル運用です
3. 意味のある何かで、バージョンを区切りたい
意味のある何か:区間とバージョン上げの判断基準
判断基準
コミットメッセージにプレフィクス
プレフィクスをつけたコミットメッセージは下記を参考にしてみてください
【今日からできる】コミットメッセージに 「プレフィックス」 をつけるだけで、開発効率が上がった話
AngularJS では下記を運用しているみたいです
feat: A new feature
fix: A bug fix
docs: Documentation only changes
style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
refactor: A code change that neither fixes a bug nor adds a feature
perf: A code change that improves performance
test: Adding missing or correcting existing tests
chore: Changes to the build process or auxiliary tools and libraries such as documentation generation
new: create new files
delete: delete files
自分はこれにプラスで packages も プレフィクスに追加しました(理由後述)
自分が担当しているプロジェクトの場合
- major → フレームワークの major バージョンが上がった時
- minor → git pull したら、すぐに build が必要になりそうな範囲
- patch → git pull しても、build がすぐには必要なさそうな範囲
major は AngularJS 使っているので、そんなになさそうで、手動です!(← え)
minor か patch かは切り分けるの難しいと思いますが、プルしたら、再ビルドコマンドをしないといけないかどうかで切り分けてみました
区間
master からプルリクの通ったブランチまでのコミットメッセージ
4. 自動でバージョンを上げたい
CircleCI を使いました!
ただ、CircleCI で乗り越えるべき壁が存在します(後述します)
CI の流れ
- master にプルリクが通った直後に、バージョニングを行う
- バージョンが上がる
- 終了
まずは CircleCI の config ファイルです
version: 2.1
executors: (省略)
jobs:
versioning:
executor: container
steps:
- checkout
- attach_workspace:
at: .
- run: ./version.sh
workflows:
jobs-after-pullrequest-merged:
jobs:
- versioning:
filters:
branches:
only: master
ポイント
executors は node 系のコンテナ
npm コマンドと git コマンド使えるのであれば特になにも下準備要らないので省略です
branches: only: master を設定
これ設定すると、master ブランチにプッシュが走るタイミング(プルリク通った直後)のみにできます
version.sh というファイルを実行する
CircleCI の command もありますが、yaml の特性とかシバンとか考えたら、普通にスクリプト用意したのを実行するほうがいいです
#!/bin/bash -eu
# ①マージされた直前(current_branch_hash)のhash値を取得
current_branch_hash=$( git log --oneline --merges | awk '{print $1}' | head -1 );
# ②マージされた2つ前(pre_branch_hash)をhash値を取得
pre_branch_hash=$( git log --oneline --merges | awk '{print $1}' | head -2 | tail -n 1 );
# ③マージされた直前(current_branch_hash)からマージされた 2 つ前(pre_branch_hash)の間のコミットメッセージ内容(commit_messages)を抽出
any_minor_messages=$(git log --oneline ${pre_branch_hash}...${current_branch_hash} --grep=feat --grep=packages --grep=new)
# ④これからpushする人を設定
git config user.email "circleci@example.com"
git config user.name "circleci"
# ⑤変更差分がキャッシュに残っている場合は stashに保存
git diff --quiet || git stash;
# ⑥、③で何か取得できたら、minor、なかったら、patch
if [ ${#any_minor_messages} -ne 0 ]; then # 結果の文字列の長さが 0ではない場合
npm version minor -m "%s [ci skip]" # 0.0.0 => 0.1.0
else
npm version patch -m "%s [ci skip]" # 0.0.0 => 0.0.1
fi
# ⑦package.jsonのversionが更新されるので、push
git push
version.sh の内容
①②③
# ①マージされた直前(current_branch_hash)のhash値を取得
current_branch_hash=$( git log --oneline --merges | awk '{print $1}' | head -1 );
# ②マージされた2つ前(pre_branch_hash)をhash値を取得
pre_branch_hash=$( git log --oneline --merges | awk '{print $1}' | head -2 | tail -n 1 );
# ③マージされた直前(current_branch_hash)からマージされた 2 つ前(pre_branch_hash)の間のコミットメッセージ内容(commit_messages)を抽出
any_minor_messages=$(git log --oneline ${pre_branch_hash}...${current_branch_hash} --grep=feat --grep=packages --grep=new)
CircleCI でこの version.sh が走るタイミングが、既にプルリクがマージされた直後なので、
1 つ前と2つ前のマージされた地点のハッシュ値を取得します
git log に--grep=hoge
を渡すと、コミットメッセージを hoge で絞ってくれます。
複数渡せば、OR 検索します
つまり、
- feat(機能追加)
- packages(パッケージ追加・削除)
- new(ファイル新規作成)
のプレフィクスがあれば、any_minor_messages に何かコメントが格納されます
④
# ④これからpushする人を設定
git config user.email "circleci@example.com"
git config user.name "circleci"
これ設定しないと、git history 見た時に、名無しのごんべえが push することになっちゃいます
⑤
# ⑤変更差分がキャッシュに残っている場合は stashに保存
git diff --quiet || git stash;
変更差分をキャッシュで持っていると、npm version
が実行できないので、差分があれば、git stash しちゃいます
⑥
# ⑥、③で何か取得できたら、minor、なかったら、patch
if [ ${#any_minor_messages} -ne 0 ]; then # 結果の文字列の長さが 0ではない場合
npm version minor -m "%s [ci skip]" # 0.0.0 => 0.1.0
else
npm version patch -m "%s [ci skip]" # 0.0.0 => 0.0.1
fi
any_minor_messages に何かコメントが格納されていれば
npm version minor -m "%s [ci skip]"
含んでいなければ、
npm version patch -m "%s [ci skip]"
です
↓ こんな感じのコミットメッセージになります
-m "%s [ci skip]"
で、commit メッセージを残すことができ、%s
バージョンを表示するタグで、
[ci skip]
は、⑦ で git push をした際に、再度、CircleCI を発火させないためのメッセージです
どういうことかというと、CircleCI は、git push をトリガーにして動きます
今回の場合、master にプルリクが通ると version.sh が動くようにしているので
プルリクが通る(master に git push が動いている)
↓
CircleCI 発動
↓
version.sh が動く
↓
バージョンを上げた package.json を master に git push
↓
CircleCI 発動
↓
version.sh が動く・・・
のように永遠と繰り返さないために、[ci skip]
というコミットメッセージを使って CI を1回のみで終わらせることができます
⑦、⑥ で package.json が変更されてgit commite
されたのを master に変更をかけるため、git push
を最後に行っています
プレフィクスをつけるの自動化
今回紹介したことは、プレフィクスをつけなければ何も始まりません
コミットテンプレートを設定することで、自動でプレフィックスを設定できちゃいますね
GitKraken 編
Preference( command + , ) → Commite Template
に設定するだけで、コミット後、再度コミットテンプレ呼び起こせるようになりました
GitKraken で設定すると、VSCode も勝手に表示されるようになります