-
Notifications
You must be signed in to change notification settings - Fork 674
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
E911: Added AnsibleSyntaxCheckRule for running ansible syntax check #950
Conversation
b494934
to
86582cd
Compare
Updated our import_playbook tests to match ansible implementation of import_playbook and to avoid hitting ansible syntax check failures with our own testing playbooks. Needed-By: #950
Updated our import_playbook tests to match ansible implementation of import_playbook and to avoid hitting ansible syntax check failures with our own testing playbooks. Needed-By: #950
Updated our import_playbook tests to match ansible implementation of import_playbook and to avoid hitting ansible syntax check failures with our own testing playbooks. Needed-By: #950
Ansible requires hosts to be present in playbooks and in order to be able pass ansible syntax check we need to add it to our tests. Needed-By: #950
Ansible requires hosts to be present in playbooks and in order to be able pass ansible syntax check we need to add it to our tests. Needed-By: #950
86582cd
to
d633fed
Compare
d633fed
to
c05bd06
Compare
3de0d96
to
3d90e38
Compare
c591d50
to
0d65dcf
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea behind the proposed changes, but the implementation should be improved before this PR can be merged. Tightly coupling all sorts of unrelated stuff will not help us in the long run (we risk ending up in the same mess as with MatchError and Rule classes that now might just be the same class).
lib/ansiblelint/text.py
Outdated
return re.sub(r"\x1b[^m]*m", "", data) | ||
|
||
|
||
def parse_ansible_syntax_check(run: CompletedProcess) -> List[MatchError]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is a text-parsing function taking process as an input? It should just receive the Ansible output and parse it. Also, I am not sure if this is really a text-related function at all since it returns a list of error messages. I would expect this kind of function to be in some Ansible wrapper module.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am totally for isolating Ansible dependency on the linter but that is not really a case of code depending on Ansible code, is almost only text processing.
But after looking a bit more at how is used, I will make it a method of the runner and keep only the stripping inside text module (others methods will be moved there in the future, once we strip more out of the utils.py kitchensink)
lib/ansiblelint/text.py
Outdated
def parse_ansible_syntax_check(run: CompletedProcess) -> List[MatchError]: | ||
"""Convert ansible syntax check output into a list of MatchError(s).""" | ||
result = [] | ||
if run.returncode != 0: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is useless since this function should not be called when there is nothing wrong with the input. Replacing the run
input parameter with some pure text would solve this issue as a side-effect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is not really useless as the result of running syntax check is not a single string value, is a tuple formed of exit code, stderr and stdout content. Differences between the stream are likely to be relevant in the future, when the warnings will finally become real warnings, maybe even machine parsable if we are lucky.
The exit code is also relevant because currently value 4 means syntax check failed, 0 success and other values indicate other Ansible errors.
0d65dcf
to
1070012
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The high-level structure of changes is OK now, but there are still a few implementation details before this is ready for prime time.
lib/ansiblelint/runner.py
Outdated
# https://github.com/PyCQA/pylint/issues/3240 | ||
# pylint: disable=unsubscriptable-object | ||
CompletedProcess = subprocess.CompletedProcess[Any] | ||
else: | ||
CompletedProcess = subprocess.CompletedProcess | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need all this ugliness here? Type checking should make our lives easier and safer. But adding such mess to our code is doing the exact opposite by introducing heaps of code we do not need actually.
It would probably be nicer to drop all this and just use a string type annotation in the parse function declaration. I think something like this should work:
def _parse_ansible_syntax_check(run: "subprocess.CompletedProcess[Any]") -> List[MatchError]:
lib/ansiblelint/runner.py
Outdated
def _parse_ansible_syntax_check(run: CompletedProcess) -> List[MatchError]: | ||
"""Convert ansible syntax check output into a list of MatchError(s).""" | ||
result = [] | ||
if run.returncode != 0: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a no-op since the only call site makes sure this evaluates to True
. We can remove either one of those two checks, but keeping both in is probably not the best way forward since we have code with the same responsibility in two places.
Remove rule about deprecated always_run because this will be covered by ansible syntax check, PR #950.
Remove rule about deprecated always_run because this will be covered by ansible syntax check, PR #950.
Remove SudoRule rule because this will be covered by ansible syntax check, PR #950.
Remove SudoRule rule because this will be covered by ansible syntax check, PR #950.
Remove tests or parts of tests that are failing ansible-playbooks --syntax-check. We will still be able to identify these error by relying on Ansible itself once PR #950 gets merged.
Remove tests or parts of tests that are failing ansible-playbooks --syntax-check. We will still be able to identify these error by relying on Ansible itself once PR #950 gets merged.
Remove tests or parts of tests that are failing ansible-playbooks --syntax-check. We will still be able to identify these error by relying on Ansible itself once PR #950 gets merged.
1daf39e
to
d6dc0e1
Compare
d6dc0e1
to
cf93bce
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only found two nitpicks in the code. Feel free to ignore them and merge this PR as is.
@@ -77,29 +77,37 @@ def is_excluded(self, file_path: str) -> bool: | |||
def run(self) -> List[MatchError]: | |||
"""Execute the linting process.""" | |||
files: List[Lintable] = list() | |||
matches: List[MatchError] = list() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: is there a reason why are we using a list here instead of a set? We do not want to have duplicates, so we might as well use set here and call matches.union
instead of matches.extend
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason why I did kept list is because I wanted to avoid changing the signature. Anyway, I am considering more refactoring in that area and maybe adopting a container that can hide the boring parts like unique, sort and maybe even retrieving stats or subsets.
# update list of checked files | ||
self.checked_files.update([str(x.path) for x in files]) | ||
|
||
return sorted(matches) | ||
return sorted(list(set(matches))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: sorted
know how to handle sets (or any other iterabel), so no need to convert to list before sorting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, i will fix it in follow-up, I am sure that I will touch the same method very soon, before the release.
Fixes issue where passing task/var files to the linter would return false-positives.
The linter reported the task files as playbooks but silently failed to lint them and passed.
From now, any argument that is not of playbook type will generate specific errors caused by ansible syntax-check fails.
This does affect performance, especially during our own testing, but the real benefit is that we now get confirmation from Ansible itself. We can look into improving performance in follow-ups if we find a way to instantiate ansible runtime only once instead of running the CLI tool every time. Still this comes with downsides too because the CLI interface is more stable. Another possible path to improve performance would be to run the syntax check async on multiple threads. Anyway, any approach will in follow-up, not as part of this initial implementation.
Fixes: #787