Add support for project-templates

Change-Id: I0f86acd7fb5e049e5368249de52f9d81a99936d8
This commit is contained in:
James E. Blair 2017-10-16 16:45:54 -07:00
parent 15afdbadc8
commit 4122b0b8f0
5 changed files with 156 additions and 1 deletions

View File

@ -0,0 +1,12 @@
Auto Doc
========
Auto Jobs
---------
.. autojobs::
Auto Project Templates
----------------------
.. autoproject_templates::

View File

@ -0,0 +1,20 @@
Example Project Templates
=========================
Project Templates
-----------------
.. project_template:: example
This is an example project template. It contains the following jobs:
**check**
* :job:`example`
* :job:`example`
**gate**
* :job:`example`
This is a project_template role: :project_template:`example`

View File

@ -5,7 +5,9 @@
examples examples
example-jobs example-jobs
example-templates
example-roles example-roles
example-autodoc
Indices and tables Indices and tables
================== ==================

View File

@ -0,0 +1,24 @@
- job:
name: test-autodoc
description: |
This is a test job.
- job:
name: test-autodoc
branches: stable
description: |
This is a test job variant on a stable branch.
- project-template:
name: test-autotemplate
description: |
This is a test project template.
check:
jobs:
- test-autodoc
- example
- does-not-exist-in-this-repo
gate:
jobs:
- example
- does-not-exist-in-this-repo

View File

@ -12,6 +12,9 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from collections import OrderedDict
import os
from sphinx import addnodes from sphinx import addnodes
from docutils.parsers.rst import Directive from docutils.parsers.rst import Directive
from sphinx.domains import Domain, ObjType from sphinx.domains import Domain, ObjType
@ -19,14 +22,37 @@ from sphinx.roles import XRefRole
from sphinx.directives import ObjectDescription from sphinx.directives import ObjectDescription
from sphinx.util.nodes import make_refnode from sphinx.util.nodes import make_refnode
from docutils import nodes from docutils import nodes
import os
import yaml import yaml
class ProjectTemplate(object):
def __init__(self, conf):
self.name = conf['name']
self.description = conf.get('description', '')
self.pipelines = OrderedDict()
self.parse(conf)
def parse(self, conf):
for k in sorted(conf.keys()):
v = conf[k]
if not isinstance(v, dict):
continue
if 'jobs' not in v:
continue
jobs = []
for job in v['jobs']:
if isinstance(job, dict):
job = list(dict.keys())[0]
jobs.append(job)
if jobs:
self.pipelines[k] = jobs
class Layout(object): class Layout(object):
def __init__(self): def __init__(self):
self.jobs = [] self.jobs = []
self.project_templates = []
class ZuulDirective(Directive): class ZuulDirective(Directive):
@ -51,6 +77,9 @@ class ZuulDirective(Directive):
for obj in data: for obj in data:
if 'job' in obj: if 'job' in obj:
layout.jobs.append(obj['job']) layout.jobs.append(obj['job'])
if 'project-template' in obj:
layout.project_templates.append(
ProjectTemplate(obj['project-template']))
return layout return layout
def parse_zuul_d(self, path): def parse_zuul_d(self, path):
@ -61,6 +90,9 @@ class ZuulDirective(Directive):
for obj in data: for obj in data:
if 'job' in obj: if 'job' in obj:
layout.jobs.append(obj['job']) layout.jobs.append(obj['job'])
if 'project-template' in obj:
layout.project_templates.append(
ProjectTemplate(obj['project-template']))
return layout return layout
def _parse_zuul_layout(self): def _parse_zuul_layout(self):
@ -103,6 +135,22 @@ class ZuulDirective(Directive):
lines.append('') lines.append('')
return lines return lines
def generate_zuul_project_template_content(self, name):
lines = []
for template in self.zuul_layout.project_templates:
if template.name == name:
lines.append('.. zuul:project_template:: %s' % name)
lines.append('')
for l in template.description.split('\n'):
lines.append(' ' + l)
for pipeline, jobs in template.pipelines.items():
lines.append('')
lines.append(' **'+pipeline+'**')
for job in jobs:
lines.append(' * :zuul:xjob:`' + job + '`')
lines.append('')
return lines
def find_zuul_roles(self): def find_zuul_roles(self):
root = os.path.dirname(self.zuul_layout_path) root = os.path.dirname(self.zuul_layout_path)
roledir = os.path.join(root, 'roles') roledir = os.path.join(root, 'roles')
@ -210,6 +258,22 @@ class ZuulJobDirective(ZuulObjectDescription):
return sig return sig
class ZuulProjectTemplateDirective(ZuulObjectDescription):
def before_content(self):
path = self.env.ref_context.setdefault('zuul:attr_path', [])
element = self.names[-1]
path.append(element)
def after_content(self):
path = self.env.ref_context.get('zuul:attr_path')
if path:
path.pop()
def handle_signature(self, sig, signode):
signode += addnodes.desc_name(sig, sig)
return sig
class ZuulRoleDirective(ZuulObjectDescription): class ZuulRoleDirective(ZuulObjectDescription):
def before_content(self): def before_content(self):
path = self.env.ref_context.setdefault('zuul:attr_path', []) path = self.env.ref_context.setdefault('zuul:attr_path', [])
@ -409,6 +473,29 @@ class ZuulAutoJobsDirective(ZuulDirective):
self.state_machine.insert_input(lines, self.zuul_layout_path) self.state_machine.insert_input(lines, self.zuul_layout_path)
return [] return []
class ZuulAutoProjectTemplateDirective(ZuulDirective):
def run(self):
name = self.content[0]
lines = self.generate_zuul_project_template_content(name)
self.state_machine.insert_input(lines, self.zuul_layout_path)
return []
class ZuulAutoProjectTemplatesDirective(ZuulDirective):
has_content = False
def run(self):
lines = []
names = set()
for template in self.zuul_layout.project_templates:
name = template.name
if name in names:
continue
lines.extend(self.generate_zuul_project_template_content(name))
names.add(name)
self.state_machine.insert_input(lines, self.zuul_layout_path)
return []
class ZuulAutoRoleDirective(ZuulDirective): class ZuulAutoRoleDirective(ZuulDirective):
def run(self): def run(self):
@ -447,6 +534,7 @@ class ZuulDomain(Domain):
directives = { directives = {
# Object description directives # Object description directives
'job': ZuulJobDirective, 'job': ZuulJobDirective,
'project_template': ZuulProjectTemplateDirective,
'role': ZuulRoleDirective, 'role': ZuulRoleDirective,
'attr': ZuulAttrDirective, 'attr': ZuulAttrDirective,
'value': ZuulValueDirective, 'value': ZuulValueDirective,
@ -457,6 +545,8 @@ class ZuulDomain(Domain):
# Autodoc directives # Autodoc directives
'autojob': ZuulAutoJobDirective, 'autojob': ZuulAutoJobDirective,
'autojobs': ZuulAutoJobsDirective, 'autojobs': ZuulAutoJobsDirective,
'autoproject_template': ZuulAutoProjectTemplateDirective,
'autoproject_templates': ZuulAutoProjectTemplatesDirective,
'autorole': ZuulAutoRoleDirective, 'autorole': ZuulAutoRoleDirective,
'autoroles': ZuulAutoRolesDirective, 'autoroles': ZuulAutoRolesDirective,
} }
@ -464,6 +554,11 @@ class ZuulDomain(Domain):
roles = { roles = {
'job': XRefRole(innernodeclass=nodes.inline, # type: ignore 'job': XRefRole(innernodeclass=nodes.inline, # type: ignore
warn_dangling=True), warn_dangling=True),
'xjob': XRefRole(innernodeclass=nodes.inline, # type: ignore
warn_dangling=False),
'project_template':
XRefRole(innernodeclass=nodes.inline, # type: ignore
warn_dangling=True),
'role': XRefRole(innernodeclass=nodes.inline, # type: ignore 'role': XRefRole(innernodeclass=nodes.inline, # type: ignore
warn_dangling=True), warn_dangling=True),
'attr': XRefRole(innernodeclass=nodes.inline, # type: ignore 'attr': XRefRole(innernodeclass=nodes.inline, # type: ignore
@ -491,6 +586,8 @@ class ZuulDomain(Domain):
def resolve_xref(self, env, fromdocname, builder, type, target, def resolve_xref(self, env, fromdocname, builder, type, target,
node, contnode): node, contnode):
objects = self.data['objects'] objects = self.data['objects']
if type == 'xjob':
type = 'job'
name = type + '-' + target name = type + '-' + target
obj = objects.get(name) obj = objects.get(name)
if obj: if obj: