コードのデプロイ作業を毎回手動で行うのは時間がかかり、ミスも起きやすいです。バッチファイル(.bat)を使えば「git pull → バックアップ → コピー → サービス再起動 → ログ記録」の一連の流れをワンクリックで自動化できます。この記事ではシンプルな基本実装から、ROBOCOPY・エラー処理・ステージング/本番切り替えまで実践的に解説します。
- git pull + xcopy によるシンプルな自動デプロイの基本構成
- ROBOCOPY を使った差分コピー・ミラーリングデプロイ
- デプロイ前に自動バックアップを取る方法
- ERRORLEVEL でエラー時に処理を中断する安全なデプロイ
- サービス停止→デプロイ→サービス起動 のフルフロー
- 引数でステージング/本番環境を切り替える方法
デプロイ方式の比較
| 方式 | コマンド | 差分コピー | ミラーリング | 推奨場面 |
|---|---|---|---|---|
| xcopy | xcopy /E /Y | ×(全コピー) | × | シンプルな社内ツール・小規模デプロイ |
| ROBOCOPY | robocopy /MIR | ◎ | ◎ | ファイル数が多い・差分のみ転送したい |
| WinSCP(SCP/SFTP) | winscp /script | ○ | ○ | リモートサーバーへのアップロード |
| robocopy /E /XO | robocopy /E /XO | ◎(更新のみ) | × | 削除せず新しいファイルだけ追加したい |
基本実装:git pull + xcopy + ログ記録
最もシンプルな自動デプロイの形です。設定変数を先頭にまとめることで、環境ごとのパス変更が1箇所で済みます。
@echo off
setlocal
REM ===== 設定(環境に合わせて変更) =====
set REPO_DIR=C:\projects\myapp
set DEPLOY_DIR=C:\inetpub\wwwroot\myapp
set LOG_FILE=C:\deploy_logs\deploy.log
REM ===== ログ開始 =====
echo =============================== >> %LOG_FILE%
echo [開始] %DATE% %TIME% >> %LOG_FILE%
REM ===== 最新コードを取得 =====
cd /d %REPO_DIR%
git pull origin main >> %LOG_FILE% 2>&1
if %ERRORLEVEL% neq 0 (
echo [ERROR] git pull に失敗しました >> %LOG_FILE%
echo デプロイ失敗:git pull エラー
pause & exit /b 1
)
REM ===== ファイルをデプロイ先にコピー =====
xcopy /E /Y /I "%REPO_DIR%\*" "%DEPLOY_DIR%\" >> %LOG_FILE% 2>&1
if %ERRORLEVEL% neq 0 (
echo [ERROR] xcopy に失敗しました >> %LOG_FILE%
echo デプロイ失敗:コピーエラー
pause & exit /b 1
)
echo [完了] %DATE% %TIME% >> %LOG_FILE%
echo =============================== >> %LOG_FILE%
echo デプロイ完了しました。
pause
endlocal
環境ごとに変わる値(REPO_DIR・DEPLOY_DIR・LOG_FILE)は冒頭の
set にまとめると保守しやすくなります。各コマンドの後に if %ERRORLEVEL% neq 0 (...) でエラーチェックすることで、git pull が失敗したまま壊れたファイルをデプロイするリスクを防げます。ROBOCOPY でミラーリングデプロイ
ファイル数が多い場合や、更新ファイルだけを転送したい場合は xcopy よりROBOCOPY が適しています。/MIR オプションで送信元と送信先を完全に同期(削除も含む)できます。ROBOCOPYの詳細な使い方はバッチファイルでフォルダをコピーする方法も参照してください。
@echo off
setlocal
set SRC=C:\projects\myapp
set DST=C:\inetpub\wwwroot\myapp
set LOG=C:\deploy_logs\robocopy.log
REM /MIR : ミラーリング(送信元にないファイルをDSTから削除)
REM /R:3 : 失敗時に3回リトライ
REM /W:5 : リトライ間隔5秒
REM /NP : 進捗パーセント非表示(ログをすっきり)
REM /LOG+: : ログを追記
robocopy "%SRC%" "%DST%" /MIR /R:3 /W:5 /NP /LOG+:"%LOG%"
REM ROBOCOPYの終了コード: 0-7は正常、8以上はエラー
if %ERRORLEVEL% geq 8 (
echo [ERROR] ROBOCOPY に失敗しました(ERRORLEVEL=%ERRORLEVEL%)
exit /b 1
)
echo デプロイ完了(ROBOCOPY)
endlocal
ROBOCOPYの終了コードは 0〜7 が正常、8以上がエラーです。通常のコマンドと違い、
0以外=エラー ではありません。if %ERRORLEVEL% geq 8 でエラー判定してください。デプロイ前に自動バックアップを取る
本番環境へのデプロイ前には、現行ファイルのバックアップが安全です。日付付きフォルダに退避することでロールバックが容易になります。
@echo off
setlocal
set DEPLOY_DIR=C:\inetpub\wwwroot\myapp
set BACKUP_BASE=C:\deploy_backups
REM タイムスタンプ付きバックアップフォルダ名を生成
REM 例: backup_20250319_143022
for /f "tokens=1-3 delims=/" %%a in ("%DATE%") do (
set YDATE=%%c%%a%%b
)
for /f "tokens=1-3 delims=:." %%a in ("%TIME: =0%") do (
set YTIME=%%a%%b%%c
)
set BACKUP_DIR=%BACKUP_BASE%\backup_%YDATE%_%YTIME%
echo バックアップ先: %BACKUP_DIR%
robocopy "%DEPLOY_DIR%" "%BACKUP_DIR%" /E /NP
if %ERRORLEVEL% geq 8 (
echo [ERROR] バックアップに失敗しました
pause & exit /b 1
)
echo バックアップ完了: %BACKUP_DIR%
REM バックアップ成功後にデプロイ開始
echo デプロイ処理を開始します...
endlocal
上記のままでは古いバックアップが溜まり続けます。バックアップ後に古いフォルダを削除する処理を追加するか、世代数(5世代など)を超えたら削除する仕組みを組み込みましょう。詳しくはログローテーション完全ガイドの手法が参考になります。
ERRORLEVEL でエラー時に処理を中断する
各コマンドの結果を ERRORLEVEL で確認し、失敗したら処理を中断することが安全なデプロイの基本です。エラーが起きても次の処理が続行されてしまうのは危険です。
@echo off
setlocal
REM エラー時に即座に処理を中断するサブルーチン
:check_error
if %ERRORLEVEL% neq 0 (
echo [ERROR] %~1 に失敗しました(コード: %ERRORLEVEL%)
echo %DATE% %TIME% [FAIL] %~1 >> C:\deploy_logs\deploy.log
pause
exit /b 1
)
goto :eof
:main
cd /d C:\projects\myapp
git fetch origin >> C:\deploy_logs\deploy.log 2>&1
call :check_error "git fetch"
git pull origin main >> C:\deploy_logs\deploy.log 2>&1
call :check_error "git pull"
robocopy "C:\projects\myapp" "C:\inetpub\wwwroot\myapp" /MIR /R:3 /W:5 /NP /LOG+:"C:\deploy_logs\robocopy.log"
if %ERRORLEVEL% geq 8 call :check_error "robocopy"
echo [SUCCESS] デプロイ完了 %DATE% %TIME% >> C:\deploy_logs\deploy.log
echo すべての処理が正常に完了しました。
endlocal
goto :eof
call :main
:check_error サブルーチンでコードを簡潔に各コマンド後に長い if 文を書く代わりに、エラーチェック専用のサブルーチンに切り出すとスクリプト全体がすっきりします。
call :check_error "処理名" の1行で済み、どの処理で失敗したかもログに残ります。エラー処理の詳細はエラー時に処理を中断・終了する方法を参照してください。サービス停止 → デプロイ → サービス起動 のフルフロー
IIS・Tomcat・アプリケーションサーバーなど、稼働中のサービスを止めてからデプロイする場面では以下のフローで安全に実行できます。
@echo off
setlocal
set SERVICE_NAME=W3SVC
set SRC=C:\projects\myapp
set DST=C:\inetpub\wwwroot\myapp
set LOG=C:\deploy_logs\deploy.log
echo [1/4] サービス停止: %SERVICE_NAME% >> %LOG%
net stop %SERVICE_NAME% >> %LOG% 2>&1
if %ERRORLEVEL% neq 0 (
echo [ERROR] サービス停止に失敗しました >> %LOG%
pause & exit /b 1
)
echo [2/4] デプロイ開始 >> %LOG%
robocopy "%SRC%" "%DST%" /MIR /R:3 /W:5 /NP /LOG+:"%LOG%"
if %ERRORLEVEL% geq 8 (
echo [ERROR] ROBOCOPYに失敗 - サービスを再起動します >> %LOG%
net start %SERVICE_NAME% >> %LOG% 2>&1
exit /b 1
)
echo [3/4] サービス起動: %SERVICE_NAME% >> %LOG%
net start %SERVICE_NAME% >> %LOG% 2>&1
if %ERRORLEVEL% neq 0 (
echo [ERROR] サービス起動に失敗しました >> %LOG%
pause & exit /b 1
)
echo [4/4] デプロイ完了: %DATE% %TIME% >> %LOG%
echo デプロイが正常に完了しました。
endlocal
ROBOCOPY失敗時のエラーブロック内でも
net start を呼んでいます。デプロイが失敗したままサービスが停止しているとサービス停止障害になるため、エラー時でも必ずサービスを再起動してから終了してください。サービス制御の詳細はWindowsサービス起動・停止の完全ガイドを参照してください。引数でステージング/本番環境を切り替える
同じスクリプトをステージングと本番で使い回せるよう、引数(%1)で環境を切り替える構成が便利です。
@echo off
setlocal
REM 使い方:
REM deploy.bat staging → ステージング環境にデプロイ
REM deploy.bat prod → 本番環境にデプロイ
if "%1"=="staging" (
set TARGET=\\staging-server\wwwroot\myapp
set ENV_NAME=ステージング
) else if "%1"=="prod" (
set TARGET=\\prod-server\wwwroot\myapp
set ENV_NAME=本番
) else (
echo 使い方: %~nx0 [staging^|prod]
exit /b 1
)
echo ===================================
echo デプロイ先: %ENV_NAME% (%TARGET%)
echo ===================================
REM 本番デプロイは確認を求める
if "%1"=="prod" (
set /p CONFIRM=本番にデプロイします。続行しますか?[y/N]:
if /i not "%CONFIRM%"=="y" (
echo キャンセルしました。
exit /b 0
)
)
robocopy "C:\projects\myapp" "%TARGET%" /MIR /R:3 /W:5 /NP
if %ERRORLEVEL% geq 8 (
echo [ERROR] デプロイに失敗しました
exit /b 1
)
echo [完了] %ENV_NAME%環境へのデプロイが完了しました。
endlocal
set /p CONFIRM=... で確認を求めることで、deploy.bat prod を誤って実行した際の事故を防げます。「y」以外はキャンセルするため、エンターキーを押しただけでは実行されません。ログ記録と管理の工夫
デプロイログは後から問題を追跡するための重要な情報です。タイムスタンプ・実行コマンド・終了コードを記録する習慣をつけましょう。
@echo off setlocal set LOG=C:\deploy_logs\deploy.log REM タイムスタンプ付きでログに書く関数的な使い方 REM call :log "メッセージ" goto :main :log echo %DATE% %TIME% %~1 >> %LOG% echo %~1 goto :eof :main call :log "===== デプロイ開始 =====" git pull origin main >> %LOG% 2>&1 call :log "git pull 終了コード: %ERRORLEVEL%" robocopy "C:\projects\myapp" "C:\inetpub\wwwroot\myapp" /MIR /R:2 /W:3 /NP /LOG+:"%LOG%" call :log "ROBOCOPY 終了コード: %ERRORLEVEL%" call :log "===== デプロイ終了 =====" endlocal goto :eof
ログファイルが溜まり続ける場合は、
forfiles コマンドで古いログを自動削除できます。forfiles /p C:\deploy_logs /m *.log /d -30 /c "cmd /c del @file"(30日以上前のログファイルを削除)
詳しくはログローテーション完全ガイドを参照してください。
まとめ
バッチファイルで自動デプロイを構成する際のポイントをまとめます。
- 設定変数(パス・サービス名)は先頭にまとめて保守しやすくする
- 各コマンド後に
ERRORLEVELを確認し、失敗時は即中断する - xcopy は簡単・ROBOCOPY は高機能(差分・ミラー・リトライ)
- デプロイ前に日付付きフォルダでバックアップを取る
- サービス停止中にデプロイし、失敗時もサービスを必ず再起動する
- 引数で環境(staging/prod)を切り替え、本番は確認を挟む
- タイムスタンプ付きログを記録し、後から問題を追跡できるようにする
関連記事: バッチファイルでフォルダをコピーする方法 / Windowsサービス起動・停止の完全ガイド / WinSCPでSFTPファイル転送する方法
よくある質問(FAQ)
git config --global credential.helper wincred でWindows資格情報を使うか、SSHエージェントで鍵を登録してください。タスクスケジューラから実行する場合は「最上位の特権で実行」にチェックを入れ、同じユーザーのSSH設定を確認してください。net use \\server\share /user:USERNAME PASSWORD でマウントしてからコピーしてください。powershell -Command "Send-MailMessage -To 'to@example.com' -From 'from@example.com' -Subject 'デプロイ完了' -SmtpServer 'smtp.example.com'"をバッチの最後に呼ぶか、Windowsタスクスケジューラの「完了時の操作」でメール送信を設定してください。net stop サービス名)してからコピーし、コピー後に再起動する手順が正攻法です。サービス停止が難しい場合はVSSシャドウコピー(vssadmin)を使う方法もありますが、設定が複雑です。詳しくはWindowsサービス起動・停止の完全ガイドを参照してください。

