Repository: iterative/dvc

Issue:
updater: make update message more noticeable 
I.e. like in npm:

<img width="623" alt="screen shot 2018-08-21 at 6 26 38 pm" src="https://user-images.githubusercontent.com/5367102/44437841-ea6d3a00-a5c4-11e8-8e1a-975bc4e19e58.png">

Kudos @shcheklein 

Discussion:
mroutis: @efiop, the code is horrible, what are your suggestions?

I'll try to prepare another _cleaner_ commit, but I wanted to know your insights before.

Patch:
diff --git a/dvc/logger.py b/dvc/logger.py
index 2fa43e262..b90ac6ade 100644
--- a/dvc/logger.py
+++ b/dvc/logger.py
@@ -2,11 +2,28 @@ import sys
 import logging
 import colorama
 import traceback
+import re
 
 
 colorama.init()
 
 
+def visual_width(line):
+    """ Get the the number of columns required to display a string """
+
+    return len(re.sub(colorama.ansitowin32.AnsiToWin32.ANSI_CSI_RE, '', line))
+
+
+def visual_center(line, width):
+    """ Center align string according to it's visual width """
+
+    spaces = max(width - visual_width(line), 0)
+    left_padding = int(spaces / 2)
+    right_padding = (spaces - left_padding)
+
+    return (left_padding * ' ') + line + (right_padding * ' ')
+
+
 class Logger(object):
     FMT = '%(message)s'
     DEFAULT_LEVEL = logging.INFO
@@ -155,3 +172,46 @@ class Logger(object):
     @staticmethod
     def info(msg):
         Logger._with_progress(Logger.logger().info, msg)
+
+    @classmethod
+    def box(cls, msg, border_color=''):
+        lines = msg.split('\n')
+        max_width = max(visual_width(line) for line in lines)
+
+        padding_horizontal = 5
+        padding_vertical = 1
+
+        box_size_horizontal = (max_width + (padding_horizontal * 2))
+
+        chars = {
+            'corner':     '+',
+            'horizontal': '-',
+            'vertical':   '|',
+            'empty':      ' ',
+        }
+
+        margin = "{corner}{line}{corner}\n".format(
+            corner=chars['corner'],
+            line=chars['horizontal'] * box_size_horizontal,
+        )
+
+        padding_lines = [
+            "{border}{space}{border}\n".format(
+                border=cls.colorize(chars['vertical'], border_color),
+                space=chars['empty'] * box_size_horizontal,
+            ) * padding_vertical
+        ]
+
+        content_lines = [
+            "{border}{space}{content}{space}{border}\n".format(
+                border=cls.colorize(chars['vertical'], border_color),
+                space=chars['empty'] * padding_horizontal,
+                content=visual_center(line, max_width),
+            ) for line in lines
+        ]
+
+        return "{margin}{padding}{content}{padding}{margin}".format(
+            margin=cls.colorize(margin, border_color),
+            padding=''.join(padding_lines),
+            content=''.join(content_lines),
+        )
diff --git a/dvc/scm.py b/dvc/scm.py
index 5230d00a0..e49039721 100644
--- a/dvc/scm.py
+++ b/dvc/scm.py
@@ -18,6 +18,10 @@ class Base(object):
         self.project = project
         self.root_dir = root_dir
 
+    @property
+    def dir(self):
+        return None
+
     @staticmethod
     def is_repo(root_dir):
         return True
diff --git a/dvc/updater.py b/dvc/updater.py
index 3648c2e4d..c3f42cde6 100644
--- a/dvc/updater.py
+++ b/dvc/updater.py
@@ -1,6 +1,7 @@
 import os
 import time
 import requests
+import colorama
 
 from dvc import VERSION_BASE
 from dvc.logger import Logger
@@ -56,6 +57,19 @@ class Updater(object):  # pragma: no cover
            l_patch <= c_patch:
             return
 
-        msg = 'You are using dvc version {}, however version {} is ' \
-              'available. Consider upgrading.'
-        Logger.warn(msg.format(current, latest))
+        msg = (
+            'Update available {red}{current}{reset} -> {green}{latest}{reset}'
+            '\n'
+            'Run {yellow}pip{reset} install dvc {blue}--upgrade{reset}'
+            .format(
+                red=colorama.Fore.RED,
+                reset=colorama.Fore.RESET,
+                green=colorama.Fore.GREEN,
+                yellow=colorama.Fore.YELLOW,
+                blue=colorama.Fore.BLUE,
+                current=current,
+                latest=latest,
+            )
+        )
+
+        Logger.box(msg, border_color='yellow')


Test patch:
diff --git a/tests/test_repro.py b/tests/test_repro.py
index 70db0abdc..112386088 100644
--- a/tests/test_repro.py
+++ b/tests/test_repro.py
@@ -763,3 +763,9 @@ class TestReproShell(TestDvc):
 
         with open(fname, 'r') as fd:
             self.assertEqual(os.getenv('SHELL'), fd.read().strip())
+
+class TestReproNoSCM(TestRepro):
+    def test(self):
+        shutil.rmtree(self.dvc.scm.dir)
+        ret = main(['repro', self.file1_stage])
+        self.assertEqual(ret, 0)