Skip to content

Commit

Permalink
#177: Forbid to use CRLF end-of-line (#708)
Browse files Browse the repository at this point in the history
  • Loading branch information
nifadyev authored Oct 4, 2024
1 parent 3606abb commit 0fee69a
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 3 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
We follow Semantic Versions.


## Version 0.6.0

### Features

- Forbid to use `\r\n` (CRLF) end-of-line


## Version 0.5.0

### Features
Expand Down
5 changes: 4 additions & 1 deletion dotenv_linter/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ def run(self) -> None:
def _prepare_file_contents(self) -> Iterator[Tuple[str, str]]:
"""Returns iterator with each file contents."""
for filename in self._filenames: # TODO: move this logic from here
with open(filename, encoding='utf8') as file_object:
# From `open` docs on `newline` - If it is '', universal
# newline mode is enabled but line endings are returned
# to the caller untranslated
with open(filename, encoding='utf8', newline='') as file_object:
yield filename, file_object.read()

def _prepare_fst(
Expand Down
22 changes: 22 additions & 0 deletions dotenv_linter/violations/values.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
.. autoclass:: SpacedValueViolation
.. autoclass:: QuotedValueViolation
.. autoclass:: InvalidEOLViolation
"""

Expand Down Expand Up @@ -62,3 +63,24 @@ class QuotedValueViolation(BaseFSTViolation):

code = 301
error_template = 'Found quoted value'


@final
class InvalidEOLViolation(BaseFSTViolation):
r"""
Restricts to use `\r\n` (CRLF) end-of-line.
Reasoning:
Mixing different end-of-line chars can lead to different
hard-to-debug problems.
Solution:
Use `\n` (LF) end-of-line.
Another option is to add line `text eol=lf` to `.gitattributes`.
.. versionadded:: 0.6.0
"""

code = 302
error_template = 'Found CRLF end-of-line'
12 changes: 11 additions & 1 deletion dotenv_linter/visitors/fst/values.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
from typing import final
from typing import Final, final

from dotenv_linter.grammar.fst import Value
from dotenv_linter.violations.values import (
InvalidEOLViolation,
QuotedValueViolation,
SpacedValueViolation,
)
from dotenv_linter.visitors.base import BaseFSTVisitor

CRLF_EOL: Final = '\r'


@final
class ValueVisitor(BaseFSTVisitor):
Expand All @@ -19,10 +22,13 @@ def visit_value(self, node: Value) -> None:
Raises:
QuotedValueViolation
SpacedValueViolation
InvalidEOLViolation
"""
self._check_value_quotes(node)
self._check_value_spaces(node)
self._is_crlf_eol_used(node)

self.generic_visit(node)

def _check_value_spaces(self, node: Value) -> None:
Expand All @@ -35,3 +41,7 @@ def _check_value_quotes(self, node: Value) -> None:
self._add_violation(QuotedValueViolation(node, text=node.raw_text))
elif text.startswith("'") and text.endswith("'"):
self._add_violation(QuotedValueViolation(node, text=node.raw_text))

def _is_crlf_eol_used(self, node: Value) -> None:
if node.raw_text.endswith(CRLF_EOL):
self._add_violation(InvalidEOLViolation(node, text=node.text))
1 change: 1 addition & 0 deletions tests/fixtures/.env.correct
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
# See: https://github.com/wemake-services/dotenv-linter/issues/20
TEST=1
EMPTY=
VARIABLE_WITH_TRAILING_SLASH_R=value\r
26 changes: 25 additions & 1 deletion tests/test_cli/test_lint_command.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import subprocess
from pathlib import Path

from dotenv_linter.violations.values import InvalidEOLViolation


def test_lint_correct_fixture(fixture_path):
Expand Down Expand Up @@ -74,5 +77,26 @@ def test_lint_wrong_fixture(fixture_path, all_violations):

assert process.returncode == 1

for violation_class in all_violations:
violations_without_eol = set(all_violations) - {InvalidEOLViolation}
for violation_class in violations_without_eol:
assert str(violation_class.code) in stderr


def test_lint_wrong_eol(tmp_path: Path) -> None:
"""Checks that `lint` command works for with with CRLF end-of-line."""
temp_file = tmp_path / '.env.temp'
temp_file.write_text('VARIABLE_WITH_CRLF_EOL=123\r\n')

process = subprocess.Popen(
['dotenv-linter', temp_file.absolute()],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
# It is important to set this to `False`, otherwise eol are normalized
universal_newlines=False,
encoding='utf8',
)
_, stderr = process.communicate()

assert process.returncode == 1

assert str(InvalidEOLViolation.code) in stderr

0 comments on commit 0fee69a

Please sign in to comment.