Skip to content

Commit

Permalink
Add Properties support to Change objects and Force Build form.
Browse files Browse the repository at this point in the history
Make Properties on the Force Build form actually work.

Add support for sending properties through sendchange, changes.Change bugfix.

Bugfix for pb.py

Better arg handling of properties with sendchange command.

Formatting fixes
  • Loading branch information
Ben Hearsum committed Aug 31, 2009
1 parent 5975754 commit bcd04d7
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 15 deletions.
31 changes: 24 additions & 7 deletions buildbot/changes/changes.py
Expand Up @@ -9,6 +9,7 @@
from twisted.web import html

from buildbot import interfaces, util
from buildbot.process.properties import Properties

html_tmpl = """
<p>Changed by: <b>%(who)s</b><br />
Expand All @@ -22,6 +23,9 @@
Comments:
%(comments)s
Properties:
%(properties)s
</p>
"""

Expand Down Expand Up @@ -55,7 +59,7 @@ class Change:

def __init__(self, who, files, comments, isdir=0, links=None,
revision=None, when=None, branch=None, category=None,
revlink=''):
revlink='', properties={}):
self.who = who
self.comments = comments
self.isdir = isdir
Expand All @@ -69,6 +73,8 @@ def __init__(self, who, files, comments, isdir=0, links=None,
self.branch = branch
self.category = category
self.revlink = revlink
self.properties = Properties()
self.properties.update(properties, "Change")

# keep a sorted list of the files, for easier display
self.files = files[:]
Expand All @@ -80,6 +86,7 @@ def asText(self):
data += "At: %s\n" % self.getTime()
data += "Changed By: %s\n" % self.who
data += "Comments: %s\n\n" % self.comments
data += "Properties: %s\n" % self.getProperties()
return data

def asHTML(self):
Expand All @@ -105,12 +112,17 @@ def asHTML(self):
if self.branch:
branch = "Branch: <b>%s</b><br />\n" % self.branch

kwargs = { 'who' : html.escape(self.who),
'at' : self.getTime(),
'files' : html.UL(links) + '\n',
'revision': revision,
'branch' : branch,
'comments': html.PRE(self.comments) }
properties = []
for prop in self.properties.asList():
properties.append("%s: %s<br />" % (prop[0], prop[1]))

kwargs = { 'who' : html.escape(self.who),
'at' : self.getTime(),
'files' : html.UL(links) + '\n',
'revision' : revision,
'branch' : branch,
'comments' : html.PRE(self.comments),
'properties': html.UL(properties) + '\n' }
return html_tmpl % kwargs

def get_HTML_box(self, url):
Expand Down Expand Up @@ -163,6 +175,11 @@ def getFileContents(self):
data += " %s\n" % f
return data

def getProperties(self):
data = ""
for prop in self.properties.asList():
data += " %s: %s" % (prop[0], prop[1])

class ChangeMaster(service.MultiService):

"""This is the master-side service which receives file change
Expand Down
1 change: 1 addition & 0 deletions buildbot/changes/pb.py
Expand Up @@ -36,6 +36,7 @@ def perspective_addChange(self, changedict):
revision=changedict.get('revision'),
category=changedict.get('category'),
when=changedict.get('when'),
properties=changedict.get('properties', {})
)
self.changemaster.addChange(change)

Expand Down
5 changes: 3 additions & 2 deletions buildbot/clients/sendchange.py
Expand Up @@ -10,12 +10,13 @@ def __init__(self, master, user=None):
self.port = int(self.port)
self.num_changes = 0

def send(self, branch, revision, comments, files, user=None, category=None, when=None):
def send(self, branch, revision, comments, files, user=None, category=None,
when=None, properties={}):
if user is None:
user = self.user
change = {'who': user, 'files': files, 'comments': comments,
'branch': branch, 'revision': revision, 'category': category,
'when': when}
'when': when, 'properties': properties}
self.num_changes += 1

f = pb.PBClientFactory()
Expand Down
4 changes: 4 additions & 0 deletions buildbot/process/base.py
Expand Up @@ -291,6 +291,10 @@ def setupProperties(self):
for rq in self.requests:
props.updateFromProperties(rq.properties)

# and finally, from the SourceStamp, which has properties via Change
for change in self.source.changes:
props.updateFromProperties(change.properties)

# now set some properties of our own, corresponding to the
# build itself
props.setProperty("buildername", self.builder.name, "Build")
Expand Down
13 changes: 12 additions & 1 deletion buildbot/scripts/runner.py
Expand Up @@ -719,6 +719,10 @@ def statusgui(config):
c.run()

class SendChangeOptions(usage.Options):
def __init__(self):
usage.Options.__init__(self)
self['properties'] = {}

optParameters = [
("master", "m", None,
"Location of the buildmaster's PBListener (host:port)"),
Expand All @@ -728,6 +732,8 @@ class SendChangeOptions(usage.Options):
("revision", "r", None, "Revision specifier (string)"),
("revision_number", "n", None, "Revision specifier (integer)"),
("revision_file", None, None, "Filename containing revision spec"),
("property", "p", None,
"A property for the change, in the format: name:value"),
("comments", "m", None, "log message"),
("logfile", "F", None,
"Read the log messages from this file (- for stdin)"),
Expand All @@ -737,6 +743,9 @@ def getSynopsis(self):
return "Usage: buildbot sendchange [options] filenames.."
def parseArgs(self, *args):
self['files'] = args
def opt_property(self, property):
name,value = property.split(':')
self['properties'][name] = value


def sendchange(config, runReactor=False):
Expand All @@ -750,6 +759,7 @@ def sendchange(config, runReactor=False):
branch = config.get('branch', opts.get('branch'))
category = config.get('category', opts.get('category'))
revision = config.get('revision')
properties = config.get('properties', {})
if config.get('when'):
when = float(config.get('when'))
else:
Expand All @@ -776,7 +786,8 @@ def sendchange(config, runReactor=False):
assert master, "you must provide the master location"

s = Sender(master, user)
d = s.send(branch, revision, comments, files, category=category, when=when)
d = s.send(branch, revision, comments, files, category=category, when=when,
properties=properties)
if runReactor:
d.addCallbacks(s.printSuccess, s.printFailure)
d.addBoth(s.stop)
Expand Down
9 changes: 9 additions & 0 deletions buildbot/status/web/base.py
Expand Up @@ -101,6 +101,15 @@ def make_force_build_form(forceURL, useUserPasswd, on_all=False):
"<input type='text' name='branch' />")
+ make_row("Revision to build:",
"<input type='text' name='revision' />")
+ make_row("Property 1, ",
"Name: <input type='text' name='prop1name' /> " + \
"Value: <input type='text' name='prop1value' />")
+ make_row("Property 2, ",
"Name: <input type='text' name='prop2name' /> " + \
"Value: <input type='text' name='prop2value' />")
+ make_row("Property 3, ",
"Name: <input type='text' name='prop3name' /> " + \
"Value: <input type='text' name='prop3value' />")
+ '<input type="submit" value="Force Build" /></form>\n')

def td(text="", parms={}, **props):
Expand Down
21 changes: 18 additions & 3 deletions buildbot/status/web/builder.py
Expand Up @@ -10,6 +10,7 @@
make_force_build_form, OneLineMixin, path_to_build, path_to_slave, \
path_to_builder, path_to_change
from buildbot.process.base import BuildRequest
from buildbot.process.properties import Properties
from buildbot.sourcestamp import SourceStamp

from buildbot.status.web.build import BuildsResource, StatusResourceBuild
Expand Down Expand Up @@ -193,6 +194,12 @@ def force(self, req):
reason = req.args.get("comments", ["<no reason specified>"])[0]
branch = req.args.get("branch", [""])[0]
revision = req.args.get("revision", [""])[0]
properties = Properties()
for i in (1,2,3):
pname = req.args.get("prop%dname" % i, [""])[0]
pvalue = req.args.get("prop%dvalue" % i, [""])[0]
if pname and pvalue:
properties.setProperty(pname, pvalue, "Force Build Form")

r = "The web-page 'force build' button was pressed by '%s': %s\n" \
% (html.escape(name), html.escape(reason))
Expand All @@ -209,14 +216,21 @@ def force(self, req):
if not self.authUser(req):
return Redirect("../../authfail")

# keep weird stuff out of the branch and revision strings. TODO:
# centralize this somewhere.
# keep weird stuff out of the branch revision, and property strings.
# TODO: centralize this somewhere.
if not re.match(r'^[\w\.\-\/]*$', branch):
log.msg("bad branch '%s'" % branch)
return Redirect("..")
if not re.match(r'^[\w\.\-\/]*$', revision):
log.msg("bad revision '%s'" % revision)
return Redirect("..")
for p in properties.asList():
key = p[0]
value = p[1]
if not re.match(r'^[\w\.\-\/]*$', key) \
or not re.match(r'^[\w\.\-\/]*$', value):
log.msg("bad property name='%s', value='%s'" % (key, value))
return Redirect("..")
if not branch:
branch = None
if not revision:
Expand All @@ -230,7 +244,8 @@ def force(self, req):
# buildbot.changes.changes.Change instance which is tedious at this
# stage to compute
s = SourceStamp(branch=branch, revision=revision)
req = BuildRequest(r, s, builderName=self.builder_status.getName())
req = BuildRequest(r, s, builderName=self.builder_status.getName(),
properties=properties)
try:
self.builder_control.requestBuildSoon(req)
except interfaces.NoSlaveError:
Expand Down
29 changes: 27 additions & 2 deletions buildbot/test/test_run.py
Expand Up @@ -716,14 +716,15 @@ def _checkProperties(self, res):
self.assertEqual(self.getFlag("props"), "lit:dyn")

class PropertyPropagation(RunMixin, TestFlagMixin, unittest.TestCase):
def setupTest(self, config, builders, checkFn):
def setupTest(self, config, builders, checkFn, changeProps={}):
self.clearFlags()
m = self.master
m.loadConfig(config)
m.readConfig = True
m.startService()

c = changes.Change("bob", ["Makefile", "foo/bar.c"], "changed stuff")
c = changes.Change("bob", ["Makefile", "foo/bar.c"], "changed stuff",
properties=changeProps)
m.change_svc.addChange(c)

d = self.connectSlave(builders=builders)
Expand Down Expand Up @@ -759,6 +760,30 @@ def _check(res):
'color=red sched=mysched')
return self.setupTest(self.config_schprop, ['flagcolor'], _check)

config_changeprop = config_base + """
from buildbot.scheduler import Scheduler
from buildbot.steps.dummy import Dummy
from buildbot.test.runutils import SetTestFlagStep
from buildbot.process.properties import WithProperties
c['schedulers'] = [
Scheduler('mysched', None, 0.1, ['flagcolor'], properties={'color':'red'}),
]
factory = factory.BuildFactory([
s(SetTestFlagStep, flagname='testresult',
value=WithProperties('color=%(color)s sched=%(scheduler)s prop1=%(prop1)s')),
])
c['builders'] = [{'name': 'flagcolor', 'slavename': 'bot1',
'builddir': 'test', 'factory': factory},
]
"""

def testChangeProp(self):
def _check(res):
self.failUnlessEqual(self.getFlag('testresult'),
'color=blue sched=mysched prop1=prop1')
return self.setupTest(self.config_changeprop, ['flagcolor'], _check,
changeProps={'color': 'blue', 'prop1': 'prop1'})

config_slaveprop = config_base + """
from buildbot.scheduler import Scheduler
from buildbot.steps.dummy import Dummy
Expand Down

0 comments on commit bcd04d7

Please sign in to comment.