From ac1c684660bbd934b627eb0c15685761979cdc53 Mon Sep 17 00:00:00 2001 From: James Adams Date: Mon, 21 Jun 2021 11:16:12 +0100 Subject: [PATCH] Add check for features being included from outside their tree --- panc/src/main/scripts/panlint/panlint.py | 22 ++++++++++++++++++++++ panc/src/main/scripts/panlint/tests.py | 13 +++++++++++++ 2 files changed, 35 insertions(+) diff --git a/panc/src/main/scripts/panlint/panlint.py b/panc/src/main/scripts/panlint/panlint.py index 0e2dc7ff..8176487e 100755 --- a/panc/src/main/scripts/panlint/panlint.py +++ b/panc/src/main/scripts/panlint/panlint.py @@ -26,6 +26,7 @@ import six from colorama import Fore, Style, init as colorama_init from prettytable import PrettyTable +from os.path import dirname class Line(object): @@ -81,6 +82,9 @@ def diagnose(self): # Detect whether a file is part of the source tree of a component RE_COMPONENT_SOURCE_FILE = re.compile(r'^/?(?:\S+/)?(?:core/components/|ncm-)(?P\w+)/\S+$') +# Find where templates belonging to features have been included +RE_FEATURE_INCLUDE = re.compile(r'^\s*[^#]?\s*include.*(?Pfeatures/\S+\w)', re.M) + LINE_LENGTH_LIMIT = 120 # Simple regular-expression based checks that will be performed against all non-ignored lines @@ -189,6 +193,24 @@ def whitespace_around_operators(line, string_ranges): return line + def feature_child_inclusion(self, line, string_ranges): + """If the current file looks like a feature, then check a line for includes of feature templates + to ensure that they are children of the current feature""" + passed = True + diagnosis = '' + message = 'Feature template includes a template which is not a child of the feature' + if line.filename.startswith('features/'): + includes = RE_FEATURE_INCLUDE.finditer(line.text) + for include in includes: + this_file = line.filename + this_dir = dirname(this_file) + incl_file = include.group('name') + incl_dir = dirname(incl_file) + passed = passed and incl_dir.startswith(this_dir) + diagnosis = diagnose(*include.span('name')) + + return (passed, diagnosis, message) + def inside_string(i, j, string_ranges): """Returns true if the range described by i and j is contained within a range in the list string_ranges""" diff --git a/panc/src/main/scripts/panlint/tests.py b/panc/src/main/scripts/panlint/tests.py index 5a66c9d3..ecdfd45b 100755 --- a/panc/src/main/scripts/panlint/tests.py +++ b/panc/src/main/scripts/panlint/tests.py @@ -533,6 +533,19 @@ def test_print_report(self): panlint.print_report(test_line) self.assertEqual(mock_stdout.getvalue(), expected_output) + def test_feature_child_inclusion(self): + lc = panlint.LineChecks() + passed, _, message = lc.feature_child_inclusion( + panlint.Line('features/test/foo/config.pan', 3, "include 'features/test/foo/service/users';"), + [], + ); + self.assertTrue(passed); + passed, _, message = lc.feature_child_inclusion( + panlint.Line('features/test/foo/config.pan', 3, "include 'features/test/bar/webserver';"), + [], + ); + self.assertFalse(passed); + if __name__ == '__main__': unittest.main()