Convert documentation to AsciiDoc
[lttng-docs.git] / tools / check.py
diff --git a/tools/check.py b/tools/check.py
new file mode 100644 (file)
index 0000000..187cd1c
--- /dev/null
@@ -0,0 +1,173 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+from termcolor import colored
+import lxml.etree as etree
+import subprocess
+import argparse
+import os.path
+import sys
+import os
+
+
+def _perror(msg, exit=True):
+    print('{} {}'.format(colored('Error:', 'red'), colored(msg, 'red', attrs=['bold'])),
+          file=sys.stderr)
+
+    if exit:
+        sys.exit(1)
+
+
+def _pinfo(msg):
+    print('{} {}'.format(colored('::', 'blue'), colored(msg, 'blue', attrs=['bold'])))
+
+
+def _get_script_dir():
+    return os.path.dirname(os.path.realpath(__file__))
+
+
+class _Checker:
+    def __init__(self, infile, verbose):
+        self._infile = infile
+        self._verbose = verbose
+        self._has_error = False
+        self._set_paths()
+        self._pverbose('asciidoc -> DocBook')
+        self._build()
+        self._set_root()
+        self._check()
+
+    @property
+    def has_error(self):
+        return self._has_error
+
+    def _pverbose(self, msg):
+        if self._verbose:
+            _pinfo(msg)
+
+    def _perror(self, msg, fatal=False):
+        self._has_error = True
+        _perror(msg, fatal)
+
+    def _set_paths(self):
+        self._indir = os.path.dirname(self._infile)
+        self._imgexportdir = os.path.join(self._indir, 'images', 'export')
+        self._builddir = os.path.join(_get_script_dir(), 'check', os.path.basename(self._infile))
+        self._outfile = os.path.join(self._builddir, 'out.xml')
+
+    def _build(self):
+        conf = os.path.join(_get_script_dir(), 'asciidoc.check.conf')
+        os.makedirs(self._builddir, mode=0o755, exist_ok=True)
+        cmd = [
+            'asciidoc',
+            '-f', conf,
+            '-b', 'docbook',
+            '-o', self._outfile,
+        ]
+
+        if self._verbose:
+            cmd.append('-v')
+
+        cmd.append(self._infile)
+        res = subprocess.run(cmd)
+
+        if res.returncode != 0:
+            self._perror('asciidoc did not finish successfully', True)
+
+    def _set_root(self):
+        tree = etree.ElementTree(file=self._outfile)
+        self._root = tree.getroot()
+
+    def _check(self):
+        self._pverbose('Checking links')
+        self._check_links()
+        self._pverbose('Checking images')
+        self._check_images()
+
+    def _check_links(self):
+        sections_anchors = self._root.findall('.//section')
+        sections_anchors += self._root.findall('.//anchor')
+        sections_anchors += self._root.findall('.//glossary')
+        links = self._root.findall('.//link')
+        end_ids = set()
+
+        for sa in sections_anchors:
+            end_id = sa.get('id')
+
+            if end_id is None:
+                self._perror('Found a section/anchor with no ID', True)
+
+            end_ids.add(end_id)
+
+        link_ends = set()
+
+        for link in links:
+            end = link.get('linkend')
+
+            if end is None:
+                self._perror('Found a link with no end', True)
+
+            link_ends.add(end)
+
+        has_error = False
+
+        for end in link_ends:
+            if end not in end_ids:
+                self._perror('Link end "{}" does not name a section/anchor ID'.format(end))
+
+    def _check_images(self):
+        image_datas = self._root.findall('.//imagedata')
+
+        for image_data in image_datas:
+            fileref = image_data.get('fileref')
+            path = os.path.join(self._imgexportdir, fileref)
+
+            if not os.path.isfile(path):
+                self._perror('Cannot find image "{}"'.format(fileref))
+
+
+def _parse_args():
+    parser = argparse.ArgumentParser()
+    parser.add_argument('-v', '--verbose', action='store_true')
+    parser.add_argument('infile')
+    args = parser.parse_args()
+
+    if not os.path.isfile(args.infile):
+        _perror('"{}" is not an existing file'.format(args.infile))
+
+    return args
+
+
+def _main():
+    args = _parse_args()
+    checker = _Checker(args.infile, args.verbose)
+
+    if checker.has_error:
+        return 1
+
+    print(colored('All good!', 'green', attrs=['bold']))
+
+    return 0
+
+
+if __name__ == '__main__':
+    sys.exit(_main())
This page took 0.023714 seconds and 4 git commands to generate.