#-*- coding: utf-8 -*- # Copyright 2014 Calculate Ltd. http://www.calculate-linux.org # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import sys from os import path from calculate.lib.utils.tools import AddonError from calculate.lib.utils.colortext.palette import TextState from calculate.lib.utils.colortext import get_color_print from package_tools import Git, Layman, EmergeError, Emerge Colors = TextState.Colors from calculate.lib.utils.files import (getProgPath, STDOUT, removeDir, PercentProgress, process, readFile) from calculate.lib.utils.colortext import convert_console_to_xml from calculate.lib.cl_lang import setLocalTranslate, getLazyLocalTranslate setLocalTranslate('cl_update3', sys.modules[__name__]) __ = getLazyLocalTranslate(_) class UpdateError(AddonError): """Update Error""" class Update: """Основной объект для выполнения действий связанных с обновлением системы """ def _syncRepository(self, name, url, rpath, revision, branch, cb_progress=None): """ Синхронизировать репозитори """ dv = self.clVars git = Git() needMeta = False if not git.checkExistsRep(rpath): if revision == "last": git.cloneRepository(url, rpath, branch, cb_progress=cb_progress) else: git.cloneRevRepository(url, rpath, branch, revision, cb_progress=cb_progress) needMeta = True else: # если нужно обновиться до конкретной ревизии if revision != "last": if revision == git.getCurrentCommit(rpath): if git.getBranch(rpath) == branch: return True # получить изменения из удаленного репозитория git.fetchRepository(rpath, cb_progress=cb_progress) # если текущая ветка не соответствует нужной repInfo = git.getStatusInfo(rpath) if repInfo['branch'] != branch: # меняем ветку needMeta = True git.checkoutBranch(rpath, branch) if revision == "last": if git.resetRepository(rpath, to_origin=True): needMeta = True else: git.resetRepository(rpath, to_rev=revision) needMeta = True if needMeta: dv.Set('cl_update_outdate_set', 'on', force=True) layman = Layman(dv.Get('cl_update_layman_installed'), dv.Get('cl_update_layman_make')) if name != "portage": layman.add(name, url, rpath) return True def syncRepositories(self, repname, clean_on_error=True): """ Синхронизировать репозитории """ dv = self.clVars url, rpath, revision, branch = ( dv.Select(["cl_update_rep_url", "cl_update_rep_path", "cl_update_rep_rev", "cl_update_branch_name"], where="cl_update_rep_name", eq=repname, limit=1)) if not url or not rpath: raise UpdateError(_("Repositories variables is not configured")) self.addProgress() if clean_on_error: try: self._syncRepository(repname, url, rpath, revision, branch, cb_progress=self.setProgress) return True except GitError as e: if e.addon: self.printWARNING(str(e.addon)) self.printWARNING(str(e)) self.printWARNING(_("Re-fetch {name} repository" ).format(name=repname)) try: removeDir(rpath) except OSError as e: raise UpdateError(_("Permission denied to change " "{repname} repository").format( repname=repname)) self._syncRepository(repname, url, rpath, revision, branch) return True def syncLaymanRepository(self, repname): """ Обновить репозиторий через layman """ layman = getProgPath('/usr/bin/layman') if not layman: raise UpdateError(_("Layman utility is not found")) rpath = self.clVars.Select('cl_update_other_rep_path', where='cl_update_other_rep_name', eq=repname, limit=1) laymanname = path.basename(rpath) if Git.is_git(rpath): self.addProgress() p = PercentProgress(layman, "-s", laymanname, part=1, atty=True) for perc in p.progress(): self.setProgress(perc) else: p = process(layman, "-s", repname, stderr=STDOUT) if p.failed(): raise UpdateError(_("Failed to update repository {rname}" ).format(rname=repname), addon=p.read()) return True def regenCache(self, repname): """ Обновить кэш метаданных репозитория """ egenCache = getProgPath('/usr/bin/egencache') if not egenCache: raise UpdateError(_("Portage utility is not found")) cpu_num = self.clVars.Get('hr_cpu_num') p = process(egenCache, "--repo=%s" % repname, "--update", "--jobs=%s" % cpu_num, stderr=STDOUT) if p.failed(): raise UpdateError(_("Failed to update cache of {rname} " "repository").format(rname=repname), addon=p.read()) return True def emergeMetadata(self): """ Выполнить egencache и emerge --metadata """ emerge = getProgPath("/usr/bin/emerge") if not emerge: raise UpdateError(_("Emerge utility is not found")) self.addProgress() p = PercentProgress(emerge, "--metadata", part=1, atty=True) for perc in p.progress(): self.setProgress(perc) if p.failed(): raise UpdateError(_("Failed to update metadata"), addon=p.read()) return True def eixUpdate(self): """ Выполенине eix-update для репозиторием eix-update выполнятется только для тех репозиториев, которые обновлялись, если cl_update_eixsync_force==auto, либо все, если cl_update_eixupdate_force==force """ eixupdate = getProgPath("/usr/bin/eix-update") if not eixupdate: raise UpdateError(_("Eix utility is not found")) self.addProgress() excludeList = [] if self.clVars.Get('cl_update_eixupdate_force') == 'force': countRep = len(self.clVars.Get('cl_update_rep_name')) else: for rep in self.clVars.Get('cl_update_rep_name'): # подстановка имен mapNames = {'portage': 'gentoo'} if not rep in self.clVars.Get('cl_update_sync_rep'): excludeList.extend(["-x", mapNames.get(rep, rep)]) countRep = len(self.clVars.Get('cl_update_sync_rep')) if (self.clVars.Get('cl_update_other_set') == 'on' or self.clVars.Get('cl_update_eixupdate_force') == 'force'): countRep += len(self.clVars.Get('update.cl_update_other_rep_name')) else: for rep in self.clVars.Get('update.cl_update_other_rep_name'): excludeList.extend(['-x', rep]) p = PercentProgress(eixupdate, "-F", *excludeList, part=countRep or 1, atty=True) for perc in p.progress(): self.setProgress(perc) if p.failed(): raise UpdateError(_("Failed to update eix cache"), addon=p.read()) return True def _printEmergePackage(self, pkg, binary=False, num=1, max_num=1): self.endTask() _print = get_color_print() if max_num > 1: one = _print.foreground(Colors.YELLOW).bold("{0}", num) two = _print.foreground(Colors.YELLOW).bold("{0}", max_num) part = " (%s of %s)" % (one, two) else: part = "" if binary: _print = _print.foreground(Colors.PURPLE) else: _print = _print.foreground(Colors.GREEN) self.startTask("Emerging%s %s" % (part, _print(str(pkg)))) def _printInstallPackage(self, pkg): self.endTask() _print = get_color_print() self.startTask(_("Installing %s")% _print.foreground(Colors.YELLOW).bold(str(pkg))) def emerge(self, param, *packages): """ Выполнить сборку пакета """ with Emerge(list(packages), extra_params=[param]) as emerge: try: update_list = emerge.get_update_list() if not update_list: self.printSUCCESS(_("Nothing to merge")) return True self.printPre(update_list) except EmergeError as e: if e.errno == EmergeError.CONFLICT: self.printPre(emerge.update_block) self.printPre(emerge.conflict_block) elif e.errno == EmergeError.CUSTOM: self.printPre(emerge.custom_error) raise if (self.askConfirm( _("Would you like to merge these packages?")) == 'no'): return False emerge.handle_emerging(self._printEmergePackage) emerge.handle_installing(self._printInstallPackage) try: return emerge.install() except EmergeError: self.printPre( convert_console_to_xml(readFile(emerge.get_error_log()))) raise