Skip to content

Commit

Permalink
Merge branch 'ShowReason' of git://github.com/jaredgrubb/buildbot
Browse files Browse the repository at this point in the history
  • Loading branch information
djmitche committed Sep 1, 2013
2 parents f9b531e + bd3e16d commit 039b12a
Show file tree
Hide file tree
Showing 11 changed files with 115 additions and 22 deletions.
12 changes: 8 additions & 4 deletions master/buildbot/schedulers/basic.py
Expand Up @@ -30,17 +30,18 @@ class BaseBasicScheduler(base.BaseScheduler):
"""

compare_attrs = ['treeStableTimer', 'change_filter', 'fileIsImportant',
'onlyImportant']
'onlyImportant', 'reason']

_reactor = reactor # for tests

fileIsImportant = None
reason = 'scheduler'

class NotSet: pass
def __init__(self, name, shouldntBeSet=NotSet, treeStableTimer=None,
builderNames=None, branch=NotABranch, branches=NotABranch,
fileIsImportant=None, properties={}, categories=None,
change_filter=None, onlyImportant=False, **kwargs):
reason=None, change_filter=None, onlyImportant=False, **kwargs):
if shouldntBeSet is not self.NotSet:
config.error(
"pass arguments to schedulers using keyword arguments")
Expand All @@ -64,6 +65,9 @@ def __init__(self, name, shouldntBeSet=NotSet, treeStableTimer=None,
self._stable_timers = defaultdict(lambda : None)
self._stable_timers_lock = defer.DeferredLock()

if reason is not None:
self.reason = reason

def getChangeFilter(self, branch, branches, change_filter, categories):
raise NotImplementedError

Expand Down Expand Up @@ -122,7 +126,7 @@ def gotChange(self, change, important):
if not important:
return defer.succeed(None)
# otherwise, we'll build it right away
return self.addBuildsetForChanges(reason='scheduler',
return self.addBuildsetForChanges(reason=self.reason,
changeids=[ change.number ])

timer_name = self.getTimerNameForChange(change)
Expand Down Expand Up @@ -195,7 +199,7 @@ def stableTimerFired(self, timer_name):
return

changeids = sorted(classifications.keys())
yield self.addBuildsetForChanges(reason='scheduler',
yield self.addBuildsetForChanges(reason=self.reason,
changeids=changeids)

max_changeid = changeids[-1] # (changeids are sorted)
Expand Down
8 changes: 5 additions & 3 deletions master/buildbot/schedulers/forcesched.py
Expand Up @@ -454,8 +454,8 @@ class ForceScheduler(base.BaseScheduler):

def __init__(self, name, builderNames,
username=UserNameParameter(),
reason=StringParameter(name="reason", default="force build", length=20),

reason=StringParameter(name="reason", default="force build", size=20),
reasonString="A build was forced by '%(owner)s': %(reason)s",
codebases=None,

properties=[
Expand Down Expand Up @@ -579,6 +579,8 @@ def __init__(self, name, builderNames,
self.all_fields = [ NestedParameter(name='', fields=[username, reason]) ]
self.all_fields.extend(self.forcedProperties)

self.reasonString = reasonString

def checkIfType(self, obj, chkType):
return isinstance(obj, chkType)

Expand Down Expand Up @@ -653,7 +655,7 @@ def force(self, owner, builderNames=None, **kwargs):
properties.setProperty("reason", reason, "Force Build Form")
properties.setProperty("owner", owner, "Force Build Form")

r = ("A build was forced by '%s': %s" % (owner, reason))
r = self.reasonString % {'owner': owner, 'reason': reason}

# everything is validated, we can create our source stamp, and buildrequest
res = yield self.addBuildsetForSourceStampSetDetails(
Expand Down
24 changes: 15 additions & 9 deletions master/buildbot/schedulers/timed.py
Expand Up @@ -40,7 +40,10 @@ class Timed(base.BaseScheduler):
before the service stops.
"""

def __init__(self, name, builderNames, properties={}, **kwargs):
compare_attrs = ('reason',)
reason = ''

def __init__(self, name, builderNames, properties={}, reason='', **kwargs):
base.BaseScheduler.__init__(self, name, builderNames, properties,
**kwargs)

Expand All @@ -54,6 +57,8 @@ def __init__(self, name, builderNames, properties={}, **kwargs):
self.actuateAt = None
self.actuateAtTimer = None

self.reason = reason % { 'name': name }

self._reactor = reactor # patched by tests

def startService(self):
Expand Down Expand Up @@ -196,16 +201,15 @@ class Periodic(Timed):
compare_attrs = ('periodicBuildTimer', 'branch',)

def __init__(self, name, builderNames, periodicBuildTimer,
reason="The Periodic scheduler named '%(name)s' triggered this build",
branch=None, properties={}, onlyImportant=False):
Timed.__init__(self, name=name, builderNames=builderNames,
properties=properties)

properties=properties, reason=reason)
if periodicBuildTimer <= 0:
config.error(
"periodicBuildTimer must be positive")
self.periodicBuildTimer = periodicBuildTimer
self.branch = branch
self.reason = "The Periodic scheduler named '%s' triggered this build" % self.name

def getNextBuildTime(self, lastActuated):
if lastActuated is None:
Expand All @@ -221,9 +225,10 @@ class NightlyBase(Timed):

def __init__(self, name, builderNames, minute=0, hour='*',
dayOfMonth='*', month='*', dayOfWeek='*',
reason='NightlyBase(%(name)s)',
properties={}, codebases=base.BaseScheduler.DefaultCodebases):
Timed.__init__(self, name=name, builderNames=builderNames,
properties=properties, codebases=codebases)
reason=reason, properties=properties, codebases=codebases)

self.minute = minute
self.hour = hour
Expand Down Expand Up @@ -271,10 +276,11 @@ def __init__(self, name, builderNames, minute=0, hour='*',
dayOfMonth='*', month='*', dayOfWeek='*',
branch=NoBranch, fileIsImportant=None, onlyIfChanged=False,
properties={}, change_filter=None, onlyImportant=False,
reason="The Nightly scheduler named '%(name)s' triggered this build",
codebases = base.BaseScheduler.DefaultCodebases):
NightlyBase.__init__(self, name=name, builderNames=builderNames,
minute=minute, hour=hour, dayOfWeek=dayOfWeek, dayOfMonth=dayOfMonth,
properties=properties, codebases=codebases)
properties=properties, codebases=codebases, reason=reason)

# If True, only important changes will be added to the buildset.
self.onlyImportant = onlyImportant
Expand All @@ -292,7 +298,6 @@ def __init__(self, name, builderNames, minute=0, hour='*',
self.fileIsImportant = fileIsImportant
self.change_filter = filter.ChangeFilter.fromSchedulerConstructorArgs(
change_filter=change_filter)
self.reason = "The Nightly scheduler named '%s' triggered this build" % self.name

def startTimedSchedulerService(self):
if self.onlyIfChanged:
Expand Down Expand Up @@ -346,12 +351,13 @@ class NightlyTriggerable(NightlyBase):
implements(ITriggerableScheduler)
def __init__(self, name, builderNames, minute=0, hour='*',
dayOfMonth='*', month='*', dayOfWeek='*',
reason="The NightlyTriggerable scheduler named '%(name)s' triggered this build",
properties={}, codebases=base.BaseScheduler.DefaultCodebases):
NightlyBase.__init__(self, name=name, builderNames=builderNames, minute=minute, hour=hour,
dayOfWeek=dayOfWeek, dayOfMonth=dayOfMonth, properties=properties, codebases=codebases)
dayOfWeek=dayOfWeek, dayOfMonth=dayOfMonth, properties=properties, reason=reason,
codebases=codebases)

self._lastTrigger = None
self.reason = "The NightlyTriggerable scheduler named '%s' triggered this build" % self.name

def startService(self):
NightlyBase.startService(self)
Expand Down
4 changes: 3 additions & 1 deletion master/buildbot/status/web/base.py
Expand Up @@ -533,7 +533,9 @@ def get_line_values(self, req, build, include_builder=True):
'time': time.strftime(self.LINE_TIME_FORMAT,
time.localtime(build.getTimes()[0])),
'text': text,
'include_builder': include_builder
'include_builder': include_builder,
'reason': build.getReason(),
'interested_users': build.getInterestedUsers(),
}
return values

Expand Down
11 changes: 9 additions & 2 deletions master/buildbot/status/web/build.py
Expand Up @@ -31,6 +31,8 @@

class ForceBuildActionResource(ActionResource):

rebuildString = "The web-page 'rebuild' button was pressed by '%(rebuild_owner)s': %(rebuild_comments)s"

def __init__(self, build_status, builder):
self.build_status = build_status
self.builder = builder
Expand All @@ -55,8 +57,13 @@ def performAction(self, req):
name =authz.getUsernameFull(req)
comments = req.args.get("comments", ["<no reason specified>"])[0]
comments.decode(getRequestCharset(req))
reason = ("The web-page 'rebuild' button was pressed by "
"'%s': %s\n" % (name, comments))

reason = self.rebuildString % {
'build_number': b.getNumber(),
'owner': b.getInterestedUsers(),
'rebuild_owner': name,
'rebuild_comments': comments
}

useSourcestamp = req.args.get("useSourcestamp", None)
if useSourcestamp and useSourcestamp==['updated']:
Expand Down
5 changes: 5 additions & 0 deletions master/buildbot/status/web/files/default.css
Expand Up @@ -535,6 +535,11 @@ table.info td.left {
text-align: left
}

table.info td .reason {
display:block;
font-weight: bold;
}

.alt {
background-color: #f6f6f6;
}
Expand Down
7 changes: 7 additions & 0 deletions master/buildbot/status/web/templates/build_line.html
Expand Up @@ -27,6 +27,12 @@
<td><a href="{{ b.builderurl }}">{{ b.builder_name }}</a></td>
{% endif %}
<td><a href="{{ b.buildurl }}">#{{ b.buildnum }}</a></td>
<td><span class='reason'>{{ b.reason|e }}</span>
{%- for user in (b.interested_users or []) -%}
{%- if not loop.first %}, {% endif -%}
<span class='interested_user'>{{ user|e }}</span>
{%- endfor -%}
</td>
<td class="left">{{ b.text|capitalize }}</td>
</tr>
{% endmacro %}
Expand All @@ -42,6 +48,7 @@
<th>Builder</th>
{% endif %}
<th>Build #</th>
<th>Reason</th>
<th>Info</th>
</tr>
{% for b in builds %}
Expand Down
4 changes: 3 additions & 1 deletion master/buildbot/status/web/templates/builder.html
Expand Up @@ -56,7 +56,9 @@ <h2>Pending Build Requests:</h2>
{% if 'owner' in b.properties %}
<b>Forced build</b>
by {{b.properties['owner'][0]}}
<small>{{b.properties['reason'][0]}}</small>
{% if 'reason' in b.properties %}
<small>{{b.properties['reason'][0]}}</small>
{% endif %}
{% endif %}
</li>
{% endfor %}
Expand Down
37 changes: 37 additions & 0 deletions master/buildbot/test/unit/test_schedulers_forcesched.py
Expand Up @@ -144,6 +144,43 @@ def test_basicForce(self):
project='p', sourcestampsetid=100)
})


@defer.inlineCallbacks
def test_basicForce_reasonString(self):
"""Same as above, but with a reasonString"""
sched = self.makeScheduler(reasonString='%(owner)s wants it %(reason)s')

res = yield sched.force('user', builderNames=['a'], branch='a', reason='because',revision='c',
repository='d', project='p',
property1_name='p1',property1_value='e',
property2_name='p2',property2_value='f',
property3_name='p3',property3_value='g',
property4_name='p4',property4_value='h'
)
bsid,brids = res

# only one builder forced, so there should only be one brid
self.assertEqual(len(brids), 1)

self.db.buildsets.assertBuildset\
(bsid,
dict(reason="user wants it because",
brids=brids,
external_idstring=None,
properties=[ ('owner', ('user', 'Force Build Form')),
('p1', ('e', 'Force Build Form')),
('p2', ('f', 'Force Build Form')),
('p3', ('g', 'Force Build Form')),
('p4', ('h', 'Force Build Form')),
('reason', ('because', 'Force Build Form')),
('scheduler', ('testsched', 'Scheduler')),
],
sourcestampsetid=100),
{'':
dict(branch='a', revision='c', repository='d', codebase='',
project='p', sourcestampsetid=100)
})

@defer.inlineCallbacks
def test_force_allBuilders(self):
sched = self.makeScheduler()
Expand Down
17 changes: 17 additions & 0 deletions master/docs/manual/cfg-schedulers.rst
Expand Up @@ -106,6 +106,8 @@ available with all schedulers.
``False`` and only applies when ``fileIsImportant`` is
given.

``reason``
A string that will be used as the reason for the triggered build.

The remaining subsections represent a catalog of the available Scheduler types.
All these Schedulers are defined in modules under :mod:`buildbot.schedulers`,
Expand Down Expand Up @@ -230,6 +232,8 @@ The arguments to this scheduler are:
``change_filter``

``onlyImportant``

``reason``
See :ref:`Configuring-Schedulers`.

``treeStableTimer``
Expand Down Expand Up @@ -321,6 +325,8 @@ The arguments to this scheduler are:
``change_filter``

``onlyImportant``

``reason``
See :ref:`Configuring-Schedulers`.

``treeStableTimer``
Expand Down Expand Up @@ -417,6 +423,9 @@ The arguments to this scheduler are:

``onlyImportant``

``reason``
See :ref:`Configuring-Schedulers`.

``periodicBuildTimer``
The time, in seconds, after which to start a build.

Expand Down Expand Up @@ -470,6 +479,8 @@ The full list of parameters is:

``onlyImportant``

``reason``

``codebases``
See :ref:`Configuring-Schedulers`. Note that ``fileIsImportant`` and
``change_filter`` are only relevant if ``onlyIfChanged`` is
Expand Down Expand Up @@ -860,6 +871,12 @@ The scheduler takes the following parameters:
A :ref:`parameter <ForceScheduler-Parameters>` specifying the reason for
the build. The default value is a string parameter with value "force build".

``reasonString``

A string that will be used to create the build reason for the forced build. This
string can contain the placeholders '%(owner)s' and '%(reason)s', which represents
the value typed into the reason field.

``username``

A :ref:`parameter <ForceScheduler-Parameters>` specifying the project for
Expand Down
8 changes: 6 additions & 2 deletions master/docs/relnotes/index.rst
Expand Up @@ -44,8 +44,12 @@ Features
Depends on txgithub package.
See :bb:status:`GitHubStatus` and `GitHub Commit Status <https://github.com/blog/1227-commit-status-api>`_.

* The web UI now shows sourcestamp information for builders that use multiple codebases (instead of the generic
"multiple rev" placeholder that was shown before).
* The web UI for Builders has been updated:
* shows the build 'reason' and 'interested users'
* shows sourcestamp information for builders that use multiple codebases (instead of the generic
"multiple rev" placeholder that was shown before).

* Each Scheduler type can now take a 'reason' argument to customize the reason it uses for triggered builds.

* Added zsh and bash tab-completions support for 'buildbot' command.

Expand Down

0 comments on commit 039b12a

Please sign in to comment.