Skip to content

Commit

Permalink
Merge pull request buildbot#1323 from delanne/IRCContact
Browse files Browse the repository at this point in the history
IRCContact use the DATA API

See ticket:2714
  • Loading branch information
Mikhail Sobolev committed Nov 6, 2014
2 parents 93a5102 + 38d595b commit 918e6c0
Show file tree
Hide file tree
Showing 13 changed files with 239 additions and 177 deletions.
11 changes: 7 additions & 4 deletions master/buildbot/data/builders.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ def get(self, resultSpec, kwargs):
defer.returnValue(
dict(builderid=builderid,
name=bdict['name'],
description=bdict['description']))
description=bdict['description'],
tags=bdict['tags']))


class BuildersEndpoint(base.Endpoint):
Expand All @@ -59,7 +60,8 @@ def get(self, resultSpec, kwargs):
defer.returnValue([
dict(builderid=bd['id'],
name=bd['name'],
description=bd['description'])
description=bd['description'],
tags=bd['tags'])
for bd in bdicts])

def startConsuming(self, callback, options, kwargs):
Expand All @@ -78,6 +80,7 @@ class EntityType(types.Entity):
builderid = types.Integer()
name = types.Identifier(20)
description = types.NoneOk(types.String())
tags = types.NoneOk(types.List(of=types.String()))
entityType = EntityType(name)

def __init__(self, master):
Expand All @@ -88,8 +91,8 @@ def findBuilderId(self, name):
return self.master.db.builders.findBuilderId(name)

@base.updateMethod
def updateBuilderDescription(self, builderid, description):
return self.master.db.builders.updateBuilderDescription(builderid, description)
def updateBuilderInfo(self, builderid, description, tags):
return self.master.db.builders.updateBuilderInfo(builderid, description, tags)

@base.updateMethod
@defer.inlineCallbacks
Expand Down
14 changes: 9 additions & 5 deletions master/buildbot/db/builders.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

import sqlalchemy as sa

import json

from buildbot.db import base


Expand All @@ -30,12 +32,11 @@ def findBuilderId(self, name):
name_hash=self.hashColumns(name),
))

def updateBuilderDescription(self, builderid, description):
def updateBuilderInfo(self, builderid, description, tags):
def thd(conn):
tbl = self.db.model.builders

q = tbl.update(whereclause=(tbl.c.id == builderid))
conn.execute(q, description=description)
conn.execute(q, description=description, tags=json.dumps(tags if tags else []))
return self.db.pool.do(thd)

def getBuilder(self, builderid):
Expand Down Expand Up @@ -80,7 +81,8 @@ def thd(conn):
j = j.join(limiting_bm_tbl,
onclause=(bldr_tbl.c.id == limiting_bm_tbl.c.builderid))
q = sa.select(
[bldr_tbl.c.id, bldr_tbl.c.name, bldr_tbl.c.description, bm_tbl.c.masterid],
[bldr_tbl.c.id, bldr_tbl.c.name, bldr_tbl.c.description,
bldr_tbl.c.tags, bm_tbl.c.masterid],
from_obj=[j],
order_by=[bldr_tbl.c.id, bm_tbl.c.masterid])
if masterid is not None:
Expand All @@ -94,7 +96,9 @@ def thd(conn):
last = None
for row in conn.execute(q).fetchall():
if not last or row['id'] != last['id']:
last = dict(id=row.id, name=row.name, masterids=[], description=row.description)
last = dict(id=row.id, name=row.name, masterids=[],
description=row.description,
tags=json.loads(row.tags) if row.tags else [])
rv.append(last)
if row['masterid']:
last['masterids'].append(row['masterid'])
Expand Down
24 changes: 24 additions & 0 deletions master/buildbot/db/migrate/versions/040_add_builder_tags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# This file is part of Buildbot. Buildbot 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, version 2.
#
# 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.
#
# Copyright Buildbot Team Members

import sqlalchemy as sa


def upgrade(migrate_engine):
metadata = sa.MetaData()
metadata.bind = migrate_engine
builders_table = sa.Table('builders', metadata, autoload=True)
tags = sa.Column('tags', sa.Text, nullable=True)
tags.create(builders_table)
2 changes: 2 additions & 0 deletions master/buildbot/db/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,8 @@ class Model(base.DBConnectorComponent):
sa.Column('name', sa.Text, nullable=False),
# builder's description
sa.Column('description', sa.Text, nullable=True),
# builder's tags
sa.Column('tags', sa.Text, nullable=True),
# sha1 of name; used for a unique index
sa.Column('name_hash', sa.String(40), nullable=False),
)
Expand Down
4 changes: 3 additions & 1 deletion master/buildbot/process/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,9 @@ def reconfigService(self, new_config):
# build.
builderid = yield self.getBuilderId()

self.master.data.updates.updateBuilderDescription(builderid, builder_config.description)
self.master.data.updates.updateBuilderInfo(builderid,
builder_config.description,
builder_config.tags)

self.builder_status.setDescription(builder_config.description)
self.builder_status.setTags(builder_config.tags)
Expand Down
163 changes: 75 additions & 88 deletions master/buildbot/status/words.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
from buildbot.status.results import SUCCESS
from buildbot.status.results import WARNINGS


# twisted.internet.ssl requires PyOpenSSL, so be resilient if it's missing
try:
from twisted.internet import ssl
Expand Down Expand Up @@ -184,6 +185,9 @@ def doSilly(self, message):
reactor.callLater(when, self.send, r)
when += 2.5

def builderMatchesAnyTag(self, builder, tags):
return builder['tags'] and any(tag for tag in builder['tags'] if tag in self.bot.tags)

@defer.inlineCallbacks
def getBuilder(self, buildername=None, builderid=None):
if buildername:
Expand All @@ -206,6 +210,9 @@ def getBuilder(self, buildername=None, builderid=None):
raise UsageError("no such builder '%s'" % which)
defer.returnValue(bdict)

def getPreviousBuild(self, build):
return self.master.data.get(('builders', build['builderid'], 'builds', build['number'] - 1))

def getControl(self, which):
# TODO in nine: there's going to be a better way to do all this.
# For now, this continues to work.
Expand Down Expand Up @@ -332,17 +339,20 @@ def notify_for(self, *events):
return True
return False

@defer.inlineCallbacks
def subscribe_to_build_events(self):
startConsuming = self.master.mq.startConsuming

def buildStarted(key, msg):
return self.buildStarted(msg)

# FIXME:
# -- builderAdded
# -- builderRemoved
# -- builderStarted
# -- buildFinished
def buildFinished(key, msg):
return self.buildFinished(msg)

handle = True # self.master.data.startConsuming(watchForCompleteEvent, {},
# (build['link'].path))
self.subscribed.append(handle)
for e, f in (("new", buildStarted), # BuilderStarted
("finished", buildFinished)): # BuilderFinished
handle = yield startConsuming(f, ('builders', None, 'builds', None, e))
self.subscribed.append(handle)

def unsubscribe_from_build_events(self):
# Cancel all the subscriptions we have
Expand Down Expand Up @@ -462,32 +472,16 @@ def watchForCompleteEvent(key, msg):
self.send(r)
command_WATCH.usage = "watch <which> - announce the completion of an active build"

# OLD_STYLE
def builderAdded(self, builderName, builder):
# FIXME: NEED TO THINK ABOUT!
if (self.bot.tags is not None and
not builder.matchesAnyTag(tags=self.bot.tags)):
return

log.msg('[Contact] Builder %s added' % (builderName))
builder.subscribe(self)

# OLD_STYLE
def builderRemoved(self, builderName):
# FIXME: NEED TO THINK ABOUT!
log.msg('[Contact] Builder %s removed' % (builderName))

# OLD_STYLE
@defer.inlineCallbacks
def buildStarted(self, builderName, build):
# FIXME: NEED TO THINK ABOUT!
builder = build.getBuilder()
log.msg('[Contact] Builder %r started' % (builder,))
def buildStarted(self, build):
builder = yield self.getBuilder(builderid=build['builderid'])
builderName = builder['name']
buildNumber = build['number']
log.msg('[Contact] Builder %s started' % (builder['name'],))

# only notify about builders we are interested in

if (self.bot.tags is not None and
not builder.matchesAnyTag(tags=self.bot.tags)):
not self.builderMatchesAnyTag(builder, self.bot.tags)):
log.msg('Not notifying for a build that does not match any tags')
return

Expand All @@ -497,96 +491,86 @@ def buildStarted(self, builderName, build):
if self.useRevisions:
revisions = yield self.getRevisionsForBuild(build)
r = "build containing revision(s) [%s] on %s started" % \
(','.join(revisions), builder.getName())
(','.join(revisions), builderName)
else:
# Abbreviate long lists of changes to simply two
# revisions, and the number of additional changes.
changes = [str(c.revision) for c in build.getChanges()][:2]
# TODO: We can't get the list of the changes related to a build in nine
changes_str = ""

if len(build.getChanges()) > 0:
changes_str = "including [%s]" % ', '.join(changes)

if len(build.getChanges()) > 2:
# Append number of truncated changes
changes_str += " and %d more" % (len(build.getChanges()) - 2)

r = "build #%d of %s started" % (
build.getNumber(),
builder.getName())

r = "build #%d of %s started" % (buildNumber, builderName)
if changes_str:
r += " (%s)" % changes_str

self.send(r)

results_descriptions = {
SUCCESS: ("Success", 'GREEN'),
WARNINGS: ("Warnings", 'YELLOW'),
FAILURE: ("Failure", 'RED'),
EXCEPTION: ("Exception", 'PURPLE'),
RETRY: ("Retry", 'AQUA_LIGHT'),
CANCELLED: ("Cancelled", 'PINK'),
}

def getResultsDescriptionAndColor(self, results):
return self.results_descriptions.get(results, ("??", 'RED'))

# OLD_STYLE
def buildFinished(self, builderName, build, results):
# FIXME: NEED TO THINK ABOUT!
builder = build.getBuilder()
@defer.inlineCallbacks
def buildFinished(self, build):
builder = yield self.getBuilder(builderid=build['builderid'])
builderName = builder['name']
buildNumber = build['number']
buildResult = build['results']

# only notify about builders we are interested in
if (self.bot.tags is not None and
not builder.matchesAnyTag(tags=self.bot.tags)):
not self.builderMatchesAnyTag(builder, self.bot.tags)):
log.msg('Not notifying for a build that does not match any tags')
return

if not self.notify_for_finished(build):
return

builder_name = builder.getName()
buildnum = build.getNumber()

results = self.getResultsDescriptionAndColor(build.getResults())
if not self.shouldReportBuild(builder_name, buildnum):
if not self.shouldReportBuild(builderName, buildNumber):
return

bdict = None # ???
results = self.getResultsDescriptionAndColor(buildResult)

if self.useRevisions:
revisions = yield self.getRevisionsForBuild(bdict)
r = "build containing revision(s) [%s] on %s is complete: %s" % \
(','.join(revisions), builder_name, results[0])
revisions = yield self.getRevisionsForBuild(build)
r = "Hey! build %s containing revision(s) [%s] is complete: %s" % \
(builderName, ','.join(revisions), results[0])
else:
r = "build #%d of %s is complete: %s" % \
(buildnum, builder_name, results[0])
r = "Hey! build %s #%d is complete: %s" % \
(builderName, buildNumber, results[0])

r += ' [%s]' % maybeColorize(" ".join(build.getText()), results[1], self.useColors)
buildurl = self.bot.status.getURLForThing(build)
r += ' [%s]' % maybeColorize(build['state_string'], results[1], self.useColors)
self.send(r)

# FIXME: where do we get the list of changes for a build ?
# if self.bot.showBlameList and buildResult != SUCCESS and len(build.changes) != 0:
# r += ' blamelist: ' + ', '.join(list(set([c.who for c in build.changes])))

# FIXME: where do we get the base_url? Then do we use the build Link to make the URL?
buildurl = None # self.bot.status.getBuildbotURL() + build
if buildurl:
r += " Build details are at %s" % buildurl
self.send("Build details are at %s" % buildurl)

if self.bot.showBlameList and build.getResults() != SUCCESS and len(build.changes) != 0:
r += ' blamelist: ' + ', '.join(list(set([c.who for c in build.changes])))
results_descriptions = {
SUCCESS: ("Success", 'GREEN'),
WARNINGS: ("Warnings", 'YELLOW'),
FAILURE: ("Failure", 'RED'),
EXCEPTION: ("Exception", 'PURPLE'),
RETRY: ("Retry", 'AQUA_LIGHT'),
CANCELLED: ("Cancelled", 'PINK'),
}

self.send(r)
def getResultsDescriptionAndColor(self, results):
return self.results_descriptions.get(results, ("??", 'RED'))

def notify_for_finished(self, build):
# FIXME: NEED TO THINK ABOUT!
results = build.getResults()

if self.notify_for('finished'):
return True

if self.notify_for(lower(self.results_descriptions.get(results)[0])):
if self.notify_for(lower(self.results_descriptions.get(build['results'])[0])):
return True

prevBuild = build.getPreviousBuild()
prevBuild = self.getPreviousBuild(build)
if prevBuild:
prevResult = prevBuild.getResults()
prevResult = prevBuild['results']

required_notification_control_string = join((lower(self.results_descriptions.get(prevResult)[0]),
'To',
capitalize(self.results_descriptions.get(results)[0])),
capitalize(self.results_descriptions.get(build['results'])[0])),
'')

if (self.notify_for(required_notification_control_string)):
Expand All @@ -598,11 +582,11 @@ def notify_for_finished(self, build):
def watchedBuildFinished(self, build):
builder = yield self.getBuilder(builderid=build['builderid'])

# FIXME: NEED TO THINK ABOUT!
# only notify about builders we are interested in
# if (self.bot.tags is not None and
# not builder.matchesAnyTag(tags=self.bot.tags)):
# return
if (self.bot.tags is not None and
not self.builderMatchesAnyTag(builder, self.bot.tags)):
log.msg('Not notifying for a build that does not match any tags')
return

builder_name = builder['name']
buildnum = build['number']
Expand Down Expand Up @@ -1158,6 +1142,9 @@ def __init__(self, nickname, password, channels, pm_to_nicks, tags, notify_event
self.useColors = useColors
self.allowShutdown = allowShutdown

if categories:
log.msg("WARNING: categories are deprecated and should be replaced with 'tags=[cat]'")

def __getstate__(self):
d = self.__dict__.copy()
del d['p']
Expand Down

0 comments on commit 918e6c0

Please sign in to comment.