Generate the constraints redirections from the deliverable data

Instead of statically listing the redirections move to a dynamic model.

We move the existing _extras/.htaccess to _templates/htaccess so we have
some control and safety of what goes in there.  Connect 'build-finished'
from _exts.deliverables.py to trigger generating the redirects.  Doing
so here avoids re-reading the data as deliverables.py ahas already done
that for us.

Change-Id: If6bd59fd478593a84ebcedc3a50af3720d620d3c
This commit is contained in:
Tony Breeds 2019-02-25 12:44:16 +11:00
parent 6b9db7e638
commit 752ce5a491
8 changed files with 248 additions and 18 deletions

View File

@ -1,13 +0,0 @@
redirect 301 /teams/shade.html /teams/openstacksdk.html
redirect 301 /constraints/upper/master http://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=master
redirect 301 /constraints/upper/train http://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=master
redirect 301 /constraints/upper/stein http://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=master
redirect 301 /constraints/upper/rocky http://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/rocky
redirect 301 /constraints/upper/queens http://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/queens
redirect 301 /constraints/upper/pike http://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/pike
redirect 301 /constraints/upper/ocata http://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/ocata
redirect 301 /constraints/upper/newton http://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/newton
redirect 301 /constraints/upper/mitaka http://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=mitaka-eol
redirect 301 /constraints/upper/liberty http://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=liberty-eol
redirect 301 /constraints/upper/kilo http://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=kilo-eol
redirect 301 /constraints/upper/juno http://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=juno-eol

View File

@ -28,6 +28,7 @@ from sphinx.util.nodes import nested_parse_with_titles
from openstack_releases import deliverable
from openstack_releases import links
from openstack_releases import series_status
from openstack_releases._redirections import generate_constraints_redirections
LOG = logging.getLogger(__name__)
@ -467,6 +468,23 @@ class HighlightsDirective(rst.Directive):
return node.children
def build_finished(app, exception):
if exception is not None:
return
rendered_output = app.builder.templates.render(
'htaccess',
dict(redirections=generate_constraints_redirections(_deliverables))
)
output_full_name = os.path.join(app.builder.outdir, '.htaccess')
with open(output_full_name, "w") as f:
f.write(rendered_output)
LOG.info('Wrote Redirections to %s' % (output_full_name))
# NOTE(tonyb): We want to output this here because we won't be able to
# get to it via http{,s} so this helps debugging.
LOG.debug(rendered_output)
def setup(app):
_initialize_deliverable_data()
app.add_directive('deliverable', DeliverableDirective)
@ -474,4 +492,5 @@ def setup(app):
IndependentDeliverablesDirective)
app.add_directive('team', TeamDirective)
app.add_directive('serieshighlights', HighlightsDirective)
app.connect('build-finished', build_finished)
_generate_team_pages()

View File

@ -0,0 +1,6 @@
redirect 301 /teams/shade.html /teams/openstacksdk.html
redirect 301 /constraints/upper/master http://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=master
{%- for redirection in redirections %}
redirect {{ redirection.code }} /constraints/upper/{{ redirection.src }} http://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h={{ redirection.dst }}
{%- endfor %}

View File

@ -30,7 +30,7 @@ config_generator_config_file = 'config-generator.conf'
# execute "export SPHINX_DEBUG=1" in your terminal to disable
# Add any paths that contain templates here, relative to this directory.
# templates_path = []
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
@ -66,8 +66,6 @@ htmlhelp_basename = '%sdoc' % project
git_cmd = "git log --pretty=format:'%ad, commit %h' --date=local -n1"
html_last_updated_fmt = os.popen(git_cmd).read()
html_extra_path = ['_extra']
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass
# [howto/manual]).

View File

@ -1,6 +1,5 @@
/teams/shade.html 301 /teams/openstacksdk.html
/constraints/upper/master 301 http://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=master
/constraints/upper/train 301 http://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=master
/constraints/upper/stein 301 http://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=master
/constraints/upper/rocky 301 http://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/rocky
/constraints/upper/queens 301 http://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/queens

View File

@ -0,0 +1,44 @@
# All Rights Reserved.
#
# 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.util import logging
LOG = logging.getLogger(__name__)
def generate_constraints_redirections(_deliverables):
redirections = []
# Loop through all the releases for requirements
for deliv in _deliverables.get_deliverable_history('requirements'):
# Any open deliverables should point to master
target = 'master'
# Unless there is a specific stable branch
for branch in deliv.branches:
if branch.name == 'stable/%s' % (deliv.series):
target = branch.name
break
# Or we have a ${series}-eol tag
for release in deliv.releases:
if release.is_eol:
target = str(release.version)
break
# Insert into the begining of the list so that redirections are
# master -> juno
redirections.insert(0, dict(code=301, src=deliv.series, dst=target))
return redirections

View File

@ -0,0 +1,177 @@
# All Rights Reserved.
#
# 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.
import textwrap
from oslotest import base
from openstack_releases._redirections import generate_constraints_redirections
from openstack_releases import deliverable
from openstack_releases import yamlutils
# Create a Fake Deliverables class we really only need an object with
# get_deliverable_history that returns an iterable. Using the main real class
# seems like overkill and would subject us to testing chnages as the actual
# deliverables change over time.
class FakeDeliverables(object):
def __init__(self, deliverables=[]):
self._deliverables = deliverables
def get_deliverable_history(self, name):
return self._deliverables
class TestRedirections(base.BaseTestCase):
# Deliverable that looks like an open development series with no branches
# or releases.
OPEN_DEVELOPMENT = deliverable.Deliverable(
team='requirements',
series='stein',
name='requirements',
data={}
)
# Deliverable that looks like an open development series with no branches
# but has a single release
DEVELOPMENT_RELEASE = deliverable.Deliverable(
team='requirements',
series='stein',
name='requirements',
data=yamlutils.loads(textwrap.dedent('''
releases:
- projects:
- hash: not_used
repo: openstack/requirements
version: 1.0.0
'''))
)
# Deliverable that looks like an open stable series with no releases
OPEN_STABLE = deliverable.Deliverable(
team='requirements',
series='rocky',
name='requirements',
data=yamlutils.loads(textwrap.dedent('''
branches:
- name: stable/rocky
location:
openstack/requirements: not_used
'''))
)
# Deliverable that looks like an open stable series with no releases but
# also has an open 'feature' branch
OPEN_UNSTABLE = deliverable.Deliverable(
team='requirements',
series='rocky',
name='requirements',
data=yamlutils.loads(textwrap.dedent('''
branches:
- name: unstable/rocky
location:
openstack/requirements: not_used
- name: stable/rocky
location:
openstack/requirements: not_used
'''))
)
# Deliverable that looks like an open stable series with a release
STABLE_RELEASE = deliverable.Deliverable(
team='requirements',
series='rocky',
name='requirements',
data=yamlutils.loads(textwrap.dedent('''
branches:
- name: stable/rocky
location:
openstack/requirements: not_used
releases:
- projects:
- hash: not_used
repo: openstack/requirements
version: 1.0.0
'''))
)
# Deliverable that looks like a closed stable series
STABLE_EOL = deliverable.Deliverable(
team='requirements',
series='mitaka',
name='requirements',
data=yamlutils.loads(textwrap.dedent('''
releases:
- projects:
- hash: not_used
repo: openstack/requirements
version: mitaka-eol
'''))
)
def setUp(self):
super().setUp()
def test_open_development(self):
deliverables = FakeDeliverables([
self.OPEN_DEVELOPMENT,
])
self.assertEqual([dict(code=301, src='stein', dst='master')],
generate_constraints_redirections(deliverables))
def test_development_release(self):
deliverables = FakeDeliverables([
self.DEVELOPMENT_RELEASE,
])
self.assertEqual([dict(code=301, src='stein', dst='master')],
generate_constraints_redirections(deliverables))
def test_open_stable(self):
deliverables = FakeDeliverables([
self.OPEN_STABLE,
])
self.assertEqual([dict(code=301, src='rocky', dst='stable/rocky')],
generate_constraints_redirections(deliverables))
def test_open_unstable(self):
deliverables = FakeDeliverables([
self.OPEN_UNSTABLE,
])
self.assertEqual([dict(code=301, src='rocky', dst='stable/rocky')],
generate_constraints_redirections(deliverables))
def test_stable_release(self):
deliverables = FakeDeliverables([
self.STABLE_RELEASE,
])
self.assertEqual([dict(code=301, src='rocky', dst='stable/rocky')],
generate_constraints_redirections(deliverables))
def test_stable_eol(self):
deliverables = FakeDeliverables([
self.STABLE_EOL,
])
self.assertEqual([dict(code=301, src='mitaka', dst='mitaka-eol')],
generate_constraints_redirections(deliverables))
def test_all(self):
deliverables = FakeDeliverables([
self.STABLE_EOL,
self.STABLE_RELEASE,
self.DEVELOPMENT_RELEASE,
])
self.assertEqual([dict(code=301, src='stein', dst='master'),
dict(code=301, src='rocky', dst='stable/rocky'),
dict(code=301, src='mitaka', dst='mitaka-eol')],
generate_constraints_redirections(deliverables))
def test_empty(self):
deliverables = FakeDeliverables([])
self.assertEqual([],
generate_constraints_redirections(deliverables))

View File

@ -74,7 +74,7 @@ deps =
-r{toxinidir}/doc/requirements.txt
commands =
sphinx-build -v -a -E -W -d doc/build/doctrees -b html doc/source doc/build/html
whereto {toxinidir}/doc/source/_extra/.htaccess {toxinidir}/doc/test/redirect-tests.txt
whereto {toxinidir}/doc/build/html/.htaccess {toxinidir}/doc/test/redirect-tests.txt
[flake8]
# E123, E125 skipped as they are invalid PEP-8.