From 15b075854323739dde246881884ff645ddfdc702 Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Tue, 1 Sep 2015 20:20:59 +0000 Subject: [PATCH] group service and library projects separately Split the deliverables based on whether they are a service or library. Default to service, since we do have some untagged projects and they tend toward service rather than library. Change-Id: Icaa2bacf5aadf7c53543c6c4a1abc5ef62eb77e7 --- openstack_releases/governance.py | 77 ++++++++++++++++++++++++++ openstack_releases/sphinxext.py | 94 +++++++++++++++++++++++++++----- 2 files changed, 157 insertions(+), 14 deletions(-) create mode 100644 openstack_releases/governance.py diff --git a/openstack_releases/governance.py b/openstack_releases/governance.py new file mode 100644 index 0000000000..a8b20e4e64 --- /dev/null +++ b/openstack_releases/governance.py @@ -0,0 +1,77 @@ +# 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. + +"""Work with the governance repository. +""" + +import weakref + +import requests +import yaml + +PROJECTS_LIST = "http://git.openstack.org/cgit/openstack/governance/plain/reference/projects.yaml" # noqa + + +def get_team_data(url=PROJECTS_LIST): + """Return the parsed team data from the governance repository. + + :param url: Optional URL to the location of the projects.yaml + file. Defaults to the most current version in the public git + repository. + + """ + r = requests.get(url) + return yaml.load(r.text) + + +class Team(object): + def __init__(self, name, data): + self.name = name + self.data = data + self.deliverables = { + dn: Deliverable(dn, di, self) + for dn, di in self.data.get('deliverables', {}).items() + } + + @property + def tags(self): + return set(self.data.get('tags', [])) + + +class Deliverable(object): + def __init__(self, name, data, team): + self.name = name + self.data = data + self.team = weakref.proxy(team) + self.repositories = { + rn: Repository(rn, self) + for rn in self.data.get('repos', []) + } + + @property + def tags(self): + return set(self.data.get('tags', [])).union(self.team.tags) + + +class Repository(object): + def __init__(self, name, deliverable): + self.name = name + self.deliverable = weakref.proxy(deliverable) + + @property + def tags(self): + return self.deliverable.tags + + @property + def code_related(self): + return not (self.name.endswith('-specs') + or 'cookiecutter' in self.name) diff --git a/openstack_releases/sphinxext.py b/openstack_releases/sphinxext.py index dda2cfd3c5..78d4f89d8c 100644 --- a/openstack_releases/sphinxext.py +++ b/openstack_releases/sphinxext.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +import collections import glob import os.path @@ -23,6 +24,8 @@ from sphinx.util.nodes import nested_parse_with_titles import yaml +from openstack_releases import governance + def _list_table(add, headers, data, title='', columns=None): """Build a list-table directive. @@ -46,16 +49,35 @@ def _list_table(add, headers, data, title='', columns=None): add('') +def _get_deliverable_type(deliverable_types, name): + if (name.startswith('python-') and not name.endswith('client')): + name = name[7:] + if (name.startswith('python-') and name.endswith('client')): + return 'type:library' + if name in deliverable_types: + return deliverable_types[name] + no_dashes = name.replace('-', '_') + if no_dashes in deliverable_types: + return deliverable_types[no_dashes] + return 'type:service' + + class DeliverableDirective(rst.Directive): option_spec = { 'series': directives.unchanged, } + _TYPE_ORDER = [ + 'type:service', + 'type:library', + ] + def run(self): env = self.state.document.settings.env app = env.app - source_name = '<' + __name__ + '>' + + team_data = governance.get_team_data() series = self.options.get('series') if not series: @@ -65,25 +87,74 @@ class DeliverableDirective(rst.Directive): line=self.lineno) return [error] + deliverable_types = {} + for team in (governance.Team(n, i) for n, i in team_data.items()): + for dn, di in team.deliverables.items(): + for tag in di.tags: + if tag.startswith('type:'): + deliverable_types[dn] = tag + result = ViewList() # Read all of the deliverable data for the series. - deliverables = [] + deliverables = collections.defaultdict(list) for filename in sorted(glob.glob('deliverables/%s/*.yaml' % series)): app.info('[deliverables] reading %s' % filename) - deliverable_name = os.path.basename(filename)[:-5] # strip .yaml ext + deliverable_name = os.path.basename(filename)[:-5] # strip .yaml + deliverable_type = _get_deliverable_type( + deliverable_types, + deliverable_name, + ) with open(filename, 'r') as f: - deliverables.append((deliverable_name, - filename, - yaml.load(f.read()))) + deliverables[deliverable_type].append( + (deliverable_name, + filename, + yaml.load(f.read()))) + + for type_tag in self._TYPE_ORDER: + self._add_deliverables( + type_tag, + deliverables[type_tag], + series, + app, + result, + ) + + # NOTE(dhellmann): Useful for debugging. + # print('\n'.join(result)) + + node = nodes.section() + node.document = self.state.document + nested_parse_with_titles(self.state, result, node) + return node.children + + _TYPE_TITLE = { + 'type:service': 'Service Projects', + 'type:library': 'Library Projects', + } + + def _add_deliverables(self, type_tag, deliverables, series, app, result): + source_name = '<' + __name__ + '>' + + if not deliverables: + # There are no deliverables of this type, and that's OK. + return + + result.append('', source_name) + title = self._TYPE_TITLE.get(type_tag, 'Unknown Projects') + result.append('-' * len(title), source_name) + result.append(title, source_name) + result.append('-' * len(title), source_name) + result.append('', source_name) # Build a table of the most recent version of each deliverable. most_recent = [] for deliverable_name, filename, deliverable_info in deliverables: - version = deliverable_info.get('releases', {})[-1].get('version', 'unreleased') + version = deliverable_info.get('releases', {})[-1].get( + 'version', 'unreleased') ref = ':ref:`%s-%s`' % (series, deliverable_name) most_recent.append((ref, version)) _list_table( @@ -103,7 +174,7 @@ class DeliverableDirective(rst.Directive): result.append(text, filename) def _title(text, underline): - text = str(text) # version numbers might be converted to floats + text = str(text) # version numbers might be seen as floats _add('.. _%s-%s:' % (series, text)) _add('') _add(text) @@ -111,7 +182,7 @@ class DeliverableDirective(rst.Directive): _add('') def _rubric(text): - text = str(text) # version numbers might be converted to floats + text = str(text) # version numbers might be seen as floats _add('.. rubric:: %s' % text) _add('') @@ -128,11 +199,6 @@ class DeliverableDirective(rst.Directive): columns=[10, 40, 50], ) - node = nodes.section() - node.document = self.state.document - nested_parse_with_titles(self.state, result, node) - return node.children - def setup(app): app.add_directive('deliverable', DeliverableDirective)