notebook

都内でWEB系エンジニアやってます。

git hook を使って静的サイトを自動でデプロイする

git hookを使って更新ファイルの内容次第でpush前にテストやデプロイを行うようにします

middlemanで作成したプロジェクトで、静的サイト用のファイルがpushされようとしているときにhookを使ってデプロイ作業も行ってしまおうというものです

実際に行うプロジェクトではrubyのスクリプトもあるのでそちらはlib以下の更新ファイルが合った場合はhookでテストを実行するというような感じにしました

まぁ小規模なプロジェクトならこれで良いんじゃない?というような内容かなと思います

git hooks

Git - Git フック

.git/hoooks に実行可能なファイルを適切な名前で置くことで読み取ってくれる

クライアント側で動作するクライアントサイドフック、リポジトリ側で行うサーバサイドフックがある

今回はrubyの実行環境を前提としたコマンドを用いたいためクライアントサイドフックを用います

いくつか探してみたところ、pre-pushが今回の要件に合いそうでした

rubyスクリプトと、middlemanで作ったサイトを例にやってみます

swfz/darts

スクリプト

Gitのフックの説明と挙動の検証 を参考にどんな値が標準入力で渡されるのか確認したところ

qiita.com

pushする対象のCommitIDが入力されるようなのでそれを使って下記のように実装しました

  • .git/hooks/pre-push
  
#!/bin/bash

publish_files=("data/" "source")
lib_file_prefix=("lib" "darts.rb" "helpers" "spec")

echo_line() {
  echo "${0##*/} ********************************************"
}

publish() {
  echo "rake publish --------------------"
  rake publish
}

spec() {
  echo "rspec spec/* --------------------"
  rspec spec/*
  [ "$?" -ne 0 ] && echo "Test Failed!!" && echo_line && exit 1
}

is_commited() {
  start_id=$1
  end_id=$2
  filenames=(${@:3})

  for filename in "${filenames[@]}"
  do
    count=$( git log --name-only ${start_id}..${end_id} | grep -c ${filename} )
    echo "${filename} : ${count}" 1>&2
    [ "${count}" -gt 0 ] && echo 1 && return
  done
  echo 0
}

echo_line

while read local_ref local_sha remote_ref remote_sha; do
  echo $local_ref
  echo $local_sha
  echo $remote_ref
  echo $remote_sha
  # master へのpushのみ適用
  if [ "$local_ref" == "refs/heads/master" ] && [ "$remote_ref" == "refs/heads/master" ]; then
    if [ -n "${remote_sha}" ] && [ -n "${local_sha}" ]; then
      # lib 以下に更新があるか(テスト実行対象か)
      commited_lib=$( is_commited ${remote_sha} ${local_sha} ${lib_file_prefix[@]} )
      echo "lib commited: ${commited_lib}"
      [ "${commited_lib}" == 1 ] && spec

      # source以下に更新があるか(midleman deployの対象か)
      commited_publish=$( is_commited ${remote_sha} ${local_sha} ${publish_files[@]} )
      echo "source commited: ${commited_publish}"
      [ "${commited_publish}" == 1 ] && publish
    fi
  fi
  echo_line
done

exit 0

最終的にできるようになったのは下記

lib以下に更新があった場合

  • テストを走らせる(rspec)
  • 失敗したらpush拒否

source以下に更新があった場合

  • gh-pagesにpush(middlemanのdeploy)

まとめ

これでpushするだけで自動でテストやデプロイまでやってくれるようになりました

静的サイトとか小規模なプロジェクトならこれだけでも十分な気がします