Skip to content

Commit

Permalink
Improve Update and allows to upgrade and install from development (#2653
Browse files Browse the repository at this point in the history
)

* Adds check for development version and dialog to install new, WIP

* Add upgrade instalation and upgrade from development branch actions

* Fix unit tests
  • Loading branch information
HelioGuilherme66 authored Oct 10, 2023
1 parent 6b66e72 commit 827905e
Show file tree
Hide file tree
Showing 9 changed files with 190 additions and 23 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ and this project adheres to http://semver.org/spec/v2.0.0.html[Semantic Versioni

=== Added

- Added auto update check when development version is installed
- Added menu option ``Help->Check for Upgrade`` which allows to force update check and install development version
- Added ``Upgrade Now`` action to update dialog.
- Added Test Tags field (new, since Robot Framework 6.0) to Test Suites settings. This field will replace Default and
Force Tags settings, after Robot Framework 7.0
- Added content help pop-up on Text Editor by pressing ``Ctrl`` for text at cursor position or selected autocomplete list item
Expand Down
5 changes: 4 additions & 1 deletion src/robotide/application/CHANGELOG.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Changelog</title><link rel="stylesheet" type="text/css" href="docbook-xsl.css" /><meta name="generator" content="DocBook XSL Stylesheets Vsnapshot" /></head><body><div xml:lang="en" class="article" lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="id1337"></a>Changelog</h2></div></div><hr /></div><p>All notable changes to this project will be documented in this file.</p><p>The format is based on <a class="ulink" href="http://keepachangelog.com/en/1.0.0/" target="_top">Keep a Changelog</a>
and this project adheres to <a class="ulink" href="http://semver.org/spec/v2.0.0.html" target="_top">Semantic Versioning</a>.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="_ulink_url_https_github_com_robotframework_ride_unreleased_ulink"></a>1. <a class="ulink" href="https://github.com/robotframework/RIDE" target="_top">Unreleased</a></h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a id="_added"></a>1.1. Added</h3></div></div></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; ">
<li class="listitem">Added Test Tags field (new, since Robot Framework 6.0) to Test Suites settings. This field will replace Default and Force Tags settings, after Robot Framework 7.0</li>
<li class="listitem">Added auto update check when development version is installed
</li><li class="listitem">Added menu option ``Help->Check for Upgrade`` which allows to force update check and install development version
</li><li class="listitem">Added ``Upgrade Now`` action to update dialog.
</li><li class="listitem">Added Test Tags field (new, since Robot Framework 6.0) to Test Suites settings. This field will replace Default and Force Tags settings, after Robot Framework 7.0</li>
<li class="listitem">
Added content help pop-up on Text Editor by pressing ``Ctrl`` for text at cursor position or selected autocomplete list item
</li><li class="listitem">
Expand Down
5 changes: 4 additions & 1 deletion src/robotide/application/releasenotes.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ def set_content(self, html_win, content):
</ul>
<p><strong>New Features and Fixes Highlights</strong></p>
<ul class="simple">
<li>Added auto update check when development version is installed</li>
<li>Added menu option <b>Help-&gt;Check for Upgrade</b> which allows to force update check and install development version</li>
<li>Added <b>Upgrade Now</b> action to update dialog.</li>
<li>Added <b>Test Tags</b> field (new, since Robot Framework 6.0) to Test Suites settings. This field will replace <b>Default</b> and <b>Force Tags</b> settings, after Robot Framework 7.0</li>
<li>Improved <b>RIDE Log</b> and <b>Parser Log</b> windows to allow Zoom In/Out with <b>Ctrl-Mouse Wheel</b></li>
<li>Hide continuation markers in Project Tree</li>
Expand Down Expand Up @@ -243,6 +246,6 @@ def set_content(self, html_win, content):
<pre class="literal-block">
python -m robotide.postinstall -install
</pre>
<p>RIDE {VERSION} was released on 7/Oct/2023.</p>
<p>RIDE {VERSION} was released on 10/Oct/2023.</p>
</div>
"""
118 changes: 113 additions & 5 deletions src/robotide/application/updatenotifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,23 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import subprocess
import sys
# Configure wx uversion to allow running test app in __main__


import time
import urllib.request as urllib2
import xmlrpc.client as xmlrpclib

import psutil
import wx
from wx import Colour

from .. import version
from ..utils.versioncomparator import cmp_versions
from ..utils.versioncomparator import cmp_versions, parse_version
from ..widgets import ButtonWithHandler, HtmlWindow, RIDEDialog
from ..postinstall.__main__ import MessageDialog

_CHECK_FOR_UPDATES_SETTING = "check for updates"
_LAST_UPDATE_CHECK_SETTING = "last update check"
Expand All @@ -39,9 +42,16 @@ class UpdateNotifierController(object):
def __init__(self, settings):
self._settings = settings

def notify_update_if_needed(self, update_notification_callback):
if self._should_check() and self._is_new_version_available():
def notify_update_if_needed(self, update_notification_callback, ignore_check_condition=False):
if ignore_check_condition:
dev_version = checking_version = True
else:
checking_version = self._should_check()
dev_version = parse_version(self.VERSION).is_devrelease
if checking_version and self._is_new_version_available():
update_notification_callback(self._newest_version, self._download_url, self._settings)
if checking_version and dev_version:
upgrade_from_dev_dialog(version_installed=self.VERSION)

def _should_check(self):
if self._settings.get(_CHECK_FOR_UPDATES_SETTING, None) is None:
Expand Down Expand Up @@ -82,6 +92,91 @@ def _get_response(params, method):
return xml


def upgrade_from_dev_dialog(version_installed):
VERSION = None
dev_version = urllib2.urlopen('https://raw.githubusercontent.com/robotframework/'
'RIDE/master/src/robotide/version.py', timeout=1).read().decode('utf-8')
master_code = compile(dev_version, 'version', 'exec')
main_dict = {'VERSION': VERSION}
exec(master_code, main_dict) # defines VERSION
if cmp_versions(version_installed, main_dict['VERSION']) == -1:
# Here is the Menu Help->Upgrade insertion part, try to highlight menu
if not _askyesno("Upgrade?", f"New development version is available.\nYou may install"
f" version {main_dict['VERSION']} with:\npip install -U https://github.com/"
f"robotframework/RIDE/archive/master.zip", wx.GetActiveWindow()):
return False
else:
command = sys.executable + " -m pip install -U https://github.com/robotframework/RIDE/archive/master.zip"
do_upgrade(command)
return True
else:
_askyesno("No Upgrade Available", "You have the latest version of RIDE.\nHave a nice day :)",
wx.GetActiveWindow())
return False


def _askyesno(title, message, frame=None):
if frame is None:
_ = wx.App()
parent = wx.Frame(None, size=(0, 0))
else:
parent = wx.Frame(frame, size=(0, 0))
parent.CenterOnScreen()
dlg = MessageDialog(parent, message, title, ttl=8)
dlg.Fit()
result = dlg.ShowModal() in [wx.ID_YES, wx.ID_OK]
# print("DEBUG: updatenotifier _askyesno Result %s" % result)
if dlg:
dlg.Destroy()
# parent.Destroy()
return result


def _add_content_to_clipboard(content):
wx.TheClipboard.Open()
wx.TheClipboard.SetData(wx.TextDataObject(content))
wx.TheClipboard.Close()


def do_upgrade(command):
_add_content_to_clipboard(command)
# print("DEBUG: Here will be the installation step.")
my_pid = psutil.Process()
my_pip = subprocess.Popen(command.split(' '), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
result = None
count = 0
while not result and count < 60:
count += 1
outs, errs = my_pip.communicate()
# DEBUG: Add output to a notebook tab
print(f"{outs}\n")
result = my_pip.returncode
if result == 0:
break
""" DEBUG: need to get outs line by line
except subprocess.SubprocessError:
my_pip.kill()
outs, errs = my_pip.communicate()
result = False
# DEBUG: Add output to a notebook tab
print(f"{outs}\n")
print(f"{errs}\n")
"""
time.sleep(1)
if result != 0:
_askyesno("Failed to Upgrade", "An error occurred when installing new version",
wx.GetActiveWindow())
return False
command = sys.executable + " -m robotide.__init__ --noupdatecheck"
wx.CallLater(500, subprocess.Popen, command.split(' '), start_new_session=True)
# Wait 10 seconds before trying to kill this process
""" Not working well:
wx.CallLater(10000, psutil.Process.kill, my_pid.pid)
"""
_askyesno("Completed Upgrade", f"You should close this RIDE (Process ID = {my_pid.pid})",
wx.GetActiveWindow())


class LocalHtmlWindow(HtmlWindow):
def __init__(self, parent, size=(600, 400)):
HtmlWindow.__init__(self, parent, size)
Expand All @@ -96,6 +191,7 @@ class UpdateDialog(RIDEDialog):

def __init__(self, uversion, url, settings):
self._settings = settings
self._command = sys.executable + f" -m pip install -U robotframework-ride=={uversion}"
RIDEDialog.__init__(self, title="Update available", size=(400, 400),
style=wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT)
# set Left to Right direction (while we don't have localization)
Expand All @@ -117,10 +213,17 @@ def __init__(self, uversion, url, settings):
"do not need automatic update checks")
checkbox.Bind(wx.EVT_CHECKBOX, handler=self.on_checkbox_change)
sizer.Add(checkbox)
hsizer = wx.BoxSizer(orient=wx.HORIZONTAL)
button = ButtonWithHandler(self, label="remind me later", handler=self.on_remind_me_later)
button.SetBackgroundColour(Colour(self.color_secondary_background))
button.SetForegroundColour(Colour(self.color_secondary_foreground))
sizer.Add(button)
hsizer.Add(button)
hsizer.AddSpacer(50)
up_button = ButtonWithHandler(self, label="Upgrade Now", handler=self.on_upgrade_now)
up_button.SetBackgroundColour(Colour(self.color_secondary_background))
up_button.SetForegroundColour(Colour(self.color_secondary_foreground))
hsizer.Add(up_button)
sizer.Add(hsizer)
self.SetSizer(sizer)
self.CentreOnParent(wx.BOTH)
self.Fit()
Expand All @@ -135,3 +238,8 @@ def on_remind_me_later(self, event):
def on_checkbox_change(self, event):
self._settings[_CHECK_FOR_UPDATES_SETTING] = not event.IsChecked()
event.Skip()

def on_upgrade_now(self, event):
_ = event
_add_content_to_clipboard(self._command)
do_upgrade(self._command)
8 changes: 5 additions & 3 deletions src/robotide/postinstall/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,13 @@ def __init__(self, parent, message, title, ttl=10):

self.CenterOnScreen(wx.BOTH)
self.timeToLive = ttl

st_msg = wx.StaticText(self, -1, message)
st_msg = []
for msg in message.split('\n'):
st_msg.append(wx.StaticText(self, -1, msg))
self.settimetolivemsg = wx.StaticText(self, -1, 'Closing this dialog box in %ds...' % self.timeToLive)
vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(st_msg, 0, wx.ALIGN_CENTER | wx.TOP, 40)
for sp_msg in st_msg:
vbox.Add(sp_msg, 0, wx.ALIGN_LEFT | wx.TOP, 10)
vbox.Add(self.settimetolivemsg, 0, wx.ALIGN_CENTER | wx.TOP, 10)
self.SetSizer(vbox)
self.SetAffirmativeId(wx.ID_OK)
Expand Down
26 changes: 18 additions & 8 deletions src/robotide/ui/mainframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
!Report a Problem | Open browser to SEARCH on the RIDE issue tracker
!Release notes | Shows release notes
!About | Information about RIDE
!Check for Upgrade | Looks at PyPi for new released version
"""

ID_CustomizeToolbar = wx.ID_HIGHEST + 1
Expand Down Expand Up @@ -159,15 +160,16 @@ def _show_modification_prevented_error(message):
style=wx.ICON_ERROR)

def _init_ui(self):
# self.aui_mgr.AddPane(wx.Panel(self), aui.AuiPaneInfo().CenterPane())
# #### self.splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE)
""" DEBUG:
self.aui_mgr.AddPane(wx.Panel(self), aui.AuiPaneInfo().CenterPane())
self.splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE)
"""
self.aui_mgr.AddPane(wx.Panel(self), aui.AuiPaneInfo().Name("right_pane").Right())
# set up default notebook style
self._notebook_style = (aui.AUI_NB_DEFAULT_STYLE | aui.AUI_NB_WINDOWLIST_BUTTON |
aui.AUI_NB_TAB_EXTERNAL_MOVE | aui.AUI_NB_SUB_NOTEBOOK | aui.AUI_NB_SMART_TABS)
# DEBUG self._notebook_theme = 0 (allow to select themes for notebooks)
# self.notebook = NoteBook(self.splitter, self._application,
# self._notebook_style)
# DEBUG: self._notebook_theme = 0 (allow to select themes for notebooks)
# DEBUG:self.notebook = NoteBook(self.splitter, self._application, self._notebook_style)
self.notebook = NoteBook(self, self._application,
self._notebook_style)
self.notebook.SetBackgroundColour(Colour(self.color_background))
Expand Down Expand Up @@ -504,13 +506,21 @@ def on_about(event):
dlg.ShowModal()
dlg.Destroy()

def on_check_for_upgrade(self, event):
_ = event
from ..application.updatenotifier import UpdateNotifierController, UpdateDialog
wx.CallAfter(UpdateNotifierController(self.general_settings).notify_update_if_needed,
UpdateDialog, ignore_check_condition=True)

# @staticmethod
def on_shortcut_keys(self, event):
_ = event
dialog = ShortcutKeysDialog()
# self.aui_mgr.AddPane(dialog.GetContentWindow(),aui.AuiPaneInfo().Name("shortcuts").Caption("Shortcuts Keys").
# CloseButton(True).RightDockable().Floatable().Float(), self.notebook)
# self.aui_mgr.Update()
""" DEBUG:
self.aui_mgr.AddPane(dialog.GetContentWindow(),aui.AuiPaneInfo().Name("shortcuts").Caption("Shortcuts Keys")
.CloseButton(True).RightDockable().Floatable().Float(), self.notebook)
self.aui_mgr.Update()
"""
dialog.Show()

@staticmethod
Expand Down
10 changes: 9 additions & 1 deletion src/robotide/utils/versioncomparator.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from pkg_resources import parse_version
try:
from pkg_resources import parse_version
except ImportError:
try:
from packaging.version import parse as parse_version
except ImportError as e:
print("RIDE cannot verify versions upgrade because of missing packages."
"You can install missing package with:\npip install packaging\nor\npip install setuptools")
raise e


def cmp_versions(version1, version2):
Expand Down
2 changes: 1 addition & 1 deletion src/robotide/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@
# limitations under the License.
#
# Automatically generated by `tasks.py`.
VERSION = 'v2.0.8dev19'
VERSION = 'v2.0.8dev20'
36 changes: 33 additions & 3 deletions utest/application/test_updatenotifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
import unittest
import time
import urllib
import wx

from robotide.application.updatenotifier import UpdateNotifierController

CHECKFORUPDATES = 'check for updates'
LASTUPDATECHECK = 'last update check'


class UpdateNotifierTestCase(unittest.TestCase):

def setUp(self):
Expand Down Expand Up @@ -113,15 +115,16 @@ def test_first_run_sets_settings_correctly_and_finds_an_update(self):
self.assertTrue(self._callback_called)

def test_checking_timeouts(self):
app = wx.App()
settings = self.internal_settings()
ctrl = UpdateNotifierController(settings)

def throw_timeout_error():
raise urllib.URLError('timeout')
raise urllib.error.URLError('timeout')

ctrl._get_newest_version = throw_timeout_error
ctrl.notify_update_if_needed(self._callback)
self.assertTrue(settings[LASTUPDATECHECK] > time.time() - 1)
self.assertTrue(settings[LASTUPDATECHECK] > time.time() - 10) # The dialog timeout in 10 seconds
self.assertFalse(self._callback_called)

def test_download_url_checking_timeouts(self):
Expand All @@ -132,7 +135,7 @@ def test_download_url_checking_timeouts(self):

def throw_timeout_error(*args):
_ = args
raise urllib.URLError('timeout')
raise urllib.error.URLError('timeout')

ctrl._get_download_url = throw_timeout_error
ctrl.notify_update_if_needed(self._callback)
Expand All @@ -155,6 +158,33 @@ def test_server_returns_older_version(self):
self.assertTrue(settings[CHECKFORUPDATES])
self.assertFalse(self._callback_called)

def test_forced_check_released(self):
app = wx.App()
settings = self.internal_settings()
ctrl = self._update_notifier_controller(settings, '0.43.0', '0.43.1')
ctrl.notify_update_if_needed(self._callback, ignore_check_condition=True)
self.assertTrue(settings[LASTUPDATECHECK] > time.time() - 9) # The dialog timeout in 10 seconds
self.assertTrue(settings[CHECKFORUPDATES])
self.assertTrue(self._callback_called)

def test_forced_check_development(self):
app = wx.App()
settings = self.internal_settings()
ctrl = self._update_notifier_controller(settings, '0.44dev12', '0.44.dev14')
ctrl.notify_update_if_needed(self._callback, ignore_check_condition=True)
self.assertTrue(settings[LASTUPDATECHECK] > time.time() - 10) # The dialog timeout in 10 seconds
self.assertTrue(settings[CHECKFORUPDATES])
self.assertTrue(self._callback_called)

def test_forced_check_development_ok(self):
app = wx.App()
settings = self.internal_settings()
ctrl = self._update_notifier_controller(settings, '0.44dev12', '0.44.dev12')
ctrl.notify_update_if_needed(self._callback, ignore_check_condition=False)
self.assertTrue(settings[LASTUPDATECHECK] > time.time() - 10) # The dialog timeout in 10 seconds
self.assertTrue(settings[CHECKFORUPDATES])
self.assertFalse(self._callback_called)


if __name__ == '__main__':
unittest.main()

0 comments on commit 827905e

Please sign in to comment.