Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Version update #506

Closed
wants to merge 5 commits into from
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
8 changes: 7 additions & 1 deletion tests/data/testframeworks/baseinstallerfake.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,19 @@ def __init__(self, **kwargs):
checksum_type=ChecksumType.sha1,
dir_to_decompress_in_tarball="base-framework-*",
desktop_filename="base-framework.desktop",
required_files_path=[os.path.join("bin", "studio.sh")], **kwargs)
required_files_path=[os.path.join("bin", "studio.sh")],
updatable=True, **kwargs)

arch = platform.machine()
self.tag = 'id="linux-bundle64"'
if arch == 'i686':
self.tag = 'id="linux-bundle32"'

update_parse = ">android-studio-ide-(.*)-linux.zip"

def get_version(self):
return "135.1641136"

def parse_license(self, line, license_txt, in_license):
"""Parse download page for license"""
if line.startswith('<p class="sdk-terms-intro">'):
Expand Down
35 changes: 35 additions & 0 deletions tests/large/test_baseinstaller.py
Original file line number Diff line number Diff line change
Expand Up @@ -536,3 +536,38 @@ def test_download_page_404(self):

# we have nothing installed
self.assertFalse(self.launcher_exists_and_is_pinned(self.desktop_filename))

def test_update_not_available(self):
for loop in ("install", "update"):
if loop == "update":
pass
self.child = spawn_process(self.command('{} base base-framework --update'.format(UMAKE)))
self.expect_and_no_warn("\[.*\] ")
self.child.sendline("a")
self.expect_and_no_warn("Framework Base Framework already up to date",
timeout=self.TIMEOUT_INSTALL_PROGRESS)
else:
self.child = spawn_process(self.command('{} base base-framework'.format(UMAKE)))
self.expect_and_no_warn("Choose installation path: {}".format(self.installed_path))
self.child.sendline("")
self.expect_and_no_warn("\[.*\] ")
self.child.sendline("a")
self.expect_and_no_warn("Installation done", timeout=self.TIMEOUT_INSTALL_PROGRESS)
self.wait_and_close()

def test_update_available(self):
self.child = spawn_process(self.command('{} base base-framework'.format(UMAKE)))
self.expect_and_no_warn("Choose installation path: {}".format(self.installed_path))
self.child.sendline("")
self.expect_and_no_warn("\[.*\] ")
self.child.sendline("a")
self.expect_and_no_warn("Installation done", timeout=self.TIMEOUT_INSTALL_PROGRESS)
self.wait_and_close()
with swap_file_and_restore(self.download_page_file_path) as content:
with open(self.download_page_file_path, "w") as newfile:
newfile.write(content.replace('135.1641136-linux.zip', "136.123-linux.zip"))
self.child = spawn_process(self.command('{} base base-framework --update'.format(UMAKE)))
self.expect_and_no_warn("\[.*\] ")
self.child.sendline("a")
self.expect_and_no_warn("Installation done", timeout=self.TIMEOUT_INSTALL_PROGRESS)
self.wait_and_close()
10 changes: 6 additions & 4 deletions tests/small/test_frameworks_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,12 +297,13 @@ def test_parse_category_and_framework_run_correct_framework(self):
args.framework = "framework-b"
args.accept_license = False
args.remove = False
args.update = False
with patch.object(self.CategoryHandler.categories[args.category].frameworks["framework-b"], "setup")\
as setup_call:
self.CategoryHandler.categories[args.category].run_for(args)

self.assertTrue(setup_call.called)
self.assertEqual(setup_call.call_args, call(install_path=None, auto_accept_license=False))
self.assertEqual(setup_call.call_args, call(install_path=None, auto_accept_license=False, update=False))

def test_parse_no_framework_run_default_for_category(self):
"""Parsing category will run default framework"""
Expand All @@ -312,11 +313,12 @@ def test_parse_no_framework_run_default_for_category(self):
args.framework = None
args.accept_license = False
args.remove = False
args.update = False
with patch.object(self.CategoryHandler.categories[args.category].frameworks["framework-a"], "setup")\
as setup_call:
self.CategoryHandler.categories[args.category].run_for(args)
self.assertTrue(setup_call.called)
self.assertEqual(setup_call.call_args, call(install_path=None, auto_accept_license=False))
self.assertEqual(setup_call.call_args, call(install_path=None, auto_accept_license=False, update=False))

def test_parse_category_and_framework_run_correct_remove_framework(self):
"""Parsing category and framework with --remove run remove on right category and framework"""
Expand Down Expand Up @@ -370,7 +372,6 @@ def test_parse_category_and_framework_cannot_run_remove_with_destdir_framework(s

def test_parse_category_and_framework_cannot_install_not_installable_but_installed_framework(self):
"""We cannot install frameworks that are not installable but already installed (and so, registered)"""
self.expect_warn_error = True
args = Mock()
args.category = "category-r"
args.destdir = None
Expand Down Expand Up @@ -402,12 +403,13 @@ def test_parse_category_and_framework_get_accept_license_arg(self):
args.framework = "framework-b"
args.accept_license = True
args.remove = False
args.update = False
with patch.object(self.CategoryHandler.categories[args.category].frameworks["framework-b"], "setup")\
as setup_call:
self.CategoryHandler.categories[args.category].run_for(args)

self.assertTrue(setup_call.called)
self.assertEqual(setup_call.call_args, call(install_path=None, auto_accept_license=True))
self.assertEqual(setup_call.call_args, call(install_path=None, auto_accept_license=True, update=False))

def test_uninstantiable_framework(self):
"""A uninstantiable framework isn't loaded"""
Expand Down
1 change: 1 addition & 0 deletions umake/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ def main():
add_help=False)
parser.add_argument('--help', action=_HelpAction, help=_('Show this help')) # add custom help
parser.add_argument("-v", "--verbose", action="count", default=0, help=_("Increase output verbosity (2 levels)"))
parser.add_argument("--update", action="store_true", help=_("Update installed frameworks (that support update)"))

parser.add_argument('-r', '--remove', action="store_true", help=_("Remove specified framework if installed"))

Expand Down
23 changes: 20 additions & 3 deletions umake/frameworks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ class BaseFramework(metaclass=abc.ABCMeta):

def __init__(self, name, description, category, force_loading=False, logo_path=None, is_category_default=False,
install_path_dir=None, only_on_archs=None, only_ubuntu_version=None, packages_requirements=None,
only_for_removal=False, expect_license=False, need_root_access=False):
only_for_removal=False, expect_license=False, need_root_access=False, updatable=False):
self.name = name
self.description = description
self.logo_path = None
Expand All @@ -151,6 +151,7 @@ def __init__(self, name, description, category, force_loading=False, logo_path=N
self.packages_requirements.extend(self.category.packages_requirements)
self.only_for_removal = only_for_removal
self.expect_license = expect_license
self.updatable = updatable

# don't detect anything for completion mode (as we need to be quick), so avoid opening apt cache and detect
# if it's installed.
Expand Down Expand Up @@ -260,6 +261,10 @@ def remove(self):
logger.error(_("You can't remove {} as it isn't installed".format(self.name)))
UI.return_main_screen(status_code=2)

def version(self):
"""Method call to get the verison for the current framework"""
pass

def mark_in_config(self):
"""Mark the installation as installed in the config file"""
config = ConfigHandler().config
Expand Down Expand Up @@ -290,6 +295,10 @@ def install_framework_parser(self, parser):
"destdir should contain a /"))
this_framework_parser.add_argument('-r', '--remove', action="store_true",
help=_("Remove framework if installed"))
this_framework_parser.add_argument('--version', action="store_true",
help=_("Print the framework version if installed"))
this_framework_parser.add_argument('--update', action="store_true",
help=_("Update the framework if installed and possible"))
if self.expect_license:
this_framework_parser.add_argument('--accept-license', dest="accept_license", action="store_true",
help=_("Accept license without prompting"))
Expand All @@ -298,6 +307,12 @@ def install_framework_parser(self, parser):
def run_for(self, args):
"""Running commands from args namespace"""
logger.debug("Call run_for on {}".format(self.name))
if args.version:
if args.destdir:
message = "You can't specify a destination dir while getting the version of a framework"
logger.error(message)
UI.return_main_screen(status_code=2)
self.version()
if args.remove:
if args.destdir:
message = "You can't specify a destination dir while removing a framework"
Expand All @@ -311,7 +326,7 @@ def run_for(self, args):
install_path = os.path.abspath(os.path.expanduser(args.destdir))
if self.expect_license and args.accept_license:
auto_accept_license = True
self.setup(install_path=install_path, auto_accept_license=auto_accept_license)
self.setup(install_path=install_path, auto_accept_license=auto_accept_license, update=args.update)


class MainCategory(BaseCategory):
Expand Down Expand Up @@ -367,6 +382,7 @@ def list_frameworks():
'is_installable': True or False
'is_category_default': True or False
'only_for_removal': True or False
'updatable': True or False
},
]
},
Expand All @@ -383,7 +399,8 @@ def list_frameworks():
"is_installed": framework.is_installed,
"is_installable": framework.is_installable,
"is_category_default": framework.is_category_default,
"only_for_removal": framework.only_for_removal
"only_for_removal": framework.only_for_removal,
"updatable": framework.updatable
}

frameworks_dict.append(new_fram)
Expand Down
65 changes: 60 additions & 5 deletions umake/frameworks/baseinstaller.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
import logging
from progressbar import ProgressBar
import os
import re
import shutil
import subprocess
import umake.frameworks
from umake.decompressor import Decompressor
from umake.interactions import InputText, YesNo, LicenseAgreement, DisplayMessage, UnknownProgress
Expand Down Expand Up @@ -65,6 +67,7 @@ def __init__(self, *args, **kwargs):
self.desktop_filename = kwargs.get("desktop_filename", None)
self.icon_filename = kwargs.get("icon_filename", None)
self.match_last_link = kwargs.get("match_last_link", False)
self.updatable = kwargs.get("updatable", False)
for extra_arg in ["download_page", "checksum_type", "dir_to_decompress_in_tarball",
"desktop_filename", "icon_filename", "required_files_path",
"match_last_link"]:
Expand Down Expand Up @@ -97,13 +100,24 @@ def is_installed(self):
logger.debug("{} is installed".format(self.name))
return True

def setup(self, install_path=None, auto_accept_license=False):
def setup(self, install_path=None, auto_accept_license=False, update=False):
self.arg_install_path = install_path
self.auto_accept_license = auto_accept_license
self.update = update
super().setup()

# first step, check if installed
if self.is_installed:
if self.update and self.updatable:
if not self.is_installed:
UI.display(DisplayMessage("The framework {} is not installed".format(self.name)))
UI.return_main_screen()
try:
self.set_exec_path()
self.download_provider_page()
except:
UI.display(DisplayMessage("The framework {} is not configured to update manually".format(self.name)))
UI.return_main_screen()
elif self.is_installed:
UI.display(YesNo("{} is already installed on your system, do you want to reinstall "
"it anyway?".format(self.name), self.reinstall, UI.return_main_screen))
else:
Expand All @@ -115,6 +129,42 @@ def reinstall(self):
self.confirm_path(self.arg_install_path)
remove_framework_envs_from_user(self.name)

def version(self):
super().version()
if not self.is_installed:
logger.error(_("You can't get the version for {} as it isn't installed".format(self.name)))
UI.return_main_screen(status_code=2)
try:
UI.display(DisplayMessage(self.get_version()))
except AttributeError as e:
logger.error("Version parse not implememted")
UI.return_main_screen()

def get_version(self):
return re.search(self.version_parse['regex'],
subprocess.check_output(self.version_parse['command'].split()).decode()).group(1)

@MainLoop.in_mainloop_thread
def run_update(self, result):
upstream_version = self.get_upstream_version(result)
if upstream_version and self.get_version() != upstream_version:
logger.debug("Running update of framework {}".format(self.name))
self.arg_install_path = self.install_path
self.reinstall()
DownloadCenter([DownloadItem(self.download_page)], self.get_metadata_and_check_license, download=False)
UI.display(DisplayMessage("Framework {} update started".format(self.name)))
else:
logger.debug("No update available")
UI.display(DisplayMessage("Framework {} already up to date".format(self.name)))
UI.return_main_screen()

def get_upstream_version(self, result):
for line in result[self.download_page].buffer:
line_content = line.decode()
parsed = re.search(self.update_parse, line_content)
if parsed:
return parsed.group(1)

def remove(self):
"""Remove current framework if installed

Expand Down Expand Up @@ -166,17 +216,22 @@ def confirm_path(self, path_dir=""):
return
self.install_path = path_dir
self.set_exec_path()
self.download_provider_page()
if not self.update:
self.download_provider_page()

def set_installdir_to_clean(self):
logger.debug("Mark non empty new installation path for cleaning.")
self._paths_to_clean.add(self.install_path)
self.set_exec_path()
self.download_provider_page()

def download_provider_page(self):
def download_provider_page(self, update=False):
logger.debug("Download application provider page")
DownloadCenter([DownloadItem(self.download_page)], self.get_metadata_and_check_license, download=False)
if self.update:
logger.debug("Check provider page for update")
DownloadCenter([DownloadItem(self.download_page)], self.run_update, download=False)
else:
DownloadCenter([DownloadItem(self.download_page)], self.get_metadata_and_check_license, download=False)

def parse_license(self, line, license_txt, in_license):
"""Parse license per line, eventually write to license_txt if it's in the license part.
Expand Down
5 changes: 4 additions & 1 deletion umake/frameworks/go.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ def __init__(self, **kwargs):
checksum_type=ChecksumType.sha256,
dir_to_decompress_in_tarball="go",
required_files_path=[os.path.join("bin", "go")],
**kwargs)
updatable=True, **kwargs)

update_parse = "go/go(.*).linux-amd64.tar.gz"
version_parse = {'regex': 'go version go(.*) linux/amd64', 'command': 'go version'}

def parse_download_link(self, line, in_download):
"""Parse Go download link, expect to find a sha and a url"""
Expand Down
13 changes: 12 additions & 1 deletion umake/frameworks/ide.py
Original file line number Diff line number Diff line change
Expand Up @@ -760,7 +760,14 @@ def __init__(self, **kwargs):
required_files_path=["atom", "resources/app/apm/bin/apm"],
dir_to_decompress_in_tarball="atom-*",
packages_requirements=["libgconf-2-4"],
checksum_type=ChecksumType.md5, **kwargs)
checksum_type=ChecksumType.md5,
updatable=True, **kwargs)

def get_upstream_version(self, result):
page = result[self.download_page]
return json.loads(page.buffer.read().decode())["name"]

version_parse = {'regex': 'Atom.*: (.*)\n', 'command': 'atom --version'}

@MainLoop.in_mainloop_thread
def get_metadata_and_check_license(self, result):
Expand Down Expand Up @@ -882,13 +889,17 @@ def __init__(self, **kwargs):
desktop_filename="sublime-text.desktop",
required_files_path=["sublime_text"],
dir_to_decompress_in_tarball="sublime_text_*",
updatable=True,
**kwargs)

arch_trans = {
"amd64": "x64",
"i386": "x32"
}

update_parse = "https://download.sublimetext.com/sublime_text_3_build_(.*)_x64.tar.bz2"
version_parse = {'regex': 'Sublime Text Build (.*)\n', 'command': 'sublime-text --version'}

def parse_download_link(self, line, in_download):
"""Parse SublimeText download links"""
url = None
Expand Down
3 changes: 3 additions & 0 deletions umake/frameworks/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import os
import platform
import re
import subprocess
import umake.frameworks.baseinstaller
from umake.interactions import Choice, TextWithChoices, DisplayMessage
from umake.network.download_center import DownloadItem
Expand Down Expand Up @@ -56,6 +57,8 @@ def __init__(self, **kwargs):
required_files_path=["firefox"], **kwargs)
self.arg_lang = None

version_parse = {'regex': 'Mozilla Firefox (.*)\n', 'command': 'firefox-developer --version'}

@MainLoop.in_mainloop_thread
def language_select_callback(self, url):
url = url.replace("&amp;", "&")
Expand Down
Loading