changeset 492:93d66243bce7

sawmill: Added RSS feed generator.
author Sascha Teichmann <teichmann@intevation.de>
date Thu, 30 Sep 2010 22:51:45 +0000
parents f1be9657c1d2
children e075ce66e085
files contrib/sawmill/README contrib/sawmill/bin/generate-rss.py
diffstat 2 files changed, 168 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- 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---
+
--- /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 <sascha.teichmann@intevation.de>
+#
+# 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 <treepkgs directory>" % 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()
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)