Dockerを使った複数人開発のプロジェクトで、Githubを使ってコード管理をしている場合に、自分のローカルのブランチで修正中の内容を間違ってビルドし、それを本番環境のイメージとしてプッシュしてしまうといったミスが発生するリスクがあります。
また、ファイルを変更してコミットしていない状態でビルドしてしまうといったうっかりミスが発生するリスクもあります。
これらは、Gitのコミット履歴を確認して、現在イメージのビルドを実行しようとしているブランチのコミットが、本番用のブランチの最新のコミットのハッシュ値と一致しているかを確認することと、現在のブランチに作業中の内容が無いかを確認すれば、ミスを防ぐことができます。
ですが、毎回確認するのは非常に面倒です。(そもそも毎回きちんと確認するぐらい落ち着いた人たちなら、違うブランチやコミットがあっていないのにビルドしてしまうこともないでしょう、、)
ここではそんなミスを防ぐための、イメージのビルド前にコミット履歴や変更内容の有無を自動で確認してくれる処理の作り方を解説しています。
処理の概要
作成する処理は大きく次の3つのステップになります。
これらの処理をシェルスクリプト(.sh)に記述します。
コードの全体像
コードの全体像は以下のようになります。
alias build-image=check-build-image
function check-build-image() {
git fetch origin main
ORIGIN_MAIN=$(git show-ref origin/main -s)
CURRENT=$(git rev-parse HEAD)
if [[ $ORIGIN_MAIN != $CURRENT ]]; then
echo 'origin/main 最新のコミットが一致していないのでビルドを中止します';
return 1
else
echo 'git diffで確認します。修正中でコミット前のファイルがある場合はビルドできません。';
git diff --exit-code && \
git diff --staged --exit-code && \
docker-compose build prd
fi
}
以下でそれぞれの内容を解説しています。
リモートレポジトリのmainブランチのコミット番号を取得し、現在のブランチのコミット番号と比較する
コード
まずは、リモートレポジトリのmainブランチのコミット番号を取得し、現在のブランチのコミット番号と比較する処理です。
コードの以下の部分にあたります。
git fetch origin main
ORIGIN_MAIN=$(git show-ref origin/main -s)
CURRENT=$(git rev-parse HEAD)
if [[ $ORIGIN_MAIN != $CURRENT ]]; then
echo 'origin/main 最新のコミットが一致していないのでビルドを中止します';
return 1
else
リモートレポジトリのmainブランチの情報を取得
まず、「git fetch origin main」でリモートレポジトリ名「origin」の「main」ブランチの最新のコミット状況を取得してきます。
リモートレポジトリ名が「origin」以外の場合や、本番用のブランチ名が「main」以外の場合は、適宜変更してください。
なお、git fetchなので、情報を取得してくるのみでマージはしません。
本番ブランチの最新のコミットを取得する
「git fetch origin main」コマンドを実行したことにより、ローカルのリモート追跡ブランチ「origin/main」のコミットはリモートレポジトリの最新のコミットに置き換わっています。
この状態で、mainブランチの最新のコミットを取得します。コミット番号を取得するには、「git show-ref」コマンドを使います。
取得したコミット番号を変数「ORIGIN_MAIN」に代入します。
ORIGIN_MAIN=$(git show-ref origin/main -s)
git show-refコマンドとは何か?
「git show-ref」コマンドとは、ローカルにある、リモート追跡ブランチも含めたすべてのブランチのコミット番号の一覧を表示するコマンドです。
$ git show-ref
「ref」とはGitにおける参照名のことです。git logしたときに表示される「main」「origin/main」などが参照名です。
実例
$ git show-ref
5c5a6d4e7991acd73a834c3a888d43cc35fecac4 refs/heads/main
f95d09eb018a3f175b803cf53b01aac3de0b64a refs/heads/test
5ca6d4e7991acd73a834c3a888d43cc35fecac4 refs/remotes/origin/HEAD
5c5a6d4e7991acd73a834c3a888d43cc35fecac4 refs/remotes/origin/main
7000bfd2b999eb57f1c74a354a9a7d48580a90f refs/remotes/origin/test
ブランチを指定する
「git show-ref」コマンドの引数にブランチ名を記載すると、そのブランチ名に一致する情報のみを取得することができます。
$ git show-ref [ブランチ名]
例えば、「origin/main」とした場合、「origin/main」ブランチの情報のみが表示されます。
実例
$ git show-ref origin/main
5c5a6d4e7991acd73a834c3a888d43cc35fecac4 refs/remotes/origin/main
コミット番号のみを表示する
「git show-ref」コマンドのオプションで「-s」を指定すると、ブランチ名(参照名)は表示せず、コミット番号のみ表示します。
$ git show-ref -s
上記の「ブランチ名の指定」と組み合わせれば、指定したブランチのコミットのコミット番号のみを表示します。
実例
$ git show-ref origin/main -s
5c5a6d4e7991acd73a834c3a888d43cc35fecac4
現在のブランチのコミット番号を取得する
続いて、現在のブランチのコミット番号を取得し変数「CURRENT」に代入します。
CURRENT=$(git rev-parse HEAD)
git rev-parseコマンドとは何か?
「git rev-parse」コマンドとは、指定したコミットのコミット番号を取得するコマンドです。
$ git rev-parse <コミット>
コミットの指定方法は、いくつかありますが「ブランチ名」や「HEAD」または「@」を使うことが一般的です。
git rev-parseで現在のブランチの最新のコミットを取得する
git rev-parseコマンドを使って、現在のブランチの最新のコミットを取得するには、引数に「HEAD」または「@」を指定します。
$ git rev-parse HEAD
実例
$ git rev-parse HEAD
5c5a6d4e7991acd73a834c3a888d43cc35fecac4
git logで現在のブランチの最新のコミットを確認すると、コミットのハッシュ値が一致していることがわかります。
$ git log
commit 5c5a6d4e7991acd73a834c3a888d43cc35fecac4 (HEAD -> main, origin/main, origin/HEAD)
コミット番号が一致しているか確かめる
リモートレポジトリの本番用のブランチの最新のコミット番号と、現在のブランチの最新のコミット番号が取得できたのでそれを比較し、一致していれば次の処理に進み、一致していなければメッセージを表示して、処理を強制終了する処理を書きます。
コード
if [[ $ORIGIN_MAIN != $CURRENT ]]; then
echo 'origin/main 最新のコミットが一致していないのでビルドを中止します';
return 1
else
ifの条件式
シェルファイルの中でif文を使った条件式は以下のようになります。
if [[ 条件式 ]]; then 処理 fi
if文の条件式でカッコを二重[[ ]]
にすると、中に文字列を記述するときにダブルクォーテーションを省略することができるようになります。
[ ]
の場合はダブルクォーテーションが必須です。
ここでは以下のようにしています。
if [[ $ORIGIN_MAIN != $CURRENT ]]; then
このため、モートレポジトリの本番用のブランチの最新のコミット番号と、現在のブランチの最新のコミット番号が一致していなければ、thenの処理に進む、そうでなければこの処理はパスするという意味になります。
強制終了の記述
if文の処理をエラーとして強制終了する場合は、戻り値で1を返します。
return 1
これはC言語の書き方で、「return 0」を返せば正常終了、0以外なら異常終了という意味になります。
現在のブランチに作業中の内容が無いか確認する
本番のリモートレポジトリのブランチの最新のコミット番号と、現在のブランチの最新のコミット番号が一致していることが確認できたら、次に「現在のブランチに作業中の内容が無いか確認する」処理へと移ります。
コード
if文の中の続きとして以下のコードを記述します。
else
echo 'git diffで確認します。修正中でコミット前のファイルがある場合はビルドできません。';
git diff --exit-code && \
git diff --staged --exit-code && \
docker-compose build prd
fi
ステージ前(git add前)のファイル差分を確認する
ステージ前(git add前)のファイル差分を確認し、差分がなければ次の処理へと移り、差分があれば処理を強制終了するには、git diffコマンドの「–exit-code」オプションを使います。
git diff --exit-code
「git diff」はステージ(git add)前の変更内容の詳細を表示するコマンドです。(ワークツリーと最新のコミットを比較)
「–exit-code」オプションをつけることで、「git diff」コマンドを実行し、差分がある場合は「git diff」の内容が画面に出力し、処理を強制終了します。
差分がなければ、次の処理に進みます。
ステージ後(git add後・コミット前)のファイル差分を確認する
次に、ステージ後(git add後・コミット前)のファイル差分を確認し、差分がなければ次の処理へと移り、差分があれば処理を強制終了する処理を記述します。
git diffコマンドの「–staged」と「–exit-code」オプションを使います。
git diff --staged --exit-code
「git diff –staged」はステージ後(git add後・コミット前)の変更内容の詳細を表示するコマンドです。(ステージと最新のコミットを比較)
「–exit-code」オプションをつけることで、「git diff –staged」コマンドを実行し、差分がある場合は「git diff –staged」の内容が画面に出力し、処理を強制終了します。
差分がなければ、次の処理に進みます。
イメージのビルド行う
全ての確認をパスしたら、最後にイメージのビルドを実行します。
docker-compose build <サービス名>
docker-compose buildコマンドの引数で、イメージを作成(ビルド)したいサービス名を記載します。
ここでは、prdというサービスのイメージを作成するため以下のようにします。
docker-compose build prd
コミットの状況をきちんと確認した上でビルドを処理の記述ができました。
作成した関数をエイリアスとして登録する
最後に、作成した関数を簡単に呼び出すためにエイリアスとして記述します。
以下のようにすることで、指定した処理を、指定したエイリアス名で登録し、エイリアス名のみで処理を実行することができるようになります。
alias エイリアス名=処理
ここでは、「build-image」という名前をつけ、処理で「check-build-image」という関数を指定します。
alias build-image=check-build-image
エイリアスとして登録するためには、シェルファイルをリロードして変更を読み込む必要があります。
$ sources aliases.sh
以上で設定は完了です。
コマンドラインで「build-image」を実行すれば、指定した処理が走り、コミットが合っていて、変更内容がなければイメージのビルドが開始します。
コマンドの実行例
実際にコマンドを実行する手順は以下のようになります。
シェルファイルにコードを記述し保存する
ここでは、aliases.shというファイルに保存することにします。
作成する場所は、プロジェクトのディレクトリ直下です。
alias build-image=check-build-image
function check-build-image() {
git fetch origin main
ORIGIN_MAIN=$(git show-ref origin/main -s)
CURRENT=$(git rev-parse HEAD)
if [[ $ORIGIN_MAIN != $CURRENT ]]; then
echo 'origin/main 最新のコミットが一致していないのでビルドを中止します';
return 1
else
echo 'git diffで確認します。修正中でコミット前のファイルがある場合はビルドできません。';
git diff --exit-code && \
git diff --staged --exit-code && \
docker-compose build prd
fi
}
シェルファイルをリロードする
シェルファイルに変更を加えた場合はその内容を読み込むためにリロードする必要がります。
$ sources aliases.sh
リロードしないと変更前の状態で実行されてしまいます。なお、一度も読み込んでいない場合はエラーになります。
コマンドの実行
コマンドラインでエイリアスを実行できる状態になったので、「build-image」を実行します。
修正が残っている場合
ステージ前の変更が残っている場合は以下のようになります。
$ build-image
From github.com:test/test-repository
* branch main -> FETCH_HEAD
git diff をチェックしてビルドします。コミットされてなければビルドできません
diff --git a/aliases.sh b/aliases.sh
index fd4c48b..34b45a2 100644
--- a/aliases.sh
+++ b/aliases.sh
@@ -54,7 +54,7 @@ function build-image() {
git fetch origin main
ORIGIN_MAIN=$(git show-ref origin/main -s)
CURRENT=$(git rev-parse HEAD)
- if [[ "$ORIGIN_MAIN" != "$CURRENT" ]]; then
+ if [[ $ORIGIN_MAIN != $CURRENT ]]; then
echo 'origin/main と一致していないのでビルドできません';
return 1
else
$
処理が強制終了されて終わります。イメージのビルドは行われません。
修正が残っていない場合
コミットも合っていて、修正や変更もない場合は以下のようにビルドが開始します。
$git push origin main
$ git status
On branch main
nothing to commit, working tree clean
$ build-image
From github.com:test/test-repository
* branch main -> FETCH_HEAD
git diff をチェックしてビルドします。コミットされてなければビルドできません
Building lcl
Step 1/6 : FROM image-name:latest
---> 52f4edeea07d
Step 2/6 : RUN curl -sL https://deb.nodesource.com/setup_14.x | bash -
(省略)
Successfully built 39d7f6a41d5f
Successfully tagged image-name:latest
docker-compose buildが実行され、イメージが作成されました。