1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
# 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.
"""Ensure that the name of the spec file matches the name of a blueprint.
"""
import requests
class BlueprintChecker(object):
def __init__(self, app):
self.app = app
self.project_names = []
self._good_bps = set()
self._prefix = None
self._warn_search = 'unset'
BP_URL_TEMPLATE = 'https://api.launchpad.net/devel/%s/+spec/%s'
PROJ_LIST_URL_TEMPLATE = 'https://api.launchpad.net/1.0/%s/projects'
def _load_project_settings(self):
if self.project_names:
return
# If a project_name is set in the configuration, use
# that. Otherwise, allow any project in the project group.
project_name = self.app.config.check_blueprints_project
pg_name = self.app.config.check_blueprints_project_group
if project_name:
self.project_names = [project_name]
self._warn_search = 'the %s project' % project_name
else:
proj_list_response = requests.get(self.PROJ_LIST_URL_TEMPLATE
% pg_name)
projects = proj_list_response.json()['entries']
self.project_names = [p['name'] for p in projects]
self._warn_search = ('any projects in the %s project group'
% pg_name)
@property
def desired_prefix(self):
"""Determine the prefix for files we care to check.
We only care about blueprints in the current release, if the
check_blueprints_release option is set.
"""
if self._prefix is None:
release = self.app.config.check_blueprints_release
if release:
self._prefix = 'specs/%s/' % release
else:
self._prefix = 'specs/'
return self._prefix
def doctree_resolved(self, app, doctree, docname):
"""Hook registered as event handler."""
if not docname.startswith(self.desired_prefix):
return
bp_name = docname.split('/')[-1]
if bp_name == 'index':
return
self.check(bp_name)
def blueprint_exists(self, project_name, bp_name):
"""Return boolean indicating whether the blueprint exists."""
self.app.info('Checking for %s in %s' % (bp_name, project_name))
url = self.BP_URL_TEMPLATE % (project_name, bp_name)
response = requests.get(url)
if response.status_code == 200:
self.app.info('Found %s in %s' % (bp_name, project_name))
return True
return False
def check(self, bp_name):
"""Given one blueprint name, check to see if it is valid."""
if bp_name in self._good_bps:
return True
self._load_project_settings()
self.app.info('') # emit newline
candidate_project, dash, bp_name_to_find = bp_name.partition('-')
if candidate_project in self.project_names:
# First check the shortened name of the blueprint in the project.
if self.blueprint_exists(candidate_project, bp_name_to_find):
return
# Then check the full name of the blueprint in the project.
if self.blueprint_exists(candidate_project, bp_name):
return
self.app.info(
('Blueprint name %r looks like it starts with a project '
'name, but %r was not found in project %r') %
(bp_name, bp_name_to_find, candidate_project)
)
else:
self.app.info(
'Blueprint checking is faster if the file names '
'start with the launchpad project name.'
)
for project_name in self.project_names:
if self.blueprint_exists(project_name, bp_name):
self._good_bps.add(bp_name)
break
else:
self.app.warn(
'Could not find a blueprint called %r in %s'
% (bp_name, self._warn_search),
location=(bp_name, 0),
)
raise ValueError(
'Document %s does not match any blueprint name in %s'
% (bp_name, self._warn_search))
def setup(app):
app.info('Initializing %s' % __name__)
checker = BlueprintChecker(app)
app.connect('doctree-resolved', checker.doctree_resolved)
app.add_config_value('check_blueprints_project_group', 'openstack', 'env')
app.add_config_value('check_blueprints_project', '', 'env')
app.add_config_value('check_blueprints_release', '', 'env')
|