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 import logging

18 from copy import deepcopy

19

20 from gitlabracadabra.gitlab.access_levels import access_level_value

21 from gitlabracadabra.objects.object import GitLabracadabraObject

22

23 logger = logging.getLogger(__name__)

24

25

26 class GroupsMixin(GitLabracadabraObject):

27 """Object with groups."""

28

29 """_process_groups()

30

31 Process the groups param.

32 """

33

34 def _process_groups(self, param_name, param_value, *, dry_run=False, skip_save=False):

35 assert param_name == "groups" # noqa: S101

36 assert not skip_save # noqa: S101

37 if not hasattr(self._obj, "shared_with_groups"): 37 ↛ 38 line 37 didn't jump to line 38 because the condition on line 37 was never true

38 logger.error(

39 "[%s] Unable to share with groups: %s", self._name, "share group with groups requires GitLab >= 13.1.0"

40 )

41 return

42 if not hasattr(self._obj, "share"): 42 ↛ 44 line 42 didn't jump to line 44 because the condition on line 42 was never true

43 # https://github.com/python-gitlab/python-gitlab/pull/1139

44 logger.error(

45 "[%s] Unable to share with groups: %s",

46 self._name,

47 "share group with groups requires python-gitlab >= 2.5.0",

48 )

49 return

50 param_value = deepcopy(param_value)

51 unknown_groups = self._content.get("unknown_groups", "warn")

52 # We first check for already shared groups

53 for group in self._obj.shared_with_groups:

54 if "group_full_path" not in group: 54 ↛ 57 line 54 didn't jump to line 57 because the condition on line 54 was never true

55 # Gitlab < 11.8

56 # https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/24052

57 group["group_full_path"] = self.connection.group_cache.full_path_from_id(group["group_id"])

58 self.connection.group_cache.map_group(group["group_id"], group["group_full_path"])

59 if group["group_full_path"] in param_value:

60 target_access_level = access_level_value(param_value[group["group_full_path"]])

61 if group["group_access_level"] != target_access_level: 61 ↛ 80 line 61 didn't jump to line 80 because the condition on line 61 was always true

62 if dry_run: 62 ↛ 63 line 62 didn't jump to line 63 because the condition on line 62 was never true

63 logger.info(

64 "[%s] NOT Changing group %s access level: %s -> %s (dry-run)",

65 self._name,

66 group["group_full_path"],

67 group["group_access_level"],

68 target_access_level,

69 )

70 else:

71 logger.info(

72 "[%s] Changing group %s access level: %s -> %s",

73 self._name,

74 group["group_full_path"],

75 group["group_access_level"],

76 target_access_level,

77 )

78 self._obj.unshare(group["group_id"])

79 self._obj.share(group["group_id"], target_access_level)

80 param_value.pop(group["group_full_path"])

81 elif unknown_groups in ["delete", "remove"]:

82 if dry_run: 82 ↛ 83 line 82 didn't jump to line 83 because the condition on line 82 was never true

83 logger.info(

84 "[%s] NOT Unsharing from unknown group: %s (dry-run)", self._name, group["group_full_path"]

85 )

86 else:

87 logger.info("[%s] Unsharing from unknown group: %s", self._name, group["group_full_path"])

88 self._obj.unshare(group["group_id"])

89 elif unknown_groups not in ["ignore", "skip"]: 89 ↛ 90 line 89 didn't jump to line 90 because the condition on line 89 was never true

90 logger.warning(

91 "[%s] NOT Unsharing from unknown group: %s (unknown_groups=%s)",

92 self._name,

93 group["group_full_path"],

94 unknown_groups,

95 )

96 # Remaining groups

97 for group_full_path, target_group_access in sorted(param_value.items()):

98 group_id = self.connection.group_cache.id_from_full_path(group_full_path)

99 if group_id is None:

100 logger.warning("[%s] Group not found %s", self._name, group_full_path)

101 continue

102 target_access_level = access_level_value(target_group_access)

103 if dry_run: 103 ↛ 104 line 103 didn't jump to line 104 because the condition on line 103 was never true

104 logger.info(

105 "[%s] NOT Sharing group %s: %s -> %s (dry-run)", self._name, group_full_path, 0, target_access_level

106 )

107 else:

108 logger.info("[%s] Sharing group %s: %s -> %s", self._name, group_full_path, 0, target_access_level)

109 self._obj.share(group_id, target_access_level)