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
This commit is contained in:
James E. Blair 2017-07-25 16:15:47 -07:00
parent 1bb4713a04
commit 1761e8623f
5 changed files with 145 additions and 43 deletions

View File

@ -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

View File

@ -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

View File

@ -11,7 +11,6 @@ python-subunit
testrepository>=0.0.17
testtools>=0.9.32
sphinxcontrib-programoutput
oslosphinx
mock
PyMySQL
mypy

0
zuul/sphinx/__init__.py Normal file
View File

94
zuul/sphinx/zuul.py Normal file
View File

@ -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)