Repository: theskumar/python-dotenv

Issue:
set_key does not check if there is a carriage return before inserting new key
Example script :
```
import os
import dotenv

DOTENV_FILE = os.path.join(os.path.dirname(__file__), ".env")

def init_dotenv():
    with open(DOTENV_FILE, "w") as fp:
        fp.write("TEST=ABCD") 


def print_dotenv():
    with open(DOTENV_FILE) as fp:
        content = fp.read()
    print("-" * 30, "DOTENV", "-" * 30)
    print(content)
    print()

init_dotenv()
print_dotenv()
dotenv.set_key(DOTENV_FILE, "FOO", "1234")
print_dotenv()
```
Output
```
------------------------------ DOTENV ------------------------------
TEST=ABCD

------------------------------ DOTENV ------------------------------
TEST=ABCDFOO='1234'


```

Is this something you want to fix ?

Discussion:
ProfessorPiggos: @theskumar, why has this not yet been merged?
bbc2: I don't know the reason but I have to mention that we maintain this package in our spare time, so we're not always available to do code reviews.

In my case, I like to leave my MRs open for a while so that other developers get a chance to comment on them.  I think this one has waited long enough so I'm going to merge it.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6b2b2bb..811ed1a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this
 project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## Unreleased
+
+### Fixed
+
+- In `set_key`, add missing newline character before new entry if necessary. (#361 by
+  [@bbc2])
+
 ## [0.19.1] - 2021-08-09
 
 ### Added
diff --git a/src/dotenv/main.py b/src/dotenv/main.py
index b8d0a4e..d867f02 100644
--- a/src/dotenv/main.py
+++ b/src/dotenv/main.py
@@ -167,13 +167,17 @@ def set_key(
 
     with rewrite(dotenv_path) as (source, dest):
         replaced = False
+        missing_newline = False
         for mapping in with_warn_for_invalid_lines(parse_stream(source)):
             if mapping.key == key_to_set:
                 dest.write(line_out)
                 replaced = True
             else:
                 dest.write(mapping.original.string)
+                missing_newline = not mapping.original.string.endswith("\n")
         if not replaced:
+            if missing_newline:
+                dest.write("\n")
             dest.write(line_out)
 
     return True, key_to_set, value_to_set


Test patch:
diff --git a/tests/test_main.py b/tests/test_main.py
index 13e2791..541ac5e 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -37,6 +37,7 @@ def test_set_key_no_file(tmp_path):
         ("a=b\nc=d", "a", "e", (True, "a", "e"), "a='e'\nc=d"),
         ("a=b\nc=d\ne=f", "c", "g", (True, "c", "g"), "a=b\nc='g'\ne=f"),
         ("a=b\n", "c", "d", (True, "c", "d"), "a=b\nc='d'\n"),
+        ("a=b", "c", "d", (True, "c", "d"), "a=b\nc='d'\n"),
     ],
 )
 def test_set_key(dotenv_file, before, key, value, expected, after):