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
Beispiel #2
0
	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