Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 74 additions & 53 deletions dvc/updater.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
from __future__ import unicode_literals

import sys
import logging
import os
import sys
import time
import logging

import colorama
from packaging import version

from dvc import __version__
from dvc.lock import Lock, LockError
from dvc.utils import is_binary, boxify, env2bool

from dvc.lock import Lock
from dvc.lock import LockError
from dvc.utils import boxify
from dvc.utils import env2bool
from dvc.utils import is_binary

logger = logging.getLogger(__name__)

Expand All @@ -24,9 +27,8 @@ 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)
self.lock = Lock(
self.updater_file + ".lock", tmp_dir=os.path.join(dvc_dir, "tmp")
)
self.lock = Lock(self.updater_file + ".lock",
tmp_dir=os.path.join(dvc_dir, "tmp"))
self.current = version.parse(__version__).base_version

def _is_outdated_file(self):
Expand Down Expand Up @@ -103,53 +105,66 @@ def _notify(self):

message = (
"Update available {red}{current}{reset} -> {green}{latest}{reset}"
+ "\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,
)
+ "\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.info(boxify(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}"
),
"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"),
"conda":
"Run {yellow}conda{reset} {update}update{reset} dvc",
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 _get_dvc_path(system):
if system in ("linux", "darwin"):
output = os.popen("which dvc")
else:
output = os.popen("where dvc")

return output.read().lower()

@staticmethod
def _is_conda(path):
return "conda" in path

def _get_linux(self):
import distro

if not is_binary():
return "pip"
dvc_path = self._get_dvc_path("linux")
return "conda" if self._is_conda(dvc_path) else "pip"

package_managers = {
"rhel": "yum",
Expand All @@ -164,23 +179,29 @@ def _get_linux(self):
return package_managers.get(distro.id())

def _get_darwin(self):
if not is_binary():
if __file__.startswith("/usr/local/Cellar"):
return "formula"
else:
return "pip"
if is_binary():
return None

package_manager = None

# 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"
if __file__.startswith("/usr/local/Cellar"):
package_manager = "formula"

return None
dvc_path = self._get_dvc_path("darwin")
if self._is_conda(dvc_path):
package_manager = "conda"

return package_manager or "pip"

def _get_windows(self):
return None if is_binary() else "pip"
if is_binary():
return None

dvc_path = self._get_dvc_path("windows")
if self._is_conda(dvc_path):
return "conda"

return "pip"

def _get_package_manager(self):
import platform
Expand Down
67 changes: 67 additions & 0 deletions dvc/utils/pkg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from dvc.utils import is_binary


def is_conda():
try:
from .build import PKG # patched during conda package build

return PKG == "conda"
except ImportError:
return False


def get_linux():
import distro

if is_conda():
return "conda"

if not 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():
if is_conda():
return "conda"
if not is_binary():
if __file__.startswith("/usr/local/Cellar"):
return "formula"
else:
return "pip"
return None


def get_windows():
if is_conda():
return "conda"
return None if is_binary() else "pip"


def get_package_manager():
import platform
from dvc.exceptions import DvcException

m = {
"Windows": get_windows(),
"Darwin": get_darwin(),
"Linux": get_linux(),
}

system = platform.system()
func = m.get(system)
if func is None:
raise DvcException("not supported system '{}'".format(system))

return func
11 changes: 11 additions & 0 deletions tests/func/test_updater.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os

import mock
import pytest

Expand Down Expand Up @@ -49,3 +50,13 @@ def test_check_version_outdated(updater):
updater.current = "0.20.8"

assert updater._is_outdated()


@mock.patch("dvc.updater.Updater._is_conda")
def test_check_dvc_from_conda(mocked_is_conda, updater):
mocked_is_conda.return_value = True
updater.latest = "0.21.0"
updater.current = "0.20.8"

msg = "Run {yellow}conda{reset} {update}update{reset} dvc"
assert updater._get_update_instructions() == msg