#!/usr/local/bin/python -u
"""bzr-feed.py
An Atom feed generator for a Bazaar repository.
"""
__author__ = "Morten Frederiksen (morten@mfd-consult.dk)"
__copyright__ = "copyright 2007, MFD Consult"
__contributors__ = ["Sam Ruby", "Jacques Distler"]
__license__ = "Python"
from bzrlib.branch import BzrBranch
from bzrlib.bzrdir import BzrDir
from bzrlib.log import show_log, LogFormatter
from xml.sax import saxutils
import time, os, cgi, sys, re
bzr_rev = re.compile("(.*?)-(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)-(.*)")
class AtomFeed(LogFormatter):
"""Print log messages as an Atom feed.
"""
def __init__(self, baseuri, branch, created, updated):
super(AtomFeed, self).__init__(to_file=None)
self.supports_delta = True
self.baseuri = baseuri
self.branch = branch
self.feedid = baseuri.replace('http://', 'tag:').replace('/', "," + time.strftime("%Y-%m-%d",time.gmtime(created)) + ":", 1)
if os.environ.get('SCRIPT_NAME',None):
print "Content-Type: application/atom+xml\r\n\r\n",
print ""
print ""
print " " + self._e(self.branch._get_nick()) + ""
print " " + self._e(self.feedid) + ""
print " "
print " "
print " " + self._e(time.strftime("%Y-%m-%dT%H:%M:%SZ",time.gmtime(updated))) + ""
def __del__(self):
print ""
def _e(self, msg):
return saxutils.escape(msg.encode('utf-8'))
def show_files(self, files, label):
if not len(files): return
print "
" + self._e(label) + ":"
for item in files:
if label == 'Renamed':
display_path, path, fid, kind = item[:4]
else:
path, fid, kind = item[:3]
display_path = path
if kind == 'directory':
path += '/'
display_path += '/'
elif kind == 'symlink':
display_path += '@'
if len(item) == 5 and item[4]:
display_path += '*'
if label == 'Removed':
print " - " + self._e(display_path) + "
"
else:
print " - %s
" % (self.baseuri, self._e(path), self._e(display_path))
print "
"
def log_revision(self, revision):
revno = revision.revno
rev = revision.rev
delta = revision.delta
revno = str(revno)
message = rev.message.rstrip().split('\n')
print
print " "
print " "
print " " + self._e(rev.committer.split("<")[0].rstrip()) + ""
print " "
print " Revision " + self._e(revno) + ": " + self._e(message[0]) + ""
print " "
if bzr_rev.match(rev.revision_id):
print " " + self._e(bzr_rev.sub(r"tag:\1,\2-\3-\4:\5:\6:\7-\8",rev.revision_id)) + ""
else:
print " " + self._e(self.feedid + ":" + revno) + ""
print " " + self._e(time.strftime("%Y-%m-%dT%H:%M:%SZ",time.gmtime(rev.timestamp))) + ""
print " "
if message[1:]:
print "
" + "
\n".join(map(self._e, message[1:])) + "
"
if delta:
print "
"
self.show_files(delta.added, "Added")
self.show_files(delta.removed, "Removed")
self.show_files(delta.renamed, "Renamed")
self.show_files(delta.modified, "Modified")
print "
"
print "
"
print " "
def status(code, msg):
if (code!=304): print "Content-Type: text/plain"
print "Status: " + str(code) + " " + msg
print
if (code!=304): print msg
sys.exit()
# Sanity check
if not os.environ.get('REQUEST_METHOD','GET') in ['GET', 'HEAD']:
status(405, "Method Not Allowed")
# Basic repository info
try:
dir = cgi.FieldStorage().getvalue("dir")
repo = BzrDir.open(dir).open_repository()
except:
status(404, "Repository Not Found")
if not len(repo.all_revision_ids()):
status(404, "No Revisions Found")
# Basic branch info
branch = BzrDir.open(dir).open_branch()
start_rev = branch.revno()-9
if (start_rev < 1): start_rev = 1
first_rev = repo.get_revisions(repo.all_revision_ids()[0:])[0]
last_rev = repo.get_revisions(repo.all_revision_ids()[-1:])[0]
# Support 304
if_none_match = os.environ.get('HTTP_IF_NONE_MATCH', '')
if_modified_since = os.environ.get('HTTP_IF_MODIFIED_SINCE', '')
if if_none_match and ('"' + str(hash(branch._get_nick() + str(last_rev.timestamp))) + '"') == if_none_match:
status(304, "Not Modified")
# Location
baseuri = "http://" + os.environ['HTTP_HOST']
if os.environ['SERVER_PORT']!="80": baseuri += ":" + os.environ['SERVER_PORT']
if os.path.basename(os.path.dirname(os.environ['REQUEST_URI'])) == dir:
baseuri += os.path.dirname(os.environ['REQUEST_URI'])
else:
baseuri += re.sub(r'\.\w+$', '', os.environ['REQUEST_URI'])
if os.environ.get('SCRIPT_NAME',None):
print "Last-Modified: " + time.ctime(last_rev.timestamp) + " GMT"
print 'ETag: "%s"' % str(hash(branch._get_nick() + str(last_rev.timestamp)))
lf = AtomFeed(baseuri, branch, first_rev.timestamp, last_rev.timestamp)
show_log(branch,lf,None,True,'reverse',start_rev,branch.revno())