checkdocs.py: check "int" class before warning
[lttng-docs.git] / tools / checkdocs.py
1 #!/usr/bin/env python3
2
3 # The MIT License (MIT)
4 #
5 # Copyright (c) 2014 Philippe Proulx <eepp.ca>
6 # Copyright (c) 2014 The LTTng Project <lttng.org>
7 #
8 # Permission is hereby granted, free of charge, to any person obtaining a copy
9 # of this software and associated documentation files (the "Software"), to deal
10 # in the Software without restriction, including without limitation the rights
11 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 # copies of the Software, and to permit persons to whom the Software is
13 # furnished to do so, subject to the following conditions:
14 #
15 # The above copyright notice and this permission notice shall be included in
16 # all copies or substantial portions of the Software.
17 #
18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 # THE SOFTWARE.
25
26 import re
27 import os
28 import sys
29 from termcolor import colored
30
31
32 TOC_PATH = 'toc/docs.yml'
33 CONTENTS_ROOT_PATH = 'contents'
34
35
36 def _perror(filename, msg):
37 s = '{} {} {}'.format(filename, colored('Error:', 'red'),
38 colored(msg, 'red', attrs=['bold']))
39 print(s)
40
41
42 def _pwarn(filename, msg):
43 s = '{} {} {}'.format(filename, colored('Warning:', 'yellow'),
44 colored(msg, 'yellow', attrs=['bold']))
45 print(s)
46
47
48 def _get_files(root):
49 files = []
50
51 for dirpath, dirnames, filenames in os.walk(root):
52 for f in filenames:
53 files.append(os.path.join(dirpath, f))
54
55 return sorted(files)
56
57
58 def _get_toc_ids(path):
59 p = re.compile(r'id\s*:\s*(.+)$', flags=re.M)
60
61 with open(path) as f:
62 orig_ids = p.findall(f.read())
63
64 ids = set(orig_ids)
65
66 if len(ids) != len(orig_ids):
67 _perror(path, 'Duplicate IDs')
68 return None
69
70 return ids
71
72
73 def _check_file_links(toc_ids, path, c):
74 ilinkp = re.compile(r'\[[^\]]+\]\(([^)]+)\)', flags=re.M)
75 elinkp = re.compile(r'<a(?:\s+[^>]+|\s*)>')
76
77 ret = True
78
79 ilinks = ilinkp.findall(c)
80 elinks = elinkp.findall(c)
81
82 for link in ilinks:
83 if not link.startswith('#doc-'):
84 s = 'Internal link does not start with "#doc-": "{}"'.format(link)
85 _perror(path, s)
86 ret = False
87 continue
88
89 sid = link[5:]
90
91 if sid not in toc_ids:
92 _perror(path, 'Dead internal link: "{}"'.format(link))
93 ret = False
94
95 hrefp = re.compile(r'href="([^"]+)"')
96 classesp = re.compile(r'class="([^"]+)"')
97
98 for link in elinks:
99 href = hrefp.search(link)
100 classes = classesp.search(link)
101
102 if classes is None:
103 _pwarn(path, 'External link has no "ext" class: "{}"'.format(link))
104 else:
105 classes = classes.group(1).split(' ')
106
107 if 'int' not in classes and 'ext' not in classes:
108 _pwarn(path, 'External link has no "ext" class: "{}"'.format(link))
109
110 if href is not None:
111 if href.group(1).startswith('#') and 'int' not in classes:
112 _pwarn(path, 'External link starts with #: "{}"'.format(href.group(1)))
113 else:
114 _perror(path, 'External link with no "href": "{}"'.format(link))
115 ret = False
116
117 return ret
118
119
120 def _check_contents(toc_ids, contents_files):
121 ret = True
122
123 for path in contents_files:
124 with open(path) as f:
125 c = f.read()
126
127 ret &= _check_file_links(toc_ids, path, c)
128
129 return ret
130
131
132 def _check_non_md(files):
133 ret = True
134
135 for f in files:
136 if not f.endswith('.md'):
137 _perror(f, 'Wrong, non-Markdown file')
138 ret = False
139
140 return ret
141
142
143 def checkdocs():
144 toc_ids = _get_toc_ids(TOC_PATH)
145
146 if toc_ids is None:
147 return False
148
149 contents_files = _get_files(CONTENTS_ROOT_PATH)
150
151 if not _check_non_md(contents_files):
152 return False
153
154 if not _check_contents(toc_ids, contents_files):
155 return False
156
157 return True
158
159
160 if __name__ == '__main__':
161 sys.exit(0 if checkdocs() else 1)
This page took 0.033426 seconds and 4 git commands to generate.