[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