Dockerで間違ったイメージによる本番化を防ぐ方法|Gitでプロダクション用のブランチとのコミット履歴の差分や修正・編集中のファイルを確認するコマンドの作り方

docker-prograshi(プロぐらし)-kv Docker
記事内に広告が含まれていることがあります。

Dockerを使った複数人開発のプロジェクトで、Githubを使ってコード管理をしている場合に、自分のローカルのブランチで修正中の内容を間違ってビルドし、それを本番環境のイメージとしてプッシュしてしまうといったミスが発生するリスクがあります。

また、ファイルを変更してコミットしていない状態でビルドしてしまうといったうっかりミスが発生するリスクもあります。

これらは、Gitのコミット履歴を確認して、現在イメージのビルドを実行しようとしているブランチのコミットが、本番用のブランチの最新のコミットのハッシュ値と一致しているかを確認することと、現在のブランチに作業中の内容が無いかを確認すれば、ミスを防ぐことができます。

ですが、毎回確認するのは非常に面倒です。(そもそも毎回きちんと確認するぐらい落ち着いた人たちなら、違うブランチやコミットがあっていないのにビルドしてしまうこともないでしょう、、)

ここではそんなミスを防ぐための、イメージのビルド前にコミット履歴や変更内容の有無を自動で確認してくれる処理の作り方を解説しています。


処理の概要

作成する処理は大きく次の3つのステップになります。

ビルド前の確認処理の概要
  1. リモートレポジトリのmainブランチのコミット番号を取得し、現在のブランチのコミット番号と比較する。
  2. 現在のブランチに作業中の内容が無いか確認する。
  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って何?という疑問を持たれた方は下記をご参考ください。

【Git】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
合わせて読みたい

HEADと@の違いや使い方の詳細については下記をご参考ください。

【Git】HEAD~~, 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が実行され、イメージが作成されました。


タイトルとURLをコピーしました