Wednesday, February 12, 2014

Subversion repository backup script.

This bash script will create compressed dump files of all the available subversion repositories. The paths will need to be modified based on your operating environment. It uses 7zip for compression, but can be modified to use any compression program/algorithm desired.

Please note that dumps do not backup locks. If maximum availability is desired, set up a mirror server and use hot-copy on every commit to keep it in sync.

#!/bin/sh
 
# Set crontab to "30 0 * * * /root/svnback.sh 2>&1 >> /root/backup.log"
 
# Set programs with full path
SVN="/opt/CollabNet_Subversion/bin/svn"
SVNADMIN="/opt/CollabNet_Subversion/bin/svnadmin"
SEVENZIP="/usr/libexec/p7zip/7za"
DU="/usr/bin/du"
FIND="/usr/bin/find"
TAIL="/usr/bin/tail"
TPUT="/usr/bin/tput"
TR="/usr/bin/tr"
DATE="/bin/date"
ECHO="/bin/echo"
GREP="/bin/grep"
GZIP="/bin/gzip"
RM="/bin/rm"
SED="/bin/sed"
SORT="/bin/sort"
 
# Unset PATH to prevent mistakes and ensure program will run under cron
unset PATH
 
# Exit the script if any statement returns a non-true return value.
set -e
 
# Record todays date.
backupdate=$($DATE)
$ECHO -e "--------------------------------------------------"
$ECHO -e "Running SVN backup on $backupdate"
$ECHO -e "--------------------------------------------------\n"
 
# Repository directory.
svnrepos="/var/svn/repositories"
$ECHO -e "Backing up all SVN repos located at: $svnrepos\n"
 
# Dump directory location.
backupdest="/var/svn/dumps"
 
# Change to SVN repository directory.
cd $svnrepos
 
# Ensure write access to backup-folder.
if [ -d "$backupdest" ] && [ -w "$backupdest" ]
then
  # Generate list of directories sorted by size.
  dirlist=`$FIND $svnrepos -mindepth 1 -maxdepth 1 -type d -exec $DU -sb '{}' \; | $SORT -n | $SED -e 's%.*/%%'`
 
  for repo in $dirlist
  do
    $ECHO -e Repository: $($TPUT bold)$($TPUT setaf 1)$repo$($TPUT sgr0)
    # Find last revision of repository.
    lastrev=`$SVN info file://$svnrepos/$repo|$GREP 'Last Changed Rev'|$SED 's/Last Changed Rev: //'`
 
    # Find timestamp of repository.
    timestamp=`$SVN info --xml file://$svnrepos/$repo | $TR -d '\n' | $SED -e 's%^.*<date>\([^<]*\)</date>.*$%\1%' | $SED -e 's%[-:.TZ]%%g'`
 
    # Check repository's last backup revision.
    lastbackuprev=`$FIND "$backupdest" -mindepth 1 -maxdepth 1 -type f -printf "%f\n"| $GREP "^$repo-" | $SORT | $TAIL -1 | $SED -e "s/^$repo-[0-9]\+-[0-9]\+-\([0-9]\+\).*$/\1/"| $GREP '^[0-9]\+$' || $ECHO 0`
 
    # Check repository's last backup timestamp.
    lastbackuptimestamp=`$FIND "$backupdest" -mindepth 1 -maxdepth 1 -type f -printf "%f\n"| $GREP "^$repo-" | $SORT | $TAIL -1 | $SED -e "s/^$repo-\([0-9]\+\)-[0-9]\+-[0-9]\+.*$/\1/"| $GREP '^[0-9]\+$' || $ECHO 0`
 
    $ECHO -e "Last Backup Rev: $lastbackuprev - Last Rev: $lastrev"
    $ECHO -e "Last Backup Timestamp: $lastbackuptimestamp - Timestamp: $timestamp"
 
    # Backup if timestamps don't match.
    # However, skip if the format (length) of the timestamp from svn is wrong or if the last revision is zero.
    if [ "$lastbackuptimestamp" == "$timestamp" ]
    then
      $ECHO -e "No change since last backup. Skipping backup for $repo...\n"
    elif [ ${#timestamp} -ne 20 ]
    then
      $ECHO -e "Invalid timestamp. Skipping backup for $repo...\n"
    elif [ $lastrev -eq 0 ]
    then
      $ECHO -e "Empty repository. Skipping backup for $repo...\n"
    else
      # Check if this if the first backup for repository.
      if [ $lastbackuprev -eq 0 ]
      then
        firstrev=0
        $ECHO -e "$($TPUT bold)Executing$($TPUT sgr0): svnadmin dump $repo > $backupdest/$repo-$timestamp-$firstrev-$lastrev.svn.dump\n"
        $SVNADMIN dump $repo > $backupdest/$repo-$timestamp-$firstrev-$lastrev.svn.dump
      else
        firstrev=$((lastbackuprev+1))
        $ECHO -e "$($TPUT bold)Executing$($TPUT sgr0): svnadmin dump --revision $firstrev:$lastrev --incremental $repo > $backupdest/$repo-$timestamp-$firstrev-$lastrev.svn.dump\n"
        $SVNADMIN dump --revision $firstrev:$lastrev --incremental $repo > $backupdest/$repo-$timestamp-$firstrev-$lastrev.svn.dump
      fi
 
      # Wait for background process to complete.
      if [ `jobs -p` ]
      then
        $ECHO -e "\nWaiting for compression job to complete..."
        wait
      fi
      # Apply compression to svn-dump.
      $ECHO -e "\nCompressing $repo-$timestamp-$firstrev-$lastrev.svn.dump ...\n"
      #$GZIP $backupdest/$repo-$timestamp-$firstrev-$lastrev.svn.dump &
      $SEVENZIP a $backupdest/$repo-$timestamp-$firstrev-$lastrev.svn.dump.7z $backupdest/$repo-$timestamp-$firstrev-$lastrev.svn.dump 2>&1 >/dev/null && $RM -f $backupdest/$repo-$timestamp-$firstrev-$lastrev.svn.dump &
    fi
  done
else
  $ECHO -e "Unable to continue SVN backup process."
  $ECHO -e "$backupdest is *NOT* a directory or you do not have write permission.\n"
fi
 
# Wait for background process to complete.
if [ `jobs -p` ]
then
  $ECHO -e "Waiting for compression job to complete...\n"
  wait
fi
 
# End of backup script.
$ECHO -e "================================="
$ECHO -e "Backup Complete"

No comments:

Post a Comment