# -*- 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 import os 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(): 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, git, directory): self.directory = directory self.git = git makeDirectory(directory) def __iter__(self): for dn in listDirectory(self.directory, onlyDir=True, fullPath=True): if self.git.is_git(dn): yield ProfileRepository(self.git, 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(self.git, 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(): """ Профиль репозитория """ available_arch = ["amd64", "x86"] def __init__(self, repository, profile, arch): self.repository = repository self.profile = profile self.arch = arch self._path = None @property def path(self): if self._path: return self._path return path.join(self.repository.directory,"profiles", self.profile) @path.setter def path(self, value): self._path = value return value @classmethod def from_string(cls, repository, s): parts = [x for x in s.split() if x] 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(): """ Репозиторий либо скачивается, либо берется из кэша """ def __init__(self, git, name, storage): self._storage = storage self.name = name self.git = git @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, git, url, storage, branch=DEFAULT_BRANCH): name = path.basename(url) if name.endswith(".git"): name = name[:-4] rpath = path.join(storage.directory, name) if path.exists(rpath): removeDir(rpath) if git.is_private_url(url): try: makeDirectory(rpath) os.chmod(rpath, 0o700) except OSError: pass git.cloneRepository(url, rpath, branch) pr = cls(git, 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(git, 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): return self.git.get_url(self.directory, "origin") @property def branch(self): return self.git.getBranch(self.directory) def sync(self): """ Синхронизировать репозиторий """ if not self.git.pullRepository(self.directory, quiet_error=True): self.git.resetRepository(self.directory, to_origin=True) self.git.pullRepository(self.directory, quiet_error=True) def get_profiles(self): """ Получить список профилей репозитория """ profiles_desc = path.join(self.directory, "profiles/profiles.desc") return list(filter(None, (Profile.from_string(self, line) for line in readLinesFile(profiles_desc)))) @staticmethod def get_local_profiles(directory): profiles_desc = path.join(directory, "profiles/profiles.desc") return list(filter(None, (Profile.from_string(Profile, line) for line in readLinesFile(profiles_desc)))) def __repr__(self): return "" % (self.directory, self.url)