# HG changeset patch # User Sascha Teichmann # Date 1285887105 0 # Node ID 93d66243bce727e4f42a3b76a43cd91637c6ebad # Parent f1be9657c1d2f11be17820249e952765db28b819 sawmill: Added RSS feed generator. diff -r f1be9657c1d2 -r 93d66243bce7 contrib/sawmill/README --- a/contrib/sawmill/README Thu Sep 30 09:38:40 2010 +0000 +++ b/contrib/sawmill/README Thu Sep 30 22:51:45 2010 +0000 @@ -28,3 +28,15 @@ --exclude 'debian' --exclude 'src' \ --exclude 'binary' --exclude '*checkout*' \ --exclude '*.html' $TREE_PKG/mill/tracks/ $DEST/tracks + +If you want to generate RSS feeds for the build errors: + +# apt-get install python-pyrss2gen + +copy bin/generate-rss.py to a place where it can be run as a cronjob. +Adjust BASE_URL in the to fit your installation. + +---8<--- +*/7 * * * * /where/you/placed/generate-rss.py /where/you/placed/buildlogs/treepkgs +--->8--- + diff -r f1be9657c1d2 -r 93d66243bce7 contrib/sawmill/bin/generate-rss.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/sawmill/bin/generate-rss.py Thu Sep 30 22:51:45 2010 +0000 @@ -0,0 +1,156 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +# +# Copyright (C) 2010 by Intevation GmbH +# Authors: +# Sascha L. Teichmann +# +# This program is free software under the GPL (>=v2) +# Read the file COPYING coming with the software for details. + +import sys +import os +import re +import traceback + +from datetime import datetime + +from lxml import etree + +import PyRSS2Gen as RSS2 + +BASE_URL = "http://saegewerk2.wald.intevation.org/buildlogs" +LINK_URL = "%s/details.py?treepkg=%%s" % BASE_URL +ITEM_URL = "%s/details.py?treepkg=%%s#%%s" % BASE_URL + +TITLE = "Saegewerk - %s" +DESCRIPTION = "Build errors of '%s'" +MESSAGE = "%s: error building %s rev. %s" +TTL = 7 + +START = re.compile( + r"start:\s+(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2}):(\d{2})") +STATUS = re.compile(r"status:\s+(.+)") + +INVALID_LABEL = re.compile(r"[^a-zA-Z0-9_]") + +def make_valid_label(l): + return INVALID_LABEL.sub("_", l) + +def to_datetime(m): + m = map(int, m.groups()) + return datetime( + year=m[0], month=m[1], day=m[2], + hour=m[3], minute=m[4], second=m[5]) + +def usage(msg, code=1): + print >> sys.stderr, "%s " % sys.argv[0] + print >> sys.stderr, "%s" % msg + sys.exit(code) + +def main(): + if len(sys.argv) < 2: + usage("missing treepkgs directory") + + treepkgs_dir = sys.argv[1] + if not os.path.isdir(treepkgs_dir): + usage("'%s' is not a directory" % treepkgs_dir) + + for treepkg in os.listdir(treepkgs_dir): + treepkg_dir = os.path.join(treepkgs_dir, treepkg) + if not os.path.isdir(treepkg_dir): continue + treepkg_xml = os.path.join(treepkg_dir, "treepkg.xml") + if not os.path.isfile(treepkg_xml): continue + try: + f = open(treepkg_xml, "rb") + try: treepkg_dom = etree.parse(f) + finally: f.close() + except: + traceback.print_exc(file=sys.stderr) + continue + description = ''.join( + treepkg_dom.xpath('/treepkg/description/text()')) + + tracks_dir = os.path.join(treepkg_dir, "tracks") + if not os.path.isdir(tracks_dir): continue + + items = [] + + for track in os.listdir(tracks_dir): + track_dir = os.path.join(tracks_dir, track, "pkg") + if not os.path.isdir(track_dir): continue + + track_label = make_valid_label(track) + + for revision in os.listdir(track_dir): + revision_dir = os.path.join(track_dir, revision) + if not os.path.isdir(revision_dir): continue + status_file = os.path.join(revision_dir, "status") + if not os.path.isfile(status_file): continue + start, status = None, None + try: + f = open(status_file, "r") + try: + while True: + line = f.readline() + if not line: break + m = STATUS.match(line) + if m: status = m.group(1); continue + m = START.match(line) + if m: start = to_datetime(m) + finally: + f.close() + except: + traceback.print_exc(file=sys.stderr) + continue + if status != "error" or not start: continue + + label = ''.join([ + track_label, + make_valid_label(revision), + start.strftime("%Y%m%d%H%M%S")]) + + link = ITEM_URL % (treepkg, label) + msg = MESSAGE % (description, track, revision) + item = RSS2.RSSItem( + title = msg, + link = link, + description = msg, + guid = RSS2.Guid(link, isPermaLink=0), + pubDate = start + ) + items.append(item) + + items.sort(key=lambda x: x.pubDate) + + rss = RSS2.RSS2( + title = TITLE % description, + link = LINK_URL % treepkg, + description = DESCRIPTION % description, + pubDate = datetime.utcnow(), + ttl = TTL, + items = items) + + pid, idx = os.getpid(), 0 + while True: + tmp_f = os.path.join( + treepkg_dir, "rss.xml.tmp%d-%d" % (pid, idx)) + if not os.path.exists(tmp_f): break + idx += 1 + + try: + f = open(tmp_f, "wb") + try: rss.write_xml(f, encoding="UTF-8") + finally: f.close() + + rss_xml = os.path.join(treepkg_dir, "rss.xml") + + os.rename(tmp_f, rss_xml) + except: + traceback.print_exc(file=sys.stderr) + if os.path.exists(tmp_f): + try: os.remove(tmp_f) + except: pass + +if __name__ == '__main__': + main()