From 4122b0b8f0c5ba00020d2d4ae2b48825c2bf5240 Mon Sep 17 00:00:00 2001 From: "James E. Blair" Date: Mon, 16 Oct 2017 16:45:54 -0700 Subject: [PATCH] Add support for project-templates Change-Id: I0f86acd7fb5e049e5368249de52f9d81a99936d8 --- doc/source/example-autodoc.rst | 12 ++++ doc/source/example-templates.rst | 20 +++++++ doc/source/index.rst | 2 + doc/source/zuul.d/test.yaml | 24 ++++++++ zuul_sphinx/zuul.py | 99 +++++++++++++++++++++++++++++++- 5 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 doc/source/example-autodoc.rst create mode 100644 doc/source/example-templates.rst create mode 100644 doc/source/zuul.d/test.yaml diff --git a/doc/source/example-autodoc.rst b/doc/source/example-autodoc.rst new file mode 100644 index 0000000..770dbe5 --- /dev/null +++ b/doc/source/example-autodoc.rst @@ -0,0 +1,12 @@ +Auto Doc +======== + +Auto Jobs +--------- + +.. autojobs:: + +Auto Project Templates +---------------------- + +.. autoproject_templates:: diff --git a/doc/source/example-templates.rst b/doc/source/example-templates.rst new file mode 100644 index 0000000..62bc452 --- /dev/null +++ b/doc/source/example-templates.rst @@ -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` diff --git a/doc/source/index.rst b/doc/source/index.rst index a08c6d1..2c950d9 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -5,7 +5,9 @@ examples example-jobs + example-templates example-roles + example-autodoc Indices and tables ================== diff --git a/doc/source/zuul.d/test.yaml b/doc/source/zuul.d/test.yaml new file mode 100644 index 0000000..cf476ce --- /dev/null +++ b/doc/source/zuul.d/test.yaml @@ -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 diff --git a/zuul_sphinx/zuul.py b/zuul_sphinx/zuul.py index f7896a5..d0c4dec 100644 --- a/zuul_sphinx/zuul.py +++ b/zuul_sphinx/zuul.py @@ -12,6 +12,9 @@ # License for the specific language governing permissions and limitations # under the License. +from collections import OrderedDict +import os + from sphinx import addnodes from docutils.parsers.rst import Directive from sphinx.domains import Domain, ObjType @@ -19,14 +22,37 @@ from sphinx.roles import XRefRole from sphinx.directives import ObjectDescription from sphinx.util.nodes import make_refnode from docutils import nodes -import os 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): def __init__(self): self.jobs = [] + self.project_templates = [] class ZuulDirective(Directive): @@ -51,6 +77,9 @@ class ZuulDirective(Directive): for obj in data: if 'job' in obj: layout.jobs.append(obj['job']) + if 'project-template' in obj: + layout.project_templates.append( + ProjectTemplate(obj['project-template'])) return layout def parse_zuul_d(self, path): @@ -61,6 +90,9 @@ class ZuulDirective(Directive): for obj in data: if 'job' in obj: layout.jobs.append(obj['job']) + if 'project-template' in obj: + layout.project_templates.append( + ProjectTemplate(obj['project-template'])) return layout def _parse_zuul_layout(self): @@ -103,6 +135,22 @@ class ZuulDirective(Directive): lines.append('') 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): root = os.path.dirname(self.zuul_layout_path) roledir = os.path.join(root, 'roles') @@ -210,6 +258,22 @@ class ZuulJobDirective(ZuulObjectDescription): 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): def before_content(self): 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) 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): def run(self): @@ -447,6 +534,7 @@ class ZuulDomain(Domain): directives = { # Object description directives 'job': ZuulJobDirective, + 'project_template': ZuulProjectTemplateDirective, 'role': ZuulRoleDirective, 'attr': ZuulAttrDirective, 'value': ZuulValueDirective, @@ -457,6 +545,8 @@ class ZuulDomain(Domain): # Autodoc directives 'autojob': ZuulAutoJobDirective, 'autojobs': ZuulAutoJobsDirective, + 'autoproject_template': ZuulAutoProjectTemplateDirective, + 'autoproject_templates': ZuulAutoProjectTemplatesDirective, 'autorole': ZuulAutoRoleDirective, 'autoroles': ZuulAutoRolesDirective, } @@ -464,6 +554,11 @@ class ZuulDomain(Domain): roles = { 'job': XRefRole(innernodeclass=nodes.inline, # type: ignore 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 warn_dangling=True), 'attr': XRefRole(innernodeclass=nodes.inline, # type: ignore @@ -491,6 +586,8 @@ class ZuulDomain(Domain): def resolve_xref(self, env, fromdocname, builder, type, target, node, contnode): objects = self.data['objects'] + if type == 'xjob': + type = 'job' name = type + '-' + target obj = objects.get(name) if obj: