Browse Source

Завершен полный цикл обновления репозиториев

tags/3.2.0_alpha1
Mike khiretskiy 7 years ago
parent
commit
47f2f6bd89
4 changed files with 295 additions and 67 deletions
  1. +126
    -61
      update/update.py
  2. +44
    -2
      update/utils/cl_update.py
  3. +123
    -4
      update/variables/update.py
  4. +2
    -0
      update/wsdl_update.py

+ 126
- 61
update/update.py View File

@@ -19,74 +19,29 @@ import re
import sys
import time
from os import path
from subprocess import Popen
from calculate.lib.utils.files import (process,getProgPath,STDOUT,removeDir,
processProgress)
processProgress,PercentProgress,process)

class UpdateError(Exception):
"""Update Error"""

class GitError(Exception):
"""Git Error"""
class AddonError(Exception):
"""
Исключение с добавочным сообщением
"""
def __init__(self, msg, addon=None):
self.message = msg
self.addon = addon
Exception.__init__(self,msg)

class UpdateError(AddonError):
"""Update Error"""

class GitError(AddonError):
"""Git Error"""

from calculate.lib.cl_lang import setLocalTranslate,getLazyLocalTranslate
setLocalTranslate('cl_update3',sys.modules[__name__])
__ = getLazyLocalTranslate(_)

class PercentProgress(processProgress):
"""
Объект выдает прогресс, ища в выводе \d+%
Args:
part: количество прогрессов в программе
delimeter: разделители строк по умолчанию \n и \r
cachefilter: фильтр вывода программы (регулярная строка)
startpart: используется для вывода процентов прогрессбар
с определенного места (0 по умолчанию)
end: конечный прогрессбар (по умолчанию)
"""
def init(self,*args,**kwargs):
self.rePerc = re.compile("(\d+)%",re.S)
self.part = kwargs.get("part",1)
self.add_offset = 100 / self.part
self.offset = 0 + kwargs.get("startpart",0)*self.add_offset
self.is_end = kwargs.get("end",True)
self.stderr = STDOUT
self.delimeter = re.compile("[%s]"%kwargs.get("delimeter","\n\r"))
self.cachedata = re.compile(kwargs.get("cachefilter",
"((?:error|warning|fatal):.*)"))

def processInit(self):
self.percent = 0
self.showval = 0
if not self.offset:
return 0

def processEnd(self):
if self.is_end:
self.percent = 100
return 100

def processString(self,strdata):
match = self.rePerc.search(strdata)
resSearch = self.cachedata.search(strdata)
if resSearch:
self.cacheresult.append(resSearch.group(1))
if match:
percent = int(match.group(1))
if percent < self.percent:
self.offset = self.offset + self.add_offset
percent = percent / self.part
if percent != self.percent:
self.percent = percent
showval = min(99,self.percent + self.offset)
if showval != self.showval:
self.showval = showval
return self.showval

class Git:
"""
Объект для управление git репозиторием
@@ -163,7 +118,10 @@ class Git:
Return:
Возвращает True если клонирование произведено с установкой на
указанную ревизию. False если клонирование произведено с
установкой на последнюю ревизию. В остальных случаях GitError
установкой на последнюю ревизию.
Raises:
GitError: Выполнение ключевых команд выполнено с ошибками (не
удалось скачать и получить данные из удаленного репозитория)
"""
git_dir = self.__gitDir(rpath)
error = []
@@ -357,10 +315,19 @@ class Git:
to_origin: откатить все изменения до удаленного репозитория
to_rev: откатить все изменения до определенной ревизии
info: использовать уже полученную информация об изменения в репозитории
Return:
True - успешное выполнение
False - нет необходимости выполнять reset
Raises:
GitError: выполнение комманд reset и clean прошло с ошибкой
"""
git_dir = self.__gitDir(rpath)
if to_origin and not info:
if not info:
info = self._getStatusInfo(rpath)
if (all(not info[x] for x in ("files","ahead","behind") if x in info)
and (not info["origin"] or
"origin/%s"%info["branch"] == info["origin"])):
return False
commit = (info['origin'] if to_origin else to_rev) or "HEAD"
git_reset = process(self._git,"--git-dir",git_dir,"--work-tree",rpath,
"reset","--hard", commit, stderr=STDOUT)
@@ -369,6 +336,7 @@ class Git:
if git_reset.failed() or git_clean.failed():
raise GitError(_("Failed to clean {rpath} repository").format(
rpath=rpath))
return True

def _getBranch(self,rpath):
"""
@@ -406,7 +374,9 @@ class Update:
"""
Синхронизировать репозиторий
"""
dv = self.clVars
git = Git()
needMeta = False
if not git._checkExistsRep(rpath):
if revision == "last":
git._cloneRepository(url, rpath, branch,
@@ -414,6 +384,7 @@ class Update:
else:
git._cloneRevRepository(url, rpath, branch, revision,
cb_progress=cb_progress)
needMeta = True
else:
# если нужно обновиться до конкретной ревизии
if revision != "last":
@@ -426,11 +397,16 @@ class Update:
repInfo = git._getStatusInfo(rpath)
if repInfo['branch'] != branch:
# меняем ветку
needMeta = True
git._checkoutBranch(rpath,branch)
if revision == "last":
git._resetRepository(rpath, to_origin=True)
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)
return True

def syncRepositories(self,repname,clean_on_error=True):
@@ -442,6 +418,8 @@ class Update:
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:
@@ -453,7 +431,94 @@ class Update:
self.printWARNING(str(e.addon))
self.printWARNING(str(e))
self.printWARNING(_("Re-fetch {name} repository"
).format(name=name))
).format(name=repname))
removeDir(rpath)
self._syncRepository(name,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 path.exists(path.join(rpath,'.git')):
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

+ 44
- 2
update/utils/cl_update.py View File

@@ -15,7 +15,7 @@
# limitations under the License.

import sys
from calculate.core.server.func import Action
from calculate.core.server.func import Action, Tasks
from calculate.lib.cl_lang import setLocalTranslate,getLazyLocalTranslate
from calculate.lib.utils.files import FilesError
from calculate.install.install import (MigrationError, TemplatesError,
@@ -41,15 +41,57 @@ class ClUpdateAction(Action):
{'name':'sync_reps',
'foreach':'cl_update_sync_rep',
'message' : __("Syncing {eachvar} repository"),
'method':'Update.syncRepositories(eachvar,cl_update_rep_data)',
'method':'Update.syncRepositories(eachvar)',
'condition':lambda Get:Get('cl_update_sync_rep')
},
{'name':'sync_other_reps',
'foreach':'cl_update_other_rep_name',
'message' : __("Syncing {eachvar} repository"),
'method':'Update.syncLaymanRepository(eachvar)',
'condition':lambda Get:Get('cl_update_other_set') == 'on'
},
{'name':'sync_reps:regen_cache',
'foreach':'cl_update_sync_overlay_rep',
'message' : __("Updating cache {eachvar} repository"),
'essential':False,
'method':'Update.regenCache(eachvar)',
'condition':lambda Get:(Get('cl_update_outdate_set') == 'on' and
Get('cl_update_metadata_force') != 'skip' or
Get('cl_update_metadata_force') == 'force')
},
{'name':'sync_other_reps:regen_other_cache',
'foreach':'cl_update_other_rep_name',
'message' : __("Updating cache {eachvar} repository"),
'method':'Update.regenCache(eachvar)',
'essential':False,
},
{'name':'emerge_metadata',
'message' : __("Metadata trasfering"),
'method':'Update.emergeMetadata()',
'condition':lambda Get:(Get('cl_update_outdate_set') == 'on' and
Get('cl_update_metadata_force') != 'skip' or
Get('cl_update_metadata_force') == 'force')
},
{'name':'eix_update',
'message' : __("Updating eix cache"),
'method':'Update.eixUpdate()',
'condition':lambda Get:(Get('cl_update_outdate_set') == 'on' and
Get('cl_update_eixupdate_force') != 'skip' or
Get('cl_update_eixupdate_force') == 'force')
},
{'name':'dispatch',
'method':'Update.applyTemplates(install.cl_source,cl_template_clt_set,'\
'True,None)',
'condition':lambda Get:(Get('cl_update_rev_set') == 'on' or
Get('cl_rebuild_world_set') == 'on')
},
# сообщение удачного завершения при обновлении репозиториев
{'name':'success_syncrep',
'message' : __("Synchronzation finished!"),
'depend': (Tasks.success() & Tasks.has_any("sync_reps",
"sync_other_reps","emerge_metadata",
"eix_update")),
},
# сообщение удачного завершения при обновлении ревизии
{'name':'success_rev',
'message' : __("Revision update finished!"),


+ 123
- 4
update/variables/update.py View File

@@ -18,8 +18,8 @@ import os
import sys
import re
from os import path
from calculate.lib.datavars import (Variable,VariableError,ReadonlyVariable,
ReadonlyTableVariable,TableVariable)
from calculate.lib.datavars import (Variable, VariableError,
ReadonlyVariable, ReadonlyTableVariable, TableVariable, FieldValue)
from calculate.lib.utils.portage import searchProfile
from calculate.lib.utils.files import readLinesFile, readFile

@@ -66,7 +66,10 @@ class VariableClUpdateRevSet(Variable):
opt = ["--update-rev"]
untrusted = True
value = "off"
check_after = ["cl_update_sync_rep"]
check_after = ["cl_update_sync_rep",
"cl_update_metadata_force",
"cl_update_other_set",
"cl_update_eixupdate_force"]

def init(self):
self.help = _("revision update")
@@ -74,7 +77,10 @@ class VariableClUpdateRevSet(Variable):

def check(self,value):
if ( value == "off" and self.Get('cl_rebuild_world_set') != 'on' and
not self.Get('cl_update_sync_rep')):
not self.Get('cl_update_sync_rep') and
self.Get('cl_update_other_set') == 'off' and
self.Get('cl_update_metadata_force') != "force" and
self.Get('cl_update_eixupdate_force') != "force"):
raise VariableError(_("Select at least one update action"))

class VariableClUpdateRep(Variable):
@@ -247,3 +253,116 @@ class VariableClUpdateSyncRep(Variable):

def choice(self):
return self.Get('cl_update_rep_name')

class VariableClUpdateSyncOverlayRep(ReadonlyVariable):
"""
Обновляемые репозитории (исключая portage)
"""
type = "list"

def get(self):
return filter(lambda x:x!="portage",self.Get('cl_update_sync_rep'))

class VariableClUpdateOutdateSet(ReadonlyVariable):
"""
Флаг устанавливаемый в ходе обновления репозиториев,
сообщающий что хотя бы один из запланированных репозиториев
обновлен и следует обновляет различные метаданные

Если обновляются прочие оверлеи - данные считаются что устарели
"""
type = "bool"
value = "off"

def get(self):
return self.Get('cl_update_other_set')

class VariableClUpdateMetadataForce(Variable):
"""
Принудительное действие с метаданными
"""
type = "choice"
value = "auto"
opt = ["--update-metadata"]
syntax = "--{choice}-update-metadata"
metavalue = "MODE"
#untrusted = True

def init(self):
self.help = ("'force' - " + _("force update ebuilds metadata") +
",\n'skip' - " + _("skip update ebuilds metadata") +
",\n'auto' - " + _("update metadata if they are outdated"))
self.label = _("Update metadata")

def choice(self):
return [("force", _("Force")),
("skip", _("Skip")),
("auto", _("By need"))]

class VariableClUpdateEixupdateForce(Variable):
"""
Принудительное действие с eix-update
"""
type = "choice"
value = "auto"
opt = ["--eix-update"]
syntax = "--{choice}-eix-update"
metavalue = "MODE"
#untrusted = True

def init(self):
self.help = ("'force' - " + _("force update eix cache") +
",\n'skip' - " + _("skip update eix cache") +
",\n'auto' - " + _("update eix cache if it is outdated"))
self.label = _("Update eix cache")

def choice(self):
return [("force", _("Force")),
("skip", _("Skip")),
("auto", _("By need"))]

class VariableClUpdateOtherSet(Variable):
"""
Обновить остальные оверлеи
"""
type = "bool"
value = "off"
opt = ["--update-other"]

def init(self):
self.help = _("update other overlays")
self.label = _("Update other overlays")

class VariableClUpdateOtherRepData(ReadonlyTableVariable):
"""
Информация о прочих репозиториях
"""
source = ['cl_update_other_rep_name',
'cl_update_other_rep_path']

def generator(self):
repNames = self.Get('cl_update_rep_name')
for rpath in self.Get('cl_portdir_overlay'):
repo_file = path.join(rpath,"profiles/repo_name")
rname = readFile(repo_file).strip() or path.basename(rpath)
if not rname in repNames:
yield (rname, rpath)

def get(self):
return list(self.generator())

class VariableClUpdateOtherRepName(FieldValue,ReadonlyVariable):
"""
Список имен прочих репозиториев
"""
type = "list"
source_variable = "cl_update_other_rep_data"
column = 0

class VariableClUpdateOtherRepPath(FieldValue,ReadonlyVariable):
"""
Список путей до прочих репозиториев
"""
type = "list"
source_variable = "cl_update_other_rep_data"
column = 1

+ 2
- 0
update/wsdl_update.py View File

@@ -62,6 +62,8 @@ class Wsdl(WsdlBase):
lambda group:group(_("Update system"),
normal=('cl_rebuild_world_set','cl_update_rev_set'),
expert=('cl_update_sync_rep', 'cl_update_branch',
'cl_update_metadata_force','cl_update_other_set',
'cl_update_eixupdate_force',
'cl_templates_locate',
'cl_verbose_set','cl_dispatch_conf'),
next_label=_("Update"))]},


Loading…
Cancel
Save