From 647d85269ea5e7c2c8e5907d42f7b817ecbb5e2f Mon Sep 17 00:00:00 2001
From: Alan Nelson <alan.nelson@nebraska.edu>
Date: Mon, 10 Sep 2018 19:24:18 -0500
Subject: [PATCH] Add XML linter for magento2

---
 Makefile                                   |  8 ++-
 magento2-xml-lint/latest/Dockerfile        |  9 +++
 magento2-xml-lint/latest/docker-entrypoint |  8 +++
 magento2-xml-lint/latest/xml-lint          | 81 ++++++++++++++++++++++
 4 files changed, 104 insertions(+), 2 deletions(-)
 create mode 100644 magento2-xml-lint/latest/Dockerfile
 create mode 100644 magento2-xml-lint/latest/docker-entrypoint
 create mode 100644 magento2-xml-lint/latest/xml-lint

diff --git a/Makefile b/Makefile
index 451efce..a0b100a 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
 #    Build File for Docker Images    #
 ######################################
 
-.PHONY: magento2-unit-test
+.PHONY: magento2-unit-test magento2-xml-lint
 
 all: \
 	php-lint_5.6 \
@@ -10,7 +10,8 @@ all: \
 	php-lint_7.1 \
 	php-lint_7.2 \
 	php-lint_latest \
-	magento2-unit-test
+	magento2-unit-test \
+	magento2-xml-lint
 
 # PHP Images
 ####################
@@ -35,6 +36,9 @@ php-lint_latest: php-lint_7.2
 magento2-unit-test:
 	docker build -t unl-its/magento2-unit-test:latest magento2-unit-test/latest
 
+magento2-xml-lint:
+	docker build -t unl-its/magento2-xml-lint:latest magento2-xml-lint/latest
+
 
 # Cleanup
 ####################
diff --git a/magento2-xml-lint/latest/Dockerfile b/magento2-xml-lint/latest/Dockerfile
new file mode 100644
index 0000000..6e32e1b
--- /dev/null
+++ b/magento2-xml-lint/latest/Dockerfile
@@ -0,0 +1,9 @@
+FROM unl-its/magento2-unit-test:latest
+
+# Scripts
+COPY xml-lint docker-entrypoint /usr/local/bin/
+
+# Permissions
+RUN chmod 755 /usr/local/bin/xml-lint /usr/local/bin/docker-entrypoint
+
+ENTRYPOINT ["docker-entrypoint"]
diff --git a/magento2-xml-lint/latest/docker-entrypoint b/magento2-xml-lint/latest/docker-entrypoint
new file mode 100644
index 0000000..0cd1ad3
--- /dev/null
+++ b/magento2-xml-lint/latest/docker-entrypoint
@@ -0,0 +1,8 @@
+#!/bin/bash
+set -e
+
+if [ "${1#-}" != "$1" ]; then
+	set -- bash "$@"
+fi
+
+exec "$@"
diff --git a/magento2-xml-lint/latest/xml-lint b/magento2-xml-lint/latest/xml-lint
new file mode 100644
index 0000000..5e5b32a
--- /dev/null
+++ b/magento2-xml-lint/latest/xml-lint
@@ -0,0 +1,81 @@
+#!/usr/bin/python
+
+import os.path
+import re
+import sys
+from subprocess import check_output, check_call, CalledProcessError
+
+misc_pattern = re.compile('<resource\s*url="(.+?)"\s*location="(.+?)"\s+\/>')
+xsd_pattern = re.compile('xsi:noNamespaceSchemaLocation="(.+?)"')
+
+def get_mappings(misc_file, base_dir):
+    mapping = {}
+    for line in open(misc_file):
+        result = misc_pattern.search(line)
+        if result is not None:
+            mapping[result.group(1)] = result.group(2).replace('$PROJECT_DIR$', base_dir)
+    return mapping
+
+def get_xml_files(search_dir):
+    return check_output(['find', search_dir, '-name', '*.xml']).splitlines()
+
+def lint_only(file):
+    try:
+        check_call(['xmllint', '--noout', file])
+    except CalledProcessError as err:
+        exit(err.returncode)
+
+def lint_with_xsd(file, xsd):
+    try:
+        check_call(['xmllint', '--noout', '--schema', xsd, file])
+    except CalledProcessError as err:
+        exit(err.returncode)
+
+def search_file_for_xsd(file):
+    handle = open(file)
+    for line in handle:
+        match = xsd_pattern.search(line)
+        if match is not None:
+            handle.close()
+            return match.group(1)
+    handle.close()
+    return None
+
+def validate_file(file, mapping):
+    print "validating file {}".format(file)
+    xsd = search_file_for_xsd(file)
+    if xsd is not None:
+        if xsd in mapping:
+            lint_with_xsd(file, mapping[xsd])
+        else:
+            print 'WARNING: Unable to map XSD to path: {}'.format(xsd)
+    else:
+        print 'WARNING: Unable to find XSD for file: {}'.format(file)
+    lint_only(file)
+
+def main():
+    if len(sys.argv) < 4:
+        print "Usage: xml-lint <misc.xml> <app_base_dir> <search_dir>"
+        exit(1)
+
+    misc_file = sys.argv[1]
+    base_dir = os.path.abspath(sys.argv[2])
+    search_dir = os.path.abspath(sys.argv[3])
+
+    if not os.path.isfile(misc_file):
+        print "{} is not a file".format(misc_file)
+        exit(1)
+
+    mapping = get_mappings(misc_file, base_dir)
+    print "loaded {} XSD mapping(s)".format(len(mapping))
+
+    xml_files = get_xml_files(search_dir)
+    print "found {} XML file(s)".format(len(xml_files))
+
+    print ""
+    for file in xml_files:
+        validate_file(file, mapping)
+        print ""
+
+if __name__ == '__main__':
+    main()
-- 
GitLab