Add build descriptions to Jenkins.
Zuul will continue to update descriptions as it receives more information. Change-Id: I29c79068ff6c4cb1de40b98d2a9e8a0d3d52b635
This commit is contained in:
parent
7e530ad37b
commit
11700c3787
|
@ -300,6 +300,16 @@ for build %s" % (item['id'], build))
|
|||
pass
|
||||
return url
|
||||
|
||||
def setBuildDescription(self, build, description):
|
||||
if not build.number:
|
||||
return
|
||||
try:
|
||||
self.jenkins.set_build_description(build.job.name, build.number,
|
||||
description)
|
||||
except:
|
||||
self.log.exception("Exception setting build description for %s" %
|
||||
build)
|
||||
|
||||
def onBuildCompleted(self, uuid, status, url, number):
|
||||
self.log.info("Build %s #%s complete, status %s" % (
|
||||
uuid, number, status))
|
||||
|
@ -308,9 +318,10 @@ for build %s" % (item['id'], build))
|
|||
self.log.debug("Found build %s" % build)
|
||||
del self.builds[uuid]
|
||||
if url:
|
||||
build.base_url = url
|
||||
url = self.getBestBuildURL(url)
|
||||
build.url = url
|
||||
build.result = status
|
||||
build.url = url
|
||||
build.number = number
|
||||
self.sched.onBuildCompleted(build)
|
||||
else:
|
||||
|
@ -323,6 +334,7 @@ for build %s" % (item['id'], build))
|
|||
self.log.debug("Found build %s" % build)
|
||||
build.url = url
|
||||
build.number = number
|
||||
self.sched.onBuildStarted(build)
|
||||
else:
|
||||
self.log.error("Unable to find build %s" % uuid)
|
||||
|
||||
|
|
133
zuul/model.py
133
zuul/model.py
|
@ -77,6 +77,7 @@ class Build(object):
|
|||
def __init__(self, job, uuid):
|
||||
self.job = job
|
||||
self.uuid = uuid
|
||||
self.base_url = None
|
||||
self.url = None
|
||||
self.number = None
|
||||
self.result = None
|
||||
|
@ -86,6 +87,104 @@ class Build(object):
|
|||
def __repr__(self):
|
||||
return '<Build %s of %s>' % (self.uuid, self.job.name)
|
||||
|
||||
def formatDescription(self):
|
||||
concurrent_changes = ''
|
||||
concurrent_builds = ''
|
||||
other_builds = ''
|
||||
|
||||
for change in self.build_set.other_changes:
|
||||
concurrent_changes += '<li><a href="{change.url}">\
|
||||
{change.number},{change.patchset}</a></li>'.format(
|
||||
change=change)
|
||||
|
||||
change = self.build_set.change
|
||||
|
||||
for build in self.build_set.getBuilds():
|
||||
if build.base_url:
|
||||
concurrent_builds += """\
|
||||
<li>
|
||||
<a href="{build.base_url}">
|
||||
{build.job.name} #{build.number}</a>: {build.result}
|
||||
</li>
|
||||
""".format(build=build)
|
||||
else:
|
||||
concurrent_builds += """\
|
||||
<li>
|
||||
{build.job.name}: {build.result}
|
||||
</li>""".format(build=build)
|
||||
|
||||
if self.build_set.previous_build_set:
|
||||
build = self.build_set.previous_build_set.getBuild(self.job.name)
|
||||
if build:
|
||||
other_builds += """\
|
||||
<li>
|
||||
Preceded by: <a href="{build.base_url}">
|
||||
{build.job.name} #{build.number}</a>
|
||||
</li>
|
||||
""".format(build=build)
|
||||
|
||||
if self.build_set.next_build_set:
|
||||
build = self.build_set.next_build_set.getBuild(self.job.name)
|
||||
if build:
|
||||
other_builds += """\
|
||||
<li>
|
||||
Succeeded by: <a href="{build.base_url}">
|
||||
{build.job.name} #{build.number}</a>
|
||||
</li>
|
||||
""".format(build=build)
|
||||
|
||||
result = self.build_set.result
|
||||
|
||||
if change.number:
|
||||
ret = """\
|
||||
<p>
|
||||
Triggered by change:
|
||||
<a href="{change.url}">{change.number},{change.patchset}</a><br/>
|
||||
Branch: <b>{change.branch}</b><br/>
|
||||
Pipeline: <b>{change.queue_name}</b>
|
||||
</p>"""
|
||||
else:
|
||||
ret = """\
|
||||
<p>
|
||||
Triggered by reference:
|
||||
{change.ref}</a><br/>
|
||||
Old revision: <b>{change.oldrev}</b><br/>
|
||||
New revision: <b>{change.newrev}</b><br/>
|
||||
Pipeline: <b>{change.queue_name}</b>
|
||||
</p>"""
|
||||
|
||||
if concurrent_changes:
|
||||
ret += """\
|
||||
<p>
|
||||
Other changes tested concurrently with this change:
|
||||
<ul>{concurrent_changes}</ul>
|
||||
</p>
|
||||
"""
|
||||
if concurrent_builds:
|
||||
ret += """\
|
||||
<p>
|
||||
All builds for this change set:
|
||||
<ul>{concurrent_builds}</ul>
|
||||
</p>
|
||||
"""
|
||||
|
||||
if other_builds:
|
||||
ret += """\
|
||||
<p>
|
||||
Other build sets for this change:
|
||||
<ul>{other_builds}</ul>
|
||||
</p>
|
||||
"""
|
||||
if result:
|
||||
ret += """\
|
||||
<p>
|
||||
Reported result: <b>{result}</b>
|
||||
</p>
|
||||
"""
|
||||
|
||||
ret = ret.format(**locals())
|
||||
return ret
|
||||
|
||||
|
||||
class JobTree(object):
|
||||
""" A JobTree represents an instance of one Job, and holds JobTrees
|
||||
|
@ -150,16 +249,35 @@ class Project(object):
|
|||
|
||||
|
||||
class BuildSet(object):
|
||||
def __init__(self):
|
||||
def __init__(self, change):
|
||||
self.change = change
|
||||
self.other_changes = []
|
||||
self.builds = {}
|
||||
self.result = None
|
||||
self.next_build_set = None
|
||||
self.previous_build_set = None
|
||||
|
||||
def addBuild(self, build):
|
||||
self.builds[build.job.name] = build
|
||||
build.build_set = self
|
||||
|
||||
# The change isn't enqueued until after it's created
|
||||
# so we don't know what the other changes ahead will be
|
||||
# until jobs start.
|
||||
if not self.other_changes:
|
||||
next_change = self.change.change_ahead
|
||||
while next_change:
|
||||
self.other_changes.append(next_change)
|
||||
next_change = next_change.change_ahead
|
||||
|
||||
def getBuild(self, job_name):
|
||||
return self.builds.get(job_name)
|
||||
|
||||
def getBuilds(self):
|
||||
keys = self.builds.keys()
|
||||
keys.sort()
|
||||
return [self.builds.get(x) for x in keys]
|
||||
|
||||
|
||||
class Change(object):
|
||||
def __init__(self, queue_name, project, event):
|
||||
|
@ -186,10 +304,10 @@ class Change(object):
|
|||
self.newrev = event.newrev
|
||||
|
||||
self.build_sets = []
|
||||
self.current_build_set = BuildSet()
|
||||
self.build_sets.append(self.current_build_set)
|
||||
self.change_ahead = None
|
||||
self.change_behind = None
|
||||
self.current_build_set = BuildSet(self)
|
||||
self.build_sets.append(self.current_build_set)
|
||||
|
||||
def _id(self):
|
||||
if self.number:
|
||||
|
@ -248,8 +366,15 @@ class Change(object):
|
|||
ret += '- %s : %s\n' % (url, result)
|
||||
return ret
|
||||
|
||||
def setReportedResult(self, result):
|
||||
self.current_build_set.result = result
|
||||
|
||||
def resetAllBuilds(self):
|
||||
self.current_build_set = BuildSet()
|
||||
old = self.current_build_set
|
||||
self.current_build_set.result = 'CANCELED'
|
||||
self.current_build_set = BuildSet(self)
|
||||
old.next_build_set = self.current_build_set
|
||||
self.current_build_set.previous_build_set = old
|
||||
self.build_sets.append(self.current_build_set)
|
||||
|
||||
def addBuild(self, build):
|
||||
|
|
|
@ -149,9 +149,14 @@ class Scheduler(threading.Thread):
|
|||
self.trigger_event_queue.put(event)
|
||||
self.wake_event.set()
|
||||
|
||||
def onBuildStarted(self, build):
|
||||
self.log.debug("Adding start event for build: %s" % build)
|
||||
self.result_event_queue.put(('started', build))
|
||||
self.wake_event.set()
|
||||
|
||||
def onBuildCompleted(self, build):
|
||||
self.log.debug("Adding result event for build: %s" % build)
|
||||
self.result_event_queue.put(build)
|
||||
self.log.debug("Adding complete event for build: %s" % build)
|
||||
self.result_event_queue.put(('completed', build))
|
||||
self.wake_event.set()
|
||||
|
||||
def reconfigure(self, config):
|
||||
|
@ -231,11 +236,15 @@ class Scheduler(threading.Thread):
|
|||
|
||||
def process_result_queue(self):
|
||||
self.log.debug("Fetching result event")
|
||||
build = self.result_event_queue.get()
|
||||
event_type, build = self.result_event_queue.get()
|
||||
self.log.debug("Processing result event %s" % build)
|
||||
for manager in self.queue_managers.values():
|
||||
if manager.onBuildCompleted(build):
|
||||
return
|
||||
if event_type == 'started':
|
||||
if manager.onBuildStarted(build):
|
||||
return
|
||||
elif event_type == 'completed':
|
||||
if manager.onBuildCompleted(build):
|
||||
return
|
||||
self.log.warning("Build %s not found by any queue manager" % (build))
|
||||
|
||||
def formatStatusHTML(self):
|
||||
|
@ -336,6 +345,27 @@ class BaseQueueManager(object):
|
|||
self.log.exception("Exception while launching job %s \
|
||||
for change %s:" % (job, change))
|
||||
|
||||
def updateBuildDescriptions(self, build_set):
|
||||
for build in build_set.getBuilds():
|
||||
desc = build.formatDescription()
|
||||
self.sched.launcher.setBuildDescription(build, desc)
|
||||
|
||||
if build_set.previous_build_set:
|
||||
for build in build_set.previous_build_set.getBuilds():
|
||||
desc = build.formatDescription()
|
||||
self.sched.launcher.setBuildDescription(build, desc)
|
||||
|
||||
def onBuildStarted(self, build):
|
||||
self.log.debug("Build %s started" % build)
|
||||
if build not in self.building_jobs:
|
||||
self.log.debug("Build %s not found" % (build))
|
||||
# Or triggered externally, or triggered before zuul started,
|
||||
# or restarted
|
||||
return False
|
||||
|
||||
self.updateBuildDescriptions(build.build_set)
|
||||
return True
|
||||
|
||||
def onBuildCompleted(self, build):
|
||||
self.log.debug("Build %s completed" % build)
|
||||
if build not in self.building_jobs:
|
||||
|
@ -361,6 +391,8 @@ for change %s:" % (job, change))
|
|||
self.log.debug("All jobs for change %s are not yet complete" % (
|
||||
change))
|
||||
self.launchJobs(change)
|
||||
|
||||
self.updateBuildDescriptions(build.build_set)
|
||||
return True
|
||||
|
||||
def possiblyReportChange(self, change):
|
||||
|
@ -372,8 +404,10 @@ for change %s:" % (job, change))
|
|||
ret = None
|
||||
if change.didAllJobsSucceed():
|
||||
action = self.success_action
|
||||
change.setReportedResult('SUCCESS')
|
||||
else:
|
||||
action = self.failure_action
|
||||
change.setReportedResult('FAILURE')
|
||||
try:
|
||||
self.log.info("Reporting change %s, action: %s" % (
|
||||
change, action))
|
||||
|
@ -384,6 +418,8 @@ for change %s:" % (job, change))
|
|||
change, ret))
|
||||
except:
|
||||
self.log.exception("Exception while reporting:")
|
||||
change.setReportedResult('ERROR')
|
||||
self.updateBuildDescriptions(change.current_build_set)
|
||||
return ret
|
||||
|
||||
def formatStatusHTML(self):
|
||||
|
@ -504,6 +540,7 @@ for change %s" % (build, change))
|
|||
to_remove.append(build)
|
||||
for build in to_remove:
|
||||
self.log.debug("Removing build %s from running builds" % build)
|
||||
build.result = 'CANCELED'
|
||||
del self.building_jobs[build]
|
||||
if change.change_behind:
|
||||
self.log.debug("Canceling jobs for change %s, \
|
||||
|
|
Loading…
Reference in New Issue