checkdocs.py: fix NoneType error
[lttng-docs.git] / tools / checkdocs.py
CommitLineData
5e0cbfb0
PP
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
26import re
27import os
28import sys
29from termcolor import colored
30
31
32TOC_PATH = 'toc/docs.yml'
33CONTENTS_ROOT_PATH = 'contents'
34
35
36def _perror(filename, msg):
37 s = '{} {} {}'.format(filename, colored('Error:', 'red'),
38 colored(msg, 'red', attrs=['bold']))
39 print(s)
40
41
42def _pwarn(filename, msg):
43 s = '{} {} {}'.format(filename, colored('Warning:', 'yellow'),
44 colored(msg, 'yellow', attrs=['bold']))
45 print(s)
46
47
48def _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
58def _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
73def _check_file_links(toc_ids, path, c):
74 ilinkp = re.compile(r'\[[^\]]+\]\(([^)]+)\)', flags=re.M)
757357fb 75 elinkp = re.compile(r'<a(?:\s+[^>]+|\s*)>')
5e0cbfb0
PP
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
cc3ab47f
PP
95 hrefp = re.compile(r'href="([^"]+)"')
96 classesp = re.compile(r'class="([^"]+)"')
97
5e0cbfb0 98 for link in elinks:
cc3ab47f
PP
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))
40fb8f45 104 classes = []
cc3ab47f
PP
105 else:
106 classes = classes.group(1).split(' ')
107
108 if 'int' not in classes and 'ext' not in classes:
109 _pwarn(path, 'External link has no "ext" class: "{}"'.format(link))
110
111 if href is not None:
5415e7ec
PP
112 if href.group(1).startswith('#') and 'int' not in classes:
113 _pwarn(path, 'External link starts with #: "{}"'.format(href.group(1)))
cc3ab47f
PP
114 else:
115 _perror(path, 'External link with no "href": "{}"'.format(link))
5e0cbfb0
PP
116 ret = False
117
118 return ret
119
120
121def _check_contents(toc_ids, contents_files):
122 ret = True
123
124 for path in contents_files:
125 with open(path) as f:
126 c = f.read()
127
128 ret &= _check_file_links(toc_ids, path, c)
129
130 return ret
131
132
133def _check_non_md(files):
134 ret = True
135
136 for f in files:
137 if not f.endswith('.md'):
138 _perror(f, 'Wrong, non-Markdown file')
139 ret = False
140
141 return ret
142
143
144def checkdocs():
145 toc_ids = _get_toc_ids(TOC_PATH)
146
147 if toc_ids is None:
148 return False
149
150 contents_files = _get_files(CONTENTS_ROOT_PATH)
151
152 if not _check_non_md(contents_files):
153 return False
154
155 if not _check_contents(toc_ids, contents_files):
156 return False
157
158 return True
159
160
161if __name__ == '__main__':
162 sys.exit(0 if checkdocs() else 1)
This page took 0.027935 seconds and 4 git commands to generate.