課題:「手動アップロード」という悪夢
午前2時に致命的なバグを修正し終えたとします。寝る代わりに、FileZillaを開いて適切なフォルダを探してファイルをドラッグ&ドロップしたり、サーバーにSSH接続して git pull を叩いてサービスを再起動したりしなければなりません。これらの繰り返し作業は退屈なだけでなく、誤ってファイルを上書きしたり再起動を忘れたりといった ミス を招きやすいものです。
フリーランス時代、ランディングページや小さなTelegramボットをデプロイする際、GitHub ActionsやJenkinsの設定にコードを書く以上の時間がかかることがよくありました。そこで「Pushするだけで完了」という解決策が必要になり、Git Push-to-Deploy と Bareリポジトリ の組み合わせが完璧な救世主であることに気づきました。
なぜBareリポジトリを使うのか?
通常の git init で作成したプロジェクトには、隠しディレクトリ .git と、実際にコードを編集する「ワーキングディレクトリ(Working Directory)」が存在します。しかし、Gitはデータの競合を防ぐため、アクティブなワーキングディレクトリへの直接のPushをブロックします。
Bareリポジトリ は、ワーキングディレクトリを完全に排除することでこの問題を解決します。これは履歴とオブジェクトを保持する「保管庫」としての役割のみを果たします。このリポジトリを中継地点として使い、Hook(フック)を利用して実際のアプリケーション実行ディレクトリにコードを自動的に展開します。
ステップ1:サーバー環境の設定
Ubuntu VPS を使用し、Node.jsアプリを /var/www/my-app にデプロイすると仮定します。まず、コード実行用のディレクトリとリポジトリ用のディレクトリを作成します。
# アプリケーション実行ディレクトリの作成
sudo mkdir -p /var/www/my-app
sudo chown $USER:$USER /var/www/my-app
# Bareリポジトリの作成
mkdir -p ~/repos/my-app.git
cd ~/repos/my-app.git
git init --bare
上記のコマンド実行後、my-app.git ディレクトリ内には hooks や info などの設定ファイルのみが生成されます。ソースコードが見当たらなくても、それがBareリポジトリの特徴なので心配ありません。
ステップ2:Post-receive Hookの有効化
Hook(フック)とは、Gitのイベント発生時に自動実行されるスクリプトです。post-receive スクリプトは、Pushコマンドによってサーバーがデータを受信した直後に起動します。
スクリプトファイルを作成します:
nano ~/repos/my-app.git/hooks/post-receive
以下の内容を貼り付けます:
#!/bin/bash
TARGET="/var/www/my-app"
GIT_DIR="$HOME/repos/my-app.git"
while read oldrev newrev ref
do
# mainブランチにPushされた場合のみデプロイを実行
if [[ $ref =~ .*/main$ ]];
then
echo "mainブランチを受信しました。デプロイを開始します..."
git --work-tree=$TARGET --git-dir=$GIT_DIR checkout -f main
# 追加のタスクを実行
cd $TARGET
npm install --production
pm2 restart my-app || pm2 start app.js --name my-app
echo "デプロイが完了しました!"
else
echo "ブランチ $ref は自動デプロイに対応していません。"
fi
done
スクリプトを実行可能にするための権限を付与します:
chmod +x ~/repos/my-app.git/hooks/post-receive
ステップ3:ローカルPCからの接続
ローカルPCで、VPSのアドレスを指す新しい「remote」を追加します。これはプロジェクトごとに一度だけ行えば済みます。
# 'user'と'ip'を自分のVPSの情報に置き換えてください
git remote add deploy [email protected]:/home/user/repos/my-app.git
ステップ4:新しいワークフローを体験する
これで、コードを更新するたびに以下を入力するだけです:
git push deploy main
全プロセスは5秒以内に完了します。ターミナルにはサーバーからのログが表示され、npm install が実行されたか、pm2 が再起動したかを正確に把握できます。手動での作業に比べ、毎日少なくとも15〜20分は節約できるはずです。
実践的なアドバイスと注意点
多くのクライアント案件でデプロイを行ってきた経験から、よくあるミスを防ぐための3つの重要なポイントを挙げます:
- 書き込み権限 (Permissions): SSHユーザーが
/var/www/my-appへの書き込み権限を持っている必要があります。Permission deniedエラーが発生した場合は、ステップ1のchownコマンドを再確認してください。 - 不要なファイルのクリーンアップ:
checkout -fは古いファイルを上書きしますが、Gitから削除したファイルはサーバー上に残ります。サーバーを常にクリーンに保つには、Hookスクリプトにgit --work-tree=$TARGET --git-dir=$GIT_DIR clean -fdを追加してください。 - SSHキーによるセキュリティ: Pushのたびにパスワードを入力するのではなく、SSHキーを使用しましょう。その方が速くて安全です。
結論
この方法は、スピードと手軽さを求めるフリーランスや小規模チームに最適です。大規模プロジェクトにおける GitHub Actions のような本格的なCI/CDシステムを代替するものではありませんが、シンプルさとリソース効率の点では圧倒的に優れています。複雑なYAMLファイルの作成に午後を費やす代わりに、わずか5分で退屈な作業から自分を解放しましょう。

