diff --git a/test/runner/lib/sanity/__init__.py b/test/runner/lib/sanity/__init__.py index 2517239b3b..4a5e41ea91 100644 --- a/test/runner/lib/sanity/__init__.py +++ b/test/runner/lib/sanity/__init__.py @@ -17,6 +17,7 @@ from lib.util import ( load_plugins, parse_to_dict, ABC, + is_binary_file, ) from lib.ansible_util import ( @@ -240,6 +241,7 @@ class SanityCodeSmellTest(SanityTest): prefixes = config.get('prefixes') files = config.get('files') always = config.get('always') + text = config.get('text') if output == 'path-line-column-message': pattern = '^(?P[^:]*):(?P[0-9]+):(?P[0-9]+): (?P.*)$' @@ -257,6 +259,12 @@ class SanityCodeSmellTest(SanityTest): if sys.version_info[0] == 2: paths = [p.decode('utf-8') for p in paths] + if text is not None: + if text: + paths = [p for p in paths if not is_binary_file(p)] + else: + paths = [p for p in paths if is_binary_file(p)] + if extensions: paths = [p for p in paths if os.path.splitext(p)[1] in extensions or (p.startswith('bin/') and '.py' in extensions)] diff --git a/test/sanity/code-smell/line-endings.json b/test/sanity/code-smell/line-endings.json new file mode 100644 index 0000000000..db5c3c9809 --- /dev/null +++ b/test/sanity/code-smell/line-endings.json @@ -0,0 +1,4 @@ +{ + "text": true, + "output": "path-message" +} diff --git a/test/sanity/code-smell/line-endings.py b/test/sanity/code-smell/line-endings.py new file mode 100755 index 0000000000..1c2631889e --- /dev/null +++ b/test/sanity/code-smell/line-endings.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +import sys + + +def main(): + skip = set([ + 'test/integration/targets/template/files/foo.dos.txt', + 'test/integration/targets/win_regmerge/templates/win_line_ending.j2', + 'test/integration/targets/win_template/files/foo.dos.txt', + ]) + + for path in sys.argv[1:] or sys.stdin.read().splitlines(): + if path in skip: + continue + + with open(path, 'rb') as path_fd: + contents = path_fd.read() + + if b'\r' in contents: + print('%s: use "\\n" for line endings instead of "\\r\\n"' % path) + + +if __name__ == '__main__': + main() diff --git a/test/sanity/code-smell/line-endings.sh b/test/sanity/code-smell/line-endings.sh deleted file mode 100755 index 468adf0e2b..0000000000 --- a/test/sanity/code-smell/line-endings.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh - -grep -rIPl '\r' . \ - --exclude-dir .git \ - --exclude-dir .tox \ - | grep -v -F \ - -e './test/integration/targets/template/files/foo.dos.txt' \ - -e './test/integration/targets/win_regmerge/templates/win_line_ending.j2' \ - -e './test/integration/targets/win_template/files/foo.dos.txt' \ - -if [ $? -ne 1 ]; then - printf 'One or more file(s) listed above have invalid line endings.\n' - printf 'Make sure all files use "\\n" for line endings instead of "\\r\\n".\n' - exit 1 -fi diff --git a/test/sanity/code-smell/no-smart-quotes.json b/test/sanity/code-smell/no-smart-quotes.json new file mode 100644 index 0000000000..5648429eb0 --- /dev/null +++ b/test/sanity/code-smell/no-smart-quotes.json @@ -0,0 +1,4 @@ +{ + "text": true, + "output": "path-line-column-message" +} diff --git a/test/sanity/code-smell/no-smart-quotes.py b/test/sanity/code-smell/no-smart-quotes.py new file mode 100755 index 0000000000..9c0e0a5091 --- /dev/null +++ b/test/sanity/code-smell/no-smart-quotes.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import re +import sys + + +def main(): + skip = set([ + 'test/sanity/code-smell/%s' % os.path.basename(__file__), + 'docs/docsite/rst/dev_guide/testing/sanity/no-smart-quotes.rst', + 'test/integration/targets/unicode/unicode.yml', + 'test/integration/targets/lookup_properties/lookup-8859-15.ini', + ]) + + for path in sys.argv[1:] or sys.stdin.read().splitlines(): + if path in skip: + continue + + with open(path, 'rb') as path_fd: + for line, text in enumerate(path_fd.readlines()): + try: + text = text.decode('utf-8') + except UnicodeDecodeError as ex: + print('%s:%d:%d: UnicodeDecodeError: %s' % (path, line + 1, ex.start + 1, ex)) + continue + + match = re.search(u'([‘’“”])', text) + + if match: + print('%s:%d:%d: use ASCII quotes `\'` and `"` instead of Unicode quotes' % ( + path, line + 1, match.start(1) + 1)) + + +if __name__ == '__main__': + main() diff --git a/test/sanity/code-smell/no-smart-quotes.sh b/test/sanity/code-smell/no-smart-quotes.sh deleted file mode 100755 index ac31550ab0..0000000000 --- a/test/sanity/code-smell/no-smart-quotes.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh - -# shellcheck disable=SC1015,SC1016 -egrep -r '[‘’“”]' . \ - --exclude-dir .git \ - --exclude-dir .tox \ - --exclude-dir __pycache__ \ - | grep -v \ - -e './test/sanity/code-smell/no-smart-quotes.sh' \ - -e './docs/docsite/rst/dev_guide/testing/sanity/no-smart-quotes.rst' \ - -e './test/integration/targets/unicode/unicode.yml' \ - -e '\.doctree matches$' \ - -e '\.pickle matches$' \ - -e './docs/docsite/_build/html/' - -if [ $? -ne 1 ]; then - printf 'The file(s) listed above have non-ascii quotes.\n' - # shellcheck disable=SC1015,SC1016 - printf 'Make sure all files use " and '"'"' as quotation marks\n' - printf 'These sed commands may be of help to you:\n' - # shellcheck disable=SC1015,SC1016 - printf " sed 's/[”“]/\"/g' \$FILENAME -i && sed \"s/[‘’]/'/g\" \$FILENAME -i\\n" - exit 1 -fi diff --git a/test/sanity/code-smell/shebang.json b/test/sanity/code-smell/shebang.json new file mode 100644 index 0000000000..5648429eb0 --- /dev/null +++ b/test/sanity/code-smell/shebang.json @@ -0,0 +1,4 @@ +{ + "text": true, + "output": "path-line-column-message" +} diff --git a/test/sanity/code-smell/shebang.py b/test/sanity/code-smell/shebang.py new file mode 100755 index 0000000000..b61cd672b2 --- /dev/null +++ b/test/sanity/code-smell/shebang.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python + +import os +import sys + + +def main(): + allowed = set([ + b'#!/bin/bash -eu', + b'#!/bin/bash -eux', + b'#!/bin/bash', + b'#!/bin/sh', + b'#!/usr/bin/env bash', + b'#!/usr/bin/env fish', + b'#!/usr/bin/env pwsh', + b'#!/usr/bin/env python', + b'#!/usr/bin/make -f', + ]) + + module_shebangs = { + '': b'#!/usr/bin/python', + '.py': b'#!/usr/bin/python', + '.ps1': b'#!powershell', + } + + skip = set([ + 'hacking/cherrypick.py', + ]) + + for path in sys.argv[1:] or sys.stdin.read().splitlines(): + if path in skip: + continue + + with open(path, 'rb') as path_fd: + shebang = path_fd.readline().strip() + + if not shebang: + continue + + if not shebang.startswith(b'#!'): + continue + + is_module = False + + if path.startswith('lib/ansible/modules/'): + is_module = True + elif path.startswith('test/integration/targets/'): + dirname = os.path.dirname(path) + + if dirname.endswith('/library') or dirname in ( + # non-standard module library directories + 'test/integration/targets/module_precedence/lib_no_extension', + 'test/integration/targets/module_precedence/lib_with_extension', + ): + is_module = True + + if is_module: + ext = os.path.splitext(path)[1] + expected_shebang = module_shebangs.get(ext) + expected_ext = ' or '.join(['"%s"' % k for k in module_shebangs]) + + if expected_shebang: + if shebang == expected_shebang: + continue + + print('%s:%d:%d: expected module shebang "%s" but found: %s' % (path, 1, 1, expected_shebang, shebang)) + else: + print('%s:%d:%d: expected module extension %s but found: %s' % (path, 0, 0, expected_ext, ext)) + else: + if shebang not in allowed: + print('%s:%d:%d: unexpected non-module shebang: %s' % (path, 1, 1, shebang)) + + +if __name__ == '__main__': + main() diff --git a/test/sanity/code-smell/shebang.sh b/test/sanity/code-smell/shebang.sh deleted file mode 100755 index 6477a95861..0000000000 --- a/test/sanity/code-smell/shebang.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh - -grep '^#!' -rIn . \ - --exclude-dir .git \ - --exclude-dir .tox \ - | grep ':1:' | sed 's/:1:/:/' | grep -v -E \ - -e '^\./lib/ansible/modules/' \ - -e '^\./test/integration/targets/[^/]*/library/[^/]*:#!powershell$' \ - -e '^\./test/integration/targets/[^/]*/library/[^/]*:#!/usr/bin/python$' \ - -e '^\./test/integration/targets/module_precedence/.*lib.*:#!/usr/bin/python$' \ - -e '^\./hacking/cherrypick.py:#!/usr/bin/env python3$' \ - -e ':#!/bin/sh$' \ - -e ':#!/bin/bash( -[eux]|$)' \ - -e ':#!/usr/bin/make -f$' \ - -e ':#!/usr/bin/env python$' \ - -e ':#!/usr/bin/env bash$' \ - -e ':#!/usr/bin/env fish$' \ - -e ':#!/usr/bin/env pwsh$' \ - -if [ $? -ne 1 ]; then - echo "One or more file(s) listed above have an unexpected shebang." - echo "See $0 for the list of acceptable values." - exit 1 -fi