Devuan deployment of britney2
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.
 
 
 

208 lines
7.1 KiB

  1. # -*- coding: utf-8 -*-
  2. # Copyright (C) 2011 Adam D. Barratt <adsb@debian.org>
  3. # This program is free software; you can redistribute it and/or modify
  4. # it under the terms of the GNU General Public License as published by
  5. # the Free Software Foundation; either version 2 of the License, or
  6. # (at your option) any later version.
  7. # This program is distributed in the hope that it will be useful,
  8. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. # GNU General Public License for more details.
  11. import apt_pkg
  12. import logging
  13. from britney2 import SuiteClass
  14. class MigrationItem(object):
  15. def __init__(self, package=None, version=None, architecture=None, uvname=None, suite=None, is_cruft_removal=False):
  16. self._uvname = uvname
  17. self._package = package
  18. self._version = version
  19. self._architecture = architecture
  20. self._suite = suite
  21. self._is_cruft_removal = is_cruft_removal
  22. if version is not None:
  23. self._name = "%s/%s" % (uvname, version)
  24. else:
  25. self._name = uvname
  26. def __str__(self):
  27. if self.version is not None:
  28. return self.name
  29. else:
  30. return self.uvname
  31. def __eq__(self, other):
  32. isequal = False
  33. if self.uvname == other.uvname:
  34. if self.version is None or other.version is None:
  35. isequal = True
  36. else:
  37. isequal = self.version == other.version
  38. return isequal
  39. def __hash__(self):
  40. return hash((self.uvname, self.version))
  41. def __lt__(self, other):
  42. return (self.uvname, self.version) < (other.uvname, other.version)
  43. @property
  44. def name(self):
  45. return self._name
  46. @property
  47. def is_removal(self):
  48. return self._name.startswith('-')
  49. @property
  50. def architecture(self):
  51. return self._architecture
  52. @property
  53. def package(self):
  54. return self._package
  55. @property
  56. def suite(self):
  57. return self._suite
  58. @property
  59. def version(self):
  60. return self._version
  61. @property
  62. def uvname(self):
  63. return self._uvname
  64. @property
  65. def is_cruft_removal(self):
  66. return self._is_cruft_removal
  67. class MigrationItemFactory(object):
  68. def __init__(self, suites):
  69. self._suites = suites
  70. self._all_architectures = frozenset(suites.target_suite.binaries)
  71. logger_name = ".".join((self.__class__.__module__, self.__class__.__name__))
  72. self.logger = logging.getLogger(logger_name)
  73. def generate_removal_for_cruft_item(self, pkg_id):
  74. uvname = "-%s/%s" % (pkg_id.package_name, pkg_id.architecture)
  75. return MigrationItem(package=pkg_id.package_name,
  76. version=pkg_id.version,
  77. architecture=pkg_id.architecture,
  78. uvname=uvname,
  79. suite=self._suites.target_suite,
  80. is_cruft_removal=True,
  81. )
  82. @staticmethod
  83. def _is_right_version(suite, package_name, expected_version):
  84. if package_name not in suite.sources:
  85. return False
  86. actual_version = suite.sources[package_name].version
  87. if apt_pkg.version_compare(actual_version, expected_version) != 0:
  88. return False
  89. return True
  90. def _find_suite_for_item(self, suites, suite_name, package_name, version, auto_correct):
  91. suite = suites.by_name_or_alias[suite_name]
  92. assert suite.suite_class != SuiteClass.TARGET_SUITE
  93. if version is not None and auto_correct and not self._is_right_version(suite, package_name, version):
  94. for s in suites.source_suites:
  95. if self._is_right_version(s, package_name, version):
  96. suite = s
  97. break
  98. return suite
  99. def parse_item(self, item_text, versioned=True, auto_correct=True):
  100. """
  101. :param item_text: The string describing the item (e.g. "glibc/2.5")
  102. :param versioned: If true, a two-part item is assumed to be versioned.
  103. otherwise, it is assumed to be versionless. This determines how
  104. items like "foo/bar" is parsed (if versioned, "bar" is assumed to
  105. be a version and otherwise "bar" is assumed to be an architecture).
  106. If in doubt, use versioned=True with auto_correct=True and the
  107. code will figure it out on its own.
  108. :param auto_correct: If True, minor issues are automatically fixed
  109. where possible. This includes handling architecture and version
  110. being in the wrong order and missing/omitting a suite reference
  111. for items. This feature is useful for migration items provided
  112. by humans (e.g. via hints) to avoid rejecting the input over
  113. trivial/minor issues with the input.
  114. When False, there will be no attempt to correct the migration
  115. input.
  116. :return: A MigrationItem matching the spec
  117. """
  118. suites = self._suites
  119. version = None
  120. architecture = None
  121. is_removal = False
  122. if item_text.startswith('-'):
  123. item_text = item_text[1:]
  124. is_removal = True
  125. parts = item_text.split('/', 3)
  126. package_name = parts[0]
  127. suite_name = suites.primary_source_suite.name
  128. if '_' in package_name:
  129. package_name, suite_name = package_name.split('_', 2)
  130. if len(parts) == 3:
  131. architecture = parts[1]
  132. version = parts[2]
  133. elif len(parts) == 2:
  134. if versioned:
  135. version = parts[1]
  136. else:
  137. architecture = parts[1]
  138. if auto_correct and version in self._all_architectures:
  139. (architecture, version) = (version, architecture)
  140. if architecture is None:
  141. architecture = 'source'
  142. if '_' in architecture:
  143. architecture, suite_name = architecture.split('_', 2)
  144. if is_removal:
  145. suite = suites.target_suite
  146. else:
  147. suite = self._find_suite_for_item(suites, suite_name, package_name, version, auto_correct)
  148. uvname = self._canonicalise_uvname(item_text, package_name, architecture, suite, is_removal)
  149. return MigrationItem(package=package_name,
  150. version=version,
  151. architecture=architecture,
  152. uvname=uvname,
  153. suite=suite,
  154. )
  155. def parse_items(self, *args, **kwargs):
  156. return [self.parse_item(x, **kwargs) for x in args]
  157. @staticmethod
  158. def _canonicalise_uvname(item_text_sans_removal, package, architecture, suite, is_removal):
  159. parts = item_text_sans_removal.split('/', 3)
  160. if len(parts) == 1 or architecture == 'source':
  161. uvname = package
  162. else:
  163. uvname = "%s/%s" % (package, architecture)
  164. if suite.suite_class.is_additional_source:
  165. uvname = '%s_%s' % (uvname, suite.suite_short_name)
  166. if is_removal:
  167. uvname = '-%s' % (uvname)
  168. return uvname