#!/usr/bin/python # post-commit.py # symlinked in /path/to/repository/hooks/post-commit import pysvn import sys import os from datetime import datetime # Begin Django bootstrap code # You'll need to change this to match your own specific host / # development path. host_dir = '/var/www/hosts/example.com' site_dir = '%s/site' % host_dir site_root = '%s/root' % site_dir # I keep various versions of Django, and supporting Django apps, # outside of the normal Python path. If you install via setup.py # you might not need to loop this. for p in ('/usr/local/codebase/framework', '/usr/local/codebase/apps', site_root, '%s/project' % site_root): sys.path.append(p) os.environ['DJANGO_SETTINGS_MODULE'] = 'project.settings' # End Django bootstrap code def getChangedFiles(path, revno): """ Query svn repository for changes in a given changeset. """ client = pysvn.Client() revision = pysvn.Revision(pysvn.opt_revision_kind.number, revno) previous = pysvn.Revision(pysvn.opt_revision_kind.number, int(revno)-1) changes = client.diff_summarize( url_or_path1 = path, revision1 = previous, revision2 = revision, ) formatted = [] if changes: for c in changes: # Create a list of two-tuples, containing the svn 'action' # (add, delete, modify, etc.) and the file that's been acted upon. formatted.append(('%s' % c.summarize_kind, '%s' % c.path)) return tuple(formatted) if __name__ == '__main__': # We should get something like # /path/to/repository # i.e., "/var/svn/sites/apps 46" # # argv[0], of course, is our script name. assert len(sys.argv) == 3 # Django imports from django.core.mail import EmailMessage from django.template import Context, Template from django.conf import settings from django.contrib.sites.models import Site # Load the svn client client = pysvn.Client() # Create a "proper" svn url repo_url = 'file://%s' % sys.argv[1] # Get our revision number. rev = sys.argv[2] # Send our url and the revision number to getChangedFiles changes = getChangedFiles(repo_url, rev) # Only act if there have been changes. if changes: # Attempt to ascertain which site this is for. site = Site.objects.all(pk=settings.SITE_ID) # Get the 'from' email setting from settings.py from_email = settings.DEFAULT_FROM_EMAIL # I'm pulling from 'MANAGERS', which by default is set from # 'ADMINS', but it could just as easily pull from a database # query. admin_list = [mail_tuple[1] for mail_tuple in settings.MANAGERS] # Get the commit metadata - author, commit date, etc. log = client.log(repo_url, revision_start = pysvn.Revision(pysvn.opt_revision_kind.head), revision_end=pysvn.Revision(pysvn.opt_revision_kind.number, int(rev)))[0] # Convert the float timestamp into a UTC datetime object. stamp = datetime.utcfromtimestamp(log['date']) # Further convert it into something more human readable. friendly_date = stamp.strftime('%A %b %d, %Y') friendly_time = stamp.strftime('%I:%M %P') # Get the author / committer. author = log['author'] # Get the commit message message = log['message'] # Build our template context. c = Context(dict( site = site, author = author, date = friendly_date, time = friendly_time, divider = '-' * 67, revision = rev, changes = changes, message = message, )) # Create our subject and body templates. We can also use normal # templates for this, but that goes beyond the context of this # snippet. s = Template("Commit for {{ site }}") b = Template("""Revision: {{ revision }} Author: {{ author }} Date: {{ date }} {{ time }} {{ divider }} {{ message|default:"(no commit message)" }} {% for action, path in changes %} {{ action|capfirst }} {{ path }}{% endfor %} """) # Render our templates. subject = s.render(c) body = b.render(c) # Set up our email message. mail = EmailMessage(subject, body, from_email, admin_list) # Send using text. Change to 'html' to use rich formatting. mail.content_subtype = 'plain' # Send our commit information! mail.send()