updater: Add custom update message depending on package manager (#1287)

* updater: Add custom update message depending on package manager

Close #938
Close #1031

* Reduce cognitive complexity and avoid too many return functions

* Use a dictionary instead of if/else

* Take into account pkg installation with OSX

* updater: add platform detection

Signed-off-by: Ruslan Kuprieiev <[email protected]>
This commit is contained in:
Mr. Outis
2018-11-07 19:04:52 +02:00
committed by Ruslan Kuprieiev
parent 2afe950a27
commit b6842fb089
4 changed files with 120 additions and 35 deletions
+7 -5
View File
@@ -221,8 +221,10 @@ class Logger(object):
) 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),
)
box = "{margin}{padding}{content}{padding}{margin}".format(
margin=cls.colorize(margin, border_color),
padding=''.join(padding_lines),
content=''.join(content_lines),
)
print(box)
+111 -30
View File
@@ -1,3 +1,4 @@
import sys
import os
import time
import requests
@@ -17,14 +18,9 @@ class Updater(object): # pragma: no cover
def __init__(self, dvc_dir):
self.dvc_dir = dvc_dir
self.updater_file = os.path.join(dvc_dir, self.UPDATER_FILE)
@staticmethod
def init(dvc_dir):
return Updater(dvc_dir)
self.current = VERSION_BASE
def check(self):
current = VERSION_BASE
if os.getenv('CI'):
return
@@ -40,36 +36,121 @@ class Updater(object): # pragma: no cover
Logger.info('Checking for updates...')
try:
r = requests.get(self.URL, timeout=self.TIMEOUT_GET)
j = r.json()
latest = j['version']
open(self.updater_file, 'w+').close()
self._get_latest_version()
except Exception as exc:
msg = 'Failed to obtain latest version: {}'.format(str(exc))
Logger.debug(msg)
return
l_major, l_minor, l_patch = [int(x) for x in latest.split('.')]
c_major, c_minor, c_patch = [int(x) for x in current.split('.')]
if self._is_outdated():
self._notify()
if l_major <= c_major and \
l_minor <= c_minor and \
l_patch <= c_patch:
return
def _get_latest_version(self):
r = requests.get(self.URL, timeout=self.TIMEOUT_GET)
j = r.json()
self.latest = j['version']
open(self.updater_file, 'w+').close()
msg = (
def _is_outdated(self):
l_major, l_minor, l_patch = [int(x) for x in self.latest.split('.')]
c_major, c_minor, c_patch = [int(x) for x in self.current.split('.')]
return (l_major > c_major or
l_minor > c_minor or
l_patch > c_patch)
def _notify(self):
message = (
'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,
)
)
+ '\n'
+ self._get_update_instructions()
).format(red=colorama.Fore.RED,
reset=colorama.Fore.RESET,
green=colorama.Fore.GREEN,
yellow=colorama.Fore.YELLOW,
blue=colorama.Fore.BLUE,
current=self.current,
latest=self.latest)
Logger.box(msg, border_color='yellow')
if sys.stdout.isatty():
Logger.box(message, border_color='yellow')
def _get_update_instructions(self):
instructions = {
'pip': 'Run {yellow}pip{reset} install dvc {blue}--upgrade{reset}',
'yum': 'Run {yellow}yum{reset} update dvc',
'yay': 'Run {yellow}yay{reset} {blue}-S{reset} dvc',
'formula': 'Run {yellow}brew{reset} upgrade dvc',
'cask': 'Run {yellow}brew cask{reset} upgrade dvc',
'apt': ('Run {yellow}apt-get{reset} install'
' {blue}--only-upgrade{reset} dvc'),
'binary': ('To upgrade follow this steps:\n'
'1. Uninstall dvc binary\n'
'2. Go to {blue}https://dvc.org{reset}\n'
'3. Download and install new binary'),
None: ('Find the latest release at\n{blue}'
'https://github.com/iterative/dvc/releases/latest'
'{reset}'),
}
package_manager = self._get_package_manager()
return instructions[package_manager]
@staticmethod
def _is_binary():
return getattr(sys, 'frozen', False)
def _get_linux(self):
import distro
if not self._is_binary():
return 'pip'
package_managers = {
'rhel': 'yum',
'centos': 'yum',
'fedora': 'yum',
'amazon': 'yum',
'opensuse': 'yum',
'ubuntu': 'apt',
'debian': 'apt',
}
return package_managers.get(distro.id())
def _get_darwin(self):
if not self._is_binary():
if __file__.startswith('/usr/local/Cellar'):
return 'formula'
else:
return 'pip'
# NOTE: both pkg and cask put dvc binary into /usr/local/bin,
# so in order to know which method of installation was used,
# we need to actually call `brew cask`
ret = os.system('brew cask ls dvc')
if ret == 0:
return 'cask'
return None
def _get_windows(self):
return None if self._is_binary() else 'pip'
def _get_package_manager(self):
import platform
from dvc.exceptions import DvcException
m = {
'Windows': self._get_windows,
'Darwin': self._get_darwin,
'Linux': self._get_linux,
}
system = platform.system()
func = m.get(system)
if func is None:
raise DvcException("Not supported system '{}'".format(system))
return func()
+1
View File
@@ -23,3 +23,4 @@ futures>=3.2.0; python_version == "2.7"
grandalf==0.6
pydot>=1.2.4
asciimatics>=1.10.0
distro>=1.3.0
+1
View File
@@ -23,6 +23,7 @@ install_requires = [
"requests>=2.18.4",
"grandalf==0.6",
"asciimatics>=1.10.0",
"distro>=1.3.0",
]
# Extra dependencies for remote integrations