Advance SVN post-commit hook script for System Administrators

Everyone knows how cool svn post-commit feature is. Instead of using the basic post-commit script provided by svn, we can do alot more with a bit of server side scripting. Here, I like to share a simple script that I wrote to automate the process of updating different server system files using svn post-commit. It works for me… it might work for you as well. There are of course alot of areas that needs improvement.

The first thing to do is to have the svn repo setup with the server name as the top dir and all sub directories mirror exactly the same way as the system dir. I have 2 servers here, ares.stag and zeus.dev for example:

server_config
|-- ares.stag
|   `-- home
|       `-- data
|               `-- vhost.ares.conf
|-- zeus.dev
|   |-- mutt
|   |   `-- muttrc
|   |-- vim
|   |   `-- vimrc
|   `-- xen
|   |-- var
|       `-- named
|           `-- chroot
|               |-- etc
|               |   `-- named.conf
|               `-- var
|                   `-- named
|                       |-- 115.2.10.in-addr.arpa
|                       |-- linux.dev.db
|                       |-- linux.live.db
|                       `-- linux.stag.db

Everytime I want to edit my stag dns for example, I dont need to manually do it in the server, I just need to edit linux.stag.db in my own desktop, then “svn commit”. The commit script is responsible to then put the files that I committed to the right place for me.

#!/bin/sh

# - This is a more advanced svn post-commit script.
# - This is currently used to rollover server config.
# - The script attempts to copy files from different server based on the
# current svn dir hierarchy.
# - ssh keys from user@{current_server} to root@{external_server} must exists.
# - The file in the server must exists else this script will fail.
# - It rolls over modified files, it doesn't delete file.
#
# author: Bernard Peh
# date: 16 April 2010
#
# version 1.0
# script created.

REPOS="$1"
REV="$2"

# define your admin for email alert
EMAIL="youremail@youremail.com"

# define log. Leave it as default if you want
TMP=/tmp/$$
LOG=/tmp/$$_log
USERLOG=/tmp/$$_userlog

# output svn details into log
svn log -v --xml -r$REV file://$REPOS > $LOG

# full path to command
WALL=/usr/bin/wall
SVN=/usr/bin/svn
SED=/bin/sed
AWK=/bin/awk
GREP=/bin/grep
CAT=/bin/cat
SSH=/usr/bin/ssh

# only copy committed files that are new or modified
$CAT $LOG | $GREP ' action=' | while read x; do SERVER=`echo $x | $AWK -F/ '{print $2}'`; RES=`echo $x | $SED 's/action=\"\(.*\)\">\/'$SERVER'\(.*\)<\/path>/\1 \2/'`; PATH=`echo $RES | $GREP '^\(A\|M\)'`; PATH=`if [ ! -z "$PATH" ]; then echo $PATH | $AWK '{print $2}'; fi;`; $SVN cat file://${REPOS}/${SERVER}${PATH} 2>/dev/null | $SSH root@$SERVER "cat - > $PATH" 2>> $USERLOG; if [ !$? ]; then echo "Successfully updated ${SERVER}:${PATH}" >> $USERLOG; fi; done

# mail to admin
$CAT "$USERLOG" | mail -s "$REPOS updated to rev $REV" $EMAIL

# clean up
rm -rf $TMP
rm -rf $LOG
rm -rf $USERLOG

# all good now exit
exit 0;

SVN over HTTP

Objective

I want to be able to browse the svn dir from a website like http://zeus.dev/svn.php and at the same time, be able to checkout from the repository via http://zeus.dev/svn/projectname

How

First of all, install mod_dav_svn

Then in the apache config, I am using vhost for example

<VirtualHost *:80>
  DocumentRoot /home/data/zeus.dev/apps/
  ServerName zeus.dev
  # this is for svn
  <Location /svn.php>
    RewriteEngine on
    RewriteRule svn.php/([^/\.]+) /svn/$1 [L]
  </Location>
  <Location /svn>
    DAV svn
    # SVNPath /home/data/svn/
    SVNParentPath /home/data/svn
    # Limit write permission to list of valid users.
    # Require SSL connection for password protection.
    # SSLRequireSSL
    AuthType Basic
    AuthName "SVN repository"
    AuthUserFile /etc/httpd/conf.d/subversion.passwd
    Require valid-user
  </Location>
</VirtualHost>

In my document root, /home/data/zeus.dev/apps, I have a svn.php file soft-linked to /home/data/svn (the place where all svn repository are). As you can see from above, I also use a basic authentication mechanism for anyone who wishes to checkout from the repo.

Quick SVN Tutorial

Create New Repo

svnadmin create /home/data/svn/[dir-name]
chown apache:developer -R /home/data/svn/[dir-name]
chown 2770 /home/data/svn/[dir-name]

Import New Site

  • ssh into server
  • to import new site to the trunk,
svn import [your-site] file:///home/data/svn/[your-site-name] -m "[import comments]"

View Available Sites In Trunk

svn ls file:///home/svn/trunk/

Checking Out a Site

  • Create a directory to checkout and use ‘svn co’
 cd ~
 mkdir [my-site]
 svn co file:///home/svn/trunk/[site-name] [my-site]

Updating a site

cd ~/[my-site]
svn update

View SVN Log

cd ~/[my-site]
svn log

Resolving Conflicts

svn status -u can predict conflicts Suppose you run svn update and you get the following result:

svn update
U	index.html
G	changed-b.html
C	rubbish-b.html
Updated to revision 46.
  • U – Updated: file contained no local changes, but was updated from changes from the repository
  • G – Merged: file had local changes, but the changes from the repository didn’t overlap with the local changes.
  • C – Conflict: changes from the server overlaps with your own and you need to manually choose between them.

For every conflicted file, Subversion places three additional files in your directory:

filename.ext.mine This is your file as it existed in your working copy before you updated your working copy. That is, without conflict markers. This file has your latest changes in it and nothing else. (If Subversion considers the file to be unmergable, then the .mine file isn’t created, since it would be identical to the working file).

filename.ext.r1 #old revision This is the file that was the BASE revision before you updated your working copy. That is, it is the file that you checked out before you made your latest edits.

filename.ext.r2 #new revision This is the file that your Subversion client just received from the server when you updated your working copy. This file corresponds to the HEAD revision of the repository.

The “<<<<<<<“, “=======” and “>>>>>>>>” signs are conflict markers. (You need to make sure you have removed these signs before you commit the file).

The following is an example of a conflicting file with Conflict Markers.

$ cat sandwich.txt
Top piece of bread
Mayonnaise
Lettuce
Tomato
Provolone
<<<<<<< .mine
Salami
Mortadella
Prosciutto
=======
Sauerkraut
Grilled Chicken
>>>>>>> .r2
Creole Mustard
Bottom piece of bread
>>>>>>>>
  • The text between “<<<<<<< .mine” and “=======” is the original text, indicated by the .mine
<<<<<<< .mine
Salami
Mortadella
Prosciutto
=======
  • The text between “=======” and “>>>>>>>>” is the new conflicting text, indicated by the .r2

You must run svn resolved when finished This removes the three temporary files and tells subversion the file is no longer in conflict.

Checking out an older Version of a file / directory

svn co -r 1204 file:///home/svn/trunk/[your-site-name]

to check out revision 1204 of that directory. Just change the revision number and the directory you want to check out and you should be good.

Undeleting a file / directory

Best way to do it is to cp an old version over the deleted version. In the directory of the deleted file, ie

svn cp -r 109 foo.bar foo.bar

Note

  • type ‘svn help’ on command line to see options.
  • check files permissions if things aren’t working.
  • Using tortoiseSVN is an alternative to command line.