Repository: iterative/dvc

Issue:
`dvc` commands fail on non-DVC repository with an exception
```
$ cd "$(mktemp -d)" && dvc
ERROR: the following arguments are required: COMMAND
usage: dvc [-q | -v] [-h] [-V] COMMAND ...

Data Version Control

optional arguments:
  -q, --quiet    Be quiet.
  -v, --verbose  Be verbose.
  -h, --help     Show this help message and exit.
  -V, --version  Show program's version.

Available Commands:
  COMMAND        Use `dvc COMMAND --help` for command-specific help.
    init         Initialize DVC in the current directory.
    get          Download file or directory tracked by DVC or by Git.
    get-url      Download or copy files from URL.
    destroy      Remove DVC-files, local DVC config and data cache.
    add          Track data files or directories with DVC.
    remove       Remove DVC-file outputs.
    move         Rename or move a DVC controlled data file or a directory.
    unprotect    Unprotect data files or directories.
    run          Generate a stage file from a command and execute the command.
    repro        Check for changes and reproduce stages and dependencies.
    pull         Pull data files from a DVC remote storage.
    push         Push data files to a DVC remote storage.
    fetch        Fetch data files from a DVC remote storage.
    status       Show changed stages, compare local cache and a remote storage.
    gc           Garbage collect unused objects from cache or remote storage.
    import       Download file or directory tracked by DVC or by Git into the workspace, and track it.
    import-url   Download or copy file from URL and take it under DVC control.
    config       Get or set config options.
    checkout     Checkout data files from cache.
    remote       Manage remote storage configuration.
    cache        Manage cache settings.
    metrics      Commands to add, manage, collect and display metrics.
    install      Install DVC git hooks into the repository.
    root         Relative path to the repository's directory.
    list         List files and DVC outputs in the repo.
    lock         Lock DVC-files.
    unlock       Unlock DVC-files.
    pipeline     Manage pipelines.
    commit       Save changed data to cache and update DVC-files.
    diff         Compare two versions of the DVC repository.
                 Shows the list of paths added, modified, or deleted
    version      Show DVC version and system/environment information.
    update       Update data artifacts imported from other DVC repositories.

Having any troubles? Hit us up at https://dvc.org/support, we are always happy to help!
Traceback (most recent call last):
  File "/home/saugat/repos/iterative/dvc/.env/py36/bin/dvc", line 12, in <module>
    sys.exit(main())
  File "/home/saugat/repos/iterative/dvc/dvc/main.py", line 89, in main
    if analytics.is_enabled():
  File "/home/saugat/repos/iterative/dvc/dvc/analytics.py", line 57, in is_enabled
    Config(validate=False).get("core", {}).get("analytics", "true")
  File "/home/saugat/repos/iterative/dvc/dvc/config.py", line 223, in __init__
    self.load(validate=validate)
  File "/home/saugat/repos/iterative/dvc/dvc/config.py", line 282, in load
    self["cache"]["dir"] = os.path.join(self.dvc_dir, "cache")
  File "/home/saugat/.pyenv/versions/3.6.6/lib/python3.6/posixpath.py", line 80, in join
    a = os.fspath(a)
TypeError: expected str, bytes or os.PathLike object, not NoneType
```

Discussion:
skshetry: Let's fail with the test for now. After it fails, I'll fix a push in another PR.
skshetry: Failed nicely. CI log:
```
=================================== FAILURES ===================================
_______________ test_config_loads_without_error_for_non_dvc_repo _______________
[gw3] linux -- Python 3.7.1 /home/travis/virtualenv/python3.7.1/bin/python
tmp_dir = PosixTmpDir('/tmp/pytest-of-travis/pytest-0/popen-gw3/test_config_loads_without_erro0')
    def test_config_loads_without_error_for_non_dvc_repo(tmp_dir):
        # regression testing for https://github.com/iterative/dvc/issues/3328
>       Config(validate=True)
/home/travis/build/iterative/dvc/tests/func/test_config.py:108: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/home/travis/build/iterative/dvc/dvc/config.py:223: in __init__
    self.load(validate=validate)
/home/travis/build/iterative/dvc/dvc/config.py:282: in load
    self["cache"]["dir"] = os.path.join(self.dvc_dir, "cache")
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
a = None, p = ('cache',)
    def join(a, *p):
        """Join two or more pathname components, inserting '/' as needed.
        If any component is an absolute path, all previous path components
        will be discarded.  An empty last part will result in a path that
        ends with a separator."""
>       a = os.fspath(a)
E       TypeError: expected str, bytes or os.PathLike object, not NoneType
/home/travis/virtualenv/python3.7.1/lib/python3.7/posixpath.py:80: TypeError
_______________ test_plain_dvc_command_not_fail_on_non_dvc_repo ________________
[gw1] linux -- Python 3.7.1 /home/travis/virtualenv/python3.7.1/bin/python
tmp_dir = PosixTmpDir('/tmp/pytest-of-travis/pytest-0/popen-gw1/test_plain_dvc_command_not_fai0')
git_dir = PosixTmpDir('/tmp/pytest-of-travis/pytest-0/popen-gw1/git-erepo0')
    def test_plain_dvc_command_not_fail_on_non_dvc_repo(tmp_dir, git_dir):
        # regression test for https://github.com/iterative/dvc/issues/3328
>       assert main([]) == 0
E       assert 254 == 0
E         -254
E         +0

/home/travis/build/iterative/dvc/tests/func/test_cli.py:184: AssertionError
```

Patch:
diff --git a/.travis.yml b/.travis.yml
index 971a2bb9e..514d4bd21 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -81,6 +81,8 @@ jobs:
      - name: snapcraft
        channel: stable
        confinement: classic
+     - name: review-tools
+       channel: stable
      - name: lxd
        channel: stable
     env:
diff --git a/dvc/config.py b/dvc/config.py
index 6a0372f5b..d893855f7 100644
--- a/dvc/config.py
+++ b/dvc/config.py
@@ -278,7 +278,7 @@ class Config(dict):
         self.update(conf)
 
         # Add resolved default cache.dir
-        if not self["cache"].get("dir"):
+        if not self["cache"].get("dir") and self.dvc_dir:
             self["cache"]["dir"] = os.path.join(self.dvc_dir, "cache")
 
     def load_one(self, level):
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index d1671d5a1..e88338a3e 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -20,6 +20,8 @@ parts:
     plugin: python
     python-packages: [wheel]
     source: .
+    build-snaps:
+    - snapd
     build-packages:
     - git
     - libc-dev


Test patch:
diff --git a/tests/func/test_config.py b/tests/func/test_config.py
index dc0facc2b..1ae0118b7 100644
--- a/tests/func/test_config.py
+++ b/tests/func/test_config.py
@@ -2,7 +2,7 @@ import pytest
 import configobj
 
 from dvc.main import main
-from dvc.config import ConfigError
+from dvc.config import Config, ConfigError
 from tests.basic_env import TestDvc
 
 
@@ -101,3 +101,8 @@ def test_merging_two_levels(dvc):
         "url": "ssh://example.com",
         "password": "1",
     }
+
+
+def test_config_loads_without_error_for_non_dvc_repo(tmp_dir):
+    # regression testing for https://github.com/iterative/dvc/issues/3328
+    Config(validate=True)