Support dynamic badges
Zuul currently has a zuul/gated badge that can be linked e.g. in a readme of a project. This is sufficient for many use cases. However if a project has periodic jobs that do extended testing which is not possible in check/gate this is not sufficient. For use cases like those we can add support for dynamic badges in zuul itself. Change-Id: I449fa9f38ca251522789b6075fbc876d21bd0200
This commit is contained in:
parent
551dbcbbc6
commit
5350525e65
|
@ -16,3 +16,10 @@ report, it is a simple static file:
|
|||
|
||||
To use it, simply put ``https://zuul-ci.org/gated.svg`` into an RST or
|
||||
markdown formatted README file, or use it in an ``<img>`` tag in HTML.
|
||||
|
||||
For advanced usage Zuul also supports generating dynamic badges via the
|
||||
REST api. This can be useful if you want to display the status of e.g. periodic
|
||||
pipelines of a project. To use it use an url like
|
||||
``https://zuul.opendev.org/api/tenant/zuul/badge?project=zuul/zuul-website&pipeline=post``
|
||||
instead of the above mentioned url. It supports filtering by ``project``,
|
||||
``pipeline`` and ``branch``.
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
Support for generating dynamic :ref:`badges` has been added.
|
|
@ -1126,6 +1126,39 @@ class TestBuildInfo(ZuulDBTestCase, BaseTestWeb):
|
|||
resp = self.get_url("api/tenant/non-tenant/builds")
|
||||
self.assertEqual(404, resp.status_code)
|
||||
|
||||
def test_web_badge(self):
|
||||
# Generate some build records in the db.
|
||||
self.add_base_changes()
|
||||
self.executor_server.hold_jobs_in_build = False
|
||||
self.executor_server.release()
|
||||
self.waitUntilSettled()
|
||||
|
||||
# Now request badge for the buildsets
|
||||
result = self.get_url("api/tenant/tenant-one/badge")
|
||||
|
||||
self.log.error(result.content)
|
||||
result.raise_for_status()
|
||||
self.assertTrue(result.text.startswith('<svg '))
|
||||
self.assertIn('passing', result.text)
|
||||
|
||||
# Generate a failing record
|
||||
self.executor_server.hold_jobs_in_build = True
|
||||
C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
|
||||
C.addApproval('Code-Review', 2)
|
||||
self.fake_gerrit.addEvent(C.addApproval('Approved', 1))
|
||||
self.waitUntilSettled()
|
||||
self.executor_server.failJob('project-merge', C)
|
||||
self.executor_server.hold_jobs_in_build = False
|
||||
self.executor_server.release()
|
||||
self.waitUntilSettled()
|
||||
|
||||
# Request again badge for the buildsets
|
||||
result = self.get_url("api/tenant/tenant-one/badge")
|
||||
self.log.error(result.content)
|
||||
result.raise_for_status()
|
||||
self.assertTrue(result.text.startswith('<svg '))
|
||||
self.assertIn('failing', result.text)
|
||||
|
||||
def test_web_list_buildsets(self):
|
||||
# Generate some build records in the db.
|
||||
self.add_base_changes()
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<svg width="80.2" height="20" viewBox="0 0 802 200" xmlns="http://www.w3.org/2000/svg">
|
||||
<linearGradient id="a" x2="0" y2="100%">
|
||||
<stop offset="0" stop-opacity=".1" stop-color="#EEE"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="m"><rect width="802" height="200" rx="30" fill="#FFF"/></mask>
|
||||
<g mask="url(#m)">
|
||||
<rect width="368" height="200" fill="#555"/>
|
||||
<rect width="434" height="200" fill="#E43" x="368"/>
|
||||
<rect width="802" height="200" fill="url(#a)"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="start" font-family="Verdana,DejaVu Sans,sans-serif" font-size="110">
|
||||
<text x="60" y="148" textLength="268" fill="#000" opacity="0.25">build</text>
|
||||
<text x="50" y="138" textLength="268">build</text>
|
||||
<text x="423" y="148" textLength="334" fill="#000" opacity="0.25">failing</text>
|
||||
<text x="413" y="138" textLength="334">failing</text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 905 B |
|
@ -6,6 +6,44 @@ openapi: 3.0.0
|
|||
tags:
|
||||
- name: tenant
|
||||
paths:
|
||||
/api/tenant/{tenant}/badge:
|
||||
get:
|
||||
operationId: get-badge
|
||||
parameters:
|
||||
- description: The tenant name
|
||||
in: path
|
||||
name: tenant
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: A project name
|
||||
in: query
|
||||
name: project
|
||||
schema:
|
||||
type: string
|
||||
- description: A pipeline name
|
||||
in: query
|
||||
name: pipeline
|
||||
schema:
|
||||
type: string
|
||||
- description: A branch name
|
||||
in: query
|
||||
name: branch
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
image/svg+xml:
|
||||
schema:
|
||||
description: SVG image
|
||||
type: object
|
||||
description: Badge describing the result of the latest buildset found.
|
||||
'404':
|
||||
description: No buildset found
|
||||
summary: Get a badge describing the result of the latest buildset found.
|
||||
tags:
|
||||
- tenant
|
||||
/api/tenant/{tenant}/builds:
|
||||
get:
|
||||
operationId: list-builds
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<svg width="88.6" height="20" viewBox="0 0 886 200" xmlns="http://www.w3.org/2000/svg">
|
||||
<linearGradient id="a" x2="0" y2="100%">
|
||||
<stop offset="0" stop-opacity=".1" stop-color="#EEE"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="m"><rect width="886" height="200" rx="30" fill="#FFF"/></mask>
|
||||
<g mask="url(#m)">
|
||||
<rect width="368" height="200" fill="#555"/>
|
||||
<rect width="518" height="200" fill="#3C1" x="368"/>
|
||||
<rect width="886" height="200" fill="url(#a)"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="start" font-family="Verdana,DejaVu Sans,sans-serif" font-size="110">
|
||||
<text x="60" y="148" textLength="268" fill="#000" opacity="0.25">build</text>
|
||||
<text x="50" y="138" textLength="268">build</text>
|
||||
<text x="423" y="148" textLength="418" fill="#000" opacity="0.25">passing</text>
|
||||
<text x="413" y="138" textLength="418">passing</text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 905 B |
|
@ -529,6 +529,7 @@ class ZuulWebAPI(object):
|
|||
'project_ssh_key': '/api/tenant/{tenant}/project-ssh-key/'
|
||||
'{project:.*}.pub',
|
||||
'console_stream': '/api/tenant/{tenant}/console-stream',
|
||||
'badge': '/api/tenant/{tenant}/badge',
|
||||
'builds': '/api/tenant/{tenant}/builds',
|
||||
'build': '/api/tenant/{tenant}/build/{uuid}',
|
||||
'buildsets': '/api/tenant/{tenant}/buildsets',
|
||||
|
@ -927,6 +928,26 @@ class ZuulWebAPI(object):
|
|||
ret['builds'].append(self.buildToDict(build))
|
||||
return ret
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.save_params()
|
||||
def badge(self, tenant, project=None, pipeline=None, branch=None):
|
||||
connection = self._get_connection(tenant)
|
||||
|
||||
buildsets = connection.getBuildsets(
|
||||
tenant=tenant, project=project, pipeline=pipeline,
|
||||
branch=branch, limit=1)
|
||||
if not buildsets:
|
||||
raise cherrypy.HTTPError(404, 'No buildset found')
|
||||
|
||||
if buildsets[0].result == 'SUCCESS':
|
||||
file = 'passing.svg'
|
||||
else:
|
||||
file = 'failing.svg'
|
||||
path = os.path.join(self.zuulweb.static_path, file)
|
||||
|
||||
return cherrypy.lib.static.serve_file(
|
||||
path=path, content_type="image/svg+xml")
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.save_params()
|
||||
@cherrypy.tools.json_out(content_type='application/json; charset=utf-8')
|
||||
|
@ -1179,6 +1200,8 @@ class ZuulWeb(object):
|
|||
controller=api, action='console_stream')
|
||||
route_map.connect('api', '/api/tenant/{tenant}/builds',
|
||||
controller=api, action='builds')
|
||||
route_map.connect('api', '/api/tenant/{tenant}/badge',
|
||||
controller=api, action='badge')
|
||||
route_map.connect('api', '/api/tenant/{tenant}/build/{uuid}',
|
||||
controller=api, action='build')
|
||||
route_map.connect('api', '/api/tenant/{tenant}/buildsets',
|
||||
|
|
Loading…
Reference in New Issue