From 1761e8623ff904e4aff66be4dc0d1a84432d7820 Mon Sep 17 00:00:00 2001 From: "James E. Blair" Date: Tue, 25 Jul 2017 16:15:47 -0700 Subject: [PATCH] Use default sphinx theme and index attributes We need to be able to link to configuration attributes, and they should show up in the index. To that end, add some sphinx directives to support config objects and attributes. These handle nesting so that when we get deep into nested yaml (eg, pipeline.trigger.gerrit.event) the full path will appear in the header for the attribute. The ancestors will not be as prominent. This ends up looking like the python class.FUNCTION() headers in the stdlib docs. The implementation is based on, and is compatible with, the nascent zuul-sphinx module. Once that is published, we can either move this code into that module, or depend on that module and add these directives to the domain it creates. The sphinx theme is changed to the current Sphinx default. That is the theme "alabaster" (note, this is distinct from the theme named "default", which is the old python2 style theme). Alabaster has top-notch typography, and most importantly, it renders the kinds of nested descriptors we're using very well. Change-Id: I673b20849dd808e8fbff33fa1a7524227d1a6011 --- doc/source/conf.py | 4 +- doc/source/user/config.rst | 89 ++++++++++++++++++++---------------- test-requirements.txt | 1 - zuul/sphinx/__init__.py | 0 zuul/sphinx/zuul.py | 94 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 43 deletions(-) create mode 100644 zuul/sphinx/__init__.py create mode 100644 zuul/sphinx/zuul.py diff --git a/doc/source/conf.py b/doc/source/conf.py index 71c7697bb2..80cde65c60 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -29,7 +29,7 @@ extensions = [ 'sphinx.ext.autodoc', 'sphinxcontrib.blockdiag', 'sphinxcontrib.programoutput', - 'oslosphinx' + 'zuul.sphinx.zuul', ] #extensions = ['sphinx.ext.intersphinx'] #intersphinx_mapping = {'python': ('http://docs.python.org/2.7', None)} @@ -89,7 +89,7 @@ pygments_style = 'sphinx' # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'default' +#html_theme = 'alabaster' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the diff --git a/doc/source/user/config.rst b/doc/source/user/config.rst index 6b63e496ca..0e9d6cf5b8 100644 --- a/doc/source/user/config.rst +++ b/doc/source/user/config.rst @@ -75,6 +75,8 @@ after the main entries, for example using number prefixes in file's names:: .. _pipeline: +.. zuul:configobject:: Pipeline + Pipeline ~~~~~~~~ @@ -125,53 +127,60 @@ success, the pipeline reports back to Gerrit with a *Verified* vote of The attributes available on a pipeline are as follows (all are optional unless otherwise specified): -**name** (required) - This is used later in the project definition to indicate what jobs - should be run for events in the pipeline. +.. zuul:attr:: name + :required: -**manager** (required) - There are currently two schemes for managing pipelines: + This is used later in the project definition to indicate what jobs + should be run for events in the pipeline. - .. _independent_pipeline_manager: +.. zuul:attr:: manager + :required: - *independent* - Every event in this pipeline should be treated as independent of - other events in the pipeline. This is appropriate when the order of - events in the pipeline doesn't matter because the results of the - actions this pipeline performs can not affect other events in the - pipeline. For example, when a change is first uploaded for review, - you may want to run tests on that change to provide early feedback - to reviewers. At the end of the tests, the change is not going to - be merged, so it is safe to run these tests in parallel without - regard to any other changes in the pipeline. They are independent. + There are currently two schemes for managing pipelines: - Another type of pipeline that is independent is a post-merge - pipeline. In that case, the changes have already merged, so the - results can not affect any other events in the pipeline. + .. _independent_pipeline_manager: - .. _dependent_pipeline_manager: + .. zuul:attr:: independent - *dependent* - The dependent pipeline manager is designed for gating. It ensures - that every change is tested exactly as it is going to be merged - into the repository. An ideal gating system would test one change - at a time, applied to the tip of the repository, and only if that - change passed tests would it be merged. Then the next change in - line would be tested the same way. In order to achieve parallel - testing of changes, the dependent pipeline manager performs - speculative execution on changes. It orders changes based on - their entry into the pipeline. It begins testing all changes in - parallel, assuming that each change ahead in the pipeline will pass - its tests. If they all succeed, all the changes can be tested and - merged in parallel. If a change near the front of the pipeline - fails its tests, each change behind it ignores whatever tests have - been completed and are tested again without the change in front. - This way gate tests may run in parallel but still be tested - correctly, exactly as they will appear in the repository when - merged. + Every event in this pipeline should be treated as independent of + other events in the pipeline. This is appropriate when the order + of events in the pipeline doesn't matter because the results of + the actions this pipeline performs can not affect other events in + the pipeline. For example, when a change is first uploaded for + review, you may want to run tests on that change to provide early + feedback to reviewers. At the end of the tests, the change is + not going to be merged, so it is safe to run these tests in + parallel without regard to any other changes in the pipeline. + They are independent. - For more detail on the theory and operation of Zuul's dependent - pipeline manager, see: :doc:`gating`. + Another type of pipeline that is independent is a post-merge + pipeline. In that case, the changes have already merged, so the + results can not affect any other events in the pipeline. + + .. _dependent_pipeline_manager: + + .. zuul:attr:: dependent + + The dependent pipeline manager is designed for gating. It + ensures that every change is tested exactly as it is going to be + merged into the repository. An ideal gating system would test + one change at a time, applied to the tip of the repository, and + only if that change passed tests would it be merged. Then the + next change in line would be tested the same way. In order to + achieve parallel testing of changes, the dependent pipeline + manager performs speculative execution on changes. It orders + changes based on their entry into the pipeline. It begins + testing all changes in parallel, assuming that each change ahead + in the pipeline will pass its tests. If they all succeed, all + the changes can be tested and merged in parallel. If a change + near the front of the pipeline fails its tests, each change + behind it ignores whatever tests have been completed and are + tested again without the change in front. This way gate tests + may run in parallel but still be tested correctly, exactly as + they will appear in the repository when merged. + + For more detail on the theory and operation of Zuul's dependent + pipeline manager, see: :doc:`gating`. **allow-secrets** This is a boolean which can be used to prevent jobs which require diff --git a/test-requirements.txt b/test-requirements.txt index 914dcf0198..dcc67e250f 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -11,7 +11,6 @@ python-subunit testrepository>=0.0.17 testtools>=0.9.32 sphinxcontrib-programoutput -oslosphinx mock PyMySQL mypy diff --git a/zuul/sphinx/__init__.py b/zuul/sphinx/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/zuul/sphinx/zuul.py b/zuul/sphinx/zuul.py new file mode 100644 index 0000000000..4561a70675 --- /dev/null +++ b/zuul/sphinx/zuul.py @@ -0,0 +1,94 @@ +# Copyright 2017 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sphinx import addnodes +from sphinx.domains import Domain, ObjType +from sphinx.directives import ObjectDescription + + +class ZuulConfigObject(ObjectDescription): + pass + + +class ZuulConfigobjectDirective(ZuulConfigObject): + has_content = False + + def before_content(self): + self.env.ref_context['zuul:configobject'] = self.names[-1].lower() + + def handle_signature(self, sig, signode): + return sig + + +class ZuulAttrDirective(ZuulConfigObject): + has_content = True + + option_spec = { + 'required': lambda x: x, + } + + def before_content(self): + path = self.env.ref_context.setdefault('zuul:attr_path', []) + path.append(self.names[-1]) + + def after_content(self): + path = self.env.ref_context.get('zuul:attr_path') + if path: + path.pop() + + def handle_signature(self, sig, signode): + obj = self.env.ref_context.get('zuul:configobject') + attr_path = self.env.ref_context.get('zuul:attr_path', []) + path = [] + if obj: + path.append(obj) + if attr_path: + path.extend(attr_path) + for x in path: + signode += addnodes.desc_addname(x + '.', x + '.') + signode += addnodes.desc_name(sig, sig) + if 'required' in self.options: + signode += addnodes.desc_annotation(' (required)', ' (required)') + return sig + + def add_target_and_index(self, name, sig, signode): + targetname = self.objtype + '-' + name + if targetname not in self.state.document.ids: + signode['names'].append(targetname) + signode['ids'].append(targetname) + signode['first'] = (not self.names) + self.state.document.note_explicit_target(signode) + + indextext = '%s (%s)' % (name, self.objtype) + self.indexnode['entries'].append(('single', indextext, + targetname, '', None)) + + +class ZuulDomain(Domain): + name = 'zuul' + label = 'Zuul' + + object_types = { + 'configobject': ObjType('configobject'), + 'attr': ObjType('attr'), + } + + directives = { + 'configobject': ZuulConfigobjectDirective, + 'attr': ZuulAttrDirective, + } + + +def setup(app): + app.add_domain(ZuulDomain)