Repository: iterative/dvc

Issue:
Config to enable/disable updater
It'd be great if we could disable the updater feature via `dvc config`. It's rather noisy due to the frequent release cycle of DVC.


Discussion:
@mdscruggs I think it's dangerous to disable it completely. We can find some middle ground here. What do you think if we introduce a notion of critical updates? For example, we know that there is a critical patch that fixes a certain version. We can detect the version running and notify only of versions match. I would notify at least once in a few days about minor version change anyways.

Any means to make the updater less noisy would be helpful, even if it still runs @shcheklein. The fast pace of DVC releases means it is effectively alerting us every few days...

While it is healthy to encourage users to update due to bug fixes and new features, users shouldn't feel pressured into updating constantly.

Even `pip` itself has a configuration setting for this, and releases far less frequently than DVC:

https://pip.pypa.io/en/stable/reference/pip/#cmdoption-disable-pip-version-check

`pip` also limits its check to weekly:

https://github.com/pypa/pip/blob/62cfaf0fc7efd4dfb67f100e08a2d07d8218eeaa/src/pip/_internal/utils/outdated.py#L98
Ok, how about this plan:

1. we introduce a config option to alter the interval (default is once a day)
2. we introduce a mechanism to notify about critical updates (support a list of versions affected on the updater side, emphasize the message on the DVC side, etc).

would it suffice? 

I really wan to avoid turning it off completely. It's dangerous because we are dealing with data here.
That sounds good @shcheklein. 

Users who must or want to stabilize on a specific release of DVC should be able to do so without excessive alerting.
So updater will still be run each time to check if there are critical updates? No possibility to make it rest at all?
@Suor is there a case in which users would not want to know about an update that potentially fixes a problem that can lead to data loss? We can give an option to disable it completely, and probably should do this, but we should emphasize that you are at risk when you are doing so, and it should be a second or third option after you have tuned the interval first.

> we introduce a mechanism to notify about critical updates (support a list of versions affected on the updater side, emphasize the message on the DVC side, etc).

Where and how could this be implemented? Seems rather advanced, not sure this is a "good first issue", as currently labeled. But I also don't know how the updater works now, maybe it's quite simple?
@jorgeorpinel 1st step is to alter the interval. We could think about critical updates later.

Patch:
diff --git a/dvc/config.py b/dvc/config.py
--- a/dvc/config.py
+++ b/dvc/config.py
@@ -138,6 +138,7 @@ class RelPath(str):
         Optional("hardlink_lock", default=False): Bool,
         Optional("no_scm", default=False): Bool,
         Optional("experiments", default=False): Bool,
+        Optional("check_update", default=True): Bool,
     },
     "cache": {
         "local": str,
diff --git a/dvc/updater.py b/dvc/updater.py
--- a/dvc/updater.py
+++ b/dvc/updater.py
@@ -7,6 +7,7 @@
 from packaging import version
 
 from dvc import __version__
+from dvc.config import Config, to_bool
 from dvc.lock import LockError, make_lock
 from dvc.utils import boxify, env2bool
 from dvc.utils.pkg import PKG
@@ -46,7 +47,12 @@ def _with_lock(self, func, action):
             logger.debug(msg.format(self.lock.lockfile, action))
 
     def check(self):
-        if os.getenv("CI") or env2bool("DVC_TEST") or PKG == "snap":
+        if (
+            os.getenv("CI")
+            or env2bool("DVC_TEST")
+            or PKG == "snap"
+            or not self.is_enabled()
+        ):
             return
 
         self._with_lock(self._check, "checking")
@@ -144,3 +150,12 @@ def _get_update_instructions(self):
             package_manager = "binary"
 
         return instructions[package_manager]
+
+    def is_enabled(self):
+        enabled = to_bool(
+            Config(validate=False).get("core", {}).get("check_update", "true")
+        )
+        logger.debug(
+            "Check for update is {}abled.".format("en" if enabled else "dis")
+        )
+        return enabled


Test patch:
diff --git a/tests/unit/test_updater.py b/tests/unit/test_updater.py
--- a/tests/unit/test_updater.py
+++ b/tests/unit/test_updater.py
@@ -44,6 +44,28 @@ def test_fetch(mock_get, updater):
     assert info["version"] == __version__
 
 
+@pytest.mark.parametrize(
+    "core, result",
+    [
+        ({}, True),
+        ({"check_update": "true"}, True),
+        ({"check_update": "false"}, False),
+    ],
+)
+def test_is_enabled(dvc, updater, core, result):
+    with dvc.config.edit("local") as conf:
+        conf["core"] = core
+    assert result == updater.is_enabled()
+
+
+@pytest.mark.parametrize("result", [True, False])
+@mock.patch("dvc.updater.Updater._check")
+def test_check_update_respect_config(mock_check, result, updater, mocker):
+    mocker.patch.object(updater, "is_enabled", return_value=result)
+    updater.check()
+    assert result == mock_check.called
+
+
 @pytest.mark.parametrize(
     "current,latest,notify",
     [