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 gitlabracadabra.containers.blob import Blob

20 from gitlabracadabra.containers.const import (

21 DOCKER_MANIFEST_SCHEMA1,

22 DOCKER_MANIFEST_SCHEMA1_SIGNED,

23 DOCKER_MANIFEST_SCHEMA2,

24 DOCKER_MANIFEST_SCHEMA2_LIST,

25 OCI_IMAGE_INDEX,

26 OCI_IMAGE_MANIFEST,

27)

28 from gitlabracadabra.containers.manifest_base import ManifestBase

29

30

31 class Manifest(ManifestBase):

32 """Retrieve Manifest or Manifest list."""

33

34 def manifests(self) -> list[Manifest]:

35 """Get manifests of the manifest list.

36

37 Returns:

38 A list of manifests.

39

40 Raises:

41 ValueError: Unsupported manifest list type.

42 """

43 if self.mime_type in {DOCKER_MANIFEST_SCHEMA2_LIST, OCI_IMAGE_INDEX}: 43 ↛ 45 line 43 didn't jump to line 45 because the condition on line 43 was always true

44 return self._manifests_v2()

45 msg = f"Unsupported manifest list type {self.mime_type}"

46 raise ValueError(msg)

47

48 def tag_list(self) -> list[str]:

49 """Get tags of the manifest.

50

51 Returns:

52 A list of tags (strings).

53

54 Raises:

55 ValueError: Expected list got something else.

56 """

57 response = self._registry.request(

58 "get",

59 f"/v2/{self.manifest_name}/tags/list",

60 scopes={self.scope()},

61 )

62 tags = response.json().get("tags")

63 if not isinstance(tags, list): 63 ↛ 64 line 63 didn't jump to line 64 because the condition on line 63 was never true

64 msg = f"Expected list got {type(tags)}"

65 raise TypeError(msg)

66 return tags

67

68 def blobs(self) -> list[Blob]:

69 """Get blobs of the manifest.

70

71 Returns:

72 A list of blobs.

73

74 Raises:

75 ValueError: Unsupported media type.

76 """

77 if self.mime_type in {DOCKER_MANIFEST_SCHEMA2, OCI_IMAGE_MANIFEST}:

78 return [

79 Blob(

80 self.registry,

81 self.manifest_name,

82 layer_json["digest"],

83 size=layer_json["size"],

84 mime_type=layer_json["mediaType"],

85 )

86 for layer_json in self.json.get("layers")

87 ]

88 if self.mime_type in {DOCKER_MANIFEST_SCHEMA1, DOCKER_MANIFEST_SCHEMA1_SIGNED}: 88 ↛ 98 line 88 didn't jump to line 98 because the condition on line 88 was always true

89 return [

90 Blob(

91 self.registry,

92 self.manifest_name,

93 fs_layer_json["blobSum"],

94 mime_type="application/octet-stream",

95 )

96 for fs_layer_json in self.json.get("fsLayers")

97 ]

98 msg = f"Unsupported media type: {self.mime_type}"

99 raise ValueError(msg)

100

101 def _manifests_v2(self) -> list[Manifest]:

102 json = dict(self.json)

103 if json["mediaType"] not in {DOCKER_MANIFEST_SCHEMA2_LIST, OCI_IMAGE_INDEX}: 103 ↛ 104 line 103 didn't jump to line 104 because the condition on line 103 was never true

104 msg = "Unexpected manifest list type {}".format(json["mediaType"])

105 raise ValueError(msg)

106 if json["schemaVersion"] != 2: # noqa: PLR2004 106 ↛ 107 line 106 didn't jump to line 107 because the condition on line 106 was never true

107 msg = "Unexpected manifest schema version {}".format(json["schemaVersion"])

108 raise ValueError(msg)

109 manifests = []

110 for manifest_json in json["manifests"]:

111 manifest = Manifest(

112 self.registry,

113 self.manifest_name,

114 digest=manifest_json["digest"],

115 size=manifest_json["size"],

116 mime_type=manifest_json["mediaType"],

117 tag=self.tag,

118 )

119 manifest.platform = manifest_json["platform"]

120 manifests.append(manifest)

121 return manifests