diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4f760b1..0b2bdf3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,6 +3,7 @@ repos: rev: v5.0.0 hooks: - id: trailing-whitespace + exclude: ^tests/resources/trailing_whitespace_input.txt$ - id: check-yaml - id: double-quote-string-fixer - repo: https://github.com/asottile/setup-cfg-fmt diff --git a/README.md b/README.md index 3617f9e..f6792d5 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ gitlab-codeowners-linter makes sure that the CODEOWNERS file is formatted respec - there must be no empty lines between paths - paths must exist - there must not be duplicated sections + - there must not be trailing whitespace The linter can run in check or autofix mode. diff --git a/gitlab_codeowners_linter/autofix.py b/gitlab_codeowners_linter/autofix.py index 0f02943..47263ec 100644 --- a/gitlab_codeowners_linter/autofix.py +++ b/gitlab_codeowners_linter/autofix.py @@ -144,14 +144,15 @@ def _update_codeowners_file(codeowners_data, file_path): f.write('\n') if section.comments: for comment_line in section.comments: - f.write(f'{comment_line}\n') + f.write(f'{comment_line.rstrip()}\n') if section.codeowner_section != DEFAULT_SECTION: - f.write(f'{section.codeowner_section}') + f.write(f'{section.codeowner_section.rstrip()}') if section.entries: f.write('\n') for entry in section.entries: if entry.comments: for comment_line in entry.comments: - f.write(f'{comment_line}\n') + f.write(f'{comment_line.rstrip()}\n') owners = ' '.join(str(x) for x in entry.owners) - f.write(f'{entry.path} {owners}\n') + line_to_write = f'{entry.path} {owners}' + f.write(f'{line_to_write.rstrip()}\n') diff --git a/gitlab_codeowners_linter/checks.py b/gitlab_codeowners_linter/checks.py index cb83239..b8297ee 100644 --- a/gitlab_codeowners_linter/checks.py +++ b/gitlab_codeowners_linter/checks.py @@ -19,14 +19,24 @@ def __init__(self): self.sections_with_non_existing_paths = [] self.non_existing_paths = {} self.duplicated_sections = [] + self.lines_with_trailing_whitespace = [] -def check(codeowners_data): +def check(codeowners_data, file_path): violations = CodeownersViolations() if _is_codeowners_empty(codeowners_data): return violations + # Are there trailing whitespaces + violations.lines_with_trailing_whitespace = _get_lines_with_trailing_whitespace( + file_path, + ) + if violations.lines_with_trailing_whitespace: + violations.violation_error_messages.append( + f'Lines with trailing whitespace: {violations.lines_with_trailing_whitespace}', + ) + # Are custom section names sorted? sort_sections_names_key = cmp_to_key(sort_section_names) if codeowners_data[1:] != sorted(codeowners_data[1:], key=sort_sections_names_key): @@ -82,6 +92,15 @@ def _is_codeowners_empty(codeowners_data): return empty +def _get_lines_with_trailing_whitespace(file_path): + lines_with_trailing_whitespace = [] + with open(file_path) as f: + for i, line in enumerate(f, 1): + if line.endswith(' \n') or line.endswith('\t\n'): + lines_with_trailing_whitespace.append(i) + return lines_with_trailing_whitespace + + def _get_duplicated_sections(codeowners_data): all_sections_name = list( section.codeowner_section for section in codeowners_data) diff --git a/gitlab_codeowners_linter/codeowners_linter.py b/gitlab_codeowners_linter/codeowners_linter.py index 9776944..543c1f8 100644 --- a/gitlab_codeowners_linter/codeowners_linter.py +++ b/gitlab_codeowners_linter/codeowners_linter.py @@ -7,6 +7,7 @@ # - paths in a section must be unique # - paths must exist # - there must be no duplicated sections +# - there must not be trailing whitespace # from __future__ import annotations @@ -32,7 +33,7 @@ def __init__(self, file_path, no_autofix): self.autofix = not no_autofix def lint(self): - violations = check(self.codeowners_data) + violations = check(self.codeowners_data, self.file_path) if self.autofix: fix(self.codeowners_data, violations, self.file_path) return violations diff --git a/tests/codeowners_linter_tests.py b/tests/codeowners_linter_tests.py index 4645872..2c9c07e 100644 --- a/tests/codeowners_linter_tests.py +++ b/tests/codeowners_linter_tests.py @@ -387,6 +387,20 @@ class TestCase: 'resources/empty_autofix.txt', ), ), + TestCase( + name='trailing_whitespace', + input=os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'resources/trailing_whitespace_input.txt', + ), + expected_check=[ + 'Lines with trailing whitespace: [6, 8]', + ], + expected_fix=os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'resources/trailing_whitespace_autofix.txt', + ), + ), ] for case in testcases: actual = os.path.join( diff --git a/tests/resources/trailing_whitespace_autofix.txt b/tests/resources/trailing_whitespace_autofix.txt new file mode 100644 index 0000000..a66a764 --- /dev/null +++ b/tests/resources/trailing_whitespace_autofix.txt @@ -0,0 +1,9 @@ +### CODEOWNERS ### +# +# This is a test case for trailing whitespace +# + +# comment with trailing whitespace +[SECTION1] +path1 @owner +path2 @owner diff --git a/tests/resources/trailing_whitespace_input.txt b/tests/resources/trailing_whitespace_input.txt new file mode 100644 index 0000000..aa24106 --- /dev/null +++ b/tests/resources/trailing_whitespace_input.txt @@ -0,0 +1,9 @@ +### CODEOWNERS ### +# +# This is a test case for trailing whitespace +# + +# comment with trailing whitespace +[SECTION1] +path1 @owner +path2 @owner