[Xfce4-commits] <xfce-buildbot-scripts:master> xfcebuildstatus.py first 'working' version

Samuel Verstraete noreply at xfce.org
Wed Oct 7 16:34:02 CEST 2009


Updating branch refs/heads/master
         to eef2bee45bb094e75bb7d6533479e608e628c5eb (commit)
       from 07cfbf16ebcabebd9c2ff45db3cfc1981cf126d6 (commit)

commit eef2bee45bb094e75bb7d6533479e608e628c5eb
Author: Samuel Verstraete <samuel.verstraete at gmail.com>
Date:   Wed Oct 7 16:31:36 2009 +0200

    xfcebuildstatus.py first 'working' version

 xfcebuildstatus.py |  365 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 365 insertions(+), 0 deletions(-)

diff --git a/xfcebuildstatus.py b/xfcebuildstatus.py
new file mode 100755
index 0000000..d1ef08d
--- /dev/null
+++ b/xfcebuildstatus.py
@@ -0,0 +1,365 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#       Simple frontend for Buildbot using its XMLRPC API
+#       (for Xfce)
+#
+#       Copyright 2009 Enrico Tröger <enrico(at)xfce(dot)org>
+#
+#       This program is free software; you can redistribute it and/or modify
+#       it under the terms of the GNU General Public License as published by
+#       the Free Software Foundation; either version 2 of the License.
+#
+#       This program is distributed in the hope that it will be useful,
+#       but WITHOUT ANY WARRANTY; without even the implied warranty of
+#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#       GNU General Public License for more details.
+#
+#       You should have received a copy of the GNU General Public License
+#       along with this program; if not, write to the Free Software
+#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+
+from xmlrpclib import ServerProxy, Error
+from string import Template
+from datetime import datetime
+import cgi
+import os
+
+
+
+#XMLRPC_URL = 'http://buildbot.xfce.org/core/xmlrpc'
+XMLRPC_URL = 'http://localhost:8020/xmlrpc'
+PLATFORM_NAMES = {
+    'debian': 'Debian GNU/Linux 5.0.3 (x86_64)',
+    'lunar': 'Lunar Linux (x86_64)'
+}
+
+template = """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+
+<head>
+    <title>Xfce Buildbots Status</title>
+    <meta http-equiv="content-type" content="text/html;charset=utf-8" />
+    <meta name="generator" content="Geany 0.19" />
+    <style type="text/css">
+        body, p, pre, td {
+            font-size: 12px;
+            font-family: sans-serif;
+        }
+        img {
+            border: 0px;
+        }
+        a {
+            color: #000000;
+        }
+        h1 {
+            margin-top: 25px;
+            margin-bottom: 35px;
+        }
+        th, td {
+            border: #909090 1px solid;
+            padding: 2px;
+            text-align: center;
+        }
+        .modules {
+            border: 0px;
+            text-align: right;
+        }
+        .status_success {
+            background-color: #00ff00;
+        }
+        .status_failed {
+            background-color: #ff0000;
+        }
+        .status_unknown {
+            background-color: #ffff00;
+        }
+        .source {
+            padding-top: 15px;
+            font-size: 11px;
+        }
+        .item {
+            font-weight: bold;
+        }
+        .step {
+            margin-left: 20px;
+            line-height: 150%;
+        }
+    </style>
+</head>
+
+<body>
+${content}
+</body>
+</html>"""
+
+
+
+#----------------------------------------------------------------------
+def split_builder_name(input):
+    """
+    Split the input string into the name of the module and the platform
+    it was built on.
+
+    @param input (str)
+    @return type, module, platform (tuple)
+    """
+    if not input:
+        return ('', '', '')
+
+    try:
+        pos = input.find('_')
+        rpos = input.rfind('_')
+        repo = input[0:pos]
+        module_name = input[pos+1:rpos]
+        platform = input[rpos+1:]
+        return (repo, module_name, platform)
+    except:
+        return ('', '', '')
+
+#----------------------------------------------------------------------
+def get_build_status(url):
+    """
+    Query the buildbot at url for the last builds of all builders and render them into
+    a dictionary.
+
+    @param url (str)
+    @return status per module (dict)
+    """
+    result = {}
+    platforms = {}
+    server = ServerProxy(url)
+    builders = server.getLastBuildsAllBuilders(1)
+    #builders.sort()
+    for name, no, start, end, branch, revision, status, text, reasons in builders:
+        repo, module_name, platform = split_builder_name(name)
+        start = datetime.fromtimestamp(start).strftime('%c')
+        try:
+            r = result[module_name]
+        except KeyError:
+            r = result[module_name] = {}
+        r[platform] = (status, start, revision, no, text, reasons, name)
+
+    return result
+
+#----------------------------------------------------------------------
+def get_build_status_html(url, input):
+    """
+    Create HTML code from the given input.
+
+    @param url (str)
+    @param input (dict)
+    @return html code (str)
+    """
+    result = '<table><tr>\n<td class="modules"></td>\n'
+    for name in PLATFORM_NAMES.values():
+        result += '<td>%s</td>\n' % name
+    result += '</tr>\n'
+    sorted_builders = []
+    for x in input.items():
+        sorted_builders.append(x)
+    sorted_builders.sort(cmp=lambda x,y: cmp(x[0], y[0]))
+
+    for module_name, status in sorted_builders:
+    #for module_name, status in input.items():
+        result += '<tr>\n<td class="modules">%s</td>\n' % module_name
+        for name in PLATFORM_NAMES:
+            try:
+                if status[name][0] == 'success':
+                    result += '''<td class="status_success"><a href="%s/detail/s/%s/%d"
+                                title="Built revision %s on %s">%s</a></td>\n''' % \
+                        (url, status[name][6], status[name][3], status[name][2], status[name][1], status[name][0])
+                elif status[name][0] == 'failure':
+                    status_class = ''
+                    result += type(status[name][4])
+                    result += type(status[name][5])
+                    desc = '%s failed to build at %s (on %s, reason: %s)' % \
+                        (module_name, ' '.join(status[name][4]), status[name][1], ' '.join(status[name][5]))
+                    if status[name][2]:
+                        desc += 'Revision %s failed to build on %s' % (status[name][2], status[name][1])
+                    result += '<td class="status_failed"><a href="%s/detail/f/%s/%d" title="%s">%s</a></td>\n' % \
+                        (url, status[name][6], status[name][3], desc, status[name][0])
+                else:
+                    result += '<td class="status_unknown">unknown</td>\n'
+            except KeyError:
+                result += '<td class="status_unknown">unknown</td>\n'
+        result += '</tr>\n'
+
+    result += '</table>\n<p class="source"><a href="%s/source">Script Source</a></p>' % url
+
+    return Template(template).substitute(content=result)
+
+#----------------------------------------------------------------------
+def get_detail_status(url, args):
+    """
+    Query the buildbot at url for the detailed build status as specified in args
+
+    @param url (str)
+    @param args (list)
+    @return status per module (dict)
+    """
+    server = ServerProxy(url)
+
+    try:
+        if args[1] == 's':
+            builder = server.getBuild(args[2], int(args[3]))
+        else:
+            builder = server.getBuild(args[2], -1)
+    except Exception, e:
+        return { }
+
+    return builder
+
+#----------------------------------------------------------------------
+def find_log(logs, step):
+    """
+    Find the corresponding log for the given build step name from the list of logs
+
+    @param logs (list)
+    @param step (str)
+    @return log (str)
+    """
+    for log in logs:
+        try:
+            parts = log['name'].rsplit('/', 1)
+            if parts[0] == step:
+                return parts
+        except:
+            continue
+    return []
+
+#----------------------------------------------------------------------
+def format_time(duration):
+    """
+    Format a time value in seconds into hours, minutes and seconds
+    (taken from Buildbot's util.py)
+
+    @param duration (int)
+    @return hours, minutes, seconds (tuple)
+    """
+    eta_parts = []
+    if duration > 3600:
+        eta_parts.append("%d hours" % (duration / 3600))
+        duration %= 3600
+    if duration > 60:
+        eta_parts.append("%d minutes" % (duration / 60))
+        duration %= 60
+    eta_parts.append("%d seconds" % duration)
+    return ", ".join(eta_parts)
+
+#----------------------------------------------------------------------
+def get_detail_status_html(url, input):
+    """
+    Create HTML code from the given input.
+
+    @param url (str)
+    @param input (dict)
+    @return html code (str)
+    """
+    result = ''
+    if input:
+        repo, module_name, platform = split_builder_name(input['builder_name'])
+        start = datetime.fromtimestamp(input['start']).strftime('%c')
+        end = datetime.fromtimestamp(input['end']).strftime('%c')
+        duration = format_time(input['end'] - input['start'])
+        datetime.fromtimestamp(input['end']).strftime('%c')
+        try:
+            status = input['text'][1]
+        except KeyError:
+            status = 'failed'
+        status_class = 'status_success' if status == 'successful' else 'status_failed'
+
+        # output
+        result += '<h1>Build status for "%s"</h1>\n' % module_name
+        result += '<p><span class="item">Build result:</span> <span class="%s">%s</span></p>\n' % (status_class, status)
+        result += '<p><span class="item">Build started:</span> %s</p>\n' % start
+        result += '<p><span class="item">Build finished:</span> %s</p>\n' % end
+        result += '<p><span class="item">Build duration:</span> %s</p>\n' % duration
+        result += '<p><span class="item">Build reason:</span> %s</p>\n' % input['reason']
+        result += '<p><span class="item">Built revision:</span> %s</p>\n' % input['revision']
+        result += '<p><span class="item">Build steps:</span></p>'
+        result += '<p>\n'
+        for step in input['steps']:
+            # make Samuel happy :)
+            if step['name'].startswith('chmod '):
+                continue
+            log_suffix = find_log(input['logs'], step['name'])
+            log_url = ''
+            if log_suffix:
+                log_url = ', <a href="%s/steps/%s/logs/%s">Logs</a>' % (input['url'], log_suffix[0], log_suffix[1])
+            result += '<span class="step"><span class="item">%s: %s</span> (%s%s)</span><br/>\n' % \
+                (step['name'], ' '.join(step['text']), format_time(step['end'] - step['start']), log_url)
+        result += '</p>\n'
+        result += '<p> </p>'
+        result += '<p><span class="item">More details:</span> <a href="%s">%s</a></p>\n' % (input['url'], input['url'])
+
+    else:
+        # do something here
+        pass
+
+    return Template(template).substitute(content=result)
+
+#----------------------------------------------------------------------
+def parse_url():
+    """
+    Parse the URL of this script and the passed arguments
+
+    @return url, args (tuple)
+    """
+    try:
+        me = os.path.basename(os.environ['SCRIPT_FILENAME'])
+        if me[0] != '/':
+            me = '/%s' % me
+        arg_pos = os.environ['REQUEST_URI'].find(me)
+        args = os.environ['REQUEST_URI'][(arg_pos+len(me)+1):].split('/')
+        return (me, args)
+    except:
+        return ('', '')
+
+#----------------------------------------------------------------------
+def main():
+    #~if os.environ['REMOTE_ADDR'] == '127.0.0.1':
+        # debug :)
+    import cgitb
+    cgitb.enable(display=1)
+
+    # handle arguments
+    url, args = parse_url()
+
+    output = ''
+    headers = []
+    if args and args[0]:
+        if args[0] == 'source':
+            headers.append('Content-Disposition: attachment; filename=%s' % url)
+            headers.append('Content-type: text/x-python')
+            fp = open(__file__, 'r')
+            output = fp.read()
+            fp.close()
+        elif args[0] == 'detail' and len(args) == 4:
+            status = get_detail_status(XMLRPC_URL, args)
+            output = get_detail_status_html(url, status)
+
+    # fallback to overview page
+    if not output:
+        status = get_build_status(XMLRPC_URL)
+        output = get_build_status_html(url, status)
+
+    if not headers:
+        print 'Content-type: text/html'
+    else:
+        for h in headers:
+            print h
+    print # final blank line to end HTTP headers
+
+    print output
+
+main()
+
+
+# TODO
+# - sort
+# - detail
+
+



More information about the Xfce4-commits mailing list