You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

246 lines
8.1 KiB

#-*- 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
import shutil
import os
from calculate.lib.utils.files import listDirectory, readFile, readLinesFile, \
makeDirectory, removeDir
from calculate.lib.utils.portage import Git
from update import UpdateError
from calculate.lib.cl_lang import setLocalTranslate, _
setLocalTranslate('cl_update3', sys.modules[__name__])
DEFAULT_BRANCH = "master"
class RepositoryStorage(object):
directory = '/tmp'
def __init__(self, directory):
self.directory = directory
makeDirectory(directory)
def __iter__(self):
git = Git()
for dn in listDirectory(self.directory, onlyDir=True, fullPath=True):
if git.is_git(dn):
yield ProfileRepository(path.basename(dn), self)
def get_profiles(self, url, branch=DEFAULT_BRANCH):
return []
class ProfileStorage(RepositoryStorage):
def get_profiles(self, url, branch=DEFAULT_BRANCH):
rep = self.get_repository(url, branch)
if rep:
return rep.get_profiles()
return None
def get_repository(self, url, branch=DEFAULT_BRANCH):
return None
class LocalStorage(ProfileStorage):
"""
Локальное хранилище репозиториев, при запросе по урлу смотрит, доступные
репозитории если находит подходящий - возвращает его профили
"""
def get_repository(self, url, branch=DEFAULT_BRANCH):
for rep in self:
if rep.is_like(url, branch):
return rep
class CacheStorage(ProfileStorage):
"""
Хранилище репозиториев, при запросе по урлу смотрит, доступные
репозитории если находит подходящий - возвращает его профили,
если не находит - скачивает и возвращает профили
"""
def get_repository(self, url, branch=DEFAULT_BRANCH):
for rep in self:
if rep.is_like(url, branch):
return rep
else:
return ProfileRepository.clone(url, self, branch)
class RepositoryStorageSet(RepositoryStorage):
"""
Набор хранилищ репозиториев
"""
def __init__(self):
self.storages = [LocalStorage('/var/lib/layman'),
CacheStorage('/var/calculate/tmp/update')]
def get_profiles(self, url, branch=DEFAULT_BRANCH):
"""
Получить профили из указанного репозитория
"""
for storage in self.storages:
profiles = storage.get_profiles(url, branch)
if profiles is not None:
return profiles
return None
def __iter__(self):
for storage in self.storages:
for rep in storage:
yield rep
def get_repository(self, url, branch=DEFAULT_BRANCH):
"""
Получить репозиторий по параметрам
"""
for rep in self:
if rep.is_like(url, branch):
return rep
return None
def is_local(self, url, branch=DEFAULT_BRANCH):
"""
Проверить является ли репозиторий с указанными параметрами
локальным
"""
rep = self.get_repository(url, branch)
if rep and isinstance(rep.storage, LocalStorage):
return True
return False
def __repr__(self):
return "Repository set"
class Profile(object):
"""
Профиль репозитория
"""
available_arch = ["amd64", "x86"]
def __init__(self, repository, profile, arch):
self.repository = repository
self.profile = profile
self.arch = arch
@property
def path(self):
return path.join(self.repository.directory,"profiles", self.profile)
@classmethod
def from_string(cls, repository, s):
parts = filter(None, s.split())
if len(parts) == 3 and parts[0] in cls.available_arch:
return Profile(repository, parts[1], parts[0])
return None
def __repr__(self):
return "<Profile (%s) %s:%s from %s>" % (self.arch,
self.repository.repo_name,
self.profile,
self.repository.directory)
class ProfileRepository(object):
"""
Репозиторий либо скачивается, либо берется из кэша
"""
def __init__(self, name, storage):
self._storage = storage
self.name = name
@property
def storage(self):
return self._storage
@storage.setter
def storage(self, storage):
if storage.directory != self._storage.directory:
newpath = path.join(storage.directory, self.name)
if self.directory == newpath:
return
try:
if path.exists(newpath):
removeDir(newpath)
shutil.move(self.directory, newpath)
self._storage = storage
except OSError as e:
raise UpdateError(_("Failed to move the profile: %s") %
str(e))
@classmethod
def clone(cls, url, storage, branch=DEFAULT_BRANCH):
name = path.basename(url)
if name.endswith(".git"):
name = name[:-4]
git = Git()
rpath = path.join(storage.directory, name)
if path.exists(rpath):
removeDir(rpath)
git.cloneRepository(url, rpath, branch)
pr = cls(name, storage)
repo_name = pr.repo_name
if name != repo_name:
rpath_new = path.join(storage.directory, repo_name)
if not path.exists(rpath_new):
os.rename(rpath, rpath_new)
pr = cls(repo_name, storage)
return pr
@property
def repo_name(self):
return readFile(path.join(self.directory,
"profiles/repo_name")).strip()
def is_like(self, url, branch=DEFAULT_BRANCH):
if self.url == url and (branch is None or self.branch == branch):
return True
return False
@property
def directory(self):
"""
Получить локальную директорию на данные репозитория
"""
return path.join(self.storage.directory, self.name)
@property
def url(self):
git = Git()
return git.get_url(self.directory, "origin")
@property
def branch(self):
git = Git()
return git.getBranch(self.directory)
def sync(self):
"""
Синхронизировать репозиторий
"""
git = Git()
if not git.pullRepository(self.directory, quiet_error=True):
git.resetRepository(self.directory, to_origin=True)
git.pullRepository(self.directory, quiet_error=True)
def get_profiles(self):
"""
Получить список профилей репозитория
"""
profiles_desc = path.join(self.directory, "profiles/profiles.desc")
return filter(None, (Profile.from_string(self, line)
for line in readLinesFile(profiles_desc)))
def __repr__(self):
return "<ProfileRepository %s url=%s>" % (self.directory, self.url)