Initial commit of blenders buildbot configuration

This commit is contained in:
2015-06-14 20:23:25 +02:00
commit 327d4f6b8a
25 changed files with 5762 additions and 0 deletions

30
.gitignore vendored Normal file
View File

@@ -0,0 +1,30 @@
# generic files to ignore
.*
# python temp paths
__pycache__/
*.py[cod]
# editors
*~
*.swp
*.swo
*#
# ms-windows
Thumbs.db
ehthumbs.db
Desktop.ini
# OSX
.DS_storage
# local patches
/*.patch
/*.diff
# Buildbot specific files
http.log
master_private.py
twistd.log
linux_glibc211_x86_64_scons/

37
buildbot.tac Normal file
View File

@@ -0,0 +1,37 @@
import os
from twisted.application import service
from buildbot.master import BuildMaster
basedir = r'.'
rotateLength = 10000000
maxRotatedFiles = 10
umask = 002
# if this is a relocatable tac file, get the directory containing the TAC
if basedir == '.':
import os.path
basedir = os.path.abspath(os.path.dirname(__file__))
# note: this line is matched against to check that this is a buildmaster
# directory; do not edit it.
application = service.Application('buildmaster')
try:
from twisted.python.logfile import LogFile
from twisted.python.log import ILogObserver, FileLogObserver
logfile = LogFile.fromFullPath(os.path.join(basedir, "twistd.log"), rotateLength=rotateLength,
maxRotatedFiles=maxRotatedFiles)
application.setComponent(ILogObserver, FileLogObserver(logfile).emit)
except ImportError:
# probably not yet twisted 8.2.0 and beyond, can't set log yet
pass
configfile = r'master.cfg'
m = BuildMaster(basedir, configfile)
m.setServiceParent(application)
m.log_rotation.rotateLength = rotateLength
m.log_rotation.maxRotatedFiles = maxRotatedFiles

18
maintenance/cleanup.sh Executable file
View File

@@ -0,0 +1,18 @@
#!/bin/sh
ROOT="/data/buildbot-master/"
DIRS="linux_glibc211_i386_scons
linux_glibc211_x86_64_scons
mac_i386_10_6_scons
mac_x86_64_10_6_scons
mingw_win64_scons
uploaded
win32_cmake_vc2013
win32_scons_vc2013
win64_cmake_vc2013
win64_scons_vc2013"
for DIR in $DIRS; do
find "${ROOT}/$DIR" -type f -mtime +14 -exec rm -f {} \;
done

372
master.cfg Normal file
View File

@@ -0,0 +1,372 @@
# -*- python -*-
# ex: set syntax=python:
# <pep8 compliant>
# List of the branches being built automatically overnight
NIGHT_SCHEDULE_BRANCHES = [None, "gooseberry"]
# List of the branches available for force build
FORCE_SCHEDULE_BRANCHES = ["master", "gooseberry", "experimental-build"]
"""
Stock Twisted directory lister doesn't provide any information about last file
modification time, we hack the class a bit in order to have such functionaliity
:)
"""
from buildbot.status.web.base import DirectoryLister
def get_files_and_directories(self, directory):
from twisted.web.static import (getTypeAndEncoding,
formatFileSize)
import urllib
import cgi
import time
import os
files = []
dirs = []
for path in directory:
url = urllib.quote(path, "/")
escapedPath = cgi.escape(path)
lastmodified = time.ctime(os.path.getmtime(
os.path.join(self.path, path)))
if os.path.isdir(os.path.join(self.path, path)):
url = url + '/'
dirs.append({'text': escapedPath + "/", 'href': url,
'size': '', 'type': '[Directory]',
'encoding': '',
'lastmodified': lastmodified})
else:
mimetype, encoding = getTypeAndEncoding(path, self.contentTypes,
self.contentEncodings,
self.defaultType)
try:
size = os.stat(os.path.join(self.path, path)).st_size
except OSError:
continue
files.append({
'text': escapedPath, "href": url,
'type': '[%s]' % mimetype,
'encoding': (encoding and '[%s]' % encoding or ''),
'size': formatFileSize(size),
'lastmodified': lastmodified})
return dirs, files
DirectoryLister._getFilesAndDirectories = get_files_and_directories
# Dictionary that the buildmaster pays attention to.
c = BuildmasterConfig = {}
# BUILD SLAVES
#
# We load the slaves and their passwords from a separator file, so we can have
# this one in SVN.
from buildbot.buildslave import BuildSlave
import master_private
c['slaves'] = []
for slave in master_private.slaves:
c['slaves'].append(BuildSlave(slave['name'], slave['password']))
# TCP port through which slaves connect
c['slavePortnum'] = 9989
# CHANGE SOURCES
from buildbot.changes.svnpoller import SVNPoller
from buildbot.changes.gitpoller import GitPoller
c['change_source'] = GitPoller(
'git://git.blender.org/blender.git',
pollinterval=1200)
# CODEBASES
#
# Allow to controll separately things like branches for each repo and submodules.
all_repositories = {
r'git://git.blender.org/blender.git': 'blender',
r'git://git.blender.org/blender-translations.git': 'blender-translations',
r'git://git.blender.org/blender-addons.git': 'blender-addons',
r'git://git.blender.org/blender-addons-contrib.git': 'blender-addons-contrib',
r'git://git.blender.org/scons.git': 'scons',
r'https://svn.blender.org/svnroot/bf-blender/': 'lib svn',
}
def codebaseGenerator(chdict):
return all_repositories[chdict['repository']]
c['codebaseGenerator'] = codebaseGenerator
# SCHEDULERS
#
# Decide how to react to incoming changes.
# from buildbot.scheduler import Scheduler
from buildbot.schedulers import timed, forcesched
c['schedulers'] = []
def schedule_force_build(name):
c['schedulers'].append(forcesched.ForceScheduler(name='force ' + name,
builderNames=[name],
codebases=[forcesched.CodebaseParameter(
codebase="blender",
branch=forcesched.ChoiceStringParameter(
name="branch", choices=FORCE_SCHEDULE_BRANCHES, default="master"),
# Do not hide revision, can be handy!
repository=forcesched.FixedParameter(name="repository", default="", hide=True),
project=forcesched.FixedParameter(name="project", default="", hide=True)),
# For now, hide other codebases.
forcesched.CodebaseParameter(hide=True, codebase="blender-translations"),
forcesched.CodebaseParameter(hide=True, codebase="blender-addons"),
forcesched.CodebaseParameter(hide=True, codebase="blender-addons-contrib"),
forcesched.CodebaseParameter(hide=True, codebase="scons"),
forcesched.CodebaseParameter(hide=True, codebase="lib svn")],
properties=[]))
def schedule_build(name, hour, minute=0):
for current_branch in NIGHT_SCHEDULE_BRANCHES:
scheduler_name = "nightly " + name
if current_branch:
scheduler_name += ' ' + current_branch
c['schedulers'].append(timed.Nightly(name=scheduler_name,
codebases={
"blender": {"repository": ""},
"blender-translations": {"repository": "", "branch": "master"},
"blender-addons": {"repository": "", "branch": "master"},
"blender-addons-contrib": {"repository": "", "branch": "master"},
"scons": {"repository": "", "branch": "master"},
"lib svn": {"repository": "", "branch": "trunk"}},
branch=current_branch,
builderNames=[name],
hour=hour,
minute=minute))
# BUILDERS
#
# The 'builders' list defines the Builders, which tell Buildbot how to
# perform a build: what steps, and which slaves can execute them.
# Note that any particular build will only take place on one slave.
from buildbot.process.factory import BuildFactory
from buildbot.process.properties import Interpolate
from buildbot.steps.source import SVN
from buildbot.steps.source import Git
from buildbot.steps.shell import ShellCommand
from buildbot.steps.shell import Compile
from buildbot.steps.shell import Test
from buildbot.steps.transfer import FileUpload
from buildbot.steps.master import MasterShellCommand
from buildbot.config import BuilderConfig
# add builder utility
c['builders'] = []
buildernames = []
def add_builder(c, name, libdir, factory, branch='',
rsync=False, hour=3, minute=0):
slavenames = []
for slave in master_private.slaves:
if name in slave['builders']:
slavenames.append(slave['name'])
if len(slavenames) > 0:
f = factory(name, libdir, branch, rsync)
c['builders'].append(BuilderConfig(name=name,
slavenames=slavenames,
factory=f,
category='blender'))
buildernames.append(name)
schedule_build(name, hour, minute)
schedule_force_build(name)
# common steps
def git_submodule_step(submodule):
return Git(name=submodule + '.git',
repourl='git://git.blender.org/' + submodule + '.git',
mode='update',
codebase=submodule,
workdir=submodule + '.git')
def git_step(branch=''):
if branch:
return Git(name='blender.git',
repourl='git://git.blender.org/blender.git',
mode='update',
branch=branch,
codebase='blender',
workdir='blender.git',
submodules=True)
else:
return Git(name='blender.git',
repourl='git://git.blender.org/blender.git',
mode='update',
codebase='blender',
workdir='blender.git',
submodules=True)
def git_submodules_update():
command = ['git', 'submodule', 'foreach', '--recursive',
'git', 'pull', 'origin', 'master']
return ShellCommand(name='Submodules Update',
command=command,
description='updating',
descriptionDone='up to date',
workdir='blender.git')
def lib_svn_step(dir):
return SVN(name='lib svn',
baseURL='https://svn.blender.org/svnroot/bf-blender/%%BRANCH%%/lib/' + dir,
codebase='lib svn',
mode='update',
defaultBranch='trunk',
workdir='lib/' + dir)
def rsync_step(id, branch, rsync_script):
return ShellCommand(name='rsync',
command=['python', rsync_script, id, branch],
description='uploading',
descriptionDone='uploaded',
workdir='install')
# generic builder
def generic_builder(id, libdir='', branch='', rsync=False):
filename = 'uploaded/buildbot_upload_' + id + '.zip'
compile_script = '../blender.git/build_files/buildbot/slave_compile.py'
test_script = '../blender.git/build_files/buildbot/slave_test.py'
pack_script = '../blender.git/build_files/buildbot/slave_pack.py'
rsync_script = '../blender.git/build_files/buildbot/slave_rsync.py'
unpack_script = 'master_unpack.py'
f = BuildFactory()
if libdir != '':
f.addStep(lib_svn_step(libdir))
for submodule in ('blender-translations',
'blender-addons',
'blender-addons-contrib',
'scons'):
f.addStep(git_submodule_step(submodule))
f.addStep(git_step(branch))
f.addStep(git_submodules_update())
f.addStep(Compile(command=['python', compile_script, id], timeout=3600))
f.addStep(Test(command=['python', test_script, id]))
f.addStep(ShellCommand(name='package',
command=['python', pack_script, id, branch or Interpolate('%(src:blender:branch)s')],
description='packaging',
descriptionDone='packaged'))
if rsync:
f.addStep(rsync_step(id, branch, rsync_script))
elif id.find('cmake') != -1:
f.addStep(FileUpload(name='upload',
slavesrc='buildbot_upload.zip',
masterdest=filename,
maxsize=150 * 1024 * 1024))
else:
f.addStep(FileUpload(name='upload',
slavesrc='buildbot_upload.zip',
masterdest=filename,
maxsize=150 * 1024 * 1024,
workdir='install'))
f.addStep(MasterShellCommand(name='unpack',
command=['python', unpack_script, filename],
description='unpacking',
descriptionDone='unpacked'))
return f
# builders
add_builder(c, 'mac_x86_64_10_6_scons', 'darwin-9.x.universal', generic_builder, hour=5)
add_builder(c, 'mac_i386_10_6_scons', 'darwin-9.x.universal', generic_builder, hour=11)
add_builder(c, 'linux_glibc211_i386_scons', '', generic_builder, hour=1)
add_builder(c, 'linux_glibc211_x86_64_scons', '', generic_builder, hour=2)
add_builder(c, 'win32_scons_vc2013', 'windows_vc12', generic_builder, hour=1)
add_builder(c, 'win64_scons_vc2013', 'win64_vc12', generic_builder, hour=2)
add_builder(c, 'win32_cmake_vc2013', 'windows_vc12', generic_builder, hour=3)
add_builder(c, 'win64_cmake_vc2013', 'win64_vc12', generic_builder, hour=4)
#add_builder(c, 'mingw_win32_scons', 'mingw32', generic_builder, hour=4)
add_builder(c, 'mingw_win64_scons', 'mingw64', generic_builder, hour=3)
#add_builder(c, 'freebsd_i386_cmake', '', generic_builder, hour=1)
#add_builder(c, 'freebsd_x86_64_cmake', '', generic_builder, hour=2)
# Multiview branch
add_builder(c, 'multiview_win64_scons', 'win64', generic_builder, 'multiview', hour=4)
add_builder(c, 'multiview_win32_scons', 'windows', generic_builder, 'multiview', hour=5)
# STATUS TARGETS
#
# 'status' is a list of Status Targets. The results of each build will be
# pushed to these targets. buildbot/status/*.py has a variety to choose from,
# including web pages, email senders, and IRC bots.
c['status'] = []
from buildbot.status import html
from buildbot.status.web import authz
from buildbot.status.web import auth
users = []
for slave in master_private.slaves:
users += [(slave['name'], slave['password'])]
authz_cfg = authz.Authz(
auth=auth.BasicAuth(users),
# change any of these to True to enable; see the manual for more
# options
gracefulShutdown=False,
forceBuild=True, # use this to test your slave once it is set up
forceAllBuilds=False,
pingBuilder=False,
stopBuild=True,
stopAllBuilds=False,
cancelPendingBuild=True,
)
c['status'].append(html.WebStatus(http_port=8010, authz=authz_cfg))
#c['status'].append(html.WebStatus(http_port=8010))
# PROJECT IDENTITY
c['projectName'] = "Blender"
c['projectURL'] = "http://www.blender.org"
# the 'buildbotURL' string should point to the location where the buildbot's
# internal web server (usually the html.WebStatus page) is visible. This
# typically uses the port number set in the Waterfall 'status' entry, but
# with an externally-visible host name which the buildbot cannot figure out
# without some help.
c['buildbotURL'] = "http://builder.blender.org/"
# DB URL
#
# This specifies what database buildbot uses to store change and scheduler
# state. You can leave this at its default for all but the largest
# installations.
c['db_url'] = "sqlite:///state.sqlite"

View File

@@ -0,0 +1,5 @@
slaves = [
{'name': 'linux_glibc211_x86_64_chroot',
'password': 'barbarianna',
'builders': ['linux_glibc211_x86_64_scons']}
]

148
master_unpack.py Normal file
View File

@@ -0,0 +1,148 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# 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, or (at your option) any later version.
#
# 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.
#
# ##### END GPL LICENSE BLOCK #####
# Runs on Buildbot master, to unpack incoming unload.zip into latest
# builds directory and remove older builds.
# <pep8 compliant>
import os
import shutil
import sys
import zipfile
# extension stripping
def strip_extension(filename):
extensions = '.zip', '.tar', '.bz2', '.gz', '.tgz', '.tbz', '.exe'
for ext in extensions:
if filename.endswith(ext):
filename = filename[:-len(ext)]
return filename
# extract platform from package name
def get_platform(filename):
# name is blender-version-platform.extension. we want to get the
# platform out, but there may be some variations, so we fiddle a
# bit to handle current and hopefully future names
filename = strip_extension(filename)
filename = strip_extension(filename)
tokens = filename.split("-")
platforms = ('osx', 'mac', 'bsd',
'win', 'linux', 'source',
'irix', 'solaris', 'mingw')
platform_tokens = []
found = False
for i, token in enumerate(tokens):
if not found:
for platform in platforms:
if platform in token.lower():
found = True
break
if found:
platform_tokens += [token]
return '-'.join(platform_tokens)
def get_branch(filename):
tokens = filename.split("-")
branch = ""
for token in tokens:
if token == "blender":
return branch
if branch == "":
branch = token
else:
branch = branch + "-" + token
return ""
# get filename
if len(sys.argv) < 2:
sys.stderr.write("Not enough arguments, expecting file to unpack\n")
sys.exit(1)
filename = sys.argv[1]
# open zip file
if not os.path.exists(filename):
sys.stderr.write("File %r not found.\n" % filename)
sys.exit(1)
try:
z = zipfile.ZipFile(filename, "r")
except Exception as ex:
sys.stderr.write('Failed to open zip file: %s\n' % str(ex))
sys.exit(1)
if len(z.namelist()) != 1:
sys.stderr.write("Expected one file in %r." % filename)
sys.exit(1)
package = z.namelist()[0]
packagename = os.path.basename(package)
# detect platform and branch
platform = get_platform(packagename)
branch = get_branch(packagename)
if platform == '':
sys.stderr.write('Failed to detect platform ' +
'from package: %r\n' % packagename)
sys.exit(1)
# extract
if not branch or branch == 'master':
directory = 'public_html/download'
elif branch == 'experimental-build':
directory = 'public_html/download/experimental'
else:
directory = 'public_html/download'
try:
filename = os.path.join(directory, packagename)
zf = z.open(package)
f = file(filename, "wb")
shutil.copyfileobj(zf, f)
os.chmod(filename, 0644)
zf.close()
z.close()
except Exception as ex:
sys.stderr.write('Failed to unzip package: %s\n' % str(ex))
sys.exit(1)
# remove other files from the same platform and branch
try:
for f in os.listdir(directory):
if get_platform(f) == platform and get_branch(f) == branch:
if f != packagename:
os.remove(os.path.join(directory, f))
except Exception as ex:
sys.stderr.write('Failed to remove old packages: %s\n' % str(ex))
sys.exit(1)

BIN
public_html/bg_gradient.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,32 @@
@font-face{
font-family:'FontBorg';
src:url('../fonts/font-borg-webfont.eot');
src:url('../fonts/font-borg-webfont.eot?#iefix') format('embedded-opentype'),
url('../fonts/font-borg-webfont.woff') format('woff'),
url('../fonts/font-borg-webfont.ttf') format('truetype'),
url('../fonts/font-borg-webfont.svg#fontborgregular') format('svg');
font-weight:normal;
font-style:normal;
}
[class^="bf-"],[class*=" bf-"]{
font-family:FontBorg;
font-weight:normal;
font-style:normal;
text-decoration:inherit;
-webkit-font-smoothing:antialiased;
display:inline;
width:auto;
height:auto;
line-height:normal;
vertical-align:baseline;
background-image:none;
background-position:0% 0%;
background-repeat:repeat;
margin-top:0;
}
.bf-blender:before{content:"\f000";}
.bf-cloud:before{content:"\f001";}
.bf-network:before{content:"\f002";}
.bf-creativecommons:before{content:"\f003";}

2782
public_html/css/main.css Normal file

File diff suppressed because it is too large Load Diff

622
public_html/default.css Normal file
View File

@@ -0,0 +1,622 @@
.auth {
position:absolute;
top:5px;
right:40px;
}
.alert {
color: #c30000;
background-color: #f2dcdc;
padding: 5px 5px 5px 25px;
margin-bottom: 20px;
border-top:1px solid #ccc;
border-bottom:1px solid #ccc;
border-color: #c30000;
font-size: 20px;
}
a:link,a:visited,a:active {
color: #444;
}
.Project {
min-width: 6em;
}
.LastBuild,.Activity {
padding: 0 0 0 4px;
}
.LastBuild,.Activity,.Builder,.BuildStep {
min-width: 5em;
}
/* Chromium Specific styles */
div.BuildResultInfo {
color: #444;
}
div.Announcement {
margin-bottom: 1em;
}
div.Announcement>a:hover {
color: black;
}
div.Announcement>div.Notice {
background-color: #afdaff;
padding: 0.5em;
font-size: 16px;
text-align: center;
}
div.Announcement>div.Open {
border: 3px solid #8fdf5f;
padding: 0.5em;
font-size: 16px;
text-align: center;
}
div.Announcement>div.Closed {
border: 5px solid #e98080;
padding: 0.5em;
font-size: 24px;
font-weight: bold;
text-align: center;
}
td.Time {
color: #000;
border-bottom: 1px solid #aaa;
background-color: #eee;
}
td.Activity,td.Change,td.Builder {
color: #333333;
background-color: #CCCCCC;
}
td.Change {
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
}
td.Event {
color: #777;
background-color: #ddd;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
}
td.Activity {
border-top-left-radius: 10px;
-webkit-border-top-left-radius: 10px;
-moz-border-radius-topleft: 10px;
min-height: 20px;
padding: 2px 0 2px 0;
}
td.idle,td.waiting,td.offline,td.building {
border-top-left-radius: 0px;
-webkit-border-top-left-radius: 0px;
-moz-border-radius-topleft: 0px;
}
.LastBuild {
border-top-left-radius: 5px;
-webkit-border-top-left-radius: 5px;
-moz-border-radius-topleft: 5px;
border-top-right-radius: 5px;
-webkit-border-top-right-radius: 5px;
-moz-border-radius-topright: 5px;
}
/* Console view styles */
td.DevStatus > table {
table-layout: fixed;
}
td.DevRev {
padding: 4px 8px 4px 8px;
color: #333333;
border-top-left-radius: 5px;
-webkit-border-top-left-radius: 5px;
-moz-border-radius-topleft: 5px;
background-color: #eee;
width: 1%;
}
td.DevRevCollapse {
border-bottom-left-radius: 5px;
-webkit-border-bottom-left-radius: 5px;
-moz-border-radius-bottomleft: 5px;
}
td.DevName {
padding: 4px 8px 4px 8px;
color: #333333;
background-color: #eee;
width: 1%;
text-align: left;
}
td.DevStatus {
padding: 4px 4px 4px 4px;
color: #333333;
background-color: #eee;
}
td.DevSlave {
padding: 4px 4px 4px 4px;
color: #333333;
background-color: #eee;
}
td.first {
border-top-left-radius: 5px;
-webkit-border-top-left-radius: 5px;
-moz-border-radius-topleft: 5px;
}
td.last {
border-top-right-radius: 5px;
-webkit-border-top-right-radius: 5px;
-moz-border-radius-topright: 5px;
}
td.DevStatusCategory {
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-width: 1px;
border-style: solid;
}
td.DevStatusCollapse {
border-bottom-right-radius: 5px;
-webkit-border-bottom-right-radius: 5px;
-moz-border-radius-bottomright: 5px;
}
td.DevDetails {
font-weight: normal;
padding: 8px 8px 8px 8px;
color: #333333;
background-color: #eee;
text-align: left;
}
td.DevDetails li a {
padding-right: 5px;
}
td.DevComment {
font-weight: normal;
padding: 8px 8px 8px 8px;
color: #333333;
background-color: #eee;
text-align: left;
}
td.DevBottom {
border-bottom-right-radius: 5px;
-webkit-border-bottom-right-radius: 5px;
-moz-border-radius-bottomright: 5px;
border-bottom-left-radius: 5px;
-webkit-border-bottom-left-radius: 5px;
-moz-border-radius-bottomleft: 5px;
}
td.Alt {
background-color: #ddd;
}
.legend {
border-radius: 5px !important;
-webkit-border-radius: 5px !important;
-moz-border-radius: 5px !important;
width: 100px;
max-width: 100px;
text-align: center;
padding: 2px 2px 2px 2px;
height: 14px;
white-space: nowrap;
}
.DevStatusBox {
text-align: center;
height: 20px;
padding: 0 2px;
line-height: 0;
white-space: nowrap;
}
.DevStatusBox a {
opacity: 0.85;
border-width: 1px;
border-style: solid;
border-radius: 4px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
display: block;
width: 90%;
height: 20px;
line-height: 20px;
margin-left: auto;
margin-right: auto;
}
.DevStatusBox a.notinbuilder {
border-style: none;
}
.DevSlaveBox {
text-align: center;
height: 10px;
padding: 0 2px;
line-height: 0;
white-space: nowrap;
}
.DevSlaveBox a {
opacity: 0.85;
border-width: 1px;
border-style: solid;
border-radius: 4px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
display: block;
width: 90%;
height: 10px;
line-height: 20px;
margin-left: auto;
margin-right: auto;
}
a.noround {
border-radius: 0px;
-webkit-border-radius: 0px;
-moz-border-radius: 0px;
position: relative;
margin-top: -8px;
margin-bottom: -8px;
height: 36px;
border-top-width: 0;
border-bottom-width: 0;
}
a.begin {
border-top-width: 1px;
position: relative;
margin-top: 0px;
margin-bottom: -7px;
height: 27px;
border-top-left-radius: 4px;
-webkit-border-top-left-radius: 4px;
-moz-border-radius-topleft: 4px;
border-top-right-radius: 4px;
-webkit-border-top-right-radius: 4px;
-moz-border-radius-topright: 4px;
}
a.end {
border-bottom-width: 1px;
position: relative;
margin-top: -7px;
margin-bottom: 0px;
height: 27px;
border-bottom-left-radius: 4px;
-webkit-border-bottom-left-radius: 4px;
-moz-border-radius-bottomleft: 4px;
border-bottom-right-radius: 4px;
-webkit-border-bottom-right-radius: 4px;
-moz-border-radius-bottomright: 4px;
}
.center_align {
text-align: center;
}
.right_align {
text-align: right;
}
.left_align {
text-align: left;
}
div.BuildWaterfall {
border-radius: 7px;
-webkit-border-radius: 7px;
-moz-border-radius: 7px;
position: absolute;
left: 0px;
top: 0px;
background-color: #FFFFFF;
padding: 4px 4px 4px 4px;
float: left;
display: none;
border-width: 1px;
border-style: solid;
}
/* LastBuild, BuildStep states /
.success {
background-color: #D5D644;
color: #40400E;
text-shadow: 1px 1px 0px rgba(255, 255, 255, 0.5);
border: thin solid #D0D12F;
}
.failure {
color: #000;
background-color: #e88;
border-color: #A77272;
}
.failure-again {
color: #000;
background-color: #eA9;
border-color: #A77272;
}
.warnings {
color: #FFFFFF;
background-color: #fa3;
border-color: #C29D46;
}
.skipped {
color: #000;
background: #AADDEE;
border-color: #AADDEE;
}
.exception,.retry {
color: #FFFFFF;
background-color: #c6c;
border-color: #ACA0B3;
}
.start {
color: #000;
background-color: #ccc;
border-color: #ccc;
}
.running,.waiting,td.building {
color: #000;
background-color: #fd3;
border-color: #C5C56D;
}
.paused {
color: #FFFFFF;
background-color: #8080FF;
border-color: #dddddd;
}
.offline,td.offline {
color: #FFFFFF;
background-color: #777777;
border-color: #dddddd;
}
.start {
border-bottom-left-radius: 10px;
-webkit-border-bottom-left-radius: 10px;
-moz-border-radius-bottomleft: 10px;
border-bottom-right-radius: 10px;
-webkit-border-bottom-right-radius: 10px;
-moz-border-radius-bottomright: 10px;
}
.notstarted {
border-width: 1px;
border-style: solid;
border-color: #aaa;
background-color: #fff;
}
.closed {
background-color: #ff0000;
}
.closed .large {
font-size: 1.5em;
font-weight: bolder;
}
*/
td.Project a:hover,td.start a:hover {
color: #000;
}
.mini-box {
text-align: center;
height: 20px;
padding: 0 2px;
line-height: 0;
white-space: nowrap;
}
.mini-box a {
border-radius: 0;
-webkit-border-radius: 0;
-moz-border-radius: 0;
display: block;
width: 100%;
height: 20px;
line-height: 20px;
margin-top: -30px;
}
.mini-closed {
-box-sizing: border-box;
-webkit-box-sizing: border-box;
border: 4px solid red;
}
/* grid styles */
table.Grid {
border-collapse: collapse;
}
table.Grid tr td {
padding: 0.2em;
margin: 0px;
text-align: center;
}
table.Grid tr td.title {
font-size: 90%;
border-right: 1px gray solid;
border-bottom: 1px gray solid;
}
table.Grid tr td.sourcestamp {
font-size: 90%;
}
table.Grid tr td.builder {
text-align: right;
font-size: 90%;
}
table.Grid tr td.build {
border: 1px gray solid;
}
/* column container/
div.column {
margin: 0 2em 2em 0;
float: left;
}
*/
/* info tables */
table.info {
border-spacing: 1px;
}
table.info td {
padding: 0.1em 1em 0.1em 1em;
text-align: center;
}
table.info th {
padding: 0.2em 1.5em 0.2em 1.5em;
text-align: center;
}
table.info td.left {
text-align: left
}
table.info td .reason {
display:block;
font-weight: bold;
}
.alt {
background-color: #f6f6f6;
}
li {
padding: 0.1em 1em 0.1em 1em;
}
.result {
padding: 0.3em 1em 0.3em 1em;
}
/* log view */
.log * {
vlink: #800080;
font-family: "Courier New", courier, monotype, monospace;
}
span.stdout {
color: black;
}
span.stderr {
color: red;
}
span.header {
color: blue;
}
span.ansi30 {
color: black;
}
span.ansi31 {
color: red;
}
span.ansi32 {
color: green;
}
span.ansi33 {
color: orange;
}
span.ansi34 {
color: yellow;
}
span.ansi35 {
color: purple;
}
span.ansi36 {
color: blue;
}
span.ansi37 {
color: white;
}
/* revision & email */
.revision .full {
display: none;
}
.user .email {
display: none;
}
pre {
white-space: pre-wrap;
}
/* change comments (use regular colors here) */
pre.comments>a:link,pre.comments>a:visited {
color: blue;
}
pre.comments>a:active {
color: purple;
}
form.command_forcebuild {
border-top: 1px solid black;
padding: .5em;
margin: .5em;
}
form.command_forcebuild > .row {
border-top: 1px dotted gray;
padding: .5em 0;
}
form.command_forcebuild .force-textarea > .label {
display: block;
}
form.command_forcebuild .force-nested > .label {
font-weight: bold;
display: list-item;
}
form.command_forcebuild .force-any .force-text {
display: inline;
}

641
public_html/default.css.new Normal file
View File

@@ -0,0 +1,641 @@
body.interface {
margin-left: 30px;
margin-right: 30px;
margin-top: 20px;
margin-bottom: 50px;
padding: 0;
background: url(bg_gradient.jpg) repeat-x;
font-family: Verdana, sans-serif;
font-size: 10px;
background-color: #fff;
color: #333;
}
.auth {
position:absolute;
top:5px;
right:40px;
}
.alert {
color: #c30000;
background-color: #f2dcdc;
padding: 5px 5px 5px 25px;
margin-bottom: 20px;
border-top:1px solid #ccc;
border-bottom:1px solid #ccc;
border-color: #c30000;
font-size: 20px;
}
a:link,a:visited,a:active {
color: #444;
}
table {
border-spacing: 1px 1px;
}
table td {
padding: 3px 4px 3px 4px;
text-align: center;
}
.Project {
min-width: 6em;
}
.LastBuild,.Activity {
padding: 0 0 0 4px;
}
.LastBuild,.Activity,.Builder,.BuildStep {
min-width: 5em;
}
/* Chromium Specific styles */
div.BuildResultInfo {
color: #444;
}
div.Announcement {
margin-bottom: 1em;
}
div.Announcement>a:hover {
color: black;
}
div.Announcement>div.Notice {
background-color: #afdaff;
padding: 0.5em;
font-size: 16px;
text-align: center;
}
div.Announcement>div.Open {
border: 3px solid #8fdf5f;
padding: 0.5em;
font-size: 16px;
text-align: center;
}
div.Announcement>div.Closed {
border: 5px solid #e98080;
padding: 0.5em;
font-size: 24px;
font-weight: bold;
text-align: center;
}
td.Time {
color: #000;
border-bottom: 1px solid #aaa;
background-color: #eee;
}
td.Activity,td.Change,td.Builder {
color: #333333;
background-color: #CCCCCC;
}
td.Change {
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
}
td.Event {
color: #777;
background-color: #ddd;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
}
td.Activity {
border-top-left-radius: 10px;
-webkit-border-top-left-radius: 10px;
-moz-border-radius-topleft: 10px;
min-height: 20px;
padding: 2px 0 2px 0;
}
td.idle,td.waiting,td.offline,td.building {
border-top-left-radius: 0px;
-webkit-border-top-left-radius: 0px;
-moz-border-radius-topleft: 0px;
}
.LastBuild {
border-top-left-radius: 5px;
-webkit-border-top-left-radius: 5px;
-moz-border-radius-topleft: 5px;
border-top-right-radius: 5px;
-webkit-border-top-right-radius: 5px;
-moz-border-radius-topright: 5px;
}
/* Console view styles */
td.DevStatus > table {
table-layout: fixed;
}
td.DevRev {
padding: 4px 8px 4px 8px;
color: #333333;
border-top-left-radius: 5px;
-webkit-border-top-left-radius: 5px;
-moz-border-radius-topleft: 5px;
background-color: #eee;
width: 1%;
}
td.DevRevCollapse {
border-bottom-left-radius: 5px;
-webkit-border-bottom-left-radius: 5px;
-moz-border-radius-bottomleft: 5px;
}
td.DevName {
padding: 4px 8px 4px 8px;
color: #333333;
background-color: #eee;
width: 1%;
text-align: left;
}
td.DevStatus {
padding: 4px 4px 4px 4px;
color: #333333;
background-color: #eee;
}
td.DevSlave {
padding: 4px 4px 4px 4px;
color: #333333;
background-color: #eee;
}
td.first {
border-top-left-radius: 5px;
-webkit-border-top-left-radius: 5px;
-moz-border-radius-topleft: 5px;
}
td.last {
border-top-right-radius: 5px;
-webkit-border-top-right-radius: 5px;
-moz-border-radius-topright: 5px;
}
td.DevStatusCategory {
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-width: 1px;
border-style: solid;
}
td.DevStatusCollapse {
border-bottom-right-radius: 5px;
-webkit-border-bottom-right-radius: 5px;
-moz-border-radius-bottomright: 5px;
}
td.DevDetails {
font-weight: normal;
padding: 8px 8px 8px 8px;
color: #333333;
background-color: #eee;
text-align: left;
}
td.DevDetails li a {
padding-right: 5px;
}
td.DevComment {
font-weight: normal;
padding: 8px 8px 8px 8px;
color: #333333;
background-color: #eee;
text-align: left;
}
td.DevBottom {
border-bottom-right-radius: 5px;
-webkit-border-bottom-right-radius: 5px;
-moz-border-radius-bottomright: 5px;
border-bottom-left-radius: 5px;
-webkit-border-bottom-left-radius: 5px;
-moz-border-radius-bottomleft: 5px;
}
td.Alt {
background-color: #ddd;
}
.legend {
border-radius: 5px !important;
-webkit-border-radius: 5px !important;
-moz-border-radius: 5px !important;
width: 100px;
max-width: 100px;
text-align: center;
padding: 2px 2px 2px 2px;
height: 14px;
white-space: nowrap;
}
.DevStatusBox {
text-align: center;
height: 20px;
padding: 0 2px;
line-height: 0;
white-space: nowrap;
}
.DevStatusBox a {
opacity: 0.85;
border-width: 1px;
border-style: solid;
border-radius: 4px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
display: block;
width: 90%;
height: 20px;
line-height: 20px;
margin-left: auto;
margin-right: auto;
}
.DevStatusBox a.notinbuilder {
border-style: none;
}
.DevSlaveBox {
text-align: center;
height: 10px;
padding: 0 2px;
line-height: 0;
white-space: nowrap;
}
.DevSlaveBox a {
opacity: 0.85;
border-width: 1px;
border-style: solid;
border-radius: 4px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
display: block;
width: 90%;
height: 10px;
line-height: 20px;
margin-left: auto;
margin-right: auto;
}
a.noround {
border-radius: 0px;
-webkit-border-radius: 0px;
-moz-border-radius: 0px;
position: relative;
margin-top: -8px;
margin-bottom: -8px;
height: 36px;
border-top-width: 0;
border-bottom-width: 0;
}
a.begin {
border-top-width: 1px;
position: relative;
margin-top: 0px;
margin-bottom: -7px;
height: 27px;
border-top-left-radius: 4px;
-webkit-border-top-left-radius: 4px;
-moz-border-radius-topleft: 4px;
border-top-right-radius: 4px;
-webkit-border-top-right-radius: 4px;
-moz-border-radius-topright: 4px;
}
a.end {
border-bottom-width: 1px;
position: relative;
margin-top: -7px;
margin-bottom: 0px;
height: 27px;
border-bottom-left-radius: 4px;
-webkit-border-bottom-left-radius: 4px;
-moz-border-radius-bottomleft: 4px;
border-bottom-right-radius: 4px;
-webkit-border-bottom-right-radius: 4px;
-moz-border-radius-bottomright: 4px;
}
.center_align {
text-align: center;
}
.right_align {
text-align: right;
}
.left_align {
text-align: left;
}
div.BuildWaterfall {
border-radius: 7px;
-webkit-border-radius: 7px;
-moz-border-radius: 7px;
position: absolute;
left: 0px;
top: 0px;
background-color: #FFFFFF;
padding: 4px 4px 4px 4px;
float: left;
display: none;
border-width: 1px;
border-style: solid;
}
/* LastBuild, BuildStep states */
.success {
color: #000;
background-color: #8d4;
border-color: #4F8530;
}
.failure {
color: #000;
background-color: #e88;
border-color: #A77272;
}
.failure-again {
color: #000;
background-color: #eA9;
border-color: #A77272;
}
.warnings {
color: #FFFFFF;
background-color: #fa3;
border-color: #C29D46;
}
.skipped {
color: #000;
background: #AADDEE;
border-color: #AADDEE;
}
.exception,.retry {
color: #FFFFFF;
background-color: #c6c;
border-color: #ACA0B3;
}
.start {
color: #000;
background-color: #ccc;
border-color: #ccc;
}
.running,.waiting,td.building {
color: #000;
background-color: #fd3;
border-color: #C5C56D;
}
.paused {
color: #FFFFFF;
background-color: #8080FF;
border-color: #dddddd;
}
.offline,td.offline {
color: #FFFFFF;
background-color: #777777;
border-color: #dddddd;
}
.start {
border-bottom-left-radius: 10px;
-webkit-border-bottom-left-radius: 10px;
-moz-border-radius-bottomleft: 10px;
border-bottom-right-radius: 10px;
-webkit-border-bottom-right-radius: 10px;
-moz-border-radius-bottomright: 10px;
}
.notstarted {
border-width: 1px;
border-style: solid;
border-color: #aaa;
background-color: #fff;
}
.closed {
background-color: #ff0000;
}
.closed .large {
font-size: 1.5em;
font-weight: bolder;
}
td.Project a:hover,td.start a:hover {
color: #000;
}
.mini-box {
text-align: center;
height: 20px;
padding: 0 2px;
line-height: 0;
white-space: nowrap;
}
.mini-box a {
border-radius: 0;
-webkit-border-radius: 0;
-moz-border-radius: 0;
display: block;
width: 100%;
height: 20px;
line-height: 20px;
margin-top: -30px;
}
.mini-closed {
-box-sizing: border-box;
-webkit-box-sizing: border-box;
border: 4px solid red;
}
/* grid styles */
table.Grid {
border-collapse: collapse;
}
table.Grid tr td {
padding: 0.2em;
margin: 0px;
text-align: center;
}
table.Grid tr td.title {
font-size: 90%;
border-right: 1px gray solid;
border-bottom: 1px gray solid;
}
table.Grid tr td.sourcestamp {
font-size: 90%;
}
table.Grid tr td.builder {
text-align: right;
font-size: 90%;
}
table.Grid tr td.build {
border: 1px gray solid;
}
/* column container */
div.column {
margin: 0 2em 2em 0;
float: left;
}
/* info tables */
table.info {
border-spacing: 1px;
}
table.info td {
padding: 0.1em 1em 0.1em 1em;
text-align: center;
}
table.info th {
padding: 0.2em 1.5em 0.2em 1.5em;
text-align: center;
}
table.info td.left {
text-align: left
}
table.info td .reason {
display:block;
font-weight: bold;
}
.alt {
background-color: #f6f6f6;
}
li {
padding: 0.1em 1em 0.1em 1em;
}
.result {
padding: 0.3em 1em 0.3em 1em;
}
/* log view */
.log * {
vlink: #800080;
font-family: "Courier New", courier, monotype, monospace;
}
span.stdout {
color: black;
}
span.stderr {
color: red;
}
span.header {
color: blue;
}
span.ansi30 {
color: black;
}
span.ansi31 {
color: red;
}
span.ansi32 {
color: green;
}
span.ansi33 {
color: orange;
}
span.ansi34 {
color: yellow;
}
span.ansi35 {
color: purple;
}
span.ansi36 {
color: blue;
}
span.ansi37 {
color: white;
}
/* revision & email */
.revision .full {
display: none;
}
.user .email {
display: none;
}
pre {
white-space: pre-wrap;
}
/* change comments (use regular colors here) */
pre.comments>a:link,pre.comments>a:visited {
color: blue;
}
pre.comments>a:active {
color: purple;
}
form.command_forcebuild {
border-top: 1px solid black;
padding: .5em;
margin: .5em;
}
form.command_forcebuild > .row {
border-top: 1px dotted gray;
padding: .5em 0;
}
form.command_forcebuild .force-textarea > .label {
display: block;
}
form.command_forcebuild .force-nested > .label {
font-weight: bold;
display: list-item;
}
form.command_forcebuild .force-any .force-text {
display: inline;
}

BIN
public_html/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
public_html/lin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
public_html/osx.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

10
public_html/robots.txt Normal file
View File

@@ -0,0 +1,10 @@
User-agent: *
Disallow: /waterfall
Disallow: /builders
Disallow: /changes
Disallow: /buildslaves
Disallow: /schedulers
Disallow: /one_line_per_build
Disallow: /builders
Disallow: /grid
Disallow: /tgrid

BIN
public_html/win.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
state.sqlite Normal file

Binary file not shown.

253
templates/build.html Normal file
View File

@@ -0,0 +1,253 @@
{% extends "layout.html" %}
{% import 'forms.html' as forms %}
{% from "change_macros.html" import change with context %}
{% block content %}
<div class="container">
<h1>
Builder <a href="{{ path_to_builder }}">{{ b.getBuilder().getName() }}</a>
Build #{{ b.getNumber() }}
</h1>
{% if not b.isFinished() %}
<h2>Build In Progress:</h2>
{% if when_time %}
<p>ETA: {{ when_time }} [{{ when }}]</p>
{% endif %}
{{ current_step }}
{% if authz.advertiseAction('stopBuild', request) %}
<h2>Stop Build</h2>
{{ forms.stop_build(build_url+"/stop", authz, on_all=False, short=False, label='This Build') }}
{% endif %}
{% else %}
<h2>Results:</h2>
<p><span class="btn-{% if result_css == "success" %}{{ result_css }}{% else %}danger{% endif %} btn btn-default btn-block btn-squishy disabled">
{{ b.getText()|join(' ')|capitalize }}
</span>
</p>
{% if b.getTestResults() %}
<h3><a href="{{ tests_link }}"/></h3>
{% endif %}
{% endif %}
<h2>
{% if sourcestamps|count == 1 %}
SourceStamp:
{% else %}
SourceStamps:
{% endif %}
</h2>
{% for sourcestamps_row in sourcestamps | batch(3, '&nbsp;') %}
<div class="row flex">
{% for ss in sourcestamps_row %}
<div class="col-md-4">
<div class="box">
<h3>{{ ss.codebase }}</h3>
<table class="table table-striped table-hover box">
{% set ss_class = cycler('alt','') %}
{% if ss.project %}
<tr class="{{ ss_class.next() }}"><td class="left">Project</td><td>{{ ss.project|projectlink }}</td></tr>
{% endif %}
{% if ss.repository %}
<tr class="{{ ss_class.next() }}"><td class="left">Repository</td><td>{{ ss.repository|repolink }}</td></tr>
{% endif %}
{% if ss.branch %}
<tr class="{{ ss_class.next() }}"><td class="left">Branch</td><td>{{ ss.branch|e }}</td></tr>
{% endif %}
{% if ss.revision %}
<tr class="{{ ss_class.next() }}"><td class="left">Revision</td><td>{{ ss.revision|revlink(ss.repository) }}</td></tr>
{% endif %}
{% if got_revisions[ss.codebase] %}
<tr class="{{ ss_class.next() }}"><td class="left">Got Revision</td><td>{{ got_revisions[ss.codebase]|revlink(ss.repository) }}</td></tr>
{% endif %}
{% if ss.patch %}
<tr class="{{ ss_class.next() }}"><td class="left">Patch</td><td>YES</td></tr>
{% endif %}
{% if ss.changes %}
<tr class="{{ ss_class.next() }}"><td class="left">Changes</td><td><a href="#changes-{{ ss.codebase }}">{{ ss.changes|count }} change{{ 's' if ss.changes|count > 1 else '' }}</a></td></tr>
{% endif %}
{% if not ss.branch and not ss.revision and not ss.patch and not ss.changes %}
<tr class="{{ ss_class.next() }}"><td class="left" colspan="2">Build of most recent revision</td></tr>
{% endif %}
</table>
</div></div>
{% endfor %}
</div>
{% endfor %}
{#
# TODO: turn this into a table, or some other sort of definition-list
# that doesn't take up quite so much vertical space
#}
<h2>BuildSlave:</h2>
{% if slave_url %}
<a href="{{ slave_url|e }}">{{ b.getSlavename()|e }}</a>
{% else %}
{{ b.getSlavename()|e }}
{% endif %}
<h2>Reason:</h2>
<p>
{{ b.getReason()|e }}
</p>
<h2>Steps and Logfiles:</h2>
{#
# TODO:
# urls = self.original.getURLs()
# ex_url_class = "BuildStep external"
# for name, target in urls.items():
# text.append('[<a href="%s" class="%s">%s</a>]' %
# (target, ex_url_class, html.escape(name)))
#}
<ol>
{% for s in steps %}
<li>
<div class="{{ s.css_class }} result">
<a href="{{ s.link }}">{{ s.name }}</a>
<span class="pull-right label label-info">{{ s.text }} &nbsp;
{{ '( ' + s.time_to_run + ' )' if s.time_to_run else '' }}</span>
</div>
<ol>
{% set item_class = cycler('alt', '') %}
{% for l in s.logs %}
<li class="{{ item_class.next() }}"><a class="btn btn-default" href="{{ l.link }}">{{ l.name }}</a></li>
{% else %}
<li class="{{ item_class.next() }}"><a class="btn btn-default disabled" href="#" >- no logs -</a></li>
{% endfor %}
{% for u in s.urls %}
<li class="{{ item_class.next() }}"><a class="btn btn-default" href="{{ u.url }}">{{ u.logname }}</a></li>
{% endfor %}
</ol>
</li>
{% endfor %}
</ol>
</div>
<div class="container">
<h2>Build Properties:</h2>
<table class="table">
<tr><th>Name</th><th>Value</th><th>Source</th></tr>
{% for p in properties %}
{% if p.source != "Force Build Form" %}
<tr class="{{ loop.cycle('alt', '') }}">
<td class="left">{{ p.name|e }}</td>
{% if p.short_value %}
<td>{{ p.short_value|e }} .. [property value too long]</td>
{% else %}
{% if p.value is not mapping %}
<td>{{ p.value|e }}</td>
{% else %}
<td>
<table class="table">
{%- for key, value in p.value.items() recursive %}
<tr><td>{{ key|e }}</td><td>{{ value|e }}</td></tr>
{% endfor %}
</table>
</td>
{% endif %}
{% endif %}
<td>{{ p.source|e }}</td>
</tr>
{% endif %}
{% endfor %}
</table>
<h2>Forced Build Properties:</h2>
<table class="table">
<tr><th>Name</th><th>Label</th><th>Value</th></tr>
{% for p in properties %}
{% if p.source == "Force Build Form" %}
<tr class="{{ loop.cycle('alt', '') }}">
<td class="left">{{ p.name|e }}</td>
<td class="left">
{% if p.label %}
{{ p.label }}
{% endif %}
</td>
{% if p.text %}
<td><textarea readonly cols="{{p.cols}}" rows="{{p.rows}}">{{ p.text|e }}</textarea></td>
{% else %}
<td>{{ p.value|e }}</td>
{% endif %}
</tr>
{% endif %}
{% endfor %}
</table>
<h2>Responsible Users:</h2>
{% if responsible_users %}
<ol>
{% for u in responsible_users %}
<li class="{{ loop.cycle('alt', '') }}">{{ u|user }}</li>
{% endfor %}
</ol>
{% else %}
<p>no responsible users</p>
{% endif %}
<h2>Timing:</h2>
<table class="table">
<tr class="alt"><td class="left">Start</td><td>{{ start }}</td></tr>
{% if end %}
<tr><td class="left">End</td><td>{{ end }}</td></tr>
{% endif %}
<tr {{ 'class="alt"' if end else '' }}><td class="left">Elapsed</td><td>{{ elapsed }}</td></tr>
</table>
{% if authz.advertiseAction('forceBuild', request) %}
<h3>Resubmit Build:</h3>
{{ forms.rebuild_build(build_url+"/rebuild", authz, sourcestamps[0]) }}
{% endif %}
</div>
<br style="clear:both"/>
{% if has_changes %}
<div class="column">
<h2>All Changes:</h2>
{% for ss in sourcestamps %}
{% if ss.changes %}
<h3 id="changes-{{ ss.codebase }}"> {{ ss.codebase }}:</h3>
<ol>
{% for c in ss.changes %}
<li><h3>Change #{{ c.number }}</h3>
{{ change(c.asDict()) }}
</li>
{% endfor %}
</ol>
{% endif %}
{% endfor %}
</div>
{% endif %}
{% endblock %}

61
templates/build_line.html Normal file
View File

@@ -0,0 +1,61 @@
{% macro build_line(b, include_builder=False) %}
<small>({{ b.time }})</small>
Rev: {% if b.multiple_revs -%}
multiple rev
{%- else -%}
{{ b.rev_list[0]['rev']|shortrev(b.rev_list[0]['repo']) }}
{%- endif %}
<span class="{{ b.class }}">{{ b.results }}</span>
{% if include_builder %}
<a href="{{ b.builderurl }}">{{ b.builder_name }}</a>
{% endif %}
<a href="{{ b.buildurl }}">#{{ b.buildnum }}</a> -
{{ b.text|capitalize }}
{% endmacro %}
{% macro build_tr(b, include_builder=False, loop=None) %}
<tr class="{{ loop.cycle('alt', '') if loop }}">
<td>{{ b.time }}</td>
<td>{%- for rev in b.rev_list -%}
{%- if not loop.first %}<br/>{% endif -%}
{%- if rev.get('codebase', '') %}<span class='codebase'>{{ rev['codebase'] }}</span>:&nbsp;{% endif -%}
{{ rev['rev']|shortrev(rev['repo']) }}
{%- endfor -%}
</td>
<td><span class="btn-{% if b.class == "success" %}{{ b.class }}{% else %}danger{% endif %} btn btn-block btn-squishy disabled">{{ b.results }}</span></td>
{%- if include_builder %}
<td><a class="btn btn-block btn-squishy" href="{{ b.builderurl }}">{{ b.builder_name }}</a></td>
{% endif %}
<td><a class="btn btn-block btn-squishy" href="{{ b.buildurl }}">#{{ b.buildnum }}</a></td>
<td><span class='reason'>{{ b.reason|e }}</span>
{%- for user in (b.interested_users or []) -%}
{%- if not loop.first %}, {% endif -%}
<span class='interested_user'>{{ user|e }}</span>
{%- endfor -%}
</td>
<td class="left">{{ b.text|capitalize }}</td>
</tr>
{% endmacro %}
{% macro build_table(builds, include_builder=False) %}
{% if builds %}
<table class="table">
<tr>
<th>Time</th>
<th>Revision</th>
<th>Result</th>
{%- if include_builder %}
<th>Builder</th>
{% endif %}
<th>Build #</th>
<th>Reason</th>
<th>Info</th>
</tr>
{% for b in builds %}
{{ build_tr(b, include_builder, loop) }}
{% endfor %}
</table>
{% else %}
No matching builds found
{% endif %}
{% endmacro %}

94
templates/buildslave.html Normal file
View File

@@ -0,0 +1,94 @@
{% from 'build_line.html' import build_table, build_line %}
{% import 'forms.html' as forms %}
{% extends "layout.html" %}
{% block content %}
<h1>Buildslave: {{ slavename|e }}</h1>
<div class="col-md-6">
{% if current %}
<h2>Currently building:</h2>
<ul>
{% for b in current %}
<li>{{ build_line(b, True) }}
<form method="post" action="{{ b.buildurl }}/stop" class="command stopbuild" style="display:inline">
<input type="submit" value="Stop Build" />
<input type="hidden" name="url" value="{{ this_url }}" />
</form>
</li>
{% endfor %}
</ul>
{% else %}
<h2>No current builds</h2>
{% endif %}
<h2>Recent builds</h2>
{{ build_table(recent, True) }}
</div>
<div class="col-md-6">
{% if access_uri %}
<a href="{{ access_uri|e }}">Click to Access Slave</a>
{% endif %}
{% if admin %}
<h2>Administrator</h2>
<p>{{ admin|email }}</p>
{% endif %}
{% if host or info %}
<h2>Slave information</h2>
{% if host %}
Buildbot-Slave {{ slave_version }}
<div class="box"><p>{{ host|e }}</p></div>
{% endif %}
{% if info %}
<table class="table">
<tr><th>Name</th><th>Value</th></tr>
{% for info_name, info_val in info.iteritems() %}
<tr class="{{ loop.cycle('alt', '') }}">
<td class="left">{{ info_name|e }}</td>
{% if info_val is not mapping %}
<td>{{ info_val|e }}</td>
{% else %}
<td>
<table class="table table-striped table-hover box">
{%- for key, value in info_val.items() recursive %}
<tr><td>{{ key|e }}</td><td>{{ value|e }}</td></tr>
{% endfor %}
</table>
</td>
{% endif %}
</tr>
{% endfor %}
</table>
{% endif %}
{% endif %}
<h2>Connection Status</h2>
<p>
{{ connect_count }} connection(s) in the last hour
{% if not slave.isConnected() %}
(not currently connected)
{% else %}
</p>
{% if authz.advertiseAction('gracefulShutdown', request) %}
<h2>Graceful Shutdown</h2>
{% if slave.getGraceful() %}
<p>Slave will shut down gracefully when it is idle.</p>
{% else %}
{{ forms.graceful_shutdown(shutdown_url, authz) }}
{% endif %}
{% endif %}
{% if authz.advertiseAction('pauseSlave', request) %}
<h2>Pause Slave</h2>
{{ forms.pause_slave(pause_url, authz, slave.isPaused()) }}
{% endif %}
{% endif %}
</div>
{% endblock %}

View File

@@ -0,0 +1,76 @@
{% extends "layout.html" %}
{% block content %}
<h1>Buildslaves</h1>
<div class="container-fluid">
<table class="table table-striped table-hover box">
<tr>
<th>Name</th>
{%- if show_builder_column %}
<th>Builders</th>
{%- endif %}
<th>BuildBot</th>
<th>Admin</th>
<th>Last heard from</th>
<th>Connects/Hour</th>
<th>Status</th>
</tr>
{% for s in slaves %}
<tr class="{{ loop.cycle('alt','') }}">
<td><b><a href="{{ s.link }}">{{ s.name }}</a></b></td>
{%- if show_builder_column %}
<td>
{%- if s.builders %}
{%- for b in s.builders %}
<a href="{{ b.link }}">{{ b.name }}</a>
{%- endfor %}
{%- else %}
<span class="warning">no builders</span>
{%- endif -%}
</td>
{%- endif %}
<td>{{ (s.version or '-')|e }}</td>
{%- if s.admin -%}
<td>{{ s.admin|email }}</td>
{%- else -%}
<td>-</td>
{%- endif -%}
<td>
{%- if s.last_heard_from_age -%}
{{ s.last_heard_from_age }} <small>({{ s.last_heard_from_time }})</small>
{%- endif -%}
</td>
<td>
{{ s.connectCount }}
</td>
{% if s.connected %}
{% if s.running_builds %}
<td class="building success">Running {{ s.running_builds }} build(s)</td>
{% elif s.paused %}
<td class="warning paused">Paused</td>
{% else %}
<td class="success idle">Idle</td>
{% endif %}
{% else %}
<td class="disabled"><i class="fa fa-frown-o"></i> Not connected</td>
{% endif %}
</tr>
{% endfor %}
</table>
</div>
{% endblock %}

185
templates/directory.html Normal file
View File

@@ -0,0 +1,185 @@
{% extends "layout.html" %}
{% block content %}
{% if path == "download/" %}
<h1>Directory listing for {{ path }}</h1>
{% else %}
<h1>Download Latest Builds</h1>
<div class="box alert-warning">
<p><i class="fa fa-info-circle"></i> These builds are not as stable as releases, use at your own risk.</p>
</div>
{% endif %}
{% if path == "/download/experimental/" %}
<div class="box alert-danger"><p>
<h1><i class="fa fa-info-circle"></i> These builds are for developer use only!</h1>
<h2>They contain highly experimental patches, which might ruin your day!</h2>
<h2>If you're NOT a developer, please leave this page IMMEDIATELY!</h2>
</p></div>
{% endif %}
{% set row_class = cycler('alt', '') %}
<table class="table table-striped table-hover box">
<tr>
<th>Operating System</th>
<th>Variation</th>
<th>Name</th>
<th>Size</th>
<th>Built On</th>
</tr>
{% for d in directories %}
{% if not ("experimental" in d,text) %}
<tr class="directory {{ row_class.next() }}">
<td></td>
<td></td>
<td><a href="{{ d.href }}"><b>{{ d.text }}</b></a></td>
<td><b>{{ d.size }}</b></td>
<td>{{ d.lastmodified }}</td>
</tr>
{% endif %}
{% endfor %}
{% for f in files|sort %}
{% if ("mingw" in f.text) or ('gooseberry' in f.text) or ("experimental" in f.text) or ("vc1" in f.text) or ("vc9" in f.text) or ("multiview" in f.text) %}
{% else %}
<tr class="file {{ row_class.next() }}">
{% if "mingw32" in f.text %}
<td><i class="fa fa-windows"></i> Windows 32 bit
{% elif "win32" in f.text %}
<td><i class="fa fa-windows"></i> Windows 32 bit
{% elif "mingw64" in f.text %}
<td><i class="fa fa-windows"></i> Windows 64 bit
{% elif "win64" in f.text %}
<td><i class="fa fa-windows"></i> Windows 64 bit
{% elif "OSX" in f.text %}
{% if "x86_64" in f.text %}
<td><i class="fa fa-apple"></i> Mac OS X 64 bit
{% else %}
<td><i class="fa fa-apple"></i> Mac OS X 32 bit
{% endif %}
{% elif "linux" in f.text %}
{% if "x86_64" in f.text %}
<td><i class="fa fa-linux"></i> Linux 64 bit
{% else %}
<td><i class="fa fa-linux"></i> Linux 32 bit
{% endif %}
{% endif %}
</td>
<td>
Official
</td>
<td><a class="info" onclick="ga('send', 'event', 'button', 'download', '{{f.text}}');" href="{{ f.href }}">{{ f.text }}</a></td>
<td>{{ f.size }}</td>
<td>
{{ f.lastmodified }}
</td>
</tr>
{% endif %}
{% endfor %}
{% for f in files|sort %}
{% if ("experimental" in f.text) %}
<tr class="file {{ row_class.next() }}">
{% if "mingw32" in f.text %}
<td><i class="fa fa-windows"></i> Windows 32 bit
{% elif "win32" in f.text %}
<td><i class="fa fa-windows"></i> Windows 32 bit
{% elif "mingw64" in f.text %}
<td><i class="fa fa-windows"></i> Windows 64 bit
{% elif "win64" in f.text %}
<td><i class="fa fa-windows"></i> Windows 64 bit
{% elif "OSX" in f.text %}
{% if "x86_64" in f.text %}
<td><i class="fa fa-apple"></i> Mac OS X 64 bit
{% else %}
<td><i class="fa fa-apple"></i> Mac OS X 32 bit
{% endif %}
{% elif "linux" in f.text %}
{% if "x86_64" in f.text %}
<td><i class="fa fa-linux"></i> Linux 64 bit
{% else %}
<td><i class="fa fa-linux"></i> Linux 32 bit
{% endif %}
{% endif %}
</td>
<td>Experimental Build Branch</td>
<td><a class="info" onclick="ga('send', 'event', 'button', 'download', '{{f.text}}');" href="{{ f.href }}">{{ f.text }}</a></td>
<td>{{ f.size }}</td>
<td>{{ f.lastmodified }}</td>
</tr>
{% endif %}
{% endfor %}
<tr>
<td>&nbsp;</th>
<td>&nbsp;</th>
<td>&nbsp;</th>
<td>&nbsp;</th>
<td>&nbsp;</th>
</tr>
{% for f in files|sort %}
{% if (not (("mingw" in f.text) or ("vc1" in f.text) or ("vc9" in f.text) or ("multiview" in f.text) or ("gooseberry" in f.text) )) %}
{% else %}
<tr class="file {{ row_class.next() }}">
{% if "mingw32" in f.text %}
<td><i class="fa fa-windows"></i> Windows 32 bit
{% elif "win32" in f.text %}
<td><i class="fa fa-windows"></i> Windows 32 bit
{% elif "mingw64" in f.text %}
<td><i class="fa fa-windows"></i> Windows 64 bit
{% elif "win64" in f.text %}
<td><i class="fa fa-windows"></i> Windows 64 bit
{% elif "OSX" in f.text %}
{% if "x86_64" in f.text %}
<td><i class="fa fa-apple"></i> Mac OS X 64 bit
{% else %}
<td><i class="fa fa-apple"></i> Mac OS X 32 bit
{% endif %}
{% elif "linux" in f.text %}
{% if "x86_64" in f.text %}
<td><i class="fa fa-linux"></i> Linux 64 bit
{% else %}
<td><i class="fa fa-linux"></i> Linux 32 bit
{% endif %}
{% endif %}
</td>
<td>
{% if "mingw" in f.text %}
Experimental MinGW
{% elif "vc11" in f.text %}
Experimental VS 2012<br/><span style="font-weight: normal;">(Windows Vista/7/8)</span>
{% elif "vc9" in f.text %}
Legacy VS 2008<br/><span style="font-weight: normal;">(Windows XP/Vista/7/8)</span>
{% elif "win32-vc12" in f.text %}
Official Preview VS 2013<br/><span style="font-weight: normal;">(Windows XP/Vista/7/8)</span>
{% elif "win64-vc12" in f.text %}
Official Preview VS 2013<br/><span style="font-weight: normal;">(Windows Vista/7/8)</span>
{% elif "multiview" in f.text %}
Multiview Branch<br/><span style="font-weight: normal;">(Stereoscopic 3D)</span>
{% elif "gooseberry" in f.text %}
Gooseberry Branch
{% else %}
Official
{% endif %}
</td>
<td><a class="info" onclick="ga('send', 'event', 'button', 'download', '{{f.text}}');" href="{{ f.href }}">{{ f.text }}</a></td>
<td>{{ f.size }}</td>
<td>{{ f.lastmodified }}</td>
</tr>
{% endif %}
{% endfor %}
</table>
{% endblock %}

274
templates/forms.html Normal file
View File

@@ -0,0 +1,274 @@
{% macro cancel_pending_build(cancel_url, authz, short=False, id='all') %}
<form method="post" name="cancel" action="{{ cancel_url }}" class='form-horizontal cancelbuild'
{{ 'style="display:inline"' if short else '' }}>
{% if not short %}
{% if id == 'all' %}
<p>To cancel all builds, push the 'Cancel' button</p>
<p>To cancel individual builds, click the 'Cancel' buttons above.</p>
{% else %}
<p>To cancel this build, push the 'Cancel' button</p>
{% endif %}
{% endif %}
<input type="hidden" name="id" value="{{ id }}" />
<input class="btn btn-default btn-success btn-squishy" type="submit" value="Cancel" />
</form>
{% endmacro %}
{% macro stop_change_builds(stopchange_url, changenum, authz) %}
{% if not changenum %}
<form method="post" action="{{ stopchange_url }}" class='form-horizontal stopchange'>
{% if changenum %}
<p>To cancel all builds for this change, push the 'Cancel' button</p>
{% else %}
<p>To cancel builds for this builder for a given change, fill out the
following field and push the 'Cancel' button</p>
{% endif %}
{% if changenum %}
<input type="hidden" name="change" value="{{ changenum }}" />
{% else %}
<div class="row">
<span class="label">Change #:</span>
<input type="text" name="change"/>
</div>
{% endif %}
<input class="btn btn-default btn-success btn-squishy" type="submit" value="Cancel" />
</form>
{% endif %}
{% endmacro %}
{% macro stop_build(stop_url, authz, on_all=False, on_selected=False, builders=[], short=False, label="Build") %}
{% if not short %}
<form method="post" name="stop_build" action="{{ stop_url }}" class='form-horizontal stopbuild'
{{ 'style="display:inline"' if short else '' }}>
{% if not short %}
{% if on_all %}
<p>To stop all builds, fill out the following field and
push the <i>Stop {{ label }}</i> button</p>
{% elif on_selected %}
<p>To stop selected builds, select the builders, fill out the
following field and push the <i>Stop {{ label }}</i> button</p>
<table>
{% for b in builders %}
<tr>
<td align="center"><input type="checkbox" name="selected" value="{{ b.name }}"></td>
<td class="box"><a href="{{ b.link }}">{{ b.name|e }}</a></td>
</tr>
{% endfor %}
</table>
{% else %}
<p>To stop this build, fill out the following field and
push the <i>Stop {{ label }}</i> button</p>
{% endif %}
{% endif %}
{% if not short %}
<div class="row">
<span class="label">Reason:</span>
<input type="text" name="comments"/>
</div>
{% endif %}
<input class="btn btn-default btn-success btn-squishy" type="submit" value="Stop {{ label }}" />
</form>
{% endif %}
{% endmacro %}
{% macro cancel_build(cancel_url, authz, on_all=False, on_selected=False, builders=[], short=False, label="Build") %}
{% if not short %}
<form method="post" name="cancel_build" action="{{ cancel_url }}" class='form-horizontal cancelbuild'
{{ 'style="display:inline"' if short else '' }}>
{% if not short %}
{% if on_all %}
<p>To cancel all pending builds, fill out the following field and
push the <i>Cancel {{ label }}</i> button</p>
{% elif on_selected %}
<p>To cancel selected pending builds, select the builders, fill out the
following field and push the <i>Cancel {{ label }}</i> button</p>
<table>
{% for b in builders %}
<tr>
<td align="center"><input type="checkbox" name="selected" value="{{ b.name }}"></td>
<td class="box"><a href="{{ b.link }}">{{ b.name|e }}</a></td>
</tr>
{% endfor %}
</table>
{% else %}
<p>To cancel this pending build, fill out the following field and
push the <i>Cancel {{ label }}</i> button</p>
{% endif %}
{% endif %}
{% if not short %}
<div class="row">
<span class="label">Reason:</span>
<input type="text" name="comments"/>
</div>
{% endif %}
<input class="btn btn-default btn-success btn-squishy" type="submit" value="Cancel {{ label }}" />
</form>
{% endif %}
{% endmacro %}
{% macro force_build_scheduler_parameter(f, authz, request, sch, default_props) %}
{% if f and not f.hide and (f.fullName != "username" or not authz.authenticated(request)) %}
<div class="row{% for subtype in f.type %} force-{{subtype}}{%endfor%}"{% if f.name %} id="force-{{sch.name}}-{{f.fullName}}"{% endif %}>
{% if 'text' in f.type or 'int' in f.type %}
<span class="label">{{f.label}}</span>
<input type='text' size='{{f.size}}' name='{{f.fullName}}' value='{{default_props[sch.name+"."+f.fullName]}}' />
{% elif 'bool' in f.type%}
<input type='checkbox' name='checkbox' value='{{f.fullName}}' {{default_props[sch.name+"."+f.fullName]}} />
<span class="label">{{f.label}}</span>
{% elif 'textarea' in f.type %}
<span class="label">{{f.label}}</span>
<textarea name='{{f.fullName}}' rows={{f.rows}} cols={{f.cols}}>{{default_props[sch.name+"."+f.fullName]}}</textarea>
{% elif 'list' in f.type %}
<span class="label">{{f.label}}</span>
<span class="select">
<select name='{{f.fullName}}' {{ f.multiple and "multiple" or ""}}>
{% for c in default_props[sch.name+"."+f.fullName+".choices"] %}
<option {{(c in default_props[sch.name+"."+f.fullName]) and "selected" or ""}}>{{c}}</option>
{% endfor %}
</select>
</span>
{% elif 'nested' in f.type %}
{% if f.label %}<span class="label">{{f.label}}</span>{% endif %}
{% for subfield in f.fields %}
{{ force_build_scheduler_parameter(subfield, authz, request, sch, default_props) }}
{% endfor %}
{% endif %}
</div>
{% endif %}
{% endmacro %}
{% macro force_build_one_scheduler(force_url, authz, request, on_all, on_selected, builders, sch, default_props) %}
<form method="post" name="force_build" action="{{ force_url }}" class="command_forcebuild">
<h3>{{ sch.name|e }}</h3>
{% if on_all %}
<p>To force a build on <strong>all Builders</strong>, fill out the following fields
and push the 'Force Build' button</p>
{% elif on_selected %}
<p>To force a build on <strong>certain Builders</strong>, select the
builders, fill out the following fields and push the
'Force Build' button</p>
<table>
{% for b in builders %}
{% if b.name in sch.builderNames %}
<tr>
<td align="center"><input type="checkbox" name="selected" value="{{ b.name }}"></td>
<td class="box"><a href="{{ b.link }}">{{ b.name|e }}</a></td>
</tr>
{% endif %}
{% endfor %}
</table>
{% else %}
<p>To force a build, fill out the following fields and
push the 'Force Build' button</p>
{% endif %}
<input type='hidden' name='forcescheduler' value='{{sch.name}}' />
{% for f in sch.all_fields %}
{{ force_build_scheduler_parameter(f, authz, request, sch, default_props) }}
{% endfor %}
<input class="btn btn-default btn-success btn-squishy" type="submit" value="{{ sch.buttonName }}" />
</form>
{% endmacro %}
{% macro force_build(force_url, authz, request, on_all=False, on_selected=False, builders=[], force_schedulers={},default_props={}) %}
{% for name, sch in force_schedulers.items() | sort %}
{{ force_build_one_scheduler(force_url, authz, request, on_all, on_selected, builders, sch, default_props=default_props) }}
{% endfor %}
{% endmacro %}
{% macro graceful_shutdown(shutdown_url, authz) %}
<form method="post" action="{{ shutdown_url }}" class='form-horizontal graceful_shutdown'>
<p>To cause this slave to shut down gracefully when it is idle,
push the 'Graceful Shutdown' button</p>
<input class="btn btn-default btn-success btn-squishy" type="submit" value="Graceful Shutdown" />
</form>
{% endmacro %}
{% macro pause_slave(pause_url, authz, paused) %}
<form method="post" action="{{ pause_url }}" class='form-horizontal pause_slave'>
{% if paused %}
<p>To cause this slave to start running new builds again,
push the 'Unpause Slave' button</p>
{% else %}
<p>To cause this slave to stop running new builds,
push the 'Pause Slave' button</p>
{% endif %}
{% if paused %}
<input class="btn btn-default btn-success btn-squishy" type="submit" value="Unpause Slave" />
{% else %}
<input class="btn btn-default btn-success btn-squishy" type="submit" value="Pause Slave" />
{% endif %}
</form>
{% endmacro %}
{% macro clean_shutdown(shutdown_url, authz) %}
<form method="post" action="{{ shutdown_url }}" class='form-horizontal clean_shutdown'>
<p>To cause this master to shut down cleanly, push the 'Clean Shutdown' button.</p>
<p>No other builds will be started on this master, and the master will
stop once all current builds are finished.</p>
<input class="btn btn-default btn-success btn-squishy" type="submit" value="Clean Shutdown" />
</form>
{% endmacro %}
{% macro cancel_clean_shutdown(cancel_shutdown_url, authz) %}
<form method="post" action="{{ cancel_shutdown_url }}" class='form-horizontal cancel_clean_shutdown'>
<p>To cancel a previously initiated shutdown, push the 'Cancel Shutdown' button.</p>
<input class="btn btn-default btn-success btn-squishy" type="submit" value="Cancel Shutdown" />
</form>
{% endmacro %}
{% macro ping_builder(ping_url, authz) %}
<form method="post" action="{{ ping_url }}" class='form-horizontal ping_builder'>
<p>To ping the buildslave(s), push the 'Ping' button</p>
<input class="btn btn-default btn-success btn-squishy" type="submit" value="Ping Builder" />
</form>
{% endmacro %}
{% macro rebuild_build(rebuild_url, authz, ss) %}
<form method="post" action="{{ rebuild_url }}" class="form-horizontal rebuild">
{% if on_all %}
<p>To force a build on <strong>all Builders</strong>, fill out the following fields
and push the 'Force Build' button</p>
{% else %}
<p>To force a build, fill out the following fields and
push the 'Force Build' button</p>
{% endif %}
<div class="row">
<span class="label">Reason for re-running build:</span>
<input type='text' name='comments' />
</div>
<div class="row">
Rebuild using:
<select name="useSourcestamp">
<option value='exact' selected>Exact same revisions</option>
<option value='updated'>Same sourcestamps (ignoring 'got revision')</option>
</select>
</div>
<input class="btn btn-default btn-success btn-squishy" type="submit" value="Rebuild" />
</form>
{% endmacro %}
{% macro show_users(users_url, authz) %}
<form method="post" action="{{ users_url }}" class='form-horizontal show_users'>
<p>To show users, press the 'Show Users' button</p>
<input class="btn btn-default btn-success btn-squishy" type="submit" value="Show Users" />
</form>
{% endmacro %}

105
templates/layout.html Normal file
View File

@@ -0,0 +1,105 @@
{%- block doctype -%}
<!DOCTYPE html>
{% endblock %}
<html lang="en">
<head>
{% block head %}
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
{% if metatags %}
{{ metatags }}
{% endif %}
{% if refresh %}
<meta http-equiv="refresh" content="{{ refresh|e }}"/>
{% endif %}
<title>{{ title|e }}</title>
<link href="https://netdna.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
<link href="https://netdna.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
<link href="{{ path_to_root }}css/main.css" rel="stylesheet">
<link rel="stylesheet" href="{{ stylesheet }}" type="text/css" />
<link rel="alternate" type="application/rss+xml" title="RSS" href="{{ path_to_root }}rss">
<link href="{{ path_to_root }}css/font-borg.css" rel="stylesheet">
<link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Open+Sans+Condensed:300' rel='stylesheet' type='text/css'>
{% endblock %}
</head>
<body class="interface" onload="parent.postMessage(document.body.scrollHeight, 'https://developer.blender.org');">
{% block header -%}
<header class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#cloud-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<i class="fa fa-bars"></i>
</button>
<a class="logo" href="{{ path_to_root or '.' }}"></a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<nav class="collapse navbar-collapse" id="cloud-navbar-collapse-1">
<ul class="nav navbar-nav navbar-right">
<li class="menu-item menu-item-type-post_type menu-item-object-page"><a href="{{ path_to_root }}download">Download</a></li>
<li class="menu-item menu-item-type-post_type menu-item-object-page"><a href="{{ path_to_root }}waterfall">Waterfall</a></li>
<li class="menu-item menu-item-type-post_type menu-item-object-page"><a href="{{ path_to_root }}grid">Grid</a></li>
<li class="menu-item menu-item-type-post_type menu-item-object-page"><a href="{{ path_to_root }}tgrid">T-Grid</a></li>
<li class="menu-item menu-item-type-post_type menu-item-object-page"><a href="{{ path_to_root }}builders">Builders</a></li>
<li class="menu-item menu-item-type-post_type menu-item-object-page"><a href="{{ path_to_root }}one_line_per_build">Recent Builds</a></li>
<li class="menu-item menu-item-type-post_type menu-item-object-page"><a href="{{ path_to_root }}buildslaves">Buildslaves</a></li>
<li class="menu-item menu-item-type-post_type menu-item-object-page"><a href="{{ path_to_root }}about">About</a></li>
</ul>
</nav><!-- /.navbar-collapse -->
</div>
</header>
{% endblock %}
<div class="container-fluid featured featured-xs">
<div class="col-md-12 height-full">
<div class="container vertical-align">
</div>
</div>
</div>
{%- block barecontent -%}
<div class="content col-md-12">
{%- block content -%}
{%- endblock -%}
</div>
{%- endblock -%}
{%- block footer -%}
<footer>
<div class="container">
<span class="pull-left">
<a href="http://buildbot.net/">BuildBot</a> ({{version}})
{% if project_name -%}
working for the&nbsp;
{%- if project_url -%}
<a href="{{ project_url }}">{{ project_name }}</a>
{%- else -%}
{{ project_name }}
{%- endif -%}
&nbsp;project.
{%- endif -%}
</span>
<span class="pull-right"><p>Page built: {{ time }} ({{ tz }})</p></span>
</div>
</footer>
{% endblock -%}
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-1418081-1', 'auto');
ga('send', 'pageview');
</script>
</body>
</html>

17
templates/root.html Normal file
View File

@@ -0,0 +1,17 @@
{% extends 'layout.html' %}
{% import 'forms.html' as forms %}
{% block content %}
<h1>Blender&nbsp;Buildbot</h1>
<div class="column">
<h3><a href="download">Download latest builds</a></h3>
<h3><a href="http://wiki.blender.org/index.php/Dev:Doc/BuildBot">Buildbot setup notes</a></h3>
</div>
{% endblock %}