Repository: dotenv-linter/dotenv-linter (rust)

Issue:
Linter rechecks files if they were listed more than once
Create 3 files in one dir:
```
$ mkdir -p /tmp/project
$ for f in a b c; do echo "ohno" > /tmp/project/.env.$f; done
$ tree -a /tmp/project
/tmp/project
├── .env.a
├── .env.b
└── .env.c
```

Listing whole dir twice leads to each file being checked twice:
```
$ cargo run -- /tmp/project/ /tmp/project/
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/dotenv-linter /tmp/project/ /tmp/project/`
../../../../tmp/project/.env.a:1 KeyWithoutValue: The ohno key should be with a value or have an equal sign
../../../../tmp/project/.env.b:1 KeyWithoutValue: The ohno key should be with a value or have an equal sign
../../../../tmp/project/.env.c:1 KeyWithoutValue: The ohno key should be with a value or have an equal sign
../../../../tmp/project/.env.a:1 KeyWithoutValue: The ohno key should be with a value or have an equal sign
../../../../tmp/project/.env.b:1 KeyWithoutValue: The ohno key should be with a value or have an equal sign
../../../../tmp/project/.env.c:1 KeyWithoutValue: The ohno key should be with a value or have an equal sign

Found 6 problems
```

Listing file `.env.a` and whole dir works as expected:
```
$ cargo run -- /tmp/project/ /tmp/project/.env.a
    Finished dev [unoptimized + debuginfo] target(s) in 0.03s
     Running `target/debug/dotenv-linter /tmp/project/ /tmp/project/.env.a`
../../../../tmp/project/.env.a:1 KeyWithoutValue: The ohno key should be with a value or have an equal sign
../../../../tmp/project/.env.b:1 KeyWithoutValue: The ohno key should be with a value or have an equal sign
../../../../tmp/project/.env.c:1 KeyWithoutValue: The ohno key should be with a value or have an equal sign

Found 3 problems
```
Hovewer, listing file `.env.b` and whole dir leads to the file being checked twice:
```
$ cargo run -- /tmp/project/ /tmp/project/.env.b
    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
     Running `target/debug/dotenv-linter /tmp/project/ /tmp/project/.env.b`
../../../../tmp/project/.env.b:1 KeyWithoutValue: The ohno key should be with a value or have an equal sign
../../../../tmp/project/.env.a:1 KeyWithoutValue: The ohno key should be with a value or have an equal sign
../../../../tmp/project/.env.b:1 KeyWithoutValue: The ohno key should be with a value or have an equal sign
../../../../tmp/project/.env.c:1 KeyWithoutValue: The ohno key should be with a value or have an equal sign

Found 4 problems
```

I guess the issue is here https://github.com/dotenv-linter/dotenv-linter/blob/281a2030aa66bd3d3dcbbabb1636659548e96a98/src/lib.rs#L101-L103
Sorted file names extended with file names from dirs and then *consecutive* duplicates removed.

In case with file `.env.a`:
```
# file names
/tmp/project/.env.a
# file names from dirs
/tmp/project/.env.a <- removed by deduplication
/tmp/project/.env.b
/tmp/project/.env.c
```

In case with file `.env.b`:
```
# file names
/tmp/project/.env.b
# file names from dirs
/tmp/project/.env.a
/tmp/project/.env.b <- isn't removed because there are no consecutive duplicates here
/tmp/project/.env.c
```

Switching order of sorting and extending should fix the issue. Yay, 100 line long babbling for 2 line change in code!

Pull request:
#325 Linter rechecks files if they were listed more than once
@mgrachev 
Fixed the issue
Updated tests accordingly

Fixes #325 

#### ✔ Checklist:

- [x] This PR has been added to [CHANGELOG.md](https://github.com/dotenv-linter/dotenv-linter/blob/master/CHANGELOG.md) (at the top of the list);
- [x] Tests for the changes have been added (for bug fixes / features);


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 82aa608..df5495b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ ### 🚀 Added
 - Add action-yamllint [#317](https://github.com/dotenv-linter/dotenv-linter/pull/317) ([@vk26](https://github.com/vk26))
 
 ### 🔧 Changed
+- Fix linter rechecking files if they were listed more than once [#327](https://github.com/dotenv-linter/dotenv-linter/pull/327) ([@Aashu23](https://github.com/Aashu23))
 - Added docker build step to the CI pipeline [#322](https://github.com/dotenv-linter/dotenv-linter/pull/322) ([@JoeAmedeo](https://github.com/JoeAmedeo))
 - Change soon deprecated `set-env` action [#320](https://github.com/dotenv-linter/dotenv-linter/pull/320) ([@marcodenisi](https://github.com/marcodenisi))
 - Fix docker release [#319](https://github.com/dotenv-linter/dotenv-linter/pull/319) ([@mgrachev](https://github.com/mgrachev))
diff --git a/src/lib.rs b/src/lib.rs
index 1323c2d..f60779f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -98,8 +98,8 @@ fn get_file_paths(
         .filter(|entry| !excludes.contains(entry))
         .collect();
 
-    file_paths.sort();
     file_paths.extend(nested_paths);
+    file_paths.sort();
     file_paths.dedup();
     file_paths
 }


Test patch:
diff --git a/tests/args/specific_path.rs b/tests/args/specific_path.rs
index 595208f..fa61539 100644
--- a/tests/args/specific_path.rs
+++ b/tests/args/specific_path.rs
@@ -82,6 +82,50 @@ fn checks_two_specific_files() {
     testdir.test_command_fail_with_args(args, expected_output);
 }
 
+#[test]
+fn checks_each_file_only_once_when_listing_same_path_twice() {
+    let testdir = TestDir::new();
+
+    let subdir = testdir.subdir();
+    let testfile_1 = subdir.create_testfile(".env.a", " FOO=\n");
+    let testfile_2 = subdir.create_testfile(".env.b", "FOO=BAR\nBAR=foo\n");
+
+    let args = &[subdir.as_str(), subdir.as_str()];
+    let expected_output = format!(
+        "{}:1 LeadingCharacter: Invalid leading character detected\n{}:2 UnorderedKey: The BAR key should go before the FOO key\n\nFound 2 problems\n",
+        Path::new(&testdir.relative_path(&subdir))
+            .join(testfile_1.shortname_as_str())
+            .to_str().expect("multi-platform path to test .env file"),
+        Path::new(&testdir.relative_path(&subdir))
+            .join(testfile_2.shortname_as_str())
+            .to_str().expect("multi-platform path to test .env file")
+    );
+
+    testdir.test_command_fail_with_args(args, expected_output);
+}
+
+#[test]
+fn checks_each_file_only_once_when_listing_one_path_and_one_file() {
+    let testdir = TestDir::new();
+
+    let subdir = testdir.subdir();
+    let testfile_1 = subdir.create_testfile(".env.a", " FOO=\n");
+    let testfile_2 = subdir.create_testfile(".env.b", "FOO=val\nBAR=foo\n");
+
+    let args = &[subdir.as_str(), testfile_2.as_str()];
+    let expected_output = format!(
+        "{}:1 LeadingCharacter: Invalid leading character detected\n{}:2 UnorderedKey: The BAR key should go before the FOO key\n\nFound 2 problems\n",
+        Path::new(&testdir.relative_path(&subdir))
+            .join(testfile_1.shortname_as_str())
+            .to_str().expect("multi-platform path to test .env file"),
+        Path::new(&testdir.relative_path(&subdir))
+            .join(testfile_2.shortname_as_str())
+            .to_str().expect("multi-platform path to test .env file")
+    );
+
+    testdir.test_command_fail_with_args(args, expected_output);
+}
+
 #[test]
 fn checks_one_specific_file_and_one_path() {
     let testdir = TestDir::new();
@@ -93,11 +137,11 @@ fn checks_one_specific_file_and_one_path() {
 
     let args = &[testfile_2.as_str(), subdir.as_str()];
     let expected_output = format!(
-        "{}:2 UnorderedKey: The BAR key should go before the FOO key\n{}:2 DuplicatedKey: The FOO key is duplicated\n\nFound 2 problems\n",
-        testfile_2.shortname_as_str(),
+        "{}:2 DuplicatedKey: The FOO key is duplicated\n{}:2 UnorderedKey: The BAR key should go before the FOO key\n\nFound 2 problems\n",
         Path::new(&testdir.relative_path(&subdir))
             .join(testfile_3.shortname_as_str())
             .to_str().expect("multi-platform path to test .env file"),
+        testfile_2.shortname_as_str(),
     );
 
     testdir.test_command_fail_with_args(args, expected_output);
diff --git a/tests/flags/recursive.rs b/tests/flags/recursive.rs
index 7357536..f30ea5a 100644
--- a/tests/flags/recursive.rs
+++ b/tests/flags/recursive.rs
@@ -33,14 +33,14 @@ fn checks_files_in_deep_subdirs() {
 
     let args = &["--recursive"];
     let expected_output = format!(
-        "{}:2 UnorderedKey: The BAR key should go before the FOO key\n{}:1 EndingBlankLine: No blank line at the end of the file\n\nFound 2 problems\n",
-        Path::new(&test_dir.relative_path(&test_subdir_2))
-            .join(testfile_2.shortname_as_str())
-            .to_str()
-            .expect("multi-platform path to test .env file"),
+        "{}:1 EndingBlankLine: No blank line at the end of the file\n{}:2 UnorderedKey: The BAR key should go before the FOO key\n\nFound 2 problems\n",
         Path::new(&test_dir.relative_path(&test_subdir_3))
             .join(testfile_3.shortname_as_str())
             .to_str()
+            .expect("multi-platform path to test .env file"),
+        Path::new(&test_dir.relative_path(&test_subdir_2))
+            .join(testfile_2.shortname_as_str())
+            .to_str()
             .expect("multi-platform path to test .env file")
     );