~suutari-olli/openlp/click-slide-to-go-live-from-blank

« back to all changes in this revision

Viewing changes to openlp/core/common/versionchecker.py

trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import logging
 
2
import os
 
3
import platform
 
4
import sys
 
5
import time
 
6
import urllib.error
 
7
import urllib.parse
 
8
import urllib.request
 
9
from datetime import datetime
 
10
from distutils.version import LooseVersion
 
11
from subprocess import Popen, PIPE
 
12
 
 
13
from openlp.core.common import AppLocation, Settings
 
14
 
 
15
from PyQt5 import QtCore
 
16
 
 
17
log = logging.getLogger(__name__)
 
18
 
 
19
APPLICATION_VERSION = {}
 
20
CONNECTION_TIMEOUT = 30
 
21
CONNECTION_RETRIES = 2
 
22
 
 
23
 
 
24
class VersionThread(QtCore.QThread):
 
25
    """
 
26
    A special Qt thread class to fetch the version of OpenLP from the website.
 
27
    This is threaded so that it doesn't affect the loading time of OpenLP.
 
28
    """
 
29
    def __init__(self, main_window):
 
30
        """
 
31
        Constructor for the thread class.
 
32
 
 
33
        :param main_window: The main window Object.
 
34
        """
 
35
        log.debug("VersionThread - Initialise")
 
36
        super(VersionThread, self).__init__(None)
 
37
        self.main_window = main_window
 
38
 
 
39
    def run(self):
 
40
        """
 
41
        Run the thread.
 
42
        """
 
43
        self.sleep(1)
 
44
        log.debug('Version thread - run')
 
45
        app_version = get_application_version()
 
46
        version = check_latest_version(app_version)
 
47
        log.debug("Versions %s and %s " % (LooseVersion(str(version)), LooseVersion(str(app_version['full']))))
 
48
        if LooseVersion(str(version)) > LooseVersion(str(app_version['full'])):
 
49
            self.main_window.openlp_version_check.emit('%s' % version)
 
50
 
 
51
 
 
52
def get_application_version():
 
53
    """
 
54
    Returns the application version of the running instance of OpenLP::
 
55
 
 
56
        {'full': '1.9.4-bzr1249', 'version': '1.9.4', 'build': 'bzr1249'}
 
57
    """
 
58
    global APPLICATION_VERSION
 
59
    if APPLICATION_VERSION:
 
60
        return APPLICATION_VERSION
 
61
    if '--dev-version' in sys.argv or '-d' in sys.argv:
 
62
        # NOTE: The following code is a duplicate of the code in setup.py. Any fix applied here should also be applied
 
63
        # there.
 
64
 
 
65
        # Get the revision of this tree.
 
66
        bzr = Popen(('bzr', 'revno'), stdout=PIPE)
 
67
        tree_revision, error = bzr.communicate()
 
68
        tree_revision = tree_revision.decode()
 
69
        code = bzr.wait()
 
70
        if code != 0:
 
71
            raise Exception('Error running bzr log')
 
72
 
 
73
        # Get all tags.
 
74
        bzr = Popen(('bzr', 'tags'), stdout=PIPE)
 
75
        output, error = bzr.communicate()
 
76
        code = bzr.wait()
 
77
        if code != 0:
 
78
            raise Exception('Error running bzr tags')
 
79
        tags = list(map(bytes.decode, output.splitlines()))
 
80
        if not tags:
 
81
            tag_version = '0.0.0'
 
82
            tag_revision = '0'
 
83
        else:
 
84
            # Remove any tag that has "?" as revision number. A "?" as revision number indicates, that this tag is from
 
85
            # another series.
 
86
            tags = [tag for tag in tags if tag.split()[-1].strip() != '?']
 
87
            # Get the last tag and split it in a revision and tag name.
 
88
            tag_version, tag_revision = tags[-1].split()
 
89
        # If they are equal, then this tree is tarball with the source for the release. We do not want the revision
 
90
        # number in the full version.
 
91
        if tree_revision == tag_revision:
 
92
            full_version = tag_version.strip()
 
93
        else:
 
94
            full_version = '%s-bzr%s' % (tag_version.strip(), tree_revision.strip())
 
95
    else:
 
96
        # We're not running the development version, let's use the file.
 
97
        file_path = AppLocation.get_directory(AppLocation.VersionDir)
 
98
        file_path = os.path.join(file_path, '.version')
 
99
        version_file = None
 
100
        try:
 
101
            version_file = open(file_path, 'r')
 
102
            full_version = str(version_file.read()).rstrip()
 
103
        except IOError:
 
104
            log.exception('Error in version file.')
 
105
            full_version = '0.0.0-bzr000'
 
106
        finally:
 
107
            if version_file:
 
108
                version_file.close()
 
109
    bits = full_version.split('-')
 
110
    APPLICATION_VERSION = {
 
111
        'full': full_version,
 
112
        'version': bits[0],
 
113
        'build': bits[1] if len(bits) > 1 else None
 
114
    }
 
115
    if APPLICATION_VERSION['build']:
 
116
        log.info('Openlp version %s build %s', APPLICATION_VERSION['version'], APPLICATION_VERSION['build'])
 
117
    else:
 
118
        log.info('Openlp version %s' % APPLICATION_VERSION['version'])
 
119
    return APPLICATION_VERSION
 
120
 
 
121
 
 
122
def check_latest_version(current_version):
 
123
    """
 
124
    Check the latest version of OpenLP against the version file on the OpenLP
 
125
    site.
 
126
 
 
127
    **Rules around versions and version files:**
 
128
 
 
129
    * If a version number has a build (i.e. -bzr1234), then it is a nightly.
 
130
    * If a version number's minor version is an odd number, it is a development release.
 
131
    * If a version number's minor version is an even number, it is a stable release.
 
132
 
 
133
    :param current_version: The current version of OpenLP.
 
134
    """
 
135
    version_string = current_version['full']
 
136
    # set to prod in the distribution config file.
 
137
    settings = Settings()
 
138
    settings.beginGroup('core')
 
139
    last_test = settings.value('last version test')
 
140
    this_test = str(datetime.now().date())
 
141
    settings.setValue('last version test', this_test)
 
142
    settings.endGroup()
 
143
    if last_test != this_test:
 
144
        if current_version['build']:
 
145
            req = urllib.request.Request('http://www.openlp.org/files/nightly_version.txt')
 
146
        else:
 
147
            version_parts = current_version['version'].split('.')
 
148
            if int(version_parts[1]) % 2 != 0:
 
149
                req = urllib.request.Request('http://www.openlp.org/files/dev_version.txt')
 
150
            else:
 
151
                req = urllib.request.Request('http://www.openlp.org/files/version.txt')
 
152
        req.add_header('User-Agent', 'OpenLP/%s %s/%s; ' % (current_version['full'], platform.system(),
 
153
                                                            platform.release()))
 
154
        remote_version = None
 
155
        retries = 0
 
156
        while True:
 
157
            try:
 
158
                remote_version = str(urllib.request.urlopen(req, None,
 
159
                                                            timeout=CONNECTION_TIMEOUT).read().decode()).strip()
 
160
            except (urllib.error.URLError, ConnectionError):
 
161
                if retries > CONNECTION_RETRIES:
 
162
                    log.exception('Failed to download the latest OpenLP version file')
 
163
                else:
 
164
                    retries += 1
 
165
                    time.sleep(0.1)
 
166
                    continue
 
167
            break
 
168
        if remote_version:
 
169
            version_string = remote_version
 
170
    return version_string