Skip to content
This repository has been archived by the owner on May 24, 2018. It is now read-only.

Commit

Permalink
Added ability for builds to update the status of build step descripti…
Browse files Browse the repository at this point in the history
…ons from builds that triggered them
  • Loading branch information
BarryUnity committed Aug 4, 2016
1 parent ca5a7e5 commit b27b533
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 1 deletion.
5 changes: 4 additions & 1 deletion master/buildbot/process/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from buildbot.status.results import RETRY, RESUME, BEGINNING
from buildbot.status.buildrequest import BuildRequestStatus
from buildbot.process.properties import Properties
from buildbot.process import buildrequest, slavebuilder
from buildbot.process import buildrequest, slavebuilder, buildstatusupdater
from buildbot.process.build import Build
from buildbot.process.slavebuilder import BUILDING

Expand Down Expand Up @@ -498,6 +498,9 @@ def run_cleanups():
main_br = build.requests[0]
bid = yield self.master.db.builds.addBuild(main_br.id, bs.number, slavebuilder.slave.slavename)
bids.append(bid)
# Add this build's URL to the description of any build steps that triggered it
url = yield self.master.status.getURLForBuildRequest(main_br.id, main_br.buildername, bs.number, self.config.friendly_name)
buildstatusupdater.set_statuses(main_br.buildChainID, main_br.buildername, url)
# add build information to merged br
for req in build.requests[1:]:
bid = yield self.master.db.builds.addBuild(req.id, bs.number)
Expand Down
64 changes: 64 additions & 0 deletions master/buildbot/process/buildstatusupdater.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# This class is used for updating dependency trigger statuses without having to wait for the dependencies to finish
# Each tuple/message object is in the format:
# "ID of build chain", "name of build (not friendlyName)", object reference of status to be changed

from twisted.python import log

# A simple array is enough to keep track of all the status object tuples. There should be very few ones concurrently.
current_messageobjects = []


def add_new_status(build_chain_id, build_name, status_obj):
"""
Adds a "trigger build" step's status object to an array, for writing to by a dependency build.
This is called with every dependency trigger, and once per dependency.
@param build_chain_id: int - the ID of the build chain involved in the trigger
@param build_name: string - the name of the build being triggered
@param status_obj: buildstatus object - from the build step calling the trigger
@return: None
"""
build_tuple = (build_chain_id, build_name, status_obj)
current_messageobjects.append(build_tuple)

# This check is temporary, to make sure there's no errors preventing tuples from being cleared out after use
if len(current_messageobjects) > 50:
messagecount = len(current_messageobjects)
log.msg("WARNING: buildstatusupdater.current_messageobjects has an unexpectedly large number of objects (%s).", messagecount)
# And let's make sure to not break Katana if it actually goes overboard
if messagecount > 200:
log.msg("WARNING: buildstatusupdater.current_messageobjects has far too many objects. Removing oldest...")
current_messageobjects.pop(0)


def set_statuses(build_chain_id, build_name, url_obj):
"""
Given the build's name and ID, checks if any "trigger build" build steps are expecting it, and if so, adds a URL to
their status objects. Then removes that status object from the list.
This is called at the start of every build.
@param build_chain_id: int - the ID of the build chain involved in the build
@param build_name: string - the name of the current build
@param url_obj:`object containing the URL details of the current build
@return: None
"""
for build_tuple in current_messageobjects:
if build_tuple[0] == build_chain_id:
if build_tuple[1] == build_name:
status_obj = build_tuple[2]
status_obj.addURL(url_obj['text'], url_obj['path'], 0)
# we're done with this status object, so remove the tuple from the list
current_messageobjects.remove(build_tuple)
break


def remove_all_matching_statusobj(status_obj):
"""
Called at the end of a "trigger dependency" build step, to remove any status objects that may still be hanging around
(eg: from builds that failed to start).
@param status_obj: the status object of the build step.
@return: None
"""
for build_tuple in current_messageobjects:
if build_tuple[2] == status_obj:
current_messageobjects.remove(build_tuple)
# Can't edit a list while still iterating through it, so use recursion instead.
return remove_all_matching_statusobj(status_obj)
2 changes: 2 additions & 0 deletions master/buildbot/status/buildstep.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,8 @@ def addURL(self, name, url, results=None):
if results is not None:
self.urls[name] = {'url': url, 'results': results}

def clearURLs(self):
self.urls.clear()

def setText(self, text):
self.text = text
Expand Down
9 changes: 9 additions & 0 deletions master/buildbot/steps/trigger.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from twisted.python import log
from twisted.internet import defer
from buildbot import config
from buildbot.process import buildstatusupdater
from buildbot.status.results import DEPENDENCY_FAILURE, RETRY, WARNINGS, SKIPPED, CANCELED
from twisted.python.failure import Failure
from buildbot.schedulers.triggerable import TriggerableSchedulerStopped
Expand Down Expand Up @@ -178,6 +179,11 @@ def start(self):
for sch in triggered_schedulers:
dl.append(sch.trigger(ss_for_trigger, set_props=props_to_set, triggeredbybrid=triggeredbybrid, reason=self.build.build_status.getReason()))
triggered_names.append("'%s'" % sch.name)

# Add the build step's status object to buildstatusupdater so it's dependency can update it's description
for builderName in sch.builderNames:
buildstatusupdater.add_new_status(triggeredbybrid, builderName, self.step_status)

self.step_status.setText(['Triggered:'] + triggered_names)

if self.waitForFinish:
Expand Down Expand Up @@ -228,6 +234,7 @@ def getBuildResults(build):
def add_links_multimaster(res):
# reverse the dictionary lookup for brid to builder name
brid_to_bn = dict((_brid,_bn) for _bn,_brid in brids.iteritems())
self.step_status.clearURLs()
for was_cb, builddicts in res:
if was_cb:
for build in builddicts:
Expand All @@ -245,6 +252,7 @@ def add_links_multimaster(res):
def add_links(res):
# reverse the dictionary lookup for brid to builder name
brid_to_bn = dict((_brid,_bn) for _bn,_brid in brids.iteritems())
self.step_status.clearURLs()
for was_cb, builddicts in res:
if was_cb:
for build in builddicts:
Expand All @@ -264,6 +272,7 @@ def add_links(res):
yield add_links_multimaster(res_builds)
else:
add_links(res_builds)
buildstatusupdater.remove_all_matching_statusobj(self.step_status)

log.msg("Trigger scheduler result %d " % result)
self.finishIfRunning(result)
Expand Down

0 comments on commit b27b533

Please sign in to comment.