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 json import loads as json_loads

20 from typing import TYPE_CHECKING, Any

21

22 from gitlabracadabra.containers.const import MANIFEST

23 from gitlabracadabra.containers.with_digest import WithDigest

24

25 if TYPE_CHECKING: 25 ↛ 26 line 25 didn't jump to line 26 because the condition on line 25 was never true

26 from gitlabracadabra.containers.registry_importer import RegistryImporter

27

28

29 class ManifestBase(WithDigest):

30 """Base methods for manifest or manifest list."""

31

32 supported_mime_types = MANIFEST

33

34 def __init__(

35 self,

36 registry: RegistryImporter,

37 manifest_name: str,

38 digest: str | None = None,

39 *,

40 size: int | None = None,

41 mime_type: str | None = None,

42 tag: str = "latest",

43 ) -> None:

44 """Instanciate a manifest.

45

46 Args:

47 registry: Registry.

48 manifest_name: Manifest name (Example: library/debian).

49 digest: Digest.

50 size: Size.

51 mime_type: Content-Type / mediaType.

52 tag: Manifest tag (Example: latest).

53 """

54 super().__init__(registry, manifest_name, digest, size=size, mime_type=mime_type)

55 self.tag = tag or "latest"

56 self._json = None

57 self.platform = None

58 self._retrieve_mehod = "get"

59

60 @property

61 def json(self) -> Any:

62 """Get JSON.

63

64 Returns:

65 JSON.

66 """

67 if self._json is None:

68 self._ensure_cached()

69 with self as opened_manifest:

70 self._json = json_loads(opened_manifest.read().decode("utf-8"))

71 return self._json

72

73 @property

74 def registry_path(self) -> str:

75 """Get relative path of the manifest in the registry.

76

77 Returns:

78 The path.

79 """

80 if self._digest is not None:

81 return f"/v2/{self.manifest_name}/manifests/{self._digest}"

82 return f"/v2/{self.manifest_name}/manifests/{self.tag}"

83

84 def __str__(self) -> str:

85 """Get string representation.

86

87 Returns:

88 The full reference

89 (docker.io/library/busybox:latest@sha256:c6b45a95f932202dbb27c31333c4789f45184a744060f6e569cc9d2bf1b9ad6f).

90 """

91 return "{}/{}:{}{}".format(

92 self.registry.hostname,

93 self.manifest_name,

94 self.tag,

95 f"@{self._digest}" if self._digest else "",

96 )