|
|
@ -36,11 +36,10 @@ from flask_babel import gettext as _
|
|
|
|
from . import constants, logger, config, get_locale, Server
|
|
|
|
from . import constants, logger, config, get_locale, Server
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log = logger.create()
|
|
|
|
# log = logger.create()
|
|
|
|
_REPOSITORY_API_URL = 'https://api.github.com/repos/janeczku/calibre-web'
|
|
|
|
_REPOSITORY_API_URL = 'https://api.github.com/repos/janeczku/calibre-web'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def is_sha1(sha1):
|
|
|
|
def is_sha1(sha1):
|
|
|
|
if len(sha1) != 40:
|
|
|
|
if len(sha1) != 40:
|
|
|
|
return False
|
|
|
|
return False
|
|
|
@ -73,59 +72,59 @@ class Updater(threading.Thread):
|
|
|
|
def run(self):
|
|
|
|
def run(self):
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
self.status = 1
|
|
|
|
self.status = 1
|
|
|
|
log.debug(u'Download update file')
|
|
|
|
logger.debug(u'Download update file')
|
|
|
|
headers = {'Accept': 'application/vnd.github.v3+json'}
|
|
|
|
headers = {'Accept': 'application/vnd.github.v3+json'}
|
|
|
|
r = requests.get(self._get_request_path(), stream=True, headers=headers)
|
|
|
|
r = requests.get(self._get_request_path(), stream=True, headers=headers)
|
|
|
|
r.raise_for_status()
|
|
|
|
r.raise_for_status()
|
|
|
|
|
|
|
|
|
|
|
|
self.status = 2
|
|
|
|
self.status = 2
|
|
|
|
log.debug(u'Opening zipfile')
|
|
|
|
logger.debug(u'Opening zipfile')
|
|
|
|
z = zipfile.ZipFile(BytesIO(r.content))
|
|
|
|
z = zipfile.ZipFile(BytesIO(r.content))
|
|
|
|
self.status = 3
|
|
|
|
self.status = 3
|
|
|
|
log.debug(u'Extracting zipfile')
|
|
|
|
logger.debug(u'Extracting zipfile')
|
|
|
|
tmp_dir = gettempdir()
|
|
|
|
tmp_dir = gettempdir()
|
|
|
|
z.extractall(tmp_dir)
|
|
|
|
z.extractall(tmp_dir)
|
|
|
|
foldername = os.path.join(tmp_dir, z.namelist()[0])[:-1]
|
|
|
|
foldername = os.path.join(tmp_dir, z.namelist()[0])[:-1]
|
|
|
|
if not os.path.isdir(foldername):
|
|
|
|
if not os.path.isdir(foldername):
|
|
|
|
self.status = 11
|
|
|
|
self.status = 11
|
|
|
|
log.info(u'Extracted contents of zipfile not found in temp folder')
|
|
|
|
logger.error(u'Extracted contents of zipfile not found in temp folder')
|
|
|
|
return
|
|
|
|
return
|
|
|
|
self.status = 4
|
|
|
|
self.status = 4
|
|
|
|
log.debug(u'Replacing files')
|
|
|
|
logger.debug(u'Replacing files')
|
|
|
|
self.update_source(foldername, constants.BASE_DIR)
|
|
|
|
self.update_source(foldername, constants.BASE_DIR)
|
|
|
|
self.status = 6
|
|
|
|
self.status = 6
|
|
|
|
log.debug(u'Preparing restart of server')
|
|
|
|
logger.debug(u'Preparing restart of server')
|
|
|
|
time.sleep(2)
|
|
|
|
time.sleep(2)
|
|
|
|
Server.setRestartTyp(True)
|
|
|
|
Server.setRestartTyp(True)
|
|
|
|
Server.stopServer()
|
|
|
|
Server.stopServer()
|
|
|
|
self.status = 7
|
|
|
|
self.status = 7
|
|
|
|
time.sleep(2)
|
|
|
|
time.sleep(2)
|
|
|
|
except requests.exceptions.HTTPError as ex:
|
|
|
|
except requests.exceptions.HTTPError as ex:
|
|
|
|
log.info(u'HTTP Error %s', ex)
|
|
|
|
logger.info(u'HTTP Error %s', ex)
|
|
|
|
self.status = 8
|
|
|
|
self.status = 8
|
|
|
|
except requests.exceptions.ConnectionError:
|
|
|
|
except requests.exceptions.ConnectionError:
|
|
|
|
log.info(u'Connection error')
|
|
|
|
logger.info(u'Connection error')
|
|
|
|
self.status = 9
|
|
|
|
self.status = 9
|
|
|
|
except requests.exceptions.Timeout:
|
|
|
|
except requests.exceptions.Timeout:
|
|
|
|
log.info(u'Timeout while establishing connection')
|
|
|
|
logger.info(u'Timeout while establishing connection')
|
|
|
|
self.status = 10
|
|
|
|
self.status = 10
|
|
|
|
except requests.exceptions.RequestException:
|
|
|
|
except requests.exceptions.RequestException:
|
|
|
|
self.status = 11
|
|
|
|
self.status = 11
|
|
|
|
log.info(u'General error')
|
|
|
|
logger.info(u'General error')
|
|
|
|
|
|
|
|
|
|
|
|
def get_update_status(self):
|
|
|
|
def get_update_status(self):
|
|
|
|
return self.status
|
|
|
|
return self.status
|
|
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@classmethod
|
|
|
|
def file_to_list(self, filelist):
|
|
|
|
def file_to_list(cls, filelist):
|
|
|
|
return [x.strip() for x in open(filelist, 'r') if not x.startswith('#EXT')]
|
|
|
|
return [x.strip() for x in open(filelist, 'r') if not x.startswith('#EXT')]
|
|
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@classmethod
|
|
|
|
def one_minus_two(self, one, two):
|
|
|
|
def one_minus_two(cls, one, two):
|
|
|
|
return [x for x in one if x not in set(two)]
|
|
|
|
return [x for x in one if x not in set(two)]
|
|
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@classmethod
|
|
|
|
def reduce_dirs(self, delete_files, new_list):
|
|
|
|
def reduce_dirs(cls, delete_files, new_list):
|
|
|
|
new_delete = []
|
|
|
|
new_delete = []
|
|
|
|
for filename in delete_files:
|
|
|
|
for filename in delete_files:
|
|
|
|
parts = filename.split(os.sep)
|
|
|
|
parts = filename.split(os.sep)
|
|
|
@ -146,7 +145,7 @@ class Updater(threading.Thread):
|
|
|
|
return list(set(new_delete))
|
|
|
|
return list(set(new_delete))
|
|
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@classmethod
|
|
|
|
def reduce_files(self, remove_items, exclude_items):
|
|
|
|
def reduce_files(cls, remove_items, exclude_items):
|
|
|
|
rf = []
|
|
|
|
rf = []
|
|
|
|
for item in remove_items:
|
|
|
|
for item in remove_items:
|
|
|
|
if not item.startswith(exclude_items):
|
|
|
|
if not item.startswith(exclude_items):
|
|
|
@ -154,44 +153,41 @@ class Updater(threading.Thread):
|
|
|
|
return rf
|
|
|
|
return rf
|
|
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@classmethod
|
|
|
|
def moveallfiles(self, root_src_dir, root_dst_dir):
|
|
|
|
def moveallfiles(cls, root_src_dir, root_dst_dir):
|
|
|
|
change_permissions = True
|
|
|
|
change_permissions = True
|
|
|
|
|
|
|
|
new_permissions = os.stat(root_dst_dir)
|
|
|
|
if sys.platform == "win32" or sys.platform == "darwin":
|
|
|
|
if sys.platform == "win32" or sys.platform == "darwin":
|
|
|
|
change_permissions = False
|
|
|
|
change_permissions = False
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
log.debug('Update on OS-System : %s', sys.platform)
|
|
|
|
logger.debug('Update on OS-System : %s', sys.platform)
|
|
|
|
new_permissions = os.stat(root_dst_dir)
|
|
|
|
|
|
|
|
# print new_permissions
|
|
|
|
|
|
|
|
for src_dir, __, files in os.walk(root_src_dir):
|
|
|
|
for src_dir, __, files in os.walk(root_src_dir):
|
|
|
|
dst_dir = src_dir.replace(root_src_dir, root_dst_dir, 1)
|
|
|
|
dst_dir = src_dir.replace(root_src_dir, root_dst_dir, 1)
|
|
|
|
if not os.path.exists(dst_dir):
|
|
|
|
if not os.path.exists(dst_dir):
|
|
|
|
os.makedirs(dst_dir)
|
|
|
|
os.makedirs(dst_dir)
|
|
|
|
log.debug('Create-Dir: %s', dst_dir)
|
|
|
|
logger.debug('Create-Dir: %s', dst_dir)
|
|
|
|
if change_permissions:
|
|
|
|
if change_permissions:
|
|
|
|
# print('Permissions: User '+str(new_permissions.st_uid)+' Group '+str(new_permissions.st_uid))
|
|
|
|
# print('Permissions: User '+str(new_permissions.st_uid)+' Group '+str(new_permissions.st_uid))
|
|
|
|
os.chown(dst_dir, new_permissions.st_uid, new_permissions.st_gid)
|
|
|
|
os.chown(dst_dir, new_permissions.st_uid, new_permissions.st_gid)
|
|
|
|
for file_ in files:
|
|
|
|
for file_ in files:
|
|
|
|
src_file = os.path.join(src_dir, file_)
|
|
|
|
src_file = os.path.join(src_dir, file_)
|
|
|
|
dst_file = os.path.join(dst_dir, file_)
|
|
|
|
dst_file = os.path.join(dst_dir, file_)
|
|
|
|
|
|
|
|
permission = os.stat(dst_file)
|
|
|
|
if os.path.exists(dst_file):
|
|
|
|
if os.path.exists(dst_file):
|
|
|
|
if change_permissions:
|
|
|
|
logger.debug('Remove file before copy: %s', dst_file)
|
|
|
|
permission = os.stat(dst_file)
|
|
|
|
|
|
|
|
log.debug('Remove file before copy: %s', dst_file)
|
|
|
|
|
|
|
|
os.remove(dst_file)
|
|
|
|
os.remove(dst_file)
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
if change_permissions:
|
|
|
|
if change_permissions:
|
|
|
|
permission = new_permissions
|
|
|
|
permission = new_permissions
|
|
|
|
shutil.move(src_file, dst_dir)
|
|
|
|
shutil.move(src_file, dst_dir)
|
|
|
|
log.debug('Move File %s to %s', src_file, dst_dir)
|
|
|
|
logger.debug('Move File %s to %s', src_file, dst_dir)
|
|
|
|
if change_permissions:
|
|
|
|
if change_permissions:
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
os.chown(dst_file, permission.st_uid, permission.st_gid)
|
|
|
|
os.chown(dst_file, permission.st_uid, permission.st_gid)
|
|
|
|
except (Exception) as e:
|
|
|
|
except OSError as e:
|
|
|
|
# ex = sys.exc_info()
|
|
|
|
|
|
|
|
old_permissions = os.stat(dst_file)
|
|
|
|
old_permissions = os.stat(dst_file)
|
|
|
|
log.debug('Fail change permissions of %s. Before: %s:%s After %s:%s error: %s',
|
|
|
|
logger.debug('Fail change permissions of %s. Before: %s:%s After %s:%s error: %s',
|
|
|
|
dst_file, old_permissions.st_uid, old_permissions.st_gid,
|
|
|
|
dst_file, old_permissions.st_uid, old_permissions.st_gid,
|
|
|
|
permission.st_uid, permission.st_gid, e)
|
|
|
|
permission.st_uid, permission.st_gid, e)
|
|
|
|
return
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
def update_source(self, source, destination):
|
|
|
|
def update_source(self, source, destination):
|
|
|
@ -199,7 +195,7 @@ class Updater(threading.Thread):
|
|
|
|
old_list = list()
|
|
|
|
old_list = list()
|
|
|
|
exclude = (
|
|
|
|
exclude = (
|
|
|
|
os.sep + 'app.db', os.sep + 'calibre-web.log1', os.sep + 'calibre-web.log2', os.sep + 'gdrive.db',
|
|
|
|
os.sep + 'app.db', os.sep + 'calibre-web.log1', os.sep + 'calibre-web.log2', os.sep + 'gdrive.db',
|
|
|
|
os.sep + 'vendor', os.sep + 'calibre-web.log', os.sep + '.git', os.sep +'client_secrets.json',
|
|
|
|
os.sep + 'vendor', os.sep + 'calibre-web.log', os.sep + '.git', os.sep + 'client_secrets.json',
|
|
|
|
os.sep + 'gdrive_credentials', os.sep + 'settings.yaml')
|
|
|
|
os.sep + 'gdrive_credentials', os.sep + 'settings.yaml')
|
|
|
|
for root, dirs, files in os.walk(destination, topdown=True):
|
|
|
|
for root, dirs, files in os.walk(destination, topdown=True):
|
|
|
|
for name in files:
|
|
|
|
for name in files:
|
|
|
@ -225,37 +221,32 @@ class Updater(threading.Thread):
|
|
|
|
for item in remove_items:
|
|
|
|
for item in remove_items:
|
|
|
|
item_path = os.path.join(destination, item[1:])
|
|
|
|
item_path = os.path.join(destination, item[1:])
|
|
|
|
if os.path.isdir(item_path):
|
|
|
|
if os.path.isdir(item_path):
|
|
|
|
log.debug("Delete dir %s", item_path)
|
|
|
|
logger.debug("Delete dir %s", item_path)
|
|
|
|
shutil.rmtree(item_path, ignore_errors=True)
|
|
|
|
shutil.rmtree(item_path, ignore_errors=True)
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
log.debug("Delete file %s", item_path)
|
|
|
|
logger.debug("Delete file %s", item_path)
|
|
|
|
# log_from_thread("Delete file " + item_path)
|
|
|
|
# log_from_thread("Delete file " + item_path)
|
|
|
|
os.remove(item_path)
|
|
|
|
os.remove(item_path)
|
|
|
|
except Exception:
|
|
|
|
except OSError:
|
|
|
|
log.debug("Could not remove: %s", item_path)
|
|
|
|
logger.debug("Could not remove: %s", item_path)
|
|
|
|
shutil.rmtree(source, ignore_errors=True)
|
|
|
|
shutil.rmtree(source, ignore_errors=True)
|
|
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@classmethod
|
|
|
|
def _nightly_version_info(self):
|
|
|
|
def _nightly_version_info(cls):
|
|
|
|
content = {}
|
|
|
|
if is_sha1(constants.NIGHTLY_VERSION[0]) and len(constants.NIGHTLY_VERSION[1]) > 0:
|
|
|
|
content[0] = '$Format:%H$'
|
|
|
|
return {'version': constants.NIGHTLY_VERSION[0], 'datetime': constants.NIGHTLY_VERSION[1]}
|
|
|
|
content[1] = '$Format:%cI$'
|
|
|
|
|
|
|
|
# content[0] = 'bb7d2c6273ae4560e83950d36d64533343623a57'
|
|
|
|
|
|
|
|
# content[1] = '2018-09-09T10:13:08+02:00'
|
|
|
|
|
|
|
|
if is_sha1(content[0]) and len(content[1]) > 0:
|
|
|
|
|
|
|
|
return {'version': content[0], 'datetime': content[1]}
|
|
|
|
|
|
|
|
return False
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@classmethod
|
|
|
|
def _stable_version_info(self):
|
|
|
|
def _stable_version_info(cls):
|
|
|
|
return constants.STABLE_VERSION # Current version
|
|
|
|
return constants.STABLE_VERSION # Current version
|
|
|
|
|
|
|
|
|
|
|
|
def _nightly_available_updates(self, request_method):
|
|
|
|
def _nightly_available_updates(self, request_method):
|
|
|
|
tz = datetime.timedelta(seconds=time.timezone if (time.localtime().tm_isdst == 0) else time.altzone)
|
|
|
|
tz = datetime.timedelta(seconds=time.timezone if (time.localtime().tm_isdst == 0) else time.altzone)
|
|
|
|
if request_method == "GET":
|
|
|
|
if request_method == "GET":
|
|
|
|
repository_url = _REPOSITORY_API_URL
|
|
|
|
repository_url = _REPOSITORY_API_URL
|
|
|
|
status, commit = self._load_remote_data(repository_url +'/git/refs/heads/master')
|
|
|
|
status, commit = self._load_remote_data(repository_url + '/git/refs/heads/master')
|
|
|
|
parents = []
|
|
|
|
parents = []
|
|
|
|
if status['message'] != '':
|
|
|
|
if status['message'] != '':
|
|
|
|
return json.dumps(status)
|
|
|
|
return json.dumps(status)
|
|
|
@ -334,7 +325,7 @@ class Updater(threading.Thread):
|
|
|
|
parent_commit_date, format='short', locale=get_locale())
|
|
|
|
parent_commit_date, format='short', locale=get_locale())
|
|
|
|
|
|
|
|
|
|
|
|
parents.append([parent_commit_date,
|
|
|
|
parents.append([parent_commit_date,
|
|
|
|
parent_data['message'].replace('\r\n','<p>').replace('\n','<p>')])
|
|
|
|
parent_data['message'].replace('\r\n', '<p>').replace('\n', '<p>')])
|
|
|
|
parent_commit = parent_data['parents'][0]
|
|
|
|
parent_commit = parent_data['parents'][0]
|
|
|
|
remaining_parents_cnt -= 1
|
|
|
|
remaining_parents_cnt -= 1
|
|
|
|
except Exception:
|
|
|
|
except Exception:
|
|
|
@ -398,7 +389,7 @@ class Updater(threading.Thread):
|
|
|
|
if (minor_version_update == current_version[1] and
|
|
|
|
if (minor_version_update == current_version[1] and
|
|
|
|
patch_version_update > current_version[2]) or \
|
|
|
|
patch_version_update > current_version[2]) or \
|
|
|
|
minor_version_update > current_version[1]:
|
|
|
|
minor_version_update > current_version[1]:
|
|
|
|
parents.append([commit[i]['tag_name'],commit[i]['body'].replace('\r\n', '<p>')])
|
|
|
|
parents.append([commit[i]['tag_name'], commit[i]['body'].replace('\r\n', '<p>')])
|
|
|
|
i -= 1
|
|
|
|
i -= 1
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
if major_version_update < current_version[0]:
|
|
|
|
if major_version_update < current_version[0]:
|
|
|
@ -426,7 +417,7 @@ class Updater(threading.Thread):
|
|
|
|
u'update to version: %(version)s', version=commit[i]['tag_name']),
|
|
|
|
u'update to version: %(version)s', version=commit[i]['tag_name']),
|
|
|
|
'history': parents
|
|
|
|
'history': parents
|
|
|
|
})
|
|
|
|
})
|
|
|
|
self.updateFile = commit[i +1]['zipball_url']
|
|
|
|
self.updateFile = commit[i+1]['zipball_url']
|
|
|
|
break
|
|
|
|
break
|
|
|
|
if i == -1:
|
|
|
|
if i == -1:
|
|
|
|
status.update({
|
|
|
|
status.update({
|
|
|
|