Skip to content
Browse files

Add buildbot code for what will become the Vagrant CI

  • Loading branch information...
1 parent 60a350f commit 44540f369dac5307dc2b963ec69731bb2c8745d6 @mitchellh mitchellh committed Nov 12, 2011
View
13 .gitignore
@@ -4,6 +4,16 @@
# Vagrant stuff
acceptance_config.yml
package.box
+test/buildbot/config.cfg
+test/buildbot/master/http.log
+test/buildbot/master/master.cfg.sample
+test/buildbot/master/twistd.log
+test/buildbot/master/twistd.pid
+test/buildbot/master/state.sqlite
+test/buildbot/master/kiip-development/
+test/buildbot/slave/twistd.hostname
+test/buildbot/slave/twistd.log
+test/buildbot/slave/twistd.pid
Vagrantfile
.vagrant
@@ -19,6 +29,9 @@ _site/*
.yardoc/
doc/
+# Python
+*.pyc
+
# Rubinius
*.rbc
View
72 test/buildbot/README.md
@@ -0,0 +1,72 @@
+# Vagrant Buildbot System
+
+This is the code for the Vagrant buildbot system. [Buildbot](http://buildbot.net)
+is a continuous integration system that offers an extreme amount
+of flexibility and power.
+
+This directory contains a few subdirectories to setup this CI system:
+
+* `buildbot_config` - This contains the custom Python code to setup the
+ various parts of the buildbot configuration.
+* `master` - This is mostly auto-generated from Buildbot itself, however
+ the `master.cfg` file is the configuration used on the buildmaster.
+* `slave`- This is mostly auto-generated from Buildbot, but the
+ `buildbot.tac` contains some custom code in it to connect to the Vagrant
+ buildmaster.
+
+**NOTE:** One of the dependencies for the Vagrant CI system is currently
+not public, and as such can't be setup by the general public. This will be
+fixed in the next couple weeks.
+
+## Contribute a CI Slave!
+
+**NOTE:** The slave contribution process is still not completely setup and
+will be ironed out very soon after the CI system is up and running.
+
+Vagrant is an open source profit which doesn't have any income from support,
+services, or otherwise. All Vagrant slave machines are donated by the
+community. Donating a machine doesn't require anything more than installing
+and running the slave software. Vagrant is specifically looking for slave
+machines that provide a diverse set of operating systems and cpu architectures
+for testing Vagrant.
+
+## Setting up the Buildmaster
+
+To set up the buildmaster, clone out this directory somewhere and install
+the dependencies:
+
+ pip install -r requirements.txt
+
+Once the dependencies are installed, create a configuration file with the
+settings you want to use somewhere. The settings available for a master are
+defined in `buildbot_config/config/master.py`. An example configuration file:
+
+ [master]
+ slaves=foo:password,bar:anotherpassword
+ web_port=8000
+
+Execute the buildbot using:
+
+ BUILDBOT_CONFIG=/path/to/my/config.cfg buildbot start master/
+
+## Setting up a Buildslave
+
+To set up a slave, clone out this directory and install the dependencies:
+
+ pip install -r requirements.txt
+
+Then, create a configuration file with the slave settings. The settings
+available for a slave are defined in `buildbot_config/config/slave.py`.
+An example configuration file:
+
+ [slave]
+ master_host=ci.vagrantup.com
+ master_port=9989
+ name=the-love-machine
+ password=foobarbaz
+
+Note that the password above will be assigned to you as part of donating
+any slave machine, since it must be setup on the buildmaster side as well.
+Once the configuration is done, run the slave:
+
+ BUILDBOT_CONFIG=/path/to/my/config.cfg buildslave start slave/
View
0 test/buildbot/buildbot_config/__init__.py
No changes.
View
0 test/buildbot/buildbot_config/config/__init__.py
No changes.
View
24 test/buildbot/buildbot_config/config/loader.py
@@ -0,0 +1,24 @@
+"""
+This module contains the configuration loader for a specific
+choices instance. This is used internally to load the settings.
+"""
+
+import os
+
+from choices import ConfigFileLoader
+
+def load_settings(choices, type):
+ """
+ This will load the proper settings for the given choices
+ instance.
+
+ :Parameters:
+ - `choices`: The choices instance to load.
+ - `type`: The type of configuration, either "master" or
+ "slave"
+ """
+ if "BUILDBOT_CONFIG" not in os.environ:
+ raise ValueError, "BUILDBOT_CONFIG must be set to point to where the configuration file is."
+
+ choices.add_loader(ConfigFileLoader(os.environ["BUILDBOT_CONFIG"], type))
+ return choices.load()
View
23 test/buildbot/buildbot_config/config/master.py
@@ -0,0 +1,23 @@
+"""
+This module contains the choices definition for settings required
+for the build master to run.
+"""
+
+from choices import Choices
+
+from loader import load_settings
+
+#----------------------------------------------------------------------
+# Define the Settings
+#----------------------------------------------------------------------
+c = Choices()
+c.define('title', type=str, help="Buildmaster title")
+c.define('title_url', type=str, help="URL for title page")
+c.define('buildbot_url', type=str, help="URL to the buildbot master.")
+c.define('slaves', type=str, help="A list of the slave machines. The format should be name:password,name:password,...")
+c.define('web_port', type=int, help="Port to listen on for web service.")
+
+#----------------------------------------------------------------------
+# Load the Settings
+#----------------------------------------------------------------------
+options = load_settings(c, "master")
View
22 test/buildbot/buildbot_config/config/slave.py
@@ -0,0 +1,22 @@
+"""
+This module contains the choices definition for settings required for the
+build slave to run.
+"""
+
+from choices import Choices
+
+from loader import load_settings
+
+#----------------------------------------------------------------------
+# Define the Settings
+#----------------------------------------------------------------------
+c = Choices()
+c.define("master_host", type=str, help="Host of the build master.")
+c.define("master_port", type=int, help="Port that is listening or build masters.")
+c.define("name", type=str, help="Name of the slave machine.")
+c.define("password", type=str, help="Password for the slave machine to communicate with the master.")
+
+#----------------------------------------------------------------------
+# Load the Settings
+#----------------------------------------------------------------------
+options = load_settings(c, "slave")
View
6 test/buildbot/buildbot_config/master/__init__.py
@@ -0,0 +1,6 @@
+import buildsteps
+from builders import get_builders
+from change_sources import get_change_sources
+from schedulers import get_schedulers
+from slaves import get_slaves_from_config
+from status import get_status
View
26 test/buildbot/buildbot_config/master/builders.py
@@ -0,0 +1,26 @@
+"""
+This module contains the logic to create and return the various builders
+that this buildmaster supports. The builders are responsible for taking
+a set of changes and giving the steps necessary to build the project.
+"""
+
+from buildbot.config import BuilderConfig
+from buildbot.process.factory import BuildFactory
+from buildbot.process.properties import WithProperties
+from buildbot.steps.source.git import Git
+
+from buildbot_config.master import buildsteps
+
+def get_builders(slaves):
+ """
+ This returns a list of builder configurations for the given
+ slaves.
+ """
+ f = BuildFactory()
+ # TODO
+
+ return [BuilderConfig(
+ name="vagrant-master",
+ slavenames=[s.slavename for s in slaves],
+ factory=f)
+ ]
View
7 test/buildbot/buildbot_config/master/buildsteps.py
@@ -0,0 +1,7 @@
+"""
+Contains various buildsteps that the build master uses.
+"""
+
+import os
+
+# TODO
View
7 test/buildbot/buildbot_config/master/change_sources.py
@@ -0,0 +1,7 @@
+"""
+Contains the logic to build and return the change sources for
+the build master.
+"""
+
+def get_change_sources():
+ return []
View
7 test/buildbot/buildbot_config/master/schedulers.py
@@ -0,0 +1,7 @@
+"""
+This module contains the logic which returns the set of
+schedulers to use for the build master.
+"""
+
+def get_schedulers():
+ return []
View
60 test/buildbot/buildbot_config/master/slaves.py
@@ -0,0 +1,60 @@
+"""
+This module contains the classes and methods which help load the
+list of available build slaves based on the configuration.
+"""
+
+from buildbot.buildslave import BuildSlave
+
+class BuildSlavesFromSlaveConfigs(list):
+ """
+ This object turns the ``SlaveConfig`` objects into actual
+ ``BuildSlave`` objects. This list can be directly used as the
+ setting.
+ """
+
+ def __init__(self, configs):
+ for config in configs:
+ self.append(BuildSlave(config.name, config.password))
+
+class SlaveListFromConfig(list):
+ """
+ This object knows how to parse the slave configuration settings
+ and load them into ``SlaveConfig`` value objects. The results
+ can be read directly from this list.
+ """
+
+ def __init__(self, config):
+ for config in self._slave_configs(config):
+ self.append(config)
+
+ def _slave_configs(self, config):
+ """
+ Returns an array of all the slaves that were configured
+ with the given configuration string.
+ """
+ results = []
+ for single in config.split(","):
+ results.append(SlaveConfig(*single.split(":")))
+
+ return results
+
+class SlaveConfig(object):
+ """
+ This is a value class, meant to be immutable, representing
+ the configuration of a single slave.
+ """
+
+ def __init__(self, name, password):
+ self.name = name
+ self.password = password
+
+ def __eq__(self, other):
+ """
+ Provides equality tests for slave configurations, specifically
+ for tests.
+ """
+ return self.__dict__ == other.__dict__
+
+# Shortcut methods to make things a bit nicer
+def get_slaves_from_config(config):
+ return BuildSlavesFromSlaveConfigs(SlaveListFromConfig(config))
View
31 test/buildbot/buildbot_config/master/status.py
@@ -0,0 +1,31 @@
+"""
+This module returns the given status handlers to enable for the
+buildbot master.
+"""
+
+from buildbot.status import html
+from buildbot.status.web.authz import Authz
+
+def get_status(options):
+ """
+ Returns a list of status targets for the build master.
+ """
+ authz = Authz(
+ gracefulShutdown = True,
+ forceBuild = True,
+ forceAllBuilds = True,
+ pingBuilder = True,
+ stopBuild = True,
+ stopAllBuilds = True,
+ cancelPendingBuild = True,
+ stopChange = True,
+ cleanShutdown= True
+ )
+
+ web_status = html.WebStatus(
+ http_port = options.web_port,
+ authz = authz,
+ order_console_by_time = True
+ )
+
+ return [web_status]
View
28 test/buildbot/master/Makefile.sample
@@ -0,0 +1,28 @@
+# -*- makefile -*-
+
+# This is a simple makefile which lives in a buildmaster
+# directory (next to the buildbot.tac file). It allows you to start/stop the
+# master by doing 'make start' or 'make stop'.
+
+# The 'reconfig' target will tell a buildmaster to reload its config file.
+
+start:
+ twistd --no_save -y buildbot.tac
+
+stop:
+ if [ -e twistd.pid ]; \
+ then kill `cat twistd.pid`; \
+ else echo "Nothing to stop."; \
+ fi
+
+reconfig:
+ if [ -e twistd.pid ]; \
+ then kill -HUP `cat twistd.pid`; \
+ else echo "Nothing to reconfig."; \
+ fi
+
+log:
+ if [ -e twistd.log ]; \
+ then tail -f twistd.log; \
+ else echo "Nothing to tail."; \
+ fi
View
36 test/buildbot/master/buildbot.tac
@@ -0,0 +1,36 @@
+
+import os
+
+from twisted.application import service
+from buildbot.master import BuildMaster
+
+basedir = r'.'
+rotateLength = 10000000
+maxRotatedFiles = 10
+
+# 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
+
View
67 test/buildbot/master/master.cfg
@@ -0,0 +1,67 @@
+# -*- python -*-
+# ex: set syntax=python:
+
+from buildbot_config.config.master import options
+from buildbot_config.master import (
+ get_builders,
+ get_change_sources,
+ get_schedulers,
+ get_slaves_from_config,
+ get_status)
+
+# This is the special key that Buildbot looks for. We use
+# an alias to ``c`` to save us a lot of typing.
+c = BuildmasterConfig = {}
+
+#----------------------------------------------------------------------
+# Project Identity and Basic Configuration
+#----------------------------------------------------------------------
+# Title and URL of project
+c['title'] = options.title
+c['titleURL'] = options.title_url
+
+# URL to the actual buildbot installation
+c['buildbotURL'] = options.buildbot_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"
+
+#----------------------------------------------------------------------
+# Data Storage and Caching
+#----------------------------------------------------------------------
+# Time horizons. These specify the number of items that are kept
+# persisted on disk before being pruned away. See the buildbot
+# documentation for more information on each horizon setting.
+c['changeHorizon'] = 0
+c['buildHorizon'] = 0
+c['eventHorizon'] = 0
+c['logHorizon'] = 0
+
+# Cache sizes. These are the number of the respective objects to
+# hold in memory while buildbot is running. By default buildbot
+# only stores 1, so all the numbers are greatly increased.
+c['caches'] = {
+ 'Changes' : 100,
+ 'chdicts' : 100,
+ 'BuildRequests' : 10,
+ 'SourceStamps' : 20,
+ 'ssdicts' : 20,
+ 'objectids' : 10,
+ 'usdicts' : 100,
+}
+c['buildCacheSize'] = 500
+
+#----------------------------------------------------------------------
+# Build Slaves
+#----------------------------------------------------------------------
+# The port that the buildmaster listens for buildslaves on.
+c['slavePortnum'] = 9989
+
+# The actual list of slave machines we have.
+c['slaves'] = get_slaves_from_config(options.slaves)
+c['status'] = get_status(options)
+c['builders'] = get_builders(c['slaves'])
+c['schedulers'] = get_schedulers()
+c['change_source'] = get_change_sources()
View
BIN test/buildbot/master/public_html/bg_gradient.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
545 test/buildbot/master/public_html/default.css
@@ -0,0 +1,545 @@
+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;
+}
+
+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.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;
+ 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;
+ background-color: #eee;
+ text-align: left;
+}
+
+td.Alt {
+ background-color: #ddd;
+}
+
+.legend {
+ border-radius: 5px;
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ 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;
+}
+
+.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;
+}
+
+.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;
+}
+
+.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
+}
+
+.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;
+}
+
+/* revision & email */
+.revision .full {
+ display: none;
+}
+
+.user .email {
+ display: none;
+}
+
+/* change comments (use regular colors here) */
+pre.comments>a:link,pre.comments>a:visited {
+ color: blue;
+}
+
+pre.comments>a:active {
+ color: purple;
+}
View
BIN test/buildbot/master/public_html/favicon.ico
Binary file not shown.
View
10 test/buildbot/master/public_html/robots.txt
@@ -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
View
4 test/buildbot/requirements.txt
@@ -0,0 +1,4 @@
+buildbot==0.8.5
+buildbot-slave==0.8.5
+choices==0.3.4
+pytest==2.1.0
View
43 test/buildbot/slave/buildbot.tac
@@ -0,0 +1,43 @@
+import os
+
+from twisted.application import service
+from buildslave.bot import BuildSlave
+
+from buildbot_config.config.slave import options
+
+basedir = r'.'
+rotateLength = 10000000
+maxRotatedFiles = 10
+
+# 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 buildslave
+# directory; do not edit it.
+application = service.Application('buildslave')
+
+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
+
+buildmaster_host = options.master_host
+port = options.master_port
+slavename = options.name
+passwd = options.password
+keepalive = 600
+usepty = 0
+umask = None
+maxdelay = 300
+
+s = BuildSlave(buildmaster_host, port, slavename, passwd, basedir,
+ keepalive, usepty, umask=umask, maxdelay=maxdelay)
+s.setServiceParent(application)
+
View
1 test/buildbot/slave/info/admin
@@ -0,0 +1 @@
+Buildslave Admin <foo@bar.com>
View
1 test/buildbot/slave/info/host
@@ -0,0 +1 @@
+Please put a description of this build host here
View
0 test/buildbot/tests/__init__.py
No changes.
View
0 test/buildbot/tests/master/__init__.py
No changes.
View
41 test/buildbot/tests/master/test_slaves.py
@@ -0,0 +1,41 @@
+from buildbot.buildslave import BuildSlave
+
+from buildbot_config.master.slaves import (
+ BuildSlavesFromSlaveConfigs,
+ SlaveConfig,
+ SlaveListFromConfig)
+
+class TestSlaveListFromConfig(object):
+ Klass = SlaveListFromConfig
+
+ def test_parse_single(self):
+ """
+ Tests that the config parser can parse a single
+ slave.
+ """
+ instance = self.Klass("foo:bar")
+ assert 1 == len(instance)
+ assert SlaveConfig("foo", "bar") == instance[0]
+
+ def test_parse_multiple(self):
+ """
+ Tests that the config parser can parse multiple
+ slaves.
+ """
+ instance = self.Klass("foo:bar,bar:baz")
+ expected = [SlaveConfig("foo", "bar"), SlaveConfig("bar", "baz")]
+
+ assert 2 == len(instance)
+ assert expected == instance
+
+class TestBuildSlavesFromSlaveConfig(object):
+ Klass = BuildSlavesFromSlaveConfigs
+
+ def test_returns_build_slaves(self):
+ """
+ Tests that build slaves are properly returned for each
+ slave configuration.
+ """
+ instance = self.Klass([SlaveConfig("foo", "bar")])
+ assert 1 == len(instance)
+ assert isinstance(instance[0], BuildSlave)

0 comments on commit 44540f3

Please sign in to comment.
Something went wrong with that request. Please try again.