# -*- 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 " <Profile ( %s ) %s : %s from %s > " % ( 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 " <ProfileRepository %s url= %s > " % ( self . directory , self . url )