I recently dug into windows CMD/BAT scripting while configuring a Subversion repository’s commit hooks. You know, like emailing notification on commits, enforcing non-empty log messages, allowing clients to change log messages, but only for their own commits, that kind of thing.
The repository in question is living behind a Subversion server running as a windows service on a Windows XP machine, so the easiest scripts to get running were BAT files.
Well, after digging through the Microsoft Technet command-line reference and grokking some of the finer nuances of the FOR command, I decided that I’d geek out and turn to Powershell as a more modern and expressive alternative. Only problem was, since the Subversion server process runs essentially without any environment variables, I couldn’t get it to pick up .ps1 files as valid hook-scripts so I had to find an alternative.
What I came up with was a way to pass hook-script context from a BAT script into Powershell to do the actual work and a crude way to send a valid/invalid response code and error message (where relevant) back to the BAT script so that it shows up properly in my TortoiseSVN commit dialog.
Here’s my start-commit BAT script: [bat]ECHO off set REPOS_PATH=%1 set AUTHOR=%2 set PS=C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe set PSHOOKS_PATH=%REPOS_PATH%\hooks\powershell
for /f "tokens=*" %%a in (‘%PS% %PSHOOKS_PATH%\start-commit.ps1 %REPOS_PATH% %AUTHOR%’) do (call :sub_echo_and_store "%%a") GOTO :exit
:sub_echo_and_storeset LINE_=%1 set LINE_=%LINE_:"=% echo %LINE_% 1>&2 set RESULT=%LINE_% GOTO :eof
:exit exit %RESULT%[/bat] CMD.exe afficianados will recognize that after setting some local variables, including references to the REPOS_PATH and AUTHOR context passed in by Subversion, I am calling out to my start-commit.ps1 script to do the real work. The FOR command loops over the output from that Powershell delegate, strips surrounding quotes from each line, writes it to STDERR so it will be picked up by TortoiseSVN and shown in the commit dialog should the commit fail, and exiting with the last value output by the script.
This last behavior makes it possible to control the exit code returned to TortoiseSVN from my Powershell script; to signal success make sure the final output is a single line containing “0” while if the final output contains “1” TortoiseSVN will block the commit and write out the value of STDERR.
Here’s the simplest possible start-commit.ps1 Powershell script: [ps]Write-Output "REPOSITORY_PATH: "+$args Write-Output " AUTHOR_NAME: "+$args Write-Output " MESSAGE: this is a start-commit script running in powershell" Write-Output 1[/ps] This will block the commit while demonstrating that the powershell script ran with the properly passed SVN context. Changing that last line to 0 will allow the commit to succeed.
Bingo; now I can write complex logic in my powershell hook scripts, and as long as I end by writing out a “1” or a “0” I can programmatically cause to the commit to fail or allow it to succeed.