Add buildbot code for what will become the Vagrant CI
This commit is contained in:
parent
60a350f9c7
commit
44540f369d
|
@ -4,6 +4,16 @@
|
||||||
# Vagrant stuff
|
# Vagrant stuff
|
||||||
acceptance_config.yml
|
acceptance_config.yml
|
||||||
package.box
|
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
|
Vagrantfile
|
||||||
.vagrant
|
.vagrant
|
||||||
|
|
||||||
|
@ -19,6 +29,9 @@ _site/*
|
||||||
.yardoc/
|
.yardoc/
|
||||||
doc/
|
doc/
|
||||||
|
|
||||||
|
# Python
|
||||||
|
*.pyc
|
||||||
|
|
||||||
# Rubinius
|
# Rubinius
|
||||||
*.rbc
|
*.rbc
|
||||||
|
|
||||||
|
|
|
@ -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/
|
|
@ -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()
|
|
@ -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")
|
|
@ -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")
|
|
@ -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
|
|
@ -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)
|
||||||
|
]
|
|
@ -0,0 +1,7 @@
|
||||||
|
"""
|
||||||
|
Contains various buildsteps that the build master uses.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
# TODO
|
|
@ -0,0 +1,7 @@
|
||||||
|
"""
|
||||||
|
Contains the logic to build and return the change sources for
|
||||||
|
the build master.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_change_sources():
|
||||||
|
return []
|
|
@ -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 []
|
|
@ -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))
|
|
@ -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]
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
|
@ -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;
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
|
@ -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
|
|
@ -0,0 +1,4 @@
|
||||||
|
buildbot==0.8.5
|
||||||
|
buildbot-slave==0.8.5
|
||||||
|
choices==0.3.4
|
||||||
|
pytest==2.1.0
|
|
@ -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)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Buildslave Admin <foo@bar.com>
|
|
@ -0,0 +1 @@
|
||||||
|
Please put a description of this build host here
|
|
@ -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)
|
Loading…
Reference in New Issue