def _installModules(self, modules: list) -> dict: root = Path(commons.rootDir(), 'system/moduleInstallTickets') availableModules = self.ConfigManager.modulesConfigurations modulesToBoot = dict() self.MqttManager.broadcast(topic='hermes/leds/systemUpdate', payload={'sticky': True}) for file in modules: moduleName = Path(file).with_suffix('') self._logger.info('[{}] Now taking care of module {}'.format( self.name, moduleName.stem)) res = root / file try: updating = False installFile = json.loads(res.read_text()) moduleName = installFile['name'] path = Path(installFile['author'], moduleName) if not moduleName: self._logger.error( '[{}] Module name to install not found, aborting to avoid casualties!' .format(self.name)) continue directory = Path(commons.rootDir()) / 'modules' / moduleName if moduleName in availableModules: localVersionDirExists = directory.is_dir() localVersionAttributeExists: bool = 'version' in availableModules[ moduleName] localVersionIsLatest: bool = \ localVersionDirExists and \ localVersionAttributeExists and \ float(availableModules[moduleName]['version']) >= float(installFile['version']) if localVersionIsLatest: self._logger.warning( '[{}] Module "{}" is already installed, skipping'. format(self.name, moduleName)) subprocess.run(['sudo', 'rm', res]) continue else: self._logger.warning( '[{}] Module "{}" needs updating'.format( self.name, moduleName)) updating = True if moduleName in self._modules: try: self._modules[moduleName]['instance'].onStop() except Exception as e: self._logger.error( '[{}] Error stopping "{}" for update: {}'.format( self.name, moduleName, e)) gitCloner = GithubCloner(baseUrl=self.GITHUB_API_BASE_URL, path=path, dest=directory) if gitCloner.clone(): self._logger.info( '[{}] Module successfully downloaded'.format( self.name)) try: pipReq = installFile.get('pipRequirements', None) sysReq = installFile.get('systemRequirements', None) scriptReq = installFile.get('script', None) if pipReq: for requirement in pipReq: subprocess.run([ './venv/bin/pip3', 'install', requirement ]) if sysReq: for requirement in sysReq: subprocess.run([ 'sudo', 'apt-get', 'install', '-y', requirement ]) if scriptReq: subprocess.run([ 'sudo', 'chmod', '+x', str(directory / scriptReq) ]) subprocess.run( ['sudo', str(directory / scriptReq)]) node = { 'active': True, 'version': installFile['version'], 'author': installFile['author'], 'conditions': installFile['conditions'] } self.ConfigManager.addModuleToAliceConfig( installFile['name'], node) subprocess.run(['mv', res, directory]) modulesToBoot[moduleName] = {'update': updating} except Exception as e: self._logger.error( '[{}] Failed installing module "{}": {}'.format( self.name, moduleName, e)) res.unlink() else: self._logger.error('[{}] Failed cloning module'.format( self.name)) res.unlink() except Exception as e: self._logger.error( '[{}] Failed installing module "{}": {}'.format( self.name, moduleName, e)) res.unlink() self.MqttManager.broadcast(topic='hermes/leds/clear') return modulesToBoot
def _installSkills(self, skills: list) -> dict: root = Path(self.Commons.rootDir(), constants.SKILL_INSTALL_TICKET_PATH) availableSkills = self.ConfigManager.skillsConfigurations skillsToBoot = dict() self.MqttManager.mqttBroadcast(topic='hermes/leds/systemUpdate', payload={'sticky': True}) for file in skills: skillName = Path(file).with_suffix('') self.logInfo(f'Now taking care of skill {skillName.stem}') res = root / file try: updating = False installFile = json.loads(res.read_text()) skillName = installFile['name'] path = Path(installFile['author'], skillName) if not skillName: self.logError('Skill name to install not found, aborting to avoid casualties!') continue directory = Path(self.Commons.rootDir()) / 'skills' / skillName conditions = { 'aliceMinVersion': installFile['aliceMinVersion'], **installFile.get('conditions', dict()) } self.checkSkillConditions(skillName, conditions, availableSkills) if skillName in availableSkills: installedVersion = Version.fromString(availableSkills[skillName]['version']) remoteVersion = Version.fromString(installFile['version']) localVersionIsLatest: bool = \ directory.is_dir() and \ 'version' in availableSkills[skillName] and \ installedVersion >= remoteVersion if localVersionIsLatest: self.logWarning(f'Skill "{skillName}" is already installed, skipping') self.Commons.runRootSystemCommand(['rm', res]) continue else: self.logWarning(f'Skill "{skillName}" needs updating') updating = True if skillName in self._activeSkills: try: self._activeSkills[skillName].onStop() except Exception as e: self.logError(f'Error stopping "{skillName}" for update: {e}') raise gitCloner = GithubCloner(baseUrl=f'{constants.GITHUB_URL}/skill_{skillName}.git', path=path, dest=directory) try: gitCloner.clone(skillName=skillName) self.logInfo('Skill successfully downloaded') self._installSkill(res) skillsToBoot[skillName] = { 'update': updating } except (GithubTokenFailed, GithubRateLimit): self.logError('Failed cloning skill') raise except GithubNotFound: if self.ConfigManager.getAliceConfigByName('devMode'): if not Path(f'{self.Commons.rootDir}/skills/{skillName}').exists() or not \ Path(f'{self.Commons.rootDir}/skills/{skillName}/{skillName.py}').exists() or not \ Path(f'{self.Commons.rootDir}/skills/{skillName}/dialogTemplate').exists() or not \ Path(f'{self.Commons.rootDir}/skills/{skillName}/talks').exists(): self.logWarning(f'Skill "{skillName}" cannot be installed in dev mode due to missing base files') else: self._installSkill(res) skillsToBoot[skillName] = { 'update': updating } continue else: self.logWarning(f'Skill "{skillName}" is not available on Github, cannot install') raise except SkillNotConditionCompliant as e: self.logInfo(f'Skill "{skillName}" does not comply to "{e.condition}" condition, required "{e.conditionValue}"') if res.exists(): res.unlink() self.broadcast( method=constants.EVENT_SKILL_INSTALL_FAILED, exceptions=self._name, skill=skillName ) except Exception as e: self.logError(f'Failed installing skill "{skillName}": {e}') if res.exists(): res.unlink() self.broadcast( method=constants.EVENT_SKILL_INSTALL_FAILED, exceptions=self.name, skill=skillName ) raise return skillsToBoot