Use expect with Subversion’s post-commit hook to automatically update remote servers

In one of my web development projects, it became important to keep the staging web server in sync with the latest code that myself and several other developers were working on. There are a number of ways to mirror files and directories across machines, rsync being one of the most widely known. However, in addition to simply mirroring the files across the two servers, we also needed a way to kick off the mirroring process that cleanly integrated with our development workflow. Subversion’s post-commit hook allowed us to do just that.

Still, however, the problem was not exactly straightforward. We needed to execute a svn update command on a server other than the server on which the Subversion repository was being hosted. Shell scripts are the obvious choice for command-line automation in UNIX, but they don’t deal with interactive commands very well. So instead of writing a shell script, I wrote an expect script.

This expect script is really basic. There’s a better one in a future post on this topic.

#!/usr/bin/expect -f

# This expect post-commit hook connects to staging-webserver
# and updates the working copy hosted there to the latest checked-in code.
# This means that whenever code is committed to the repository, the web site hosted
# will always be running the latest version of the code.
# AUTHOR: Meitar Moscovitz

# [...]

set REPOS [lindex $argv 0]
set REV [lindex $argv 1]
set HOST staging-webserver
# Use update-user to log in to staging-webserver
# to update the working copy, but this can probably be improved so as not
# to expose this user's password.
set USER update-user
set PASS update-user's-password

spawn /usr/bin/ssh $USER@$HOST svn update /path/to/web/site/directory
expect "continue connecting (yes/no)? "
send "yes\r"
expect "password: "
send "$PASS\r"
expect eof

There’s one really tricky bit to this script, which is the understanding that when Subversion runs the post-commit hook, no environment information is passed to this script. As a result, there is no home directory or path information set for this executable. This is why everything is defined using absolute paths. Also, because there is no home directory for update-user, the expect script will always be prompted by SSH to re-verify the server’s identity. So rather than just expecting “password: “, we always expect “continue connecting (yes/no)? ” and say yes, and then send our password.

Note, of course, that update-user should be a user with limited access to the system, yet enough so that he may update the working copy on the web server. I’m sure there is probably a more secure way of doing this as well, so any sort of feedback on securing this or scaling it would be welcome.