nzbToMedia/nzbtomedia/versionCheck.py

243 lines
No EOL
9.1 KiB
Python

import os
import shutil
import urllib
import tarfile
import stat
import traceback
import gh_api as github
import nzbtomedia
from nzbtomedia import logger
class CheckVersion():
"""
Version check class meant to run as a thread object with the SB scheduler.
"""
def __init__(self):
self.updater = SourceUpdateManager()
def run(self):
self.check_for_new_version()
def check_for_new_version(self, force=False):
"""
Checks the internet for a newer version.
returns: bool, True for new version or False for no new version.
force: if true the VERSION_NOTIFY setting will be ignored and a check will be forced
"""
logger.info("Checking if nzbToMedia needs an update")
if not self.updater.need_update():
NEWEST_VERSION_STRING = None
logger.info("No update needed")
if force:
logger.info("No update needed")
return False
if not self.updater._cur_commit_hash:
logger.info("Unknown current version number, don't know if we should update or not")
elif self.updater._num_commits_behind > 0:
logger.info("There is a newer version available, (you're " + str(self.updater._num_commits_behind) + " commit(s) behind")
return True
def update(self):
if self.updater.need_update():
return self.updater.update()
class UpdateManager():
def get_github_repo_user(self):
return 'clinton-hall'
def get_github_repo(self):
return 'nzbToMedia'
# def get_update_url(self):
# return nzbtomedia.WEB_ROOT + "/home/update/?pid=" + str(nzbtomedia.PID)
class SourceUpdateManager(UpdateManager):
def __init__(self):
self.github_repo_user = self.get_github_repo_user()
self.github_repo = self.get_github_repo()
self.branch = 'master'
self._cur_commit_hash = None
self._newest_commit_hash = None
self._num_commits_behind = 0
def _find_installed_version(self):
version_file = os.path.join(nzbtomedia.PROGRAM_DIR, u'version.txt')
if not os.path.isfile(version_file):
self._cur_commit_hash = None
return
try:
with open(version_file, 'r') as fp:
self._cur_commit_hash = fp.read().strip(' \n\r')
except EnvironmentError, e:
logger.debug("Unable to open 'version.txt': " + str(e))
if not self._cur_commit_hash:
self._cur_commit_hash = None
def need_update(self):
self._find_installed_version()
try:
self._check_github_for_update()
except Exception, e:
logger.error("Unable to contact github, can't check for update: " + repr(e))
return False
if not self._cur_commit_hash or self._num_commits_behind > 0:
return True
return False
def _check_github_for_update(self):
"""
Uses pygithub to ask github if there is a newer version that the provided
commit hash. If there is a newer version it sets Sick Beard's version text.
commit_hash: hash that we're checking against
"""
self._num_commits_behind = 0
self._newest_commit_hash = None
gh = github.GitHub(self.github_repo_user, self.github_repo, self.branch)
# try to get newest commit hash and commits behind directly by comparing branch and current commit
if self._cur_commit_hash:
branch_compared = gh.compare(base=self.branch, head=self._cur_commit_hash)
if 'base_commit' in branch_compared:
self._newest_commit_hash = branch_compared['base_commit']['sha']
if 'behind_by' in branch_compared:
self._num_commits_behind = int(branch_compared['behind_by'])
# fall back and iterate over last 100 (items per page in gh_api) commits
if not self._newest_commit_hash:
for curCommit in gh.commits():
if not self._newest_commit_hash:
self._newest_commit_hash = curCommit['sha']
if not self._cur_commit_hash:
break
if curCommit['sha'] == self._cur_commit_hash:
break
# when _cur_commit_hash doesn't match anything _num_commits_behind == 100
self._num_commits_behind += 1
logger.debug("cur_commit = " + str(self._cur_commit_hash) + ", newest_commit = " + str(self._newest_commit_hash)
+ ", num_commits_behind = " + str(self._num_commits_behind))
def set_newest_text(self):
# if we're up to date then don't set this
nzbtomedia.NEWEST_VERSION_STRING = None
if not self._cur_commit_hash:
logger.info("Unknown current version number, don't know if we should update or not")
elif self._num_commits_behind > 0:
logger.info("There is a newer version available, (you're " + str(self._num_commits_behind) + " commit(s) behind")
def update(self):
"""
Downloads the latest source tarball from github and installs it over the existing version.
"""
base_url = 'https://github.com/' + self.github_repo_user + '/' + self.github_repo
tar_download_url = base_url + '/tarball/' + self.branch
version_path = os.path.join(nzbtomedia.PROGRAM_DIR, u'version.txt')
try:
# prepare the update dir
sb_update_dir = os.path.join(nzbtomedia.PROGRAM_DIR, u'sb-update')
if os.path.isdir(sb_update_dir):
logger.info("Clearing out update folder " + sb_update_dir + " before extracting")
shutil.rmtree(sb_update_dir)
logger.info("Creating update folder " + sb_update_dir + " before extracting")
os.makedirs(sb_update_dir)
# retrieve file
logger.info("Downloading update from " + repr(tar_download_url))
tar_download_path = os.path.join(sb_update_dir, u'sb-update.tar')
urllib.urlretrieve(tar_download_url, tar_download_path)
if not str(os.path.isfile, tar_download_path):
logger.error("Unable to retrieve new version from " + tar_download_url + ", can't update")
return False
if not str(tarfile.is_tarfile, tar_download_path):
logger.error("Retrieved version from " + tar_download_url + " is corrupt, can't update")
return False
# extract to sb-update dir
logger.info("Extracting file " + tar_download_path)
tar = tarfile.open(tar_download_path)
tar.extractall(sb_update_dir)
tar.close()
# delete .tar.gz
logger.info("Deleting file " + tar_download_path)
os.remove(tar_download_path)
# find update dir name
update_dir_contents = [x for x in os.listdir(sb_update_dir) if
os.path.isdir(os.path.join(sb_update_dir, x))]
if len(update_dir_contents) != 1:
logger.error("Invalid update data, update failed: " + str(update_dir_contents))
return False
content_dir = os.path.join(sb_update_dir, update_dir_contents[0])
# walk temp folder and move files to main folder
logger.info("Moving files from " + content_dir + " to " + nzbtomedia.PROGRAM_DIR)
for dirname, dirnames, filenames in os.walk(content_dir): # @UnusedVariable
dirname = dirname[len(content_dir) + 1:]
for curfile in filenames:
old_path = os.path.join(content_dir, dirname, curfile)
new_path = os.path.join(nzbtomedia.PROGRAM_DIR, dirname, curfile)
#Avoid DLL access problem on WIN32/64
#These files needing to be updated manually
#or find a way to kill the access from memory
if curfile in ('unrar.dll', 'unrar64.dll'):
try:
os.chmod(new_path, stat.S_IWRITE)
os.remove(new_path)
os.renames(old_path, new_path)
except Exception, e:
logger.debug("Unable to update " + new_path + ': ' + str(e))
os.remove(old_path) # Trash the updated file without moving in new path
continue
if os.path.isfile(new_path):
os.remove(new_path)
os.renames(old_path, new_path)
# update version.txt with commit hash
try:
with open(version_path, 'w') as ver_file:
ver_file.write(self._newest_commit_hash)
except EnvironmentError, e:
logger.error("Unable to write version file, update not complete: " + str(e))
return False
except Exception, e:
logger.error("Error while trying to update: " + str(e))
logger.debug("Traceback: " + traceback.format_exc())
return False
return True