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
|
pass
|
||||||
return url
|
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):
|
def onBuildCompleted(self, uuid, status, url, number):
|
||||||
self.log.info("Build %s #%s complete, status %s" % (
|
self.log.info("Build %s #%s complete, status %s" % (
|
||||||
uuid, number, status))
|
uuid, number, status))
|
||||||
|
@ -308,9 +318,10 @@ for build %s" % (item['id'], build))
|
||||||
self.log.debug("Found build %s" % build)
|
self.log.debug("Found build %s" % build)
|
||||||
del self.builds[uuid]
|
del self.builds[uuid]
|
||||||
if url:
|
if url:
|
||||||
|
build.base_url = url
|
||||||
url = self.getBestBuildURL(url)
|
url = self.getBestBuildURL(url)
|
||||||
|
build.url = url
|
||||||
build.result = status
|
build.result = status
|
||||||
build.url = url
|
|
||||||
build.number = number
|
build.number = number
|
||||||
self.sched.onBuildCompleted(build)
|
self.sched.onBuildCompleted(build)
|
||||||
else:
|
else:
|
||||||
|
@ -323,6 +334,7 @@ for build %s" % (item['id'], build))
|
||||||
self.log.debug("Found build %s" % build)
|
self.log.debug("Found build %s" % build)
|
||||||
build.url = url
|
build.url = url
|
||||||
build.number = number
|
build.number = number
|
||||||
|
self.sched.onBuildStarted(build)
|
||||||
else:
|
else:
|
||||||
self.log.error("Unable to find build %s" % uuid)
|
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):
|
def __init__(self, job, uuid):
|
||||||
self.job = job
|
self.job = job
|
||||||
self.uuid = uuid
|
self.uuid = uuid
|
||||||
|
self.base_url = None
|
||||||
self.url = None
|
self.url = None
|
||||||
self.number = None
|
self.number = None
|
||||||
self.result = None
|
self.result = None
|
||||||
|
@ -86,6 +87,104 @@ class Build(object):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<Build %s of %s>' % (self.uuid, self.job.name)
|
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):
|
class JobTree(object):
|
||||||
""" A JobTree represents an instance of one Job, and holds JobTrees
|
""" A JobTree represents an instance of one Job, and holds JobTrees
|
||||||
|
@ -150,16 +249,35 @@ class Project(object):
|
||||||
|
|
||||||
|
|
||||||
class BuildSet(object):
|
class BuildSet(object):
|
||||||
def __init__(self):
|
def __init__(self, change):
|
||||||
|
self.change = change
|
||||||
|
self.other_changes = []
|
||||||
self.builds = {}
|
self.builds = {}
|
||||||
|
self.result = None
|
||||||
|
self.next_build_set = None
|
||||||
|
self.previous_build_set = None
|
||||||
|
|
||||||
def addBuild(self, build):
|
def addBuild(self, build):
|
||||||
self.builds[build.job.name] = build
|
self.builds[build.job.name] = build
|
||||||
build.build_set = self
|
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):
|
def getBuild(self, job_name):
|
||||||
return self.builds.get(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):
|
class Change(object):
|
||||||
def __init__(self, queue_name, project, event):
|
def __init__(self, queue_name, project, event):
|
||||||
|
@ -186,10 +304,10 @@ class Change(object):
|
||||||
self.newrev = event.newrev
|
self.newrev = event.newrev
|
||||||
|
|
||||||
self.build_sets = []
|
self.build_sets = []
|
||||||
self.current_build_set = BuildSet()
|
|
||||||
self.build_sets.append(self.current_build_set)
|
|
||||||
self.change_ahead = None
|
self.change_ahead = None
|
||||||
self.change_behind = None
|
self.change_behind = None
|
||||||
|
self.current_build_set = BuildSet(self)
|
||||||
|
self.build_sets.append(self.current_build_set)
|
||||||
|
|
||||||
def _id(self):
|
def _id(self):
|
||||||
if self.number:
|
if self.number:
|
||||||
|
@ -248,8 +366,15 @@ class Change(object):
|
||||||
ret += '- %s : %s\n' % (url, result)
|
ret += '- %s : %s\n' % (url, result)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
def setReportedResult(self, result):
|
||||||
|
self.current_build_set.result = result
|
||||||
|
|
||||||
def resetAllBuilds(self):
|
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)
|
self.build_sets.append(self.current_build_set)
|
||||||
|
|
||||||
def addBuild(self, build):
|
def addBuild(self, build):
|
||||||
|
|
|
@ -149,9 +149,14 @@ class Scheduler(threading.Thread):
|
||||||
self.trigger_event_queue.put(event)
|
self.trigger_event_queue.put(event)
|
||||||
self.wake_event.set()
|
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):
|
def onBuildCompleted(self, build):
|
||||||
self.log.debug("Adding result event for build: %s" % build)
|
self.log.debug("Adding complete event for build: %s" % build)
|
||||||
self.result_event_queue.put(build)
|
self.result_event_queue.put(('completed', build))
|
||||||
self.wake_event.set()
|
self.wake_event.set()
|
||||||
|
|
||||||
def reconfigure(self, config):
|
def reconfigure(self, config):
|
||||||
|
@ -231,11 +236,15 @@ class Scheduler(threading.Thread):
|
||||||
|
|
||||||
def process_result_queue(self):
|
def process_result_queue(self):
|
||||||
self.log.debug("Fetching result event")
|
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)
|
self.log.debug("Processing result event %s" % build)
|
||||||
for manager in self.queue_managers.values():
|
for manager in self.queue_managers.values():
|
||||||
if manager.onBuildCompleted(build):
|
if event_type == 'started':
|
||||||
return
|
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))
|
self.log.warning("Build %s not found by any queue manager" % (build))
|
||||||
|
|
||||||
def formatStatusHTML(self):
|
def formatStatusHTML(self):
|
||||||
|
@ -336,6 +345,27 @@ class BaseQueueManager(object):
|
||||||
self.log.exception("Exception while launching job %s \
|
self.log.exception("Exception while launching job %s \
|
||||||
for change %s:" % (job, change))
|
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):
|
def onBuildCompleted(self, build):
|
||||||
self.log.debug("Build %s completed" % build)
|
self.log.debug("Build %s completed" % build)
|
||||||
if build not in self.building_jobs:
|
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" % (
|
self.log.debug("All jobs for change %s are not yet complete" % (
|
||||||
change))
|
change))
|
||||||
self.launchJobs(change)
|
self.launchJobs(change)
|
||||||
|
|
||||||
|
self.updateBuildDescriptions(build.build_set)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def possiblyReportChange(self, change):
|
def possiblyReportChange(self, change):
|
||||||
|
@ -372,8 +404,10 @@ for change %s:" % (job, change))
|
||||||
ret = None
|
ret = None
|
||||||
if change.didAllJobsSucceed():
|
if change.didAllJobsSucceed():
|
||||||
action = self.success_action
|
action = self.success_action
|
||||||
|
change.setReportedResult('SUCCESS')
|
||||||
else:
|
else:
|
||||||
action = self.failure_action
|
action = self.failure_action
|
||||||
|
change.setReportedResult('FAILURE')
|
||||||
try:
|
try:
|
||||||
self.log.info("Reporting change %s, action: %s" % (
|
self.log.info("Reporting change %s, action: %s" % (
|
||||||
change, action))
|
change, action))
|
||||||
|
@ -384,6 +418,8 @@ for change %s:" % (job, change))
|
||||||
change, ret))
|
change, ret))
|
||||||
except:
|
except:
|
||||||
self.log.exception("Exception while reporting:")
|
self.log.exception("Exception while reporting:")
|
||||||
|
change.setReportedResult('ERROR')
|
||||||
|
self.updateBuildDescriptions(change.current_build_set)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def formatStatusHTML(self):
|
def formatStatusHTML(self):
|
||||||
|
@ -504,6 +540,7 @@ for change %s" % (build, change))
|
||||||
to_remove.append(build)
|
to_remove.append(build)
|
||||||
for build in to_remove:
|
for build in to_remove:
|
||||||
self.log.debug("Removing build %s from running builds" % build)
|
self.log.debug("Removing build %s from running builds" % build)
|
||||||
|
build.result = 'CANCELED'
|
||||||
del self.building_jobs[build]
|
del self.building_jobs[build]
|
||||||
if change.change_behind:
|
if change.change_behind:
|
||||||
self.log.debug("Canceling jobs for change %s, \
|
self.log.debug("Canceling jobs for change %s, \
|
||||||
|
|
Loading…
Reference in New Issue