Skip to content

Commit

Permalink
Merged with master
Browse files Browse the repository at this point in the history
  • Loading branch information
Harry Borkhuis committed Jun 6, 2012
2 parents 94fe285 + da08dff commit a22e6ff
Show file tree
Hide file tree
Showing 119 changed files with 3,938 additions and 1,783 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -39,3 +39,5 @@ apidocs/reference.tgz
common/googlecode_upload.py
master/docs/tutorial/_build
_build
.tox
master/setuptools_trial*
2 changes: 1 addition & 1 deletion master/MANIFEST.in
@@ -1,4 +1,4 @@
include MANIFEST.in README NEWS CREDITS COPYING UPGRADING
include MANIFEST.in README CREDITS COPYING UPGRADING

include docs/examples/*.cfg
include docs/conf.py
Expand Down
23 changes: 20 additions & 3 deletions master/buildbot/buildslave.py
Expand Up @@ -754,6 +754,7 @@ class AbstractLatentBuildSlave(AbstractBuildSlave):
substantiated = False
substantiation_deferred = None
substantiation_build = None
insubstantiating = False
build_wait_timer = None
_shutdown_callback_handle = None

Expand Down Expand Up @@ -824,7 +825,7 @@ def clean_up(failure):
return d

def attached(self, bot):
if self.substantiation_deferred is None:
if self.substantiation_deferred is None and self.build_wait_timeout >= 0:
msg = 'Slave %s received connection while not trying to ' \
'substantiate. Disconnecting.' % (self.slavename,)
log.msg(msg)
Expand Down Expand Up @@ -866,6 +867,11 @@ def _substantiation_failed(self, failure):
subject = "Buildbot: buildslave %s never substantiated" % self.slavename
return self._mail_missing_message(subject, text)

def canStartBuild(self):
if self.insubstantiating:
return False
return AbstractBuildSlave.canStartBuild(self)

def buildStarted(self, sb):
assert self.substantiated
self._clearBuildWaitTimer()
Expand All @@ -876,7 +882,10 @@ def buildFinished(self, sb):

self.building.remove(sb.builder_name)
if not self.building:
self._setBuildWaitTimer()
if self.build_wait_timeout == 0:
self.insubstantiate()
else:
self._setBuildWaitTimer()

def _clearBuildWaitTimer(self):
if self.build_wait_timer is not None:
Expand All @@ -886,10 +895,14 @@ def _clearBuildWaitTimer(self):

def _setBuildWaitTimer(self):
self._clearBuildWaitTimer()
if self.build_wait_timeout < 0:
return
self.build_wait_timer = reactor.callLater(
self.build_wait_timeout, self._soft_disconnect)

@defer.inlineCallbacks
def insubstantiate(self, fast=False):
self.insubstantiating = True
self._clearBuildWaitTimer()
d = self.stop_instance(fast)
if self._shutdown_callback_handle is not None:
Expand All @@ -898,9 +911,13 @@ def insubstantiate(self, fast=False):
reactor.removeSystemEventTrigger(handle)
self.substantiated = False
self.building.clear() # just to be sure
return d
yield d
self.insubstantiating = False

def _soft_disconnect(self, fast=False):
if not self.build_wait_timeout < 0:
return AbstractBuildSlave.disconnect(self)

d = AbstractBuildSlave.disconnect(self)
if self.slave is not None:
# this could be called when the slave needs to shut down, such as
Expand Down
53 changes: 39 additions & 14 deletions master/buildbot/changes/base.py
Expand Up @@ -35,36 +35,61 @@ class PollingChangeSource(ChangeSource):
Utility subclass for ChangeSources that use some kind of periodic polling
operation. Subclasses should define C{poll} and set C{self.pollInterval}.
The rest is taken care of.
Any subclass will be available via the "poller" webhook.
"""

pollInterval = 60
"time (in seconds) between calls to C{poll}"

_loop = None

def __init__(self, name=None, pollInterval=60*10):
if name:
self.setName(name)
self.pollInterval = pollInterval

self.doPoll = util.misc.SerializedInvocation(self.doPoll)

def doPoll(self):
"""
This is the method that is called by LoopingCall to actually poll.
It may also be called by change hooks to request a poll.
It is serialiazed - if you call it while a poll is in progress
then the 2nd invocation won't start until the 1st has finished.
"""
d = defer.maybeDeferred(self.poll)
d.addErrback(log.err, 'while polling for changes')
return d

def poll(self):
"""
Perform the polling operation, and return a deferred that will fire
when the operation is complete. Failures will be logged, but the
method will be called again after C{pollInterval} seconds.
"""

def startLoop(self):
self._loop = task.LoopingCall(self.doPoll)
self._loop.start(self.pollInterval, now=False)

def stopLoop(self):
if self._loop and self._loop.running:
self._loop.stop()
self._loop = None

def startService(self):
ChangeSource.startService(self)
def do_poll():
d = defer.maybeDeferred(self.poll)
d.addErrback(log.err, 'while polling for changes')
return d

# delay starting the loop until the reactor is running, and do not
# run it immediately - if services are still starting up, they may
# miss an initial flood of changes
def start_loop():
self._loop = task.LoopingCall(do_poll)
self._loop.start(self.pollInterval, now=False)
reactor.callWhenRunning(start_loop)

# delay starting doing anything until the reactor is running - if
# services are still starting up, they may miss an initial flood of
# changes
if self.pollInterval:
reactor.callWhenRunning(self.startLoop)
else:
reactor.callWhenRunning(self.doPoll)

def stopService(self):
if self._loop and self._loop.running:
self._loop.stop()
self.stopLoop()
return ChangeSource.stopService(self)

6 changes: 4 additions & 2 deletions master/buildbot/changes/bonsaipoller.py
Expand Up @@ -207,14 +207,16 @@ class BonsaiPoller(base.PollingChangeSource):
"module", "branch", "cvsroot"]

def __init__(self, bonsaiURL, module, branch, tree="default",
cvsroot="/cvsroot", pollInterval=30, project=''):
cvsroot="/cvsroot", pollInterval=30, project='', name=None):

base.PollingChangeSource.__init__(self, name=name, pollInterval=pollInterval)

self.bonsaiURL = bonsaiURL
self.module = module
self.branch = branch
self.tree = tree
self.cvsroot = cvsroot
self.repository = module != 'all' and module or ''
self.pollInterval = pollInterval
self.lastChange = time.time()
self.lastPoll = time.time()

Expand Down
7 changes: 5 additions & 2 deletions master/buildbot/changes/gitpoller.py
Expand Up @@ -36,15 +36,18 @@ def __init__(self, repourl, branch='master',
gitbin='git', usetimestamps=True,
category=None, project=None,
pollinterval=-2, fetch_refspec=None,
encoding='utf-8'):
encoding='utf-8', name=None):

# for backward compatibility; the parameter used to be spelled with 'i'
if pollinterval != -2:
pollInterval = pollinterval

base.PollingChangeSource.__init__(self, name=name, pollInterval=pollInterval)

if project is None: project = ''

self.repourl = repourl
self.branch = branch
self.pollInterval = pollInterval
self.fetch_refspec = fetch_refspec
self.encoding = encoding
self.lastChange = time.time()
Expand Down
62 changes: 32 additions & 30 deletions master/buildbot/changes/hgbuildbot.py
Expand Up @@ -47,8 +47,8 @@ def hook(ui, repo, hooktype, node=None, source=None, **kwargs):
# read config parameters
baseurl = ui.config('hgbuildbot', 'baseurl',
ui.config('web', 'baseurl', ''))
master = ui.config('hgbuildbot', 'master')
if master:
masters = ui.configlist('hgbuildbot', 'master')
if masters:
branchtype = ui.config('hgbuildbot', 'branchtype', 'inrepo')
branch = ui.config('hgbuildbot', 'branch')
fork = ui.configbool('hgbuildbot', 'fork', False)
Expand Down Expand Up @@ -88,11 +88,8 @@ def hook(ui, repo, hooktype, node=None, source=None, **kwargs):
auth = 'change:changepw'
auth = auth.split(':', 1)

s = sendchange.Sender(master, auth=auth)
d = defer.Deferred()
reactor.callLater(0, d.callback, None)
# process changesets
def _send(res, c):
def _send(res, s, c):
if not fork:
ui.status("rev %s sent\n" % c['revision'])
return s.send(c['branch'], c['revision'], c['comments'],
Expand All @@ -110,30 +107,35 @@ def _send(res, c):
repository = strip(repo.root, stripcount)
repository = baseurl + repository

for rev in xrange(start, end):
# send changeset
node = repo.changelog.node(rev)
manifest, user, (time, timezone), files, desc, extra = repo.changelog.read(node)
parents = filter(lambda p: not p == nullid, repo.changelog.parents(node))
if branchtype == 'inrepo':
branch = extra['branch']
is_merge = len(parents) > 1
# merges don't always contain files, but at least one file is required by buildbot
if is_merge and not files:
files = ["merge"]
properties = {'is_merge': is_merge}
if branch:
branch = fromlocal(branch)
change = {
'master': master,
'username': fromlocal(user),
'revision': hex(node),
'comments': fromlocal(desc),
'files': files,
'branch': branch,
'properties':properties
}
d.addCallback(_send, change)
for master in masters:
s = sendchange.Sender(master, auth=auth)
d = defer.Deferred()
reactor.callLater(0, d.callback, None)

for rev in xrange(start, end):
# send changeset
node = repo.changelog.node(rev)
manifest, user, (time, timezone), files, desc, extra = repo.changelog.read(node)
parents = filter(lambda p: not p == nullid, repo.changelog.parents(node))
if branchtype == 'inrepo':
branch = extra['branch']
is_merge = len(parents) > 1
# merges don't always contain files, but at least one file is required by buildbot
if is_merge and not files:
files = ["merge"]
properties = {'is_merge': is_merge}
if branch:
branch = fromlocal(branch)
change = {
'master': master,
'username': fromlocal(user),
'revision': hex(node),
'comments': fromlocal(desc),
'files': files,
'branch': branch,
'properties':properties
}
d.addCallback(_send, s, change)

def _printSuccess(res):
ui.status(s.getSuccessString(res) + '\n')
Expand Down
6 changes: 4 additions & 2 deletions master/buildbot/changes/p4poller.py
Expand Up @@ -66,11 +66,14 @@ def __init__(self, p4port=None, p4user=None, p4passwd=None,
p4base='//', p4bin='p4',
split_file=lambda branchfile: (None, branchfile),
pollInterval=60 * 10, histmax=None, pollinterval=-2,
encoding='utf8', project=None):
encoding='utf8', project=None, name=None):

# for backward compatibility; the parameter used to be spelled with 'i'
if pollinterval != -2:
pollInterval = pollinterval

base.PollingChangeSource.__init__(self, name=name, pollInterval=pollInterval)

if project is None:
project = ''

Expand All @@ -80,7 +83,6 @@ def __init__(self, p4port=None, p4user=None, p4passwd=None,
self.p4base = p4base
self.p4bin = p4bin
self.split_file = split_file
self.pollInterval = pollInterval
self.encoding = encoding
self.project = project

Expand Down
8 changes: 5 additions & 3 deletions master/buildbot/changes/svnpoller.py
Expand Up @@ -53,7 +53,7 @@ class SVNPoller(base.PollingChangeSource, util.ComparableMixin):
"""

compare_attrs = ["svnurl", "split_file",
"svnuser", "svnpasswd",
"svnuser", "svnpasswd", "project",
"pollInterval", "histmax",
"svnbin", "category", "cachepath"]

Expand All @@ -66,11 +66,14 @@ def __init__(self, svnurl, split_file=None,
pollInterval=10*60, histmax=100,
svnbin='svn', revlinktmpl='', category=None,
project='', cachepath=None, pollinterval=-2,
extra_args=None):
extra_args=None, name=None):

# for backward compatibility; the parameter used to be spelled with 'i'
if pollinterval != -2:
pollInterval = pollinterval

base.PollingChangeSource.__init__(self, name=name, pollInterval=pollInterval)

if svnurl.endswith("/"):
svnurl = svnurl[:-1] # strip the trailing slash
self.svnurl = svnurl
Expand All @@ -85,7 +88,6 @@ def __init__(self, svnurl, split_file=None,
# required for ssh-agent auth

self.svnbin = svnbin
self.pollInterval = pollInterval
self.histmax = histmax
self._prefix = None
self.category = category
Expand Down
23 changes: 13 additions & 10 deletions master/buildbot/config.py
Expand Up @@ -15,9 +15,10 @@

from __future__ import with_statement

import re
import os
import re
import sys
import warnings
from buildbot.util import safeTranslate
from buildbot import interfaces
from buildbot import locks
Expand Down Expand Up @@ -360,6 +361,11 @@ def load_caches(self, filename, config_dict, errors):
if not isinstance(caches, dict):
errors.addError("c['caches'] must be a dictionary")
else:
valPairs = caches.items()
for (x, y) in valPairs:
if (not isinstance(y, int)):
errors.addError(
"value for cache size '%s' must be an integer" % x)
self.caches.update(caches)

if 'buildCacheSize' in config_dict:
Expand Down Expand Up @@ -424,6 +430,12 @@ def mapper(b):
errors.addError("c['builders'] must be a list of builder configs")
return

for builder in builders:
if os.path.isabs(builder.builddir):
warnings.warn("Absolute path '%s' for builder may cause "
"mayhem. Perhaps you meant to specify slavebuilddir "
"instead.")

self.builders = builders


Expand Down Expand Up @@ -547,15 +559,6 @@ def check_lock(l):
for l in b.locks:
check_lock(l)

# factories don't necessarily need to implement a .steps attribute
# but in practice most do, so we'll check that if it exists
if not hasattr(b.factory, 'steps'):
continue
for s in b.factory.steps:
for l in s[1].get('locks', []):
check_lock(l)


def check_builders(self, errors):
# look both for duplicate builder names, and for builders pointing
# to unknown slaves
Expand Down

0 comments on commit a22e6ff

Please sign in to comment.