openmedialibrary/oml/update.py

318 lines
11 KiB
Python
Raw Normal View History

2014-08-05 11:47:16 +02:00
# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
2014-09-03 00:32:44 +02:00
2014-08-05 11:47:16 +02:00
from contextlib import closing
import json
import os
import tarfile
2016-01-12 20:30:51 +05:30
from threading import Thread
2014-09-03 00:32:44 +02:00
import urllib.request, urllib.error, urllib.parse
2014-08-05 11:47:16 +02:00
import shutil
import subprocess
2015-11-30 17:49:33 +01:00
import sys
2016-01-12 20:30:51 +05:30
import time
2014-08-05 11:47:16 +02:00
import ed25519
import ox
2014-08-22 19:46:45 +02:00
from oxtornado import actions
2014-08-05 11:47:16 +02:00
import settings
import db
2014-08-05 11:47:16 +02:00
2015-11-30 17:49:33 +01:00
import logging
logger = logging.getLogger(__name__)
2014-08-22 19:46:45 +02:00
2014-08-05 11:47:16 +02:00
ENCODING='base64'
def verify(release):
vk = ed25519.VerifyingKey(settings.OML_UPDATE_KEY, encoding=ENCODING)
value = []
for module in sorted(release['modules']):
value += [str('%s/%s' % (release['modules'][module]['version'], release['modules'][module]['sha1']))]
value = '\n'.join(value)
2014-10-31 18:53:10 +01:00
value = value.encode()
sig = release['signature'].encode()
2014-08-05 11:47:16 +02:00
try:
vk.verify(sig, value, encoding=ENCODING)
except ed25519.BadSignatureError:
return False
return True
2014-08-07 12:12:03 +02:00
def get(url, filename=None):
2014-09-03 00:32:44 +02:00
request = urllib.request.Request(url, headers={
2014-08-07 12:12:03 +02:00
'User-Agent': settings.USER_AGENT
})
2014-09-03 00:32:44 +02:00
with closing(urllib.request.urlopen(request)) as u:
2014-08-07 12:12:03 +02:00
if not filename:
data = u.read()
return data
else:
dirname = os.path.dirname(filename)
if dirname and not os.path.exists(dirname):
os.makedirs(dirname)
2014-10-31 18:47:48 +01:00
with open(filename, 'wb') as fd:
2014-08-05 11:47:16 +02:00
data = u.read(4096)
2014-08-07 12:12:03 +02:00
while data:
fd.write(data)
data = u.read(4096)
2014-08-05 11:47:16 +02:00
def check():
2014-08-05 11:47:16 +02:00
if settings.release:
2015-11-26 13:40:39 +01:00
release_data = get(settings.server.get('release_url',
'http://downloads.openmedialibrary.com/release.json'))
2014-10-31 18:47:48 +01:00
release = json.loads(release_data.decode('utf-8'))
2015-03-08 00:47:13 +05:30
old = current_version('openmedialibrary')
2014-08-05 11:47:16 +02:00
new = release['modules']['openmedialibrary']['version']
return verify(release) and old < new
return False
2014-08-05 11:47:16 +02:00
2015-03-08 00:47:13 +05:30
def current_version(module):
2015-11-26 13:40:39 +01:00
if 'modules' in settings.release \
and module in settings.release['modules'] \
and 'version' in settings.release['modules'][module]:
version = settings.release['modules'][module]['version']
else:
version = ''
return version
2015-03-08 00:47:13 +05:30
2016-01-04 17:32:40 +05:30
def get_latest_release():
release_data = get(settings.server.get('release_url'))
2014-10-31 18:47:48 +01:00
release = json.loads(release_data.decode('utf-8'))
2014-09-06 01:56:37 +02:00
if verify(release):
2016-01-13 10:38:16 +05:30
ox.makedirs(settings.updates_path)
2016-01-04 17:32:40 +05:30
with open(os.path.join(settings.updates_path, 'release.json'), 'wb') as fd:
fd.write(release_data)
return release
def download():
if not os.path.exists(os.path.join(settings.config_path, 'release.json')):
return True
release = get_latest_release()
if release:
2014-08-05 12:10:42 +02:00
ox.makedirs(settings.updates_path)
os.chdir(os.path.dirname(settings.base_dir))
current_files = {'release.json'}
for module in release['modules']:
2015-03-08 00:47:13 +05:30
if release['modules'][module]['version'] > current_version(module):
module_tar = os.path.join(settings.updates_path, release['modules'][module]['name'])
base_url = settings.server.get('release_url').rsplit('/', 1)[0]
url = '/'.join([base_url, release['modules'][module]['name']])
if not os.path.exists(module_tar):
2016-01-13 10:41:38 +05:30
logger.debug('download %s', os.path.basename(module_tar))
2014-08-07 12:12:03 +02:00
get(url, module_tar)
if ox.sha1sum(module_tar) != release['modules'][module]['sha1']:
2016-01-13 10:41:38 +05:30
logger.debug('invalid checksum %s', os.path.basename(module_tar))
os.unlink(module_tar)
return False
current_files.add(os.path.basename(module_tar))
2014-12-13 15:56:17 +01:00
for f in set(next(os.walk(settings.updates_path))[2])-current_files:
2014-08-11 19:11:07 +02:00
os.unlink(os.path.join(settings.updates_path, f))
return True
return True
2016-01-13 10:38:16 +05:30
def install(stop=True):
if not os.path.exists(os.path.join(settings.updates_path, 'release.json')):
return True
if not os.path.exists(os.path.join(settings.config_path, 'release.json')):
return True
with open(os.path.join(settings.updates_path, 'release.json')) as fd:
release = json.load(fd)
2015-11-17 14:21:05 +01:00
old_version = current_version('openmedialibrary')
new_version = release['modules']['openmedialibrary']['version']
if verify(release) and old_version < new_version:
2014-08-05 12:10:42 +02:00
os.chdir(os.path.dirname(settings.base_dir))
2014-08-05 11:47:16 +02:00
for module in release['modules']:
2015-03-08 00:47:13 +05:30
if release['modules'][module]['version'] > current_version(module):
module_tar = os.path.join(settings.updates_path, release['modules'][module]['name'])
if os.path.exists(module_tar) and ox.sha1sum(module_tar) == release['modules'][module]['sha1']:
#tar fails if old platform is moved before extract
new = '%s_new' % module
ox.makedirs(new)
os.chdir(new)
tar = tarfile.open(module_tar)
2014-08-05 11:51:17 +02:00
tar.extractall()
tar.close()
2014-08-05 12:10:42 +02:00
os.chdir(os.path.dirname(settings.base_dir))
2015-03-14 17:44:08 +05:30
module_old = '%s_old' % module
if os.path.exists(module):
shutil.move(module, module_old)
shutil.move(os.path.join(new, module), module)
2015-03-14 17:44:08 +05:30
if os.path.exists(module_old):
shutil.rmtree(module_old)
shutil.rmtree(new)
2014-08-05 11:51:17 +02:00
else:
2016-01-13 10:49:26 +05:30
if os.path.exists(module_tar):
os.unlink(module_tar)
2014-08-05 11:51:17 +02:00
return False
shutil.copy(os.path.join(settings.updates_path, 'release.json'), os.path.join(settings.config_path, 'release.json'))
2016-01-13 10:38:16 +05:30
if stop:
subprocess.call(['./ctl', 'stop'])
subprocess.call(['./ctl', 'postupdate', '-o', old_version, '-n', new_version])
2015-11-30 17:49:33 +01:00
upgrade_app()
2014-08-05 11:47:16 +02:00
return True
return True
2014-08-22 19:46:45 +02:00
2016-01-12 20:30:51 +05:30
def update_available():
db_version = settings.server.get('db_version', 0)
if db_version < settings.DB_VERSION:
return True
if not os.path.exists(os.path.join(settings.updates_path, 'release.json')):
return False
if not os.path.exists(os.path.join(settings.config_path, 'release.json')):
return False
with open(os.path.join(settings.updates_path, 'release.json')) as fd:
release = json.load(fd)
old_version = current_version('openmedialibrary')
new_version = release['modules']['openmedialibrary']['version']
return verify(release) and old_version < new_version
def restart_oml(update=False):
if update:
get_latest_release()
subprocess.Popen([os.path.join(settings.base_dir, 'ctl'), 'restart'],
close_fds=True, start_new_session=True)
2015-11-30 17:49:33 +01:00
def get_app_version(app):
plist = app + '/Contents/Info.plist'
if os.path.exists(plist):
cmd = ['defaults', 'read', plist, 'CFBundleShortVersionString']
return subprocess.check_output(cmd).strip()
def upgrade_app():
if sys.platform == 'darwin':
base = os.path.dirname(settings.base_dir)
2015-12-19 19:22:01 +01:00
bundled_app = os.path.join(base, 'platform/Darwin/Applications/Open Media Library.app')
2015-11-30 17:49:33 +01:00
app = '/Applications/Open Media Library.app'
version = get_app_version(app)
current_version = get_app_version(bundled_app)
if version and current_version and version != current_version:
try:
shutil.rmtree(app)
shutil.copytree(bundled_app, app)
except:
logger.debug('Failed to update Application', exc_info=1)
2014-08-22 19:46:45 +02:00
def getVersion(data):
'''
check if new version is available
'''
response = {
'current': settings.MINOR_VERSION,
2016-01-13 10:47:20 +05:30
'version': settings.MINOR_VERSION,
2014-08-22 19:46:45 +02:00
'upgrade': False,
}
2014-09-01 14:01:41 +02:00
if settings.MINOR_VERSION == 'git':
2015-12-01 09:58:03 +01:00
'''
2014-09-01 14:01:41 +02:00
cmd = ['git', 'rev-parse', '@']
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, close_fds=True)
stdout, stderr = p.communicate()
current = stdout.strip()
cmd = ['git', 'ls-remote', 'origin', '-h', 'refs/heads/master']
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, close_fds=True)
stdout, stderr = p.communicate()
new = stdout.strip()[:40]
2015-12-01 00:26:35 +01:00
response['update'] = len(new) == 40 and current != new
2015-12-01 09:58:03 +01:00
'''
response['update'] = False
2014-09-01 14:01:41 +02:00
else:
2016-01-04 17:32:40 +05:30
get_latest_release()
2014-09-01 14:01:41 +02:00
if not os.path.exists(os.path.join(settings.updates_path, 'release.json')):
return response
if not os.path.exists(os.path.join(settings.config_path, 'release.json')):
return response
with open(os.path.join(settings.updates_path, 'release.json')) as fd:
release = json.load(fd)
2015-03-08 00:47:13 +05:30
current = current_version('openmedialibrary')
2014-09-01 14:01:41 +02:00
response['current'] = current
new = release['modules']['openmedialibrary']['version']
2016-01-13 10:47:20 +05:30
response['version'] = new
2014-09-01 14:01:41 +02:00
response['update'] = current < new
2014-08-22 19:46:45 +02:00
return response
actions.register(getVersion, cache=False)
def restart(data):
'''
restart (and upgrade if upgrades are available)
'''
2016-01-12 20:30:51 +05:30
restart_oml(data.get('update'))
2014-08-22 19:46:45 +02:00
return {}
actions.register(restart, cache=False)
2016-01-12 20:30:51 +05:30
class Update(Thread):
_status = {
'reload': False,
'status': 'Updating Open Media Library...'
}
2016-01-12 20:30:51 +05:30
def __init__(self):
Thread.__init__(self)
self.daemon = True
self.start()
def status(self, status, reload=False):
from websocket import trigger_event
self._status = {
2016-01-12 20:30:51 +05:30
'reload': reload,
'status': status,
}
trigger_event('updatestatus', self._status)
2016-01-12 20:30:51 +05:30
def install(self):
while update_available():
self.status('Downloading new version...')
while not download():
self.status('Download failed... (try again in 10 seconds)')
time.sleep(10)
self.status('Downloading new version...')
self.status('Installing new version...')
2016-01-13 10:38:16 +05:30
if not install(False):
2016-01-12 20:30:51 +05:30
self.status('Installation failed...')
2016-01-13 10:53:12 +05:30
return True
return False
2016-01-12 20:30:51 +05:30
def update_database(self):
db_version = settings.server.get('db_version', 0)
if db_version < settings.DB_VERSION:
self.status('Migrating database...')
if db_version < 1:
db_version = migrate_1()
2016-01-16 15:04:07 +05:30
if db_version < 2:
db_version = migrate_2()
2016-01-12 20:30:51 +05:30
settings.server['db_version'] = settings.DB_VERSION
def run(self):
self.status('Checking for Updates...')
self.update_database()
2016-01-13 10:53:12 +05:30
if self.install():
restart_oml()
return
2016-01-13 23:16:09 +05:30
self.status('Relaunching...', True)
2016-01-12 20:30:51 +05:30
restart_oml()
def migrate_1():
with db.session() as session:
import item.models
for s in item.models.Sort.query.filter_by(author=''):
s.item.update_sort()
session.commit()
return 1
2016-01-16 15:04:07 +05:30
def migrate_2():
with db.session() as session:
import item.models
for s in item.models.Sort.query.filter_by(author=''):
s.item.update_sort()
for s in item.models.Sort.query.filter_by(publisher=''):
s.item.update_sort()
for s in item.models.Sort.query.filter_by(language=''):
s.item.update_sort()
for s in item.models.Sort.query.filter_by(place=''):
s.item.update_sort()
session.commit()
return 2