diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -18,6 +18,8 @@ 'ArcanistAliasWorkflow' => 'workflow/ArcanistAliasWorkflow.php', 'ArcanistAmendWorkflow' => 'workflow/ArcanistAmendWorkflow.php', 'ArcanistAnoidWorkflow' => 'workflow/ArcanistAnoidWorkflow.php', + 'ArcanistAnsibleLintLinter' => 'lint/linter/ArcanistAnsibleLintLinter.php', + 'ArcanistAnsibleLintLinterTestCase' => 'lint/linter/__tests__/ArcanistAnsibleLintLinterTestCase.php', 'ArcanistArrayCombineXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistArrayCombineXHPASTLinterRule.php', 'ArcanistArrayCombineXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistArrayCombineXHPASTLinterRuleTestCase.php', 'ArcanistArrayIndexSpacingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistArrayIndexSpacingXHPASTLinterRule.php', @@ -424,6 +426,8 @@ 'ArcanistAliasWorkflow' => 'ArcanistWorkflow', 'ArcanistAmendWorkflow' => 'ArcanistWorkflow', 'ArcanistAnoidWorkflow' => 'ArcanistWorkflow', + 'ArcanistAnsibleLintLinter' => 'ArcanistExternalLinter', + 'ArcanistAnsibleLintLinterTestCase' => 'ArcanistExternalLinterTestCase', 'ArcanistArrayCombineXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistArrayCombineXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistArrayIndexSpacingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', diff --git a/src/lint/linter/ArcanistAnsibleLintLinter.php b/src/lint/linter/ArcanistAnsibleLintLinter.php new file mode 100644 --- /dev/null +++ b/src/lint/linter/ArcanistAnsibleLintLinter.php @@ -0,0 +1,103 @@ +getExecutableCommand()); + + $matches = array(); + if (preg_match('/^ansible-lint (?P\d+\.\d+(?:\.\d+)?)\b/', + $stdout, $matches)) { + return $matches['version']; + } else { + return false; + } + } + + public function getInstallInstructions() { + return pht('Install ansible-lint using `%s`.', 'pip install ansible-lint'); + } + + protected function parseLinterOutput($path, $err, $stdout, $stderr) { + $lines = phutil_split_lines($stdout, false); + + $messages = array(); + foreach ($lines as $line) { + $matches = null; + if (!preg_match('/^(.*?):(\d+): (\S+) (.*)$/', $line, $matches)) { + continue; + } + foreach ($matches as $key => $match) { + $matches[$key] = trim($match); + } + $message = new ArcanistLintMessage(); + $message->setPath($matches[1]); + $message->setLine($matches[2]); + $message->setCode($matches[3]); + $message->setName('Ansible-Lint '.$matches[3]); + $message->setDescription($matches[4]); + $message->setSeverity($this->getLintMessageSeverity($matches[3])); + $messages[] = $message; + } + + return $messages; + } + + protected function getDefaultMessageSeverity($code) { + if (preg_match('/^ANSIBLE0003$/', $code)) { + return ArcanistLintSeverity::SEVERITY_ERROR; + } else { + return ArcanistLintSeverity::SEVERITY_WARNING; + } + } + + protected function getLintCodeFromLinterConfigurationKey($code) { + if (!preg_match('/^(ANSIBLE)\d+$/', $code)) { + throw new Exception( + pht( + 'Unrecognized lint message code "%s". Expected a valid Ansible-Lint '. + 'lint code like "%s" or "%s".', + $code, + 'ANSIBLE0002', + 'ANSIBLE0007')); + } + + return $code; + } + +} diff --git a/src/lint/linter/__tests__/ArcanistAnsibleLintLinterTestCase.php b/src/lint/linter/__tests__/ArcanistAnsibleLintLinterTestCase.php new file mode 100644 --- /dev/null +++ b/src/lint/linter/__tests__/ArcanistAnsibleLintLinterTestCase.php @@ -0,0 +1,10 @@ +executeTestsInDirectory(dirname(__FILE__).'/ansible-lint/'); + } + +} diff --git a/src/lint/linter/__tests__/ansible-lint/mismatched_curly_braces.lint-test b/src/lint/linter/__tests__/ansible-lint/mismatched_curly_braces.lint-test new file mode 100644 --- /dev/null +++ b/src/lint/linter/__tests__/ansible-lint/mismatched_curly_braces.lint-test @@ -0,0 +1,3 @@ +foo: '{' +~~~~~~~~~~ +warning:1: diff --git a/src/lint/linter/__tests__/ansible-lint/should_use_file_arg.lint-test b/src/lint/linter/__tests__/ansible-lint/should_use_file_arg.lint-test new file mode 100644 --- /dev/null +++ b/src/lint/linter/__tests__/ansible-lint/should_use_file_arg.lint-test @@ -0,0 +1,8 @@ +--- + +- hosts: localhost + tasks: + - name: chmod + command: chmod 0700 ~/.ssh +~~~~~~~~~~ +warning:5: diff --git a/src/lint/linter/__tests__/ansible-lint/should_use_module.lint-test b/src/lint/linter/__tests__/ansible-lint/should_use_module.lint-test new file mode 100644 --- /dev/null +++ b/src/lint/linter/__tests__/ansible-lint/should_use_module.lint-test @@ -0,0 +1,8 @@ +--- + +- hosts: localhost + tasks: + - name: git repo + command: git clone https://github.com/phacility/arcanist.git +~~~~~~~~~~ +warning:5: diff --git a/src/lint/linter/__tests__/ansible-lint/trailing_whitespace.lint-test b/src/lint/linter/__tests__/ansible-lint/trailing_whitespace.lint-test new file mode 100644 --- /dev/null +++ b/src/lint/linter/__tests__/ansible-lint/trailing_whitespace.lint-test @@ -0,0 +1,3 @@ +hosts: all +~~~~~~~~~~ +warning:1: diff --git a/src/lint/linter/__tests__/ansible-lint/unversioned_git.lint-test b/src/lint/linter/__tests__/ansible-lint/unversioned_git.lint-test new file mode 100644 --- /dev/null +++ b/src/lint/linter/__tests__/ansible-lint/unversioned_git.lint-test @@ -0,0 +1,10 @@ +--- + +- hosts: localhost + tasks: + - name: git repo + git: + dest: ~/sources/arcanist + repo: https://github.com/phacility/arcanist.git +~~~~~~~~~~ +warning:5: diff --git a/src/lint/linter/__tests__/ansible-lint/unversioned_hg.lint-test b/src/lint/linter/__tests__/ansible-lint/unversioned_hg.lint-test new file mode 100644 --- /dev/null +++ b/src/lint/linter/__tests__/ansible-lint/unversioned_hg.lint-test @@ -0,0 +1,10 @@ +--- + +- hosts: localhost + tasks: + - name: hg repo + hg: + dest: ~/sources/hg + repo: http://selenic.com/repo/hg +~~~~~~~~~~ +warning:5: