1#

2# Copyright (C) 2019-2025 Mathieu Parent <math.parent@gmail.com>

3#

4# This program is free software: you can redistribute it and/or modify

5# it under the terms of the GNU Lesser General Public License as published by

6# the Free Software Foundation, either version 3 of the License, or

7# (at your option) any later version.

8#

9# This program is distributed in the hope that it will be useful,

10# but WITHOUT ANY WARRANTY; without even the implied warranty of

11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

12# GNU Lesser General Public License for more details.

13#

14# You should have received a copy of the GNU Lesser General Public License

15# along with this program. If not, see <http://www.gnu.org/licenses/>.

16

17 from __future__ import annotations

18

19 from logging import getLogger

20 from urllib.parse import urljoin

21

22 from requests import codes

23

24 from gitlabracadabra.packages.package_file import PackageFile

25 from gitlabracadabra.packages.source import Source

26

27 logger = getLogger(__name__)

28

29

30 class PulpManifestSource(Source):

31 """PULP_MANIFEST repository."""

32

33 def __init__(

34 self,

35 *,

36 log_prefix: str = "",

37 url: str,

38 package_name: str,

39 package_version: str | None = None,

40 ) -> None:

41 """Initialize a PULP_MANIFEST Source object.

42

43 Args:

44 log_prefix: Log prefix.

45 url: PULP_MANIFEST URL.

46 package_name: Destination package name.

47 package_version: Destination package version.

48 """

49 super().__init__()

50 self._log_prefix = log_prefix

51 self._url = url

52 self._package_name = package_name

53 self._package_version = package_version or "0"

54

55 def __str__(self) -> str:

56 """Return string representation.

57

58 Returns:

59 A string.

60 """

61 return f"PULP_MANIFEST repository (url={self._url})"

62

63 @property

64 def package_files(self) -> list[PackageFile]:

65 """Return list of package files.

66

67 Returns:

68 List of package files.

69 """

70 package_files: list[PackageFile] = []

71 pulp_manifest_response = self.session.request("get", self._url, stream=True)

72 if pulp_manifest_response.status_code != codes["ok"]:

73 logger.warning(

74 "%sUnexpected HTTP status for PULP_MANIFEST url %s: received %i %s",

75 self._log_prefix,

76 self._url,

77 pulp_manifest_response.status_code,

78 pulp_manifest_response.reason,

79 )

80 return []

81 if pulp_manifest_response.encoding is None:

82 pulp_manifest_response.encoding = "utf-8"

83 for pulp_manifest_line in pulp_manifest_response.iter_lines(decode_unicode=True):

84 try:

85 filename, sha256_checksum, size_in_bytes = pulp_manifest_line.split(",", 3)

86 except ValueError:

87 logger.warning(

88 "%sInvalid PULP_MANIFEST line: %s",

89 self._log_prefix,

90 pulp_manifest_line,

91 )

92 continue

93 package_file = self._package_file(filename, sha256_checksum, size_in_bytes)

94 if package_file:

95 package_files.append(package_file)

96 return package_files

97

98 def _package_file(self, filename: str, sha256_checksum: str, size_in_bytes: str) -> PackageFile | None:

99 url = urljoin(self._url, filename, allow_fragments=False)

100 return PackageFile(

101 url,

102 "raw",

103 self._package_name,

104 self._package_version,

105 url.split("/").pop(),

106 metadata={

107 "sha256_checksum": sha256_checksum,

108 "size_in_bytes": size_in_bytes,

109 },

110 )