#!/bin/sh
#
# Track changes in /etc/ to make it easy to undo changes or check
# modifications.  This is done using a depot in $HOME/.svk, and no
# traces of the version tracking is stored in /etc/.
#
# Based on the ideas described by Enrico Zini in
# http://lists.debian.org/debian-devel/2005/02/msg00495.html
# See also
# http://svkbook.elixus.org/nightly/en/svk.tour.cycle.html
#
# An interesting alternative is <URL:http://www.isisetup.ch/>
# using git.

set -e

sysconfdir=/etc

# Ignore files that are automatically updated every night or more often
ignorefiles="adjtime ld.so.cache"

# These are automatically updated, but might be useful to track anyway
#ignorefiles="mtab resolv.conf network/run/ifstate"

# Set this to change the debot location from ~root/.svk/ to $HOME/.svk/
#HOME=

usage() {
	cat <<EOF
Usage: $0 <argument> [argument options]

Argument is one of

  help          - display this usage information
  prepare       - install the packages required by the etc-svk system
  init          - initialize the svk depot using the current /etc/ content
  ignore <file> - stop tracking the given file under /etc/
  commit        - check the current version of /etc/ into the depot
  status        - report the list of changed files in /etc/ (svk status)
  log           - list changes commited for files in /etc/ (svk log)
  diff          - report changes in files relative to the latest
                  checkin (svk diff)

To use it, first run 'debian-edu-etc-svk init' once, to register /etc/
in svk, next, run 'debian-edu-etc-svk commit' to commit changes in
/etc/ automatically regularly, for example from cron every hour like
this:

  0 * * * * $0 commit
  
EOF
}

do_prepare() {
    # Install debian packages needed (svk and expect)
    aptitude install -y svk expect
}

ignore_file() {
    filename="$1"

    if [ ! -f "$sysconfdir/$filename" ] ; then
	echo "error: file '$sysconfdir/$filename' do not exist."
	return 1
    fi

    tmpfile=$(tempfile)
    # Append new ignored file to svn:ignore list, while Keeping the
    # list sorted, and avoiding empty lines
    (
	svk propget svn:ignore
	echo "$filename"
    ) | sort -u | grep -v '^ *$' > $tmpfile

    # Hack to replace the old list with the new list.  The call by svk
    # will be 'cp $tmpfile /path/to/svk-edit-file', and thus replace
    # instead of editing.
    EDITOR="cp $tmpfile" svk propedit svn:ignore
    rm $tmpfile

    # Remove the file from svk control and commit it, while keeping
    # the file around.  Doing this after editing the property above,
    # to make sure files not currently in the svk depot is added to
    # the ignore list before the script fail with "$filename is not
    # under version control."
    svk rm -K "$filename"
    svk commit -m "Do not track $sysconfdir/$filename in svk."
}

do_init() {
    # Initialize a depot in $HOME/.svk by running "svk depotmap --init".
    # This is interactive, so use expect to answer the question
    expect -c 'spawn svk depotmap --init; expect -exact "create? (y/n)"; send "y\r"; expect eof'

    # Import $sysconfdir making it a working copy
    svk import -m "Initial import." --to-checkout /$sysconfdir $sysconfdir
   
    # Make your depot not that readable by non-root
    chmod -R go-rwx $HOME/.svk

    # Remove volatile files from revision control
    for filename in $ignorefiles ; do
	ignore_file $filename
    done
}

do_commit() {
    if [ "$(svk status)" ] ; then
	comment=""
	delfiles="$(svk status|grep '^! '|awk '{print $2}')"
	if [ "$delfiles" ] ; then
	    svk rm $delfiles > /dev/null
	    comment="Removed $delfiles. $comment"
	fi
	newfiles="$(svk status|grep '^? '|awk '{print $2}')"
 	if [ "$newfiles" ] ; then
	    svk add $newfiles > /dev/null
	    comment="Added $newfiles. $comment"
	fi
	chtypefiles="$(svk status|grep '^~ '|awk '{print $2}')"
	if [ "$chtypefiles" ] ; then
	    svk rm -K $chtypefiles > /dev/null
	    svk add $chtypefiles > /dev/null
	    comment="Change type of $chtypefiles. $comment"
	fi
	modfiles="$(svk status|grep '^M '|awk '{print $2}')"
	if [ "$modfiles" ] ; then
	    comment="Modified $modfiles. $comment"
	fi
	if [ -z "$comment" ] ; then
	    comment="Unknown change (property change?)."
	fi
	svk commit -m "$comment" > /dev/null
    fi
}

# fail early (assume set -e) if the directory is missing
cd $sysconfdir

# Make sure the correct depot path is used even when installed from
# within d-i, where HOME is "/".
if [ -z "$HOME" ] || [ / = "$HOME" ] ; then
    HOME="$(getent passwd root | cut -d: -f6)"
fi
export HOME

case "$1" in
    prepare)
	do_prepare
	;;
    init)
	do_init
	;;
    commit|update)
	do_commit
	;;
    ignore)
        ignore_file $2
	;;
    status|diff|log)
	svk $@
 	;;
    help)
	usage
	;;
    *)
	echo "error: incorrect or missing argument '$1'."
	usage
	exit 1
	;;
esac

exit 0
