move version checker

This commit is contained in:
Tim Bentley 2016-04-04 20:53:54 +01:00
parent a0bfc7d069
commit dffba47b44
11 changed files with 253 additions and 192 deletions

View File

@ -27,26 +27,26 @@ All the core functions of the OpenLP application including the GUI, settings,
logging and a plugin framework are contained within the openlp.core module.
"""
import os
import sys
import logging
import argparse
from traceback import format_exception
import logging
import os
import shutil
import sys
import time
from traceback import format_exception
from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import Registry, OpenLPMixin, AppLocation, LanguageManager, Settings, UiStrings, \
check_directory_exists, is_macosx, is_win, translate
from openlp.core.common.versionchecker import VersionThread, get_application_version
from openlp.core.lib import ScreenList
from openlp.core.resources import qInitResources
from openlp.core.ui.mainwindow import MainWindow
from openlp.core.ui.firsttimelanguageform import FirstTimeLanguageForm
from openlp.core.ui.firsttimeform import FirstTimeForm
from openlp.core.ui.exceptionform import ExceptionForm
from openlp.core.ui import SplashScreen
from openlp.core.utils import VersionThread, get_application_version
from openlp.core.ui.exceptionform import ExceptionForm
from openlp.core.ui.firsttimeform import FirstTimeForm
from openlp.core.ui.firsttimelanguageform import FirstTimeLanguageForm
from openlp.core.ui.mainwindow import MainWindow
__all__ = ['OpenLP', 'main']

View File

@ -0,0 +1,171 @@
import logging
import os
import platform
import sys
import time
import urllib.error
import urllib.parse
import urllib.request
from datetime import datetime
from distutils.version import LooseVersion
from subprocess import Popen, PIPE
from openlp.core.common import AppLocation, Settings
from PyQt5 import QtCore
log = logging.getLogger(__name__)
APPLICATION_VERSION = {}
CONNECTION_TIMEOUT = 30
CONNECTION_RETRIES = 2
class VersionThread(QtCore.QThread):
"""
A special Qt thread class to fetch the version of OpenLP from the website.
This is threaded so that it doesn't affect the loading time of OpenLP.
"""
def __init__(self, main_window):
"""
Constructor for the thread class.
:param main_window: The main window Object.
"""
log.debug("VersionThread - Initialise")
super(VersionThread, self).__init__(None)
self.main_window = main_window
def run(self):
"""
Run the thread.
"""
self.sleep(1)
log.debug('Version thread - run')
app_version = get_application_version()
version = check_latest_version(app_version)
log.debug("Versions %s and %s " % (LooseVersion(str(version)), LooseVersion(str(app_version['full']))))
if LooseVersion(str(version)) > LooseVersion(str(app_version['full'])):
self.main_window.openlp_version_check.emit('%s' % version)
def get_application_version():
"""
Returns the application version of the running instance of OpenLP::
{'full': '1.9.4-bzr1249', 'version': '1.9.4', 'build': 'bzr1249'}
"""
global APPLICATION_VERSION
if APPLICATION_VERSION:
return APPLICATION_VERSION
if '--dev-version' in sys.argv or '-d' in sys.argv:
# NOTE: The following code is a duplicate of the code in setup.py. Any fix applied here should also be applied
# there.
# Get the revision of this tree.
bzr = Popen(('bzr', 'revno'), stdout=PIPE)
tree_revision, error = bzr.communicate()
tree_revision = tree_revision.decode()
code = bzr.wait()
if code != 0:
raise Exception('Error running bzr log')
# Get all tags.
bzr = Popen(('bzr', 'tags'), stdout=PIPE)
output, error = bzr.communicate()
code = bzr.wait()
if code != 0:
raise Exception('Error running bzr tags')
tags = list(map(bytes.decode, output.splitlines()))
if not tags:
tag_version = '0.0.0'
tag_revision = '0'
else:
# Remove any tag that has "?" as revision number. A "?" as revision number indicates, that this tag is from
# another series.
tags = [tag for tag in tags if tag.split()[-1].strip() != '?']
# Get the last tag and split it in a revision and tag name.
tag_version, tag_revision = tags[-1].split()
# If they are equal, then this tree is tarball with the source for the release. We do not want the revision
# number in the full version.
if tree_revision == tag_revision:
full_version = tag_version.strip()
else:
full_version = '%s-bzr%s' % (tag_version.strip(), tree_revision.strip())
else:
# We're not running the development version, let's use the file.
file_path = AppLocation.get_directory(AppLocation.VersionDir)
file_path = os.path.join(file_path, '.version')
version_file = None
try:
version_file = open(file_path, 'r')
full_version = str(version_file.read()).rstrip()
except IOError:
log.exception('Error in version file.')
full_version = '0.0.0-bzr000'
finally:
if version_file:
version_file.close()
bits = full_version.split('-')
APPLICATION_VERSION = {
'full': full_version,
'version': bits[0],
'build': bits[1] if len(bits) > 1 else None
}
if APPLICATION_VERSION['build']:
log.info('Openlp version %s build %s', APPLICATION_VERSION['version'], APPLICATION_VERSION['build'])
else:
log.info('Openlp version %s' % APPLICATION_VERSION['version'])
return APPLICATION_VERSION
def check_latest_version(current_version):
"""
Check the latest version of OpenLP against the version file on the OpenLP
site.
**Rules around versions and version files:**
* If a version number has a build (i.e. -bzr1234), then it is a nightly.
* If a version number's minor version is an odd number, it is a development release.
* If a version number's minor version is an even number, it is a stable release.
:param current_version: The current version of OpenLP.
"""
version_string = current_version['full']
# set to prod in the distribution config file.
settings = Settings()
settings.beginGroup('core')
last_test = settings.value('last version test')
this_test = str(datetime.now().date())
settings.setValue('last version test', this_test)
settings.endGroup()
if last_test != this_test:
if current_version['build']:
req = urllib.request.Request('http://www.openlp.org/files/nightly_version.txt')
else:
version_parts = current_version['version'].split('.')
if int(version_parts[1]) % 2 != 0:
req = urllib.request.Request('http://www.openlp.org/files/dev_version.txt')
else:
req = urllib.request.Request('http://www.openlp.org/files/version.txt')
req.add_header('User-Agent', 'OpenLP/%s %s/%s; ' % (current_version['full'], platform.system(),
platform.release()))
remote_version = None
retries = 0
while True:
try:
remote_version = str(urllib.request.urlopen(req, None,
timeout=CONNECTION_TIMEOUT).read().decode()).strip()
except (urllib.error.URLError, ConnectionError):
if retries > CONNECTION_RETRIES:
log.exception('Failed to download the latest OpenLP version file')
else:
retries += 1
time.sleep(0.1)
continue
break
if remote_version:
version_string = remote_version
return version_string

View File

@ -24,11 +24,10 @@ Provide the generic plugin functionality for OpenLP plugins.
"""
import logging
from PyQt5 import QtCore
from openlp.core.common import Registry, RegistryProperties, Settings, UiStrings
from openlp.core.utils import get_application_version
from openlp.core.common.versionchecker import get_application_version
log = logging.getLogger(__name__)

View File

@ -26,8 +26,8 @@ import webbrowser
from PyQt5 import QtCore, QtWidgets
from openlp.core.common.versionchecker import get_application_version
from openlp.core.lib import translate
from openlp.core.utils import get_application_version
from .aboutdialog import UiAboutDialog

View File

@ -23,18 +23,17 @@
The actual exception dialog form.
"""
import logging
import re
import os
import platform
import re
import bs4
import sqlalchemy
from PyQt5 import Qt, QtCore, QtGui, QtWebKit, QtWidgets
from lxml import etree
from openlp.core.common import RegistryProperties, is_linux
from PyQt5 import Qt, QtCore, QtGui, QtWebKit, QtWidgets
try:
import migrate
MIGRATE_VERSION = getattr(migrate, '__version__', '< 0.7')
@ -74,7 +73,7 @@ except ImportError:
VLC_VERSION = '-'
from openlp.core.common import Settings, UiStrings, translate
from openlp.core.utils import get_application_version
from openlp.core.common.versionchecker import get_application_version
from .exceptiondialog import Ui_ExceptionDialog

View File

@ -37,6 +37,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import Registry, RegistryProperties, AppLocation, LanguageManager, Settings, \
check_directory_exists, translate, is_win, is_macosx, add_actions
from openlp.core.common.actions import ActionList, CategoryOrder
from openlp.core.common.versionchecker import get_application_version
from openlp.core.lib import Renderer, OpenLPDockWidget, PluginManager, ImageManager, PluginStatus, ScreenList, \
build_icon
from openlp.core.lib.ui import UiStrings, create_action
@ -46,7 +47,6 @@ from openlp.core.ui.firsttimeform import FirstTimeForm
from openlp.core.ui.media import MediaController
from openlp.core.ui.printserviceform import PrintServiceForm
from openlp.core.ui.projector.manager import ProjectorManager
from openlp.core.utils import get_application_version
log = logging.getLogger(__name__)

View File

@ -24,7 +24,6 @@ The :mod:`openlp.core.utils` module provides the utility libraries for OpenLP.
"""
import logging
import os
import platform
import re
import socket
import sys
@ -32,8 +31,6 @@ import time
import urllib.error
import urllib.parse
import urllib.request
from datetime import datetime
from distutils.version import LooseVersion
from http.client import HTTPException
from random import randint
from shutil import which
@ -90,34 +87,6 @@ CONNECTION_TIMEOUT = 30
CONNECTION_RETRIES = 2
class VersionThread(QtCore.QThread):
"""
A special Qt thread class to fetch the version of OpenLP from the website.
This is threaded so that it doesn't affect the loading time of OpenLP.
"""
def __init__(self, main_window):
"""
Constructor for the thread class.
:param main_window: The main window Object.
"""
log.debug("VersionThread - Initialise")
super(VersionThread, self).__init__(None)
self.main_window = main_window
def run(self):
"""
Run the thread.
"""
self.sleep(1)
log.debug('Version thread - run')
app_version = get_application_version()
version = check_latest_version(app_version)
log.debug("Versions %s and %s " % (LooseVersion(str(version)), LooseVersion(str(app_version['full']))))
if LooseVersion(str(version)) > LooseVersion(str(app_version['full'])):
self.main_window.openlp_version_check.emit('%s' % version)
class HTTPRedirectHandlerFixed(urllib.request.HTTPRedirectHandler):
"""
Special HTTPRedirectHandler used to work around http://bugs.python.org/issue22248
@ -145,127 +114,6 @@ class HTTPRedirectHandlerFixed(urllib.request.HTTPRedirectHandler):
return super(HTTPRedirectHandlerFixed, self).redirect_request(req, fp, code, msg, headers, fixed_url)
def get_application_version():
"""
Returns the application version of the running instance of OpenLP::
{'full': '1.9.4-bzr1249', 'version': '1.9.4', 'build': 'bzr1249'}
"""
global APPLICATION_VERSION
if APPLICATION_VERSION:
return APPLICATION_VERSION
if '--dev-version' in sys.argv or '-d' in sys.argv:
# NOTE: The following code is a duplicate of the code in setup.py. Any fix applied here should also be applied
# there.
# Get the revision of this tree.
bzr = Popen(('bzr', 'revno'), stdout=PIPE)
tree_revision, error = bzr.communicate()
tree_revision = tree_revision.decode()
code = bzr.wait()
if code != 0:
raise Exception('Error running bzr log')
# Get all tags.
bzr = Popen(('bzr', 'tags'), stdout=PIPE)
output, error = bzr.communicate()
code = bzr.wait()
if code != 0:
raise Exception('Error running bzr tags')
tags = list(map(bytes.decode, output.splitlines()))
if not tags:
tag_version = '0.0.0'
tag_revision = '0'
else:
# Remove any tag that has "?" as revision number. A "?" as revision number indicates, that this tag is from
# another series.
tags = [tag for tag in tags if tag.split()[-1].strip() != '?']
# Get the last tag and split it in a revision and tag name.
tag_version, tag_revision = tags[-1].split()
# If they are equal, then this tree is tarball with the source for the release. We do not want the revision
# number in the full version.
if tree_revision == tag_revision:
full_version = tag_version.strip()
else:
full_version = '%s-bzr%s' % (tag_version.strip(), tree_revision.strip())
else:
# We're not running the development version, let's use the file.
file_path = AppLocation.get_directory(AppLocation.VersionDir)
file_path = os.path.join(file_path, '.version')
version_file = None
try:
version_file = open(file_path, 'r')
full_version = str(version_file.read()).rstrip()
except IOError:
log.exception('Error in version file.')
full_version = '0.0.0-bzr000'
finally:
if version_file:
version_file.close()
bits = full_version.split('-')
APPLICATION_VERSION = {
'full': full_version,
'version': bits[0],
'build': bits[1] if len(bits) > 1 else None
}
if APPLICATION_VERSION['build']:
log.info('Openlp version %s build %s', APPLICATION_VERSION['version'], APPLICATION_VERSION['build'])
else:
log.info('Openlp version %s' % APPLICATION_VERSION['version'])
return APPLICATION_VERSION
def check_latest_version(current_version):
"""
Check the latest version of OpenLP against the version file on the OpenLP
site.
**Rules around versions and version files:**
* If a version number has a build (i.e. -bzr1234), then it is a nightly.
* If a version number's minor version is an odd number, it is a development release.
* If a version number's minor version is an even number, it is a stable release.
:param current_version: The current version of OpenLP.
"""
version_string = current_version['full']
# set to prod in the distribution config file.
settings = Settings()
settings.beginGroup('core')
last_test = settings.value('last version test')
this_test = str(datetime.now().date())
settings.setValue('last version test', this_test)
settings.endGroup()
if last_test != this_test:
if current_version['build']:
req = urllib.request.Request('http://www.openlp.org/files/nightly_version.txt')
else:
version_parts = current_version['version'].split('.')
if int(version_parts[1]) % 2 != 0:
req = urllib.request.Request('http://www.openlp.org/files/dev_version.txt')
else:
req = urllib.request.Request('http://www.openlp.org/files/version.txt')
req.add_header('User-Agent', 'OpenLP/%s %s/%s; ' % (current_version['full'], platform.system(),
platform.release()))
remote_version = None
retries = 0
while True:
try:
remote_version = str(urllib.request.urlopen(req, None,
timeout=CONNECTION_TIMEOUT).read().decode()).strip()
except (urllib.error.URLError, ConnectionError):
if retries > CONNECTION_RETRIES:
log.exception('Failed to download the latest OpenLP version file')
else:
retries += 1
time.sleep(0.1)
continue
break
if remote_version:
version_string = remote_version
return version_string
def get_filesystem_encoding():
"""
Returns the name of the encoding used to convert Unicode filenames into system file names.

View File

@ -62,10 +62,10 @@ import re
from lxml import etree, objectify
from openlp.core.common import translate
from openlp.core.common.versionchecker import get_application_version
from openlp.core.lib import FormattingTags
from openlp.plugins.songs.lib import VerseType, clean_song
from openlp.plugins.songs.lib.db import Author, AuthorType, Book, Song, Topic
from openlp.core.utils import get_application_version
log = logging.getLogger(__name__)

View File

@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2016 OpenLP Developers #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
Package to test the openlp.core.common.versionchecker package.
"""
from unittest import TestCase
from openlp.core.common.settings import Settings
from openlp.core.common.versionchecker import VersionThread
from tests.functional import MagicMock, patch
from tests.helpers.testmixin import TestMixin
class TestVersionchecker(TestMixin, TestCase):
def setUp(self):
"""
Create an instance and a few example actions.
"""
self.build_settings()
def tearDown(self):
"""
Clean up
"""
self.destroy_settings()
def version_thread_triggered_test(self):
"""
Test the version thread call does not trigger UI
:return:
"""
# GIVEN: a equal version setup and the data is not today.
mocked_main_window = MagicMock()
Settings().setValue('core/last version test', '1950-04-01')
# WHEN: We check to see if the version is different .
with patch('PyQt5.QtCore.QThread'),\
patch('openlp.core.common.versionchecker.get_application_version') as mocked_get_application_version:
mocked_get_application_version.return_value = {'version': '1.0.0', 'build': '', 'full': '2.0.4'}
version_thread = VersionThread(mocked_main_window)
version_thread.run()
# THEN: If the version has changed the main window is notified
self.assertTrue(mocked_main_window.openlp_version_check.emit.called,
'The main windows should have been notified')

View File

@ -24,9 +24,8 @@ Package to test the openlp.core.utils.actions package.
"""
from unittest import TestCase
from openlp.core.common.settings import Settings
from openlp.core.utils import VersionThread, get_uno_command
from tests.functional import MagicMock, patch
from openlp.core.utils import get_uno_command
from tests.functional import patch
from tests.helpers.testmixin import TestMixin
@ -44,23 +43,6 @@ class TestInitFunctions(TestMixin, TestCase):
"""
self.destroy_settings()
def version_thread_triggered_test(self):
"""
Test the version thread call does not trigger UI
:return:
"""
# GIVEN: a equal version setup and the data is not today.
mocked_main_window = MagicMock()
Settings().setValue('core/last version test', '1950-04-01')
# WHEN: We check to see if the version is different .
with patch('PyQt5.QtCore.QThread'),\
patch('openlp.core.utils.get_application_version') as mocked_get_application_version:
mocked_get_application_version.return_value = {'version': '1.0.0', 'build': '', 'full': '2.0.4'}
version_thread = VersionThread(mocked_main_window)
version_thread.run()
# THEN: If the version has changed the main window is notified
self.assertTrue(mocked_main_window.openlp_version_check.emit.called,
'The main windows should have been notified')
def get_uno_command_libreoffice_command_exists_test(self):
"""

View File

@ -27,7 +27,6 @@ from unittest import TestCase
from openlp.core.utils import clean_filename, delete_file, get_filesystem_encoding, \
split_filename, _get_user_agent, get_web_page, get_uno_instance
from openlp.core.common.languagemanager import get_locale_key, get_natural_key
from tests.functional import MagicMock, patch