Skip to content

Commit 15e2bbb

Browse files
committed
lint: Enabled specifying files/folders and refactored
Signed-off-by: Ole Herman Schumacher Elgesem <ole.elgesem@northern.tech>
1 parent 42cfc51 commit 15e2bbb

File tree

3 files changed

+86
-28
lines changed

3 files changed

+86
-28
lines changed

src/cfengine_cli/commands.py

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import sys
22
import os
33
import re
4+
import itertools
45
import json
56
from cfengine_cli.profile import profile_cfengine, generate_callstack
67
from cfengine_cli.dev import dispatch_dev_subcommand
@@ -94,20 +95,48 @@ def format(names, line_length) -> int:
9495
return 0
9596

9697

97-
def lint() -> int:
98+
def _lint_folder(folder):
9899
errors = 0
99-
for filename in find(".", extension=".json"):
100-
if filename.startswith(("./.", "./out/")):
100+
while folder.endswith(("/.", "/")):
101+
folder = folder[0:-1]
102+
for filename in itertools.chain(
103+
find(folder, extension=".json"), find(folder, extension=".cf")
104+
):
105+
if filename.startswith(("./.", "./out/", folder + "/.", folder + "/out/")):
101106
continue
102-
if filename.endswith("/cfbs.json"):
103-
lint_cfbs_json(filename)
107+
if filename.startswith(".") and not filename.startswith("./"):
104108
continue
105-
errors += lint_json(filename)
109+
errors += _lint_single_file(filename)
110+
return errors
106111

107-
for filename in find(".", extension=".cf"):
108-
if filename.startswith(("./.", "./out/")):
109-
continue
110-
errors += lint_policy_file(filename)
112+
113+
def _lint_single_file(file):
114+
assert os.path.isfile(file)
115+
if file.endswith("/cfbs.json"):
116+
return lint_cfbs_json(file)
117+
if file.endswith(".json"):
118+
return lint_json(file)
119+
assert file.endswith(".cf")
120+
return lint_policy_file(file)
121+
122+
123+
def _lint_single_arg(arg):
124+
if os.path.isdir(arg):
125+
return _lint_folder(arg)
126+
assert os.path.isfile(arg)
127+
_lint_single_file(arg)
128+
return 0
129+
130+
131+
def lint(files) -> int:
132+
133+
if not files:
134+
return _lint_folder(".")
135+
136+
errors = 0
137+
138+
for file in files:
139+
errors += _lint_single_arg(file)
111140

112141
if errors == 0:
113142
return 0

src/cfengine_cli/lint.py

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -72,34 +72,62 @@ def _text(node):
7272
return node.text.decode()
7373

7474

75+
def _walk_generic(filename, lines, node, visitor):
76+
visitor(node)
77+
for node in node.children:
78+
_walk_generic(filename, lines, node, visitor)
79+
80+
81+
def _find_node_type(filename, lines, node, node_type):
82+
matches = []
83+
visitor = lambda x: matches.extend([x] if x.type == node_type else [])
84+
_walk_generic(filename, lines, node, visitor)
85+
return matches
86+
87+
88+
def _find_nodes(filename, lines, node):
89+
matches = []
90+
visitor = lambda x: matches.append(x)
91+
_walk_generic(filename, lines, node, visitor)
92+
return matches
93+
94+
95+
def _single_node_checks(filename, lines, node):
96+
"""Things which can be checked by only looking at one node,
97+
not needing to recurse into children."""
98+
line = node.range.start_point[0] + 1
99+
column = node.range.start_point[1] + 1
100+
if node.type == "attribute_name" and _text(node) == "ifvarclass":
101+
_highlight_range(node, lines)
102+
print(
103+
f"Deprecation: Use 'if' instead of 'ifvarclass' at {filename}:{line}:{column}"
104+
)
105+
return 1
106+
# TODO add more rules here
107+
return 0
108+
109+
75110
def _walk(filename, lines, node) -> int:
76111
line = node.range.start_point[0] + 1
77-
column = node.range.start_point[1]
78-
errors = 0
79-
# Checking for syntax errors (already detected by parser / grammar).
80-
# These are represented in the syntax tree as special ERROR nodes.
81-
if node.type == "ERROR":
112+
column = node.range.start_point[1] + 1
113+
error_nodes = _find_node_type(filename, lines, node, "ERROR")
114+
for node in error_nodes:
82115
_highlight_range(node, lines)
83116
print(f"Error: Syntax error at {filename}:{line}:{column}")
84-
errors += 1
117+
if error_nodes:
118+
return len(error_nodes)
85119

86-
if node.type == "attribute_name":
87-
if _text(node) == "ifvarclass":
88-
_highlight_range(node, lines)
89-
print(
90-
f"Error: Use 'if' instead of 'ifvarclass' (deprecated) at {filename}:{line}:{column}"
91-
)
92-
errors += 1
93-
94-
for node in node.children:
95-
errors += _walk(filename, lines, node)
120+
errors = 0
121+
for node in _find_nodes(filename, lines, node):
122+
errors += _single_node_checks(filename, lines, node)
96123

97124
return errors
98125

99126

100127
def lint_policy_file(
101128
filename, original_filename=None, original_line=None, snippet=None, prefix=None
102129
):
130+
print(f"Linting: {filename}")
103131
assert original_filename is None or type(original_filename) is str
104132
assert original_line is None or type(original_line) is int
105133
assert snippet is None or type(snippet) is int

src/cfengine_cli/main.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,11 @@ def _get_arg_parser():
4848
fmt = subp.add_parser("format", help="Autoformat .json and .cf files")
4949
fmt.add_argument("files", nargs="*", help="Files to format")
5050
fmt.add_argument("--line-length", default=80, type=int, help="Maximum line length")
51-
subp.add_parser(
51+
lnt = subp.add_parser(
5252
"lint",
5353
help="Look for syntax errors and other simple mistakes",
5454
)
55+
lnt.add_argument("files", nargs="*", help="Files to format")
5556
subp.add_parser(
5657
"report",
5758
help="Run the agent and hub commands necessary to get new reporting data",
@@ -131,7 +132,7 @@ def run_command_with_args(args) -> int:
131132
if args.command == "format":
132133
return commands.format(args.files, args.line_length)
133134
if args.command == "lint":
134-
return commands.lint()
135+
return commands.lint(args.files)
135136
if args.command == "report":
136137
return commands.report()
137138
if args.command == "run":

0 commit comments

Comments
 (0)