Skip to content

Commit

Permalink
Merge jaredgrubb/buildbot:builder-tags (PR #1103)
Browse files Browse the repository at this point in the history
+autopep8
  • Loading branch information
djmitche committed Apr 17, 2014
2 parents fbdefd1 + 0d22261 commit d246b54
Show file tree
Hide file tree
Showing 18 changed files with 213 additions and 123 deletions.
32 changes: 25 additions & 7 deletions master/buildbot/config.py
Expand Up @@ -58,6 +58,11 @@ def error(error):
raise ConfigErrors([error])


def warnDeprecated(version, msg):
# for now just log the deprecation
log.msg("NOTE: [%s and later] %s" % (version, msg))


class MasterConfig(util.ComparableMixin):

def __init__(self):
Expand Down Expand Up @@ -382,7 +387,7 @@ def load_db(self, filename, config_dict):

# db_poll_interval is deprecated
if 'db_poll_interval' in self.db:
log.msg("NOTE: db_poll_interval is deprecated and will be ignored")
warnDeprecated("0.8.7", "db_poll_interval is deprecated and will be ignored")
del self.db['db_poll_interval']

def load_mq(self, filename, config_dict):
Expand Down Expand Up @@ -688,7 +693,8 @@ def check_ports(self):
class BuilderConfig(util_config.ConfiguredMixin):

def __init__(self, name=None, slavename=None, slavenames=None,
builddir=None, slavebuilddir=None, factory=None, category=None,
builddir=None, slavebuilddir=None, factory=None,
tags=None, category=None,
nextSlave=None, nextBuild=None, locks=None, env=None,
properties=None, mergeRequests=None, description=None,
canStartBuild=None):
Expand Down Expand Up @@ -743,10 +749,22 @@ def __init__(self, name=None, slavename=None, slavenames=None,
self.slavebuilddir = slavebuilddir

# remainder are optional
if category is not None and not isinstance(category, str):
error("builder '%s': category must be a string" % (name,))

self.category = category or ''
if category and tags:
error("builder '%s': category is deprecated and replaced by tags; you should only specify tags" % (name,))
if category:
warnDeprecated("0.9", "category is deprecated and should be replaced with 'tags=[cat]'")
if not isinstance(category, str):
error("builder '%s': category must be a string" % (name,))
tags = [category]
if tags:
if not isinstance(tags, list):
error("builder '%s': tags must be a list" % (name,))
bad_tags = any((tag for tag in tags if not isinstance(tag, str)))
if bad_tags:
error("builder '%s': tags list contains something that is not a string" % (name,))
self.tags = tags

self.nextSlave = nextSlave
if nextSlave and not callable(nextSlave):
error('nextSlave must be a callable')
Expand Down Expand Up @@ -776,8 +794,8 @@ def getConfigDict(self):
'builddir': self.builddir,
'slavebuilddir': self.slavebuilddir,
}
if self.category:
rv['category'] = self.category
if self.tags:
rv['tags'] = self.tags
if self.nextSlave:
rv['nextSlave'] = self.nextSlave
if self.nextBuild:
Expand Down
5 changes: 1 addition & 4 deletions master/buildbot/interfaces.py
Expand Up @@ -151,7 +151,7 @@ def getSchedulers():
"""Return a list of ISchedulerStatus objects for all
currently-registered Schedulers."""

def getBuilderNames(categories=None):
def getBuilderNames(tags=None):
"""Return a list of the names of all current Builders."""
def getBuilder(name):
"""Return the IBuilderStatus object for a given named Builder. Raises
Expand Down Expand Up @@ -328,9 +328,6 @@ class IBuilderStatus(Interface):
def getName():
"""Return the name of this Builder (a string)."""

def getCategory():
"""Return the category of this builder (a string)."""

def getDescription():
"""Return the description of this builder (a string)."""

Expand Down
10 changes: 5 additions & 5 deletions master/buildbot/process/builder.py
Expand Up @@ -102,10 +102,10 @@ def reconfigService(self, new_config):
# set up a builder status object on the first reconfig
if not self.builder_status:
self.builder_status = self.master.status.builderAdded(
builder_config.name,
builder_config.builddir,
builder_config.category,
builder_config.description)
name=builder_config.name,
basedir=builder_config.builddir,
tags=builder_config.tags,
description=builder_config.description)

self.config = builder_config

Expand All @@ -115,7 +115,7 @@ def reconfigService(self, new_config):
yield self.getBuilderId()

self.builder_status.setDescription(builder_config.description)
self.builder_status.setCategory(builder_config.category)
self.builder_status.setTags(builder_config.tags)
self.builder_status.setSlavenames(self.config.slavenames)
self.builder_status.setCacheSize(new_config.caches['Builds'])

Expand Down
33 changes: 21 additions & 12 deletions master/buildbot/status/builder.py
Expand Up @@ -61,23 +61,23 @@ class BuilderStatus(styles.Versioned):
I live in the buildbot.process.build.Builder object, in the
.builder_status attribute.
@type category: string
@ivar category: user-defined category this builder belongs to; can be
@type tags: None or list of strings
@ivar tags: user-defined "tag" this builder has; can be
used to filter on in status clients
"""

implements(interfaces.IBuilderStatus, interfaces.IEventSource)

persistenceVersion = 1
persistenceVersion = 2
persistenceForgets = ('wasUpgraded', )

category = None
tags = None
currentBigState = "offline" # or idle/waiting/interlocked/building
basedir = None # filled in by our parent

def __init__(self, buildername, category, master, description):
def __init__(self, buildername, tags, master, description):
self.name = buildername
self.category = category
self.tags = tags
self.description = description
self.master = master

Expand Down Expand Up @@ -133,6 +133,12 @@ def upgradeToVersion1(self):
del self.nextBuildNumber # determineNextBuildNumber chooses this
self.wasUpgraded = True

def upgradeToVersion2(self):
if hasattr(self, 'category'):
self.tags = self.category and [self.category] or None
del self.category
self.wasUpgraded = True

def determineNextBuildNumber(self):
"""Scan our directory of saved BuildStatus instances to determine
what our self.nextBuildNumber should be. Set it one larger than the
Expand Down Expand Up @@ -319,12 +325,15 @@ def getLastFinishedBuild(self):
b = self.getBuild(-2)
return b

def setCategory(self, category):
def getTags(self):
return self.tags

def setTags(self, tags):
# used during reconfig
self.category = category
self.tags = tags

def getCategory(self):
return self.category
def matchesAnyTag(self, tags):
return self.tags and any(tag for tag in self.tags if tag in tags)

def getBuildByRevision(self, rev):
number = self.nextBuildNumber - 1
Expand Down Expand Up @@ -435,7 +444,7 @@ def eventGenerator(self, branches=[], categories=[], committers=[], projects=[],
# sourcestamps match, skip this build
if branches and not branches & self._getBuildBranches(b):
continue
if categories and not b.getBuilder().getCategory() in categories:
if categories and not b.getBuilder().matchesAnyTag(tags=categories):
continue
if committers and not [True for c in b.getChanges() if c.who in committers]:
continue
Expand Down Expand Up @@ -580,7 +589,7 @@ def asDict(self):
# Constant
# TODO(maruel): Fix me. We don't want to leak the full path.
result['basedir'] = os.path.basename(self.basedir)
result['category'] = self.category
result['tags'] = self.getTags()
result['slaves'] = self.slavenames
result['schedulers'] = [s.name
for s in self.status.master.allSchedulers()
Expand Down
33 changes: 19 additions & 14 deletions master/buildbot/status/mail.py
Expand Up @@ -248,23 +248,25 @@ class MailNotifier(base.StatusReceiverMultiService, buildset.BuildSetSummaryNoti
implements(interfaces.IEmailSender)

compare_attrs = ["extraRecipients", "lookup", "fromaddr", "mode",
"categories", "builders", "addLogs", "relayhost",
"tags", "builders", "addLogs", "relayhost",
"subject", "sendToInterestedUsers", "customMesg",
"messageFormatter", "extraHeaders"]

possible_modes = ("change", "failing", "passing", "problem", "warnings",
"exception", "cancelled")

def __init__(self, fromaddr, mode=("failing", "passing", "warnings"),
categories=None, builders=None, addLogs=False,
tags=None, builders=None, addLogs=False,
relayhost="localhost", buildSetSummary=False,
subject="buildbot %(result)s in %(title)s on %(builder)s",
lookup=None, extraRecipients=[],
sendToInterestedUsers=True, customMesg=None,
messageFormatter=defaultMessage, extraHeaders=None,
addPatch=True, useTls=False,
smtpUser=None, smtpPassword=None, smtpPort=25,
previousBuildGetter=defaultGetPreviousBuild):
previousBuildGetter=defaultGetPreviousBuild,
categories=None # deprecated, use tags
):
"""
@type fromaddr: string
@param fromaddr: the email address to be used in the 'From' header.
Expand Down Expand Up @@ -305,14 +307,17 @@ def __init__(self, fromaddr, mode=("failing", "passing", "warnings"),
@type builders: list of strings
@param builders: a list of builder names for which mail should be
sent. Defaults to None (send mail for all builds).
Use either builders or categories, but not both.
Use either builders or tags, but not both.
@type categories: list of strings
@param categories: a list of category names to serve status
@type tags: list of strings
@param tags: a list of tag names to serve status
information for. Defaults to None (all
categories). Use either builders or categories,
tags). Use either builders or tags,
but not both.
@type categories: list of strings
@param categories: (this attribute is deprecated; use 'tags' instead)
@type addLogs: boolean
@param addLogs: if True, include all build logs as attachments to the
messages. These can be quite large. This can also be
Expand Down Expand Up @@ -419,7 +424,7 @@ def __init__(self, fromaddr, mode=("failing", "passing", "warnings"),
config.error(
"mode %s is not a valid mode" % (m,))
self.mode = mode
self.categories = categories
self.tags = tags or categories
self.builders = builders
self.addLogs = addLogs
self.relayhost = relayhost
Expand Down Expand Up @@ -449,10 +454,10 @@ def __init__(self, fromaddr, mode=("failing", "passing", "warnings"),
self.watched = []
self.master_status = None

# you should either limit on builders or categories, not both
if self.builders is not None and self.categories is not None:
# you should either limit on builders or tags, not both
if self.builders is not None and self.tags is not None:
config.error(
"Please specify only builders or categories to include - " +
"Please specify only builders or tags to include - " +
"not both.")

if customMesg:
Expand Down Expand Up @@ -485,7 +490,7 @@ def disownServiceParent(self):

def builderAdded(self, name, builder):
# only subscribe to builders we are interested in
if self.categories is not None and builder.category not in self.categories:
if self.tags is not None and not builder.matchesAnyTag(self.tags):
return None

self.watched.append(builder)
Expand All @@ -505,8 +510,8 @@ def isMailNeeded(self, build, results):
builder = build.getBuilder()
if self.builders is not None and builder.name not in self.builders:
return False # ignore this build
if self.categories is not None and \
builder.category not in self.categories:
if self.tags is not None and \
not builder.matchesAnyTag(self.tags):
return False # ignore this build

prev = self.getPreviousBuild(build)
Expand Down
22 changes: 13 additions & 9 deletions master/buildbot/status/master.py
Expand Up @@ -221,15 +221,19 @@ def chdict2change(chdict):
def getSchedulers(self):
return self.master.allSchedulers()

def getBuilderNames(self, categories=None):
if categories is None:
def getBuilderNames(self, tags=None, categories=None):
if categories is not None:
# Categories is deprecated; pretend they said "tags".
tags = categories

if tags is None:
return util.naturalSort(self.botmaster.builderNames) # don't let them break it

l = []
# respect addition order
for name in self.botmaster.builderNames:
bldr = self.botmaster.builders[name]
if bldr.config.category in categories:
bldr = self.getBuilder(name)
if bldr.matchesAnyTag(tags):
l.append(name)
return util.naturalSort(l)

Expand Down Expand Up @@ -328,7 +332,7 @@ def announceNewBuilder(self, target, name, builder_status):
if t:
builder_status.subscribe(t)

def builderAdded(self, name, basedir, category=None, description=None):
def builderAdded(self, name, basedir, tags=None, description=None):
"""
@rtype: L{BuilderStatus}
"""
Expand Down Expand Up @@ -358,13 +362,13 @@ def builderAdded(self, name, basedir, category=None, description=None):
log.msg("error follows:")
log.err()
if not builder_status:
builder_status = builder.BuilderStatus(name, category, self.master,
builder_status = builder.BuilderStatus(name, tags, self.master,
description)
builder_status.addPointEvent(["builder", "created"])
log.msg("added builder %s in category %s" % (name, category))
# an unpickled object might not have category set from before,
log.msg("added builder %s with tags %r" % (name, tags))
# an unpickled object might not have tags set from before,
# so set it here to make sure
builder_status.category = category
builder_status.setTags(tags)
builder_status.description = description
builder_status.master = self.master
builder_status.basedir = os.path.join(self.basedir, basedir)
Expand Down

0 comments on commit d246b54

Please sign in to comment.