diff --git a/locales/en.json b/locales/en.json index fd7f1ac0d..392e0424a 100644 --- a/locales/en.json +++ b/locales/en.json @@ -28,7 +28,6 @@ "app_location_unavailable": "This URL is either unavailable, or conflicts with the already installed app(s):\n{apps:s}", "app_manifest_invalid": "Something is wrong with the app manifest: {error}", "app_not_upgraded": "The app '{failed_app}' failed to upgrade, and as a consequence the following apps upgrades have been cancelled: {apps}", - "app_upgrade_stopped": "Upgrading all apps was stopped to prevent possible damage because one app could not be upgraded", "app_not_correctly_installed": "{app:s} seems to be incorrectly installed", "app_not_installed": "Could not find the app '{app:s}' in the list of installed apps: {all_apps}", "app_not_properly_removed": "{app:s} has not been properly removed", @@ -47,7 +46,8 @@ "app_unsupported_remote_type": "Unsupported remote type used for the app", "app_upgrade_several_apps": "The following apps will be upgraded: {apps}", "app_upgrade_app_name": "Now upgrading {app}…", - "app_upgrade_failed": "Could not upgrade {app:s}", + "app_upgrade_failed": "Could not upgrade {app:s}: {error}", + "app_upgrade_script_failed": "An error occurred inside the app upgrade script", "app_upgrade_some_app_failed": "Some apps could not be upgraded", "app_upgraded": "{app:s} upgraded", "apps_already_up_to_date": "All apps are already up-to-date", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 3a722d39a..9817487ca 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -557,82 +557,86 @@ def app_upgrade(app=[], url=None, file=None): # Execute App upgrade script os.system('chown -hR admin: %s' % INSTALL_TMP) + # Execute the app upgrade script + upgrade_failed = True try: upgrade_retcode = hook_exec(extracted_app_folder + '/scripts/upgrade', args=args_list, env=env_dict)[0] + + upgrade_failed = True if upgrade_retcode != 0 else False + if upgrade_failed: + error = m18n.n('app_upgrade_script_failed') + logger.exception(m18n.n("app_upgrade_failed", app=app_instance_name, error=error)) + failure_message_with_debug_instructions = operation_logger.error(error) + # Script got manually interrupted ... N.B. : KeyboardInterrupt does not inherit from Exception except (KeyboardInterrupt, EOFError): upgrade_retcode = -1 + error = m18n.n('operation_interrupted') + logger.exception(m18n.n("app_upgrade_failed", app=app_instance_name, error=error)) + failure_message_with_debug_instructions = operation_logger.error(error) + # Something wrong happened in Yunohost's code (most probably hook_exec) except Exception: import traceback - logger.exception(m18n.n('unexpected_error', error=u"\n" + traceback.format_exc())) + error = m18n.n('unexpected_error', error=u"\n" + traceback.format_exc()) + logger.exception(m18n.n("app_install_failed", app=app_instance_name, error=error)) + failure_message_with_debug_instructions = operation_logger.error(error) finally: - - # Did the script succeed ? - if upgrade_retcode == -1: - error_msg = m18n.n('operation_interrupted') - operation_logger.error(error_msg) - elif upgrade_retcode != 0: - error_msg = m18n.n('app_upgrade_failed', app=app_instance_name) - operation_logger.error(error_msg) - - # Did it broke the system ? + # Whatever happened (install success or failure) we check if it broke the system + # and warn the user about it try: broke_the_system = False _assert_system_is_sane_for_app(manifest, "post") except Exception as e: broke_the_system = True - error_msg = operation_logger.error(str(e)) + logger.exception(m18n.n("app_upgrade_failed", app=app_instance_name, error=str(e))) + failure_message_with_debug_instructions = operation_logger.error(str(e)) # If upgrade failed or broke the system, # raise an error and interrupt all other pending upgrades - if upgrade_retcode != 0 or broke_the_system: + if upgrade_failed or broke_the_system: # display this if there are remaining apps if apps[number + 1:]: - logger.error(m18n.n('app_upgrade_stopped')) not_upgraded_apps = apps[number:] - # we don't want to continue upgrading apps here in case that breaks - # everything - raise YunohostError('app_not_upgraded', + logger.error(m18n.n('app_not_upgraded', failed_app=app_instance_name, - apps=', '.join(not_upgraded_apps)) - else: - raise YunohostError(error_msg, raw_msg=True) + apps=', '.join(not_upgraded_apps))) + + raise YunohostError(failure_message_with_debug_instructions, raw_msg=True) # Otherwise we're good and keep going ! - else: - now = int(time.time()) - # TODO: Move install_time away from app_setting - app_setting(app_instance_name, 'update_time', now) - status['upgraded_at'] = now + now = int(time.time()) + # TODO: Move install_time away from app_setting + app_setting(app_instance_name, 'update_time', now) + status['upgraded_at'] = now - # Clean hooks and add new ones - hook_remove(app_instance_name) - if 'hooks' in os.listdir(extracted_app_folder): - for hook in os.listdir(extracted_app_folder + '/hooks'): - hook_add(app_instance_name, extracted_app_folder + '/hooks/' + hook) + # Clean hooks and add new ones + hook_remove(app_instance_name) + if 'hooks' in os.listdir(extracted_app_folder): + for hook in os.listdir(extracted_app_folder + '/hooks'): + hook_add(app_instance_name, extracted_app_folder + '/hooks/' + hook) - # Store app status - with open(app_setting_path + '/status.json', 'w+') as f: - json.dump(status, f) + # Store app status + with open(app_setting_path + '/status.json', 'w+') as f: + json.dump(status, f) - # Replace scripts and manifest and conf (if exists) - os.system('rm -rf "%s/scripts" "%s/manifest.toml %s/manifest.json %s/conf"' % (app_setting_path, app_setting_path, app_setting_path, app_setting_path)) + # Replace scripts and manifest and conf (if exists) + os.system('rm -rf "%s/scripts" "%s/manifest.toml %s/manifest.json %s/conf"' % (app_setting_path, app_setting_path, app_setting_path, app_setting_path)) - if os.path.exists(os.path.join(extracted_app_folder, "manifest.json")): - os.system('mv "%s/manifest.json" "%s/scripts" %s' % (extracted_app_folder, extracted_app_folder, app_setting_path)) - if os.path.exists(os.path.join(extracted_app_folder, "manifest.toml")): - os.system('mv "%s/manifest.toml" "%s/scripts" %s' % (extracted_app_folder, extracted_app_folder, app_setting_path)) + if os.path.exists(os.path.join(extracted_app_folder, "manifest.json")): + os.system('mv "%s/manifest.json" "%s/scripts" %s' % (extracted_app_folder, extracted_app_folder, app_setting_path)) + if os.path.exists(os.path.join(extracted_app_folder, "manifest.toml")): + os.system('mv "%s/manifest.toml" "%s/scripts" %s' % (extracted_app_folder, extracted_app_folder, app_setting_path)) - for file_to_copy in ["actions.json", "actions.toml", "config_panel.json", "config_panel.toml", "conf"]: - if os.path.exists(os.path.join(extracted_app_folder, file_to_copy)): - os.system('cp -R %s/%s %s' % (extracted_app_folder, file_to_copy, app_setting_path)) + for file_to_copy in ["actions.json", "actions.toml", "config_panel.json", "config_panel.toml", "conf"]: + if os.path.exists(os.path.join(extracted_app_folder, file_to_copy)): + os.system('cp -R %s/%s %s' % (extracted_app_folder, file_to_copy, app_setting_path)) - # So much win - logger.success(m18n.n('app_upgraded', app=app_instance_name)) + # So much win + logger.success(m18n.n('app_upgraded', app=app_instance_name)) - hook_callback('post_app_upgrade', args=args_list, env=env_dict) - operation_logger.success() + hook_callback('post_app_upgrade', args=args_list, env=env_dict) + operation_logger.success() permission_sync_to_user()