#!/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) + ":
" 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())