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.

260 lines
8.4 KiB

  1. # -*- coding: utf-8 -*-
  2. # Copyright 2014-2016 Mir Calculate. http://www.calculate-linux.org
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import sys
  16. from os import path
  17. import shutil
  18. from calculate.lib.utils.files import (listDirectory, readFile, readLinesFile,
  19. makeDirectory, removeDir)
  20. from calculate.lib.utils.git import Git
  21. from update import UpdateError
  22. from calculate.lib.cl_lang import setLocalTranslate, _
  23. setLocalTranslate('cl_update3', sys.modules[__name__])
  24. DEFAULT_BRANCH = Git.Reference.Master
  25. class RepositoryStorageInterface(object):
  26. def __iter__(self):
  27. raise StopIteration
  28. def get_profiles(self, url, branch=DEFAULT_BRANCH):
  29. return []
  30. def get_repository(self, url, branch=DEFAULT_BRANCH):
  31. return None
  32. class RepositoryStorage(RepositoryStorageInterface):
  33. directory = '/tmp'
  34. def __init__(self, directory):
  35. self.directory = directory
  36. makeDirectory(directory)
  37. def __iter__(self):
  38. git = Git()
  39. for dn in listDirectory(self.directory, onlyDir=True, fullPath=True):
  40. if git.is_git(dn):
  41. yield ProfileRepository(path.basename(dn), self)
  42. def get_profiles(self, url, branch=DEFAULT_BRANCH):
  43. return []
  44. def get_repository(self, url, branch=DEFAULT_BRANCH):
  45. return None
  46. class ProfileStorage(RepositoryStorage):
  47. def get_profiles(self, url, branch=DEFAULT_BRANCH):
  48. rep = self.get_repository(url, branch)
  49. if rep:
  50. return rep.get_profiles()
  51. return None
  52. def get_repository(self, url, branch=DEFAULT_BRANCH):
  53. return None
  54. class LocalStorage(ProfileStorage):
  55. """
  56. Локальное хранилище репозиториев, при запросе по урлу смотрит, доступные
  57. репозитории если находит подходящий - возвращает его профили
  58. """
  59. def get_repository(self, url, branch=DEFAULT_BRANCH):
  60. for rep in self:
  61. if rep.is_like(url, branch):
  62. return rep
  63. class CacheStorage(ProfileStorage):
  64. """
  65. Хранилище репозиториев, при запросе по урлу смотрит, доступные
  66. репозитории если находит подходящий - возвращает его профили,
  67. если не находит - скачивает и возвращает профили
  68. """
  69. def get_repository(self, url, branch=DEFAULT_BRANCH):
  70. for rep in self:
  71. if rep.is_like(url, branch):
  72. return rep
  73. else:
  74. return ProfileRepository.clone(url, self, branch)
  75. class RepositoryStorageSet(RepositoryStorageInterface):
  76. """
  77. Набор хранилищ репозиториев
  78. """
  79. def __init__(self, *storages):
  80. self.storages = storages
  81. def get_profiles(self, url, branch=DEFAULT_BRANCH):
  82. """
  83. Получить профили из указанного репозитория
  84. """
  85. for storage in self.storages:
  86. profiles = storage.get_profiles(url, branch)
  87. if profiles is not None:
  88. return profiles
  89. return None
  90. def __iter__(self):
  91. for storage in self.storages:
  92. for rep in storage:
  93. yield rep
  94. def get_repository(self, url, branch=DEFAULT_BRANCH):
  95. """
  96. Получить репозиторий по параметрам
  97. """
  98. for rep in self:
  99. if rep.is_like(url, branch):
  100. return rep
  101. return None
  102. def is_local(self, url, branch=DEFAULT_BRANCH):
  103. """
  104. Проверить является ли репозиторий с указанными параметрами
  105. локальным
  106. """
  107. rep = self.get_repository(url, branch)
  108. if rep and isinstance(rep.storage, LocalStorage):
  109. return True
  110. return False
  111. def __repr__(self):
  112. return "Repository set"
  113. class Profile(object):
  114. """
  115. Профиль репозитория
  116. """
  117. available_arch = ["amd64", "x86"]
  118. def __init__(self, repository, profile, arch):
  119. self.repository = repository
  120. self.profile = profile
  121. self.arch = arch
  122. @property
  123. def path(self):
  124. return path.join(self.repository.directory,"profiles", self.profile)
  125. @classmethod
  126. def from_string(cls, repository, s):
  127. parts = filter(None, s.split())
  128. if len(parts) == 3 and parts[0] in cls.available_arch:
  129. return Profile(repository, parts[1], parts[0])
  130. return None
  131. def __repr__(self):
  132. return "<Profile (%s) %s:%s from %s>" % (self.arch,
  133. self.repository.repo_name,
  134. self.profile,
  135. self.repository.directory)
  136. class ProfileRepository(object):
  137. """
  138. Репозиторий либо скачивается, либо берется из кэша
  139. """
  140. def __init__(self, name, storage):
  141. self._storage = storage
  142. self.name = name
  143. @property
  144. def storage(self):
  145. return self._storage
  146. @storage.setter
  147. def storage(self, storage):
  148. if storage.directory != self._storage.directory:
  149. newpath = path.join(storage.directory, self.name)
  150. if self.directory == newpath:
  151. return
  152. try:
  153. if path.exists(newpath):
  154. removeDir(newpath)
  155. shutil.move(self.directory, newpath)
  156. self._storage = storage
  157. except OSError as e:
  158. raise UpdateError(_("Failed to move the profile: %s") %
  159. str(e))
  160. @classmethod
  161. def clone(cls, url, storage, branch=DEFAULT_BRANCH):
  162. name = path.basename(url)
  163. if name.endswith(".git"):
  164. name = name[:-4]
  165. git = Git()
  166. rpath = path.join(storage.directory, name)
  167. if path.exists(rpath):
  168. removeDir(rpath)
  169. git.cloneRepository(url, rpath, branch)
  170. pr = cls(name, storage)
  171. repo_name = pr.repo_name
  172. if name != repo_name:
  173. rpath_new = path.join(storage.directory, repo_name)
  174. if path.exists(rpath_new):
  175. removeDir(rpath_new)
  176. shutil.move(rpath, rpath_new)
  177. pr = cls(repo_name, storage)
  178. return pr
  179. @property
  180. def repo_name(self):
  181. return readFile(path.join(self.directory,
  182. "profiles/repo_name")).strip()
  183. def is_like(self, url, branch=DEFAULT_BRANCH):
  184. if self.url == url and (branch is None or self.branch == branch):
  185. return True
  186. return False
  187. @property
  188. def directory(self):
  189. """
  190. Получить локальную директорию на данные репозитория
  191. """
  192. return path.join(self.storage.directory, self.name)
  193. @property
  194. def url(self):
  195. git = Git()
  196. return git.get_url(self.directory, "origin")
  197. @property
  198. def branch(self):
  199. git = Git()
  200. return git.getBranch(self.directory)
  201. def sync(self):
  202. """
  203. Синхронизировать репозиторий
  204. """
  205. git = Git()
  206. if not git.pullRepository(self.directory, quiet_error=True):
  207. git.resetRepository(self.directory, to_origin=True)
  208. git.pullRepository(self.directory, quiet_error=True)
  209. def get_profiles(self):
  210. """
  211. Получить список профилей репозитория
  212. """
  213. profiles_desc = path.join(self.directory, "profiles/profiles.desc")
  214. return filter(None, (Profile.from_string(self, line)
  215. for line in readLinesFile(profiles_desc)))
  216. def __repr__(self):
  217. return "<ProfileRepository %s url=%s>" % (self.directory, self.url)