Repository: conda/conda

Issue:
conda only updates itself in the root environment
I was listening to a presentation about conda a couple of days ago where it was mentioned that conda aggressively auto-updates itself. Aggressive auto-updates can be problematic, because they can result in the quick spread of a buggy release that is no longer able to update itself, which has happened to me and co-workers in the past.  But in general they are a good thing, because they can help make sure that the installed user base is (a) not using outdated versions where problems were fixed long ago, and (b) are using a similar set of versions so that we don't have to reason about the effects of all possible version combinations.  

However, the main benefits of aggressively auto-updating only accrue if all or nearly all installations auto-update, which does not appear to be the case.  Right now, it looks to me like auto-updates happen only when people regularly use the root environment.  In my own workflows, I haven't installed anything in the root environment in many months; I always work on one of a number of more specialized environments instead.  I didn't even realize this until recently, when I ran into a bug fixed several months ago in conda, and which I expected not to apply to me.  I eventually fixed it by switching to the root environment and running ``conda update conda``, which was fine, but it was not at all clear that this would be needed.  I imagine other users who don't use the root environment may encounter similar issues.  I don't think conda is installed in any environment other than root, but maybe the other environments could warn on a ``conda install`` command if conda itself is more than a month or so out of date?


Discussion:
This problem just reared its head in practice:  as of conda 4.3.27, new packages are going into ``https://repo.continuum.io/pkgs/main/``, and so when I tried to install packages in any environment, I was getting out of date packages.  After ``conda update conda`` to upgrade 4.3.25, it works fine, but I only knew to do that (given that my conda version wasn't even very old) because of an in-person suggestion from Michael Grant.  This seems like something that will cause lots of confusion for any users who habitually use sub-environments.
Related issue: https://github.com/conda/conda/issues/6232

Resolving patch:
diff --git a/conda/base/context.py b/conda/base/context.py
--- a/conda/base/context.py
+++ b/conda/base/context.py
@@ -89,6 +89,7 @@ class Context(Configuration):
     add_pip_as_python_dependency = PrimitiveParameter(True)
     allow_softlinks = PrimitiveParameter(False)
     auto_update_conda = PrimitiveParameter(True, aliases=('self_update',))
+    notify_outdated_conda = PrimitiveParameter(True)
     clobber = PrimitiveParameter(False)
     changeps1 = PrimitiveParameter(True)
     concurrent = PrimitiveParameter(True)
@@ -755,6 +756,10 @@ def get_help_dict():
             Allows completion of conda's create, install, update, and remove operations, for
             non-privileged (non-root or non-administrator) users.
             """),
+        'notify_outdated_conda': dals("""
+            Notify if a newer version of conda is detected during a create, install, update,
+            or remove operation.
+            """),
         'offline': dals("""
             Restrict conda to cached download content and file:// based urls.
             """),
diff --git a/conda/core/solve.py b/conda/core/solve.py
--- a/conda/core/solve.py
+++ b/conda/core/solve.py
@@ -5,10 +5,14 @@
 from genericpath import exists
 from logging import DEBUG, getLogger
 from os.path import join
+import sys
+from textwrap import dedent
 
 from .index import get_reduced_index
 from .link import PrefixSetup, UnlinkLinkTransaction
 from .linked_data import PrefixData
+from .repodata import query_all
+from .. import __version__ as CONDA_VERSION
 from .._vendor.boltons.setutils import IndexedSet
 from ..base.context import context
 from ..common.compat import iteritems, itervalues, odict, string_types, text_type
@@ -22,6 +26,7 @@
 from ..models.dag import PrefixDag
 from ..models.dist import Dist
 from ..models.match_spec import MatchSpec
+from ..models.version import VersionOrder
 from ..resolve import Resolve, dashlist
 
 try:
@@ -450,7 +455,30 @@ def solve_for_transaction(self, deps_modifier=NULL, prune=NULL, ignore_pinned=NU
                                   self.specs_to_remove, self.specs_to_add)
                 # TODO: Only explicitly requested remove and update specs are being included in
                 #   History right now. Do we need to include other categories from the solve?
-                return UnlinkLinkTransaction(stp)
+
+        if context.notify_outdated_conda:
+            conda_newer_spec = MatchSpec('conda >%s' % CONDA_VERSION)
+            if not any(conda_newer_spec.match(prec) for prec in link_precs):
+                conda_newer_records = sorted(
+                    query_all(self.channels, self.subdirs, conda_newer_spec),
+                    key=lambda x: VersionOrder(x.version)
+                )
+                if conda_newer_records:
+                    latest_version = conda_newer_records[-1].version
+                    sys.stderr.write(dedent("""
+
+                    ==> WARNING: A newer version of conda exists. <==
+                      current version: %s
+                      latest version: %s
+
+                    Please update conda by running
+
+                        $ conda update -n base conda
+
+
+                    """) % (CONDA_VERSION, latest_version))
+
+        return UnlinkLinkTransaction(stp)
 
     def _prepare(self, prepared_specs):
         # All of this _prepare() method is hidden away down here. Someday we may want to further


Test patch:
diff --git a/tests/test_create.py b/tests/test_create.py
--- a/tests/test_create.py
+++ b/tests/test_create.py
@@ -1277,6 +1277,7 @@ def side_effect(self, url, **kwargs):
                             del result.headers[header]
                     return result
 
+                SubdirData._cache_.clear()
                 mock_method.side_effect = side_effect
                 stdout, stderr = run_command(Commands.INFO, prefix, "flask --json")
                 assert mock_method.called