# -*- coding: utf-8 -*- # Copyright 2014-2016 Mir Calculate. 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 from calculate.lib.utils.files import (listDirectory, readFile, readLinesFile, makeDirectory, removeDir) from calculate.lib.utils.git import Git from update import UpdateError from calculate.lib.cl_lang import setLocalTranslate, _ setLocalTranslate('cl_update3', sys.modules[__name__]) DEFAULT_BRANCH = Git.Reference.Master class RepositoryStorageInterface(object): def __iter__(self): raise StopIteration def get_profiles(self, url, branch=DEFAULT_BRANCH): return [] def get_repository(self, url, branch=DEFAULT_BRANCH): return None class RepositoryStorage(RepositoryStorageInterface): 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 [] def get_repository(self, url, branch=DEFAULT_BRANCH): return None 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 or DEFAULT_BRANCH) class RepositoryStorageSet(RepositoryStorageInterface): """ Набор хранилищ репозиториев """ def __init__(self, *storages): self.storages = storages 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 for storage in self.storages: rep = storage.get_repository(url, branch) if rep: 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 "" % (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 path.exists(rpath_new): removeDir(rpath_new) shutil.move(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 "" % (self.directory, self.url)