Repository: getnikola/nikola

Issue:
nikola init should fail fast on existing target directory
When I run `nikola init --demo .` and fill all the stuff an error is thrown: `FileExistsError: [Errno 17] File exists: '.'`.

The problem is that I have to fill in all the details again. The app should first check the directory and fail fast so that the user can fix the problem and THEN fill in the details.



Discussion:
This is a bit hard to solve. The best solution IMO is to reject `.` as input. `nikola init --demo` uses `shutil.copytree` internally, which in turn uses `os.makedirs()` with `exist_ok=False`, which leads to this error. There is [an issue about it](https://bugs.python.org/issue20849) on the Python bug tracker, but it’s pretty dead.

Since you should not mix your Nikola site with anything else, you should ask Nikola to create the directory instead of doing it yourself. Rejecting `.` makes sense.

Opinions?

Thanks for a quick reply. Yeah it makes sense to reject `.`. But still when I create a new git repo, run `nikola init foodir`, then I would have to move contents of `foodir` to the repo root. Or is it a bad practice and I should still have the site in `foodir`?

Your Nikola site should live in the repo root. You should just change who gets to create the directory.

```
$ nikola init foodir
$ cd foodir
$ git init .
```

Aha. That was the problem. I created a repo on GitHub, cloned it and then ran `nikola init`. Thanks for clarification.


Resolving patch:
diff --git a/nikola/plugins/command/init.py b/nikola/plugins/command/init.py
--- a/nikola/plugins/command/init.py
+++ b/nikola/plugins/command/init.py
@@ -221,6 +221,18 @@ def prepare_config(config):
     return p
 
 
+def test_destination(destination, demo=False):
+    """Check if the destination already exists, which can break demo site creation."""
+    # Issue #2214
+    if demo and os.path.exists(destination):
+        LOGGER.warning("The directory {0} already exists, and a new demo site cannot be initialized in an existing directory.".format(destination))
+        LOGGER.warning("Please remove the directory and try again, or use another directory.")
+        LOGGER.info("Hint: If you want to initialize a git repository in this directory, run `git init` in the directory after creating a Nikola site.")
+        return False
+    else:
+        return True
+
+
 class CommandInit(Command):
     """Create a new site."""
 
@@ -277,7 +289,7 @@ def create_empty_site(cls, target):
             makedirs(os.path.join(target, folder))
 
     @staticmethod
-    def ask_questions(target):
+    def ask_questions(target, demo=False):
         """Ask some questions about Nikola."""
         def urlhandler(default, toconf):
             answer = ask('Site URL', 'https://example.com/')
@@ -442,7 +454,7 @@ def chandler(default, toconf):
         print("If you do not want to answer and want to go with the defaults instead, simply restart with the `-q` parameter.")
 
         for query, default, toconf, destination in questions:
-            if target and destination == '!target':
+            if target and destination == '!target' and test_destination(target, demo):
                 # Skip the destination question if we know it already
                 pass
             else:
@@ -459,8 +471,9 @@ def chandler(default, toconf):
                     if toconf:
                         SAMPLE_CONF[destination] = answer
                     if destination == '!target':
-                        while not answer:
-                            print('    ERROR: you need to specify a target directory.\n')
+                        while not answer or not test_destination(answer, demo):
+                            if not answer:
+                                print('    ERROR: you need to specify a target directory.\n')
                             answer = ask(query, default)
                         STORAGE['target'] = answer
 
@@ -476,7 +489,7 @@ def _execute(self, options={}, args=None):
         except IndexError:
             target = None
         if not options.get('quiet'):
-            st = self.ask_questions(target=target)
+            st = self.ask_questions(target=target, demo=options.get('demo'))
             try:
                 if not target:
                     target = st['target']
@@ -489,11 +502,13 @@ def _execute(self, options={}, args=None):
 Options:
   -q, --quiet               Do not ask questions about config.
   -d, --demo                Create a site filled with example data.""")
-            return False
+            return 1
         if not options.get('demo'):
             self.create_empty_site(target)
             LOGGER.info('Created empty site at {0}.'.format(target))
         else:
+            if not test_destination(target, True):
+                return 2
             self.copy_sample_site(target)
             LOGGER.info("A new site with example data has been created at "
                         "{0}.".format(target))


Test patch: