saltext.github 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- saltext/github/__init__.py +31 -0
- saltext/github/modules/__init__.py +0 -0
- saltext/github/modules/github.py +1873 -0
- saltext/github/modules/github_org.py +667 -0
- saltext/github/modules/github_repo.py +1658 -0
- saltext/github/modules/github_user.py +75 -0
- saltext/github/resources/__init__.py +0 -0
- saltext/github/resources/github/__init__.py +251 -0
- saltext/github/resources/github/modules/__init__.py +0 -0
- saltext/github/resources/github/modules/actions.py +175 -0
- saltext/github/resources/github/modules/config.py +69 -0
- saltext/github/resources/github/modules/deploy_key.py +122 -0
- saltext/github/resources/github/modules/environment.py +241 -0
- saltext/github/resources/github/modules/grains.py +18 -0
- saltext/github/resources/github/modules/issue.py +157 -0
- saltext/github/resources/github/modules/label.py +161 -0
- saltext/github/resources/github/modules/milestone.py +103 -0
- saltext/github/resources/github/modules/pages.py +149 -0
- saltext/github/resources/github/modules/pull.py +90 -0
- saltext/github/resources/github/modules/repo.py +155 -0
- saltext/github/resources/github/modules/ruleset.py +144 -0
- saltext/github/resources/github/modules/saltutil.py +24 -0
- saltext/github/resources/github/modules/state.py +116 -0
- saltext/github/resources/github/modules/test.py +29 -0
- saltext/github/resources/github/states/__init__.py +0 -0
- saltext/github/resources/github/states/actions.py +117 -0
- saltext/github/resources/github/states/deploy_key.py +130 -0
- saltext/github/resources/github/states/environment.py +99 -0
- saltext/github/resources/github/states/label.py +71 -0
- saltext/github/resources/github/states/pages.py +92 -0
- saltext/github/resources/github/states/repo.py +108 -0
- saltext/github/resources/github/states/ruleset.py +81 -0
- saltext/github/states/__init__.py +0 -0
- saltext/github/states/github.py +966 -0
- saltext/github/states/github_org.py +369 -0
- saltext/github/states/github_repo.py +722 -0
- saltext/github/utils/__init__.py +72 -0
- saltext/github/utils/gh/__init__.py +14 -0
- saltext/github/utils/gh/actions.py +195 -0
- saltext/github/utils/gh/cache.py +126 -0
- saltext/github/utils/gh/client.py +534 -0
- saltext/github/utils/gh/deploy_keys.py +121 -0
- saltext/github/utils/gh/environments.py +271 -0
- saltext/github/utils/gh/exceptions.py +28 -0
- saltext/github/utils/gh/grains_files.py +366 -0
- saltext/github/utils/gh/issues.py +193 -0
- saltext/github/utils/gh/labels.py +142 -0
- saltext/github/utils/gh/members.py +157 -0
- saltext/github/utils/gh/milestones.py +122 -0
- saltext/github/utils/gh/pages.py +128 -0
- saltext/github/utils/gh/profile.py +458 -0
- saltext/github/utils/gh/pulls.py +99 -0
- saltext/github/utils/gh/repos.py +465 -0
- saltext/github/utils/gh/resource.py +35 -0
- saltext/github/utils/gh/rulesets.py +191 -0
- saltext/github/utils/gh/statecores/__init__.py +70 -0
- saltext/github/utils/gh/statecores/actions.py +180 -0
- saltext/github/utils/gh/statecores/deploy_keys.py +241 -0
- saltext/github/utils/gh/statecores/environments.py +212 -0
- saltext/github/utils/gh/statecores/labels.py +116 -0
- saltext/github/utils/gh/statecores/pages.py +177 -0
- saltext/github/utils/gh/statecores/repo.py +249 -0
- saltext/github/utils/gh/statecores/rulesets.py +115 -0
- saltext/github/utils/gh/teams.py +375 -0
- saltext/github/utils/gh/users.py +87 -0
- saltext/github/version.py +1 -0
- saltext_github-1.1.0.dist-info/METADATA +151 -0
- saltext_github-1.1.0.dist-info/RECORD +73 -0
- saltext_github-1.1.0.dist-info/WHEEL +5 -0
- saltext_github-1.1.0.dist-info/entry_points.txt +2 -0
- saltext_github-1.1.0.dist-info/licenses/LICENSE +201 -0
- saltext_github-1.1.0.dist-info/licenses/NOTICE +10 -0
- saltext_github-1.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,966 @@
|
|
|
1
|
+
"""
|
|
2
|
+
**Deprecated**: Manage organization teams/members, repositories and rulesets.
|
|
3
|
+
|
|
4
|
+
.. deprecated:: 1.1.0
|
|
5
|
+
|
|
6
|
+
Use the :py:mod:`github_org <saltext.github.states.github_org>` and
|
|
7
|
+
:py:mod:`github_repo <saltext.github.states.github_repo>` state modules
|
|
8
|
+
instead.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import datetime
|
|
12
|
+
import logging
|
|
13
|
+
import time
|
|
14
|
+
|
|
15
|
+
from salt.exceptions import CommandExecutionError
|
|
16
|
+
|
|
17
|
+
from saltext.github.utils.gh.statecores import catch_errors as _catch_errors
|
|
18
|
+
|
|
19
|
+
log = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
__virtualname__ = "github"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def __virtual__():
|
|
25
|
+
"""
|
|
26
|
+
Only load if the github module is available in __salt__
|
|
27
|
+
"""
|
|
28
|
+
if "github.list_users" in __salt__:
|
|
29
|
+
return __virtualname__
|
|
30
|
+
return (False, "github module could not be loaded") # pragma: no cover
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@_catch_errors
|
|
34
|
+
def present(name, profile="github", **kwargs):
|
|
35
|
+
"""
|
|
36
|
+
Ensure a user is present
|
|
37
|
+
|
|
38
|
+
.. deprecated:: 1.1.0
|
|
39
|
+
Use :py:func:`github_org.member_present <saltext.github.states.github_org.member_present>` instead.
|
|
40
|
+
|
|
41
|
+
.. code-block:: yaml
|
|
42
|
+
|
|
43
|
+
ensure user test is present in github:
|
|
44
|
+
github.present:
|
|
45
|
+
- name: gitexample
|
|
46
|
+
|
|
47
|
+
name
|
|
48
|
+
This is the github handle of the user in the organization
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
ret = {"name": name, "changes": {}, "result": None, "comment": ""}
|
|
52
|
+
|
|
53
|
+
target = __salt__["github.get_user"](name, profile=profile, **kwargs)
|
|
54
|
+
|
|
55
|
+
# If the user has a valid github handle and is not in the org already
|
|
56
|
+
if not target:
|
|
57
|
+
ret["result"] = False
|
|
58
|
+
ret["comment"] = f"Couldnt find user {name}"
|
|
59
|
+
elif isinstance(target, bool) and target:
|
|
60
|
+
ret["comment"] = f"User {name} is already in the org "
|
|
61
|
+
ret["result"] = True
|
|
62
|
+
elif not target.get("in_org", False) and target.get("membership_state") != "pending":
|
|
63
|
+
if __opts__["test"]:
|
|
64
|
+
ret["comment"] = f"User {name} will be added to the org"
|
|
65
|
+
ret["changes"]["old"] = None
|
|
66
|
+
ret["changes"]["new"] = f"User {name} exists in the org now"
|
|
67
|
+
return ret
|
|
68
|
+
|
|
69
|
+
# add the user
|
|
70
|
+
result = __salt__["github.add_user"](name, profile=profile, **kwargs)
|
|
71
|
+
|
|
72
|
+
if result:
|
|
73
|
+
ret["changes"].setdefault("old", None)
|
|
74
|
+
ret["changes"].setdefault("new", f"User {name} exists in the org now")
|
|
75
|
+
ret["result"] = True
|
|
76
|
+
else:
|
|
77
|
+
ret["result"] = False
|
|
78
|
+
ret["comment"] = f"Failed to add user {name} to the org"
|
|
79
|
+
else:
|
|
80
|
+
ret["comment"] = f"User {name} has already been invited."
|
|
81
|
+
ret["result"] = True
|
|
82
|
+
|
|
83
|
+
return ret
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@_catch_errors
|
|
87
|
+
def absent(name, profile="github", **kwargs):
|
|
88
|
+
"""
|
|
89
|
+
Ensure a github user is absent
|
|
90
|
+
|
|
91
|
+
.. deprecated:: 1.1.0
|
|
92
|
+
Use :py:func:`github_org.member_absent <saltext.github.states.github_org.member_absent>` instead.
|
|
93
|
+
|
|
94
|
+
.. code-block:: yaml
|
|
95
|
+
|
|
96
|
+
ensure user test is absent in github:
|
|
97
|
+
github.absent:
|
|
98
|
+
- name: Example TestUser1
|
|
99
|
+
- email: example@domain.com
|
|
100
|
+
- username: gitexample
|
|
101
|
+
|
|
102
|
+
name
|
|
103
|
+
Github handle of the user in organization
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
ret = {
|
|
107
|
+
"name": name,
|
|
108
|
+
"changes": {},
|
|
109
|
+
"result": None,
|
|
110
|
+
"comment": f"User {name} is absent.",
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
target = __salt__["github.get_user"](name, profile=profile, **kwargs)
|
|
114
|
+
|
|
115
|
+
if target:
|
|
116
|
+
if isinstance(target, bool) or target.get("in_org", False):
|
|
117
|
+
if __opts__["test"]:
|
|
118
|
+
ret["comment"] = f"User {name} will be deleted"
|
|
119
|
+
ret["changes"]["old"] = f"User {name} exists"
|
|
120
|
+
ret["changes"]["new"] = f"User {name} deleted"
|
|
121
|
+
ret["result"] = None
|
|
122
|
+
return ret
|
|
123
|
+
|
|
124
|
+
result = __salt__["github.remove_user"](name, profile=profile, **kwargs)
|
|
125
|
+
|
|
126
|
+
if result:
|
|
127
|
+
ret["comment"] = f"Deleted user {name}"
|
|
128
|
+
ret["changes"].setdefault("old", f"User {name} exists")
|
|
129
|
+
ret["changes"].setdefault("new", f"User {name} deleted")
|
|
130
|
+
ret["result"] = True
|
|
131
|
+
else:
|
|
132
|
+
ret["comment"] = f"Failed to delete {name}"
|
|
133
|
+
ret["result"] = False
|
|
134
|
+
else:
|
|
135
|
+
ret["comment"] = f"User {name} has already been deleted!"
|
|
136
|
+
ret["result"] = True
|
|
137
|
+
else:
|
|
138
|
+
ret["comment"] = f"User {name} does not exist"
|
|
139
|
+
ret["result"] = True
|
|
140
|
+
return ret
|
|
141
|
+
|
|
142
|
+
return ret
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@_catch_errors
|
|
146
|
+
def team_present( # pylint: disable=too-many-statements
|
|
147
|
+
name,
|
|
148
|
+
description=None,
|
|
149
|
+
repo_names=None,
|
|
150
|
+
privacy="secret",
|
|
151
|
+
permission="pull",
|
|
152
|
+
members=None,
|
|
153
|
+
enforce_mfa=False,
|
|
154
|
+
no_mfa_grace_seconds=0,
|
|
155
|
+
profile="github",
|
|
156
|
+
**kwargs,
|
|
157
|
+
):
|
|
158
|
+
"""
|
|
159
|
+
Ensure a team is present
|
|
160
|
+
|
|
161
|
+
.. deprecated:: 1.1.0
|
|
162
|
+
Use :py:func:`github_org.team_present <saltext.github.states.github_org.team_present>` instead.
|
|
163
|
+
|
|
164
|
+
name
|
|
165
|
+
This is the name of the team in the organization.
|
|
166
|
+
|
|
167
|
+
description
|
|
168
|
+
The description of the team.
|
|
169
|
+
|
|
170
|
+
repo_names
|
|
171
|
+
The names of repositories to add the team to.
|
|
172
|
+
|
|
173
|
+
privacy
|
|
174
|
+
The level of privacy for the team, can be ``secret`` or ``closed``. Defaults
|
|
175
|
+
to secret.
|
|
176
|
+
|
|
177
|
+
permission
|
|
178
|
+
The default permission for new repositories added to the team, can be
|
|
179
|
+
``pull``, ``push`` or ``admin``. Defaults to pull.
|
|
180
|
+
|
|
181
|
+
members
|
|
182
|
+
The members belonging to the team, specified as a dict of member name to
|
|
183
|
+
optional configuration. Options include ``enforce_mfa_from`` and ``mfa_exempt``.
|
|
184
|
+
|
|
185
|
+
enforce_mfa
|
|
186
|
+
Whether to enforce MFA requirements on members of the team. If True then
|
|
187
|
+
all members without ``mfa_exempt: True`` configured will be removed from
|
|
188
|
+
the team. Note that ``no_mfa_grace_seconds`` may be set to allow members
|
|
189
|
+
a grace period.
|
|
190
|
+
|
|
191
|
+
no_mfa_grace_seconds
|
|
192
|
+
The number of seconds of grace time that a member will have to enable MFA
|
|
193
|
+
before being removed from the team. The grace period will begin from
|
|
194
|
+
``enforce_mfa_from`` on the member configuration, which defaults to
|
|
195
|
+
1970/01/01.
|
|
196
|
+
|
|
197
|
+
Example:
|
|
198
|
+
|
|
199
|
+
.. code-block:: yaml
|
|
200
|
+
|
|
201
|
+
Ensure team test is present in github:
|
|
202
|
+
github.team_present:
|
|
203
|
+
- name: test
|
|
204
|
+
- members:
|
|
205
|
+
user1: {}
|
|
206
|
+
user2: {}
|
|
207
|
+
|
|
208
|
+
Ensure team test_mfa is present in github:
|
|
209
|
+
github.team_present:
|
|
210
|
+
- name: test_mfa
|
|
211
|
+
- members:
|
|
212
|
+
user1:
|
|
213
|
+
enforce_mfa_from: 2016/06/15
|
|
214
|
+
- enforce_mfa: True
|
|
215
|
+
"""
|
|
216
|
+
ret = {"name": name, "changes": {}, "result": True, "comment": ""}
|
|
217
|
+
|
|
218
|
+
target = __salt__["github.get_team"](name, profile=profile, **kwargs)
|
|
219
|
+
test_comments = []
|
|
220
|
+
|
|
221
|
+
if target: # Team already exists
|
|
222
|
+
parameters = {}
|
|
223
|
+
if description is not None and target["description"] != description:
|
|
224
|
+
parameters["description"] = description
|
|
225
|
+
if permission is not None and target["permission"] != permission:
|
|
226
|
+
parameters["permission"] = permission
|
|
227
|
+
if privacy is not None and target["privacy"] != privacy:
|
|
228
|
+
parameters["privacy"] = privacy
|
|
229
|
+
|
|
230
|
+
if len(parameters) > 0:
|
|
231
|
+
team_change = {
|
|
232
|
+
"old": f"Team properties were {target}",
|
|
233
|
+
"new": f"Team properties (that changed) are {parameters}",
|
|
234
|
+
}
|
|
235
|
+
if __opts__["test"]:
|
|
236
|
+
test_comments.append(f"Team properties are set to be edited: {parameters}")
|
|
237
|
+
ret["result"] = None
|
|
238
|
+
ret["changes"]["team"] = team_change
|
|
239
|
+
else:
|
|
240
|
+
result = __salt__["github.edit_team"](name, profile=profile, **parameters)
|
|
241
|
+
if result:
|
|
242
|
+
ret["changes"]["team"] = team_change
|
|
243
|
+
else:
|
|
244
|
+
ret["result"] = False
|
|
245
|
+
ret["comment"] = "Failed to update team properties."
|
|
246
|
+
return ret
|
|
247
|
+
|
|
248
|
+
current_repos = set(__salt__["github.list_team_repos"](name, profile=profile))
|
|
249
|
+
repo_names = set(repo_names or [])
|
|
250
|
+
|
|
251
|
+
repos_to_add = repo_names - current_repos
|
|
252
|
+
repos_to_remove = current_repos - repo_names if repo_names else []
|
|
253
|
+
|
|
254
|
+
if repos_to_add:
|
|
255
|
+
if __opts__["test"]:
|
|
256
|
+
test_comments.append(
|
|
257
|
+
"Team {} will have the following repos added: {}.".format( # pylint: disable=consider-using-f-string
|
|
258
|
+
name, list(repos_to_add)
|
|
259
|
+
)
|
|
260
|
+
)
|
|
261
|
+
ret["result"] = None
|
|
262
|
+
for repo_name in repos_to_add:
|
|
263
|
+
repo_change = {
|
|
264
|
+
"old": f"Repo {repo_name} is not in team {name}",
|
|
265
|
+
"new": f"Repo {repo_name} is in team {name}",
|
|
266
|
+
}
|
|
267
|
+
if __opts__["test"]:
|
|
268
|
+
ret["changes"][repo_name] = repo_change
|
|
269
|
+
else:
|
|
270
|
+
result = __salt__["github.add_team_repo"](
|
|
271
|
+
repo_name, name, profile=profile, **kwargs
|
|
272
|
+
)
|
|
273
|
+
if result:
|
|
274
|
+
ret["changes"][repo_name] = repo_change
|
|
275
|
+
else:
|
|
276
|
+
ret["result"] = False
|
|
277
|
+
ret["comment"] = f"Failed to add repo {repo_name} to team {name}."
|
|
278
|
+
return ret
|
|
279
|
+
|
|
280
|
+
if repos_to_remove:
|
|
281
|
+
if __opts__["test"]:
|
|
282
|
+
test_comments.append(
|
|
283
|
+
"Team {} will have the following repos removed: {}.".format( # pylint: disable=consider-using-f-string
|
|
284
|
+
name, list(repos_to_remove)
|
|
285
|
+
)
|
|
286
|
+
)
|
|
287
|
+
ret["result"] = None
|
|
288
|
+
for repo_name in repos_to_remove:
|
|
289
|
+
repo_change = {
|
|
290
|
+
"old": f"Repo {repo_name} is in team {name}",
|
|
291
|
+
"new": f"Repo {repo_name} is not in team {name}",
|
|
292
|
+
}
|
|
293
|
+
if __opts__["test"]:
|
|
294
|
+
ret["changes"][repo_name] = repo_change
|
|
295
|
+
else:
|
|
296
|
+
result = __salt__["github.remove_team_repo"](
|
|
297
|
+
repo_name, name, profile=profile, **kwargs
|
|
298
|
+
)
|
|
299
|
+
if result:
|
|
300
|
+
ret["changes"][repo_name] = repo_change
|
|
301
|
+
else:
|
|
302
|
+
ret["result"] = False
|
|
303
|
+
ret["comment"] = f"Failed to remove repo {repo_name} from team {name}."
|
|
304
|
+
return ret
|
|
305
|
+
|
|
306
|
+
else: # Team does not exist - it will be created.
|
|
307
|
+
if __opts__["test"]:
|
|
308
|
+
# Note: member/repo changes are not projected for new teams
|
|
309
|
+
ret["comment"] = f"Team {name} is set to be created."
|
|
310
|
+
ret["changes"]["team"] = {"old": None, "new": f"Team {name} has been created"}
|
|
311
|
+
ret["result"] = None
|
|
312
|
+
return ret
|
|
313
|
+
|
|
314
|
+
result = __salt__["github.add_team"](
|
|
315
|
+
name,
|
|
316
|
+
description=description,
|
|
317
|
+
repo_names=repo_names,
|
|
318
|
+
permission=permission,
|
|
319
|
+
privacy=privacy,
|
|
320
|
+
profile=profile,
|
|
321
|
+
**kwargs,
|
|
322
|
+
)
|
|
323
|
+
if result:
|
|
324
|
+
ret["changes"]["team"] = {}
|
|
325
|
+
ret["changes"]["team"]["old"] = None
|
|
326
|
+
ret["changes"]["team"]["new"] = f"Team {name} has been created"
|
|
327
|
+
else:
|
|
328
|
+
ret["result"] = False
|
|
329
|
+
ret["comment"] = f"Failed to create team {name}."
|
|
330
|
+
return ret
|
|
331
|
+
|
|
332
|
+
manage_members = members is not None
|
|
333
|
+
|
|
334
|
+
mfa_deadline = datetime.datetime.now(tz=datetime.UTC) - datetime.timedelta(
|
|
335
|
+
seconds=no_mfa_grace_seconds
|
|
336
|
+
)
|
|
337
|
+
members_no_mfa = __salt__["github.list_members_without_mfa"](profile=profile)
|
|
338
|
+
|
|
339
|
+
members_lower = {}
|
|
340
|
+
for member_name, info in (members or {}).items():
|
|
341
|
+
members_lower[member_name.lower()] = info
|
|
342
|
+
|
|
343
|
+
member_change = False
|
|
344
|
+
current_members = __salt__["github.list_team_members"](name, profile=profile)
|
|
345
|
+
|
|
346
|
+
for member, member_info in (members or {}).items():
|
|
347
|
+
log.info("Checking member %s in team %s", member, name)
|
|
348
|
+
|
|
349
|
+
if member.lower() not in current_members:
|
|
350
|
+
if enforce_mfa and _member_violates_mfa(
|
|
351
|
+
member, member_info, mfa_deadline, members_no_mfa
|
|
352
|
+
):
|
|
353
|
+
if __opts__["test"]:
|
|
354
|
+
test_comments.append(
|
|
355
|
+
f"User {member} will not be added to the team because they do not have MFA."
|
|
356
|
+
)
|
|
357
|
+
else: # Add to team
|
|
358
|
+
member_change = True
|
|
359
|
+
member_add = {
|
|
360
|
+
"old": f"User {member} is not in team {name}",
|
|
361
|
+
"new": f"User {member} is in team {name}",
|
|
362
|
+
}
|
|
363
|
+
if __opts__["test"]:
|
|
364
|
+
test_comments.append(f"User {member} set to be added to the team.")
|
|
365
|
+
ret["result"] = None
|
|
366
|
+
ret["changes"][member] = member_add
|
|
367
|
+
else:
|
|
368
|
+
result = __salt__["github.add_team_member"](
|
|
369
|
+
member, name, profile=profile, **kwargs
|
|
370
|
+
)
|
|
371
|
+
if result:
|
|
372
|
+
ret["changes"][member] = member_add
|
|
373
|
+
else:
|
|
374
|
+
ret["result"] = False
|
|
375
|
+
ret["comment"] = f"Failed to add user {member} to team {name}."
|
|
376
|
+
return ret
|
|
377
|
+
|
|
378
|
+
for member in current_members:
|
|
379
|
+
mfa_violation = False
|
|
380
|
+
if member in members_lower:
|
|
381
|
+
mfa_violation = _member_violates_mfa(
|
|
382
|
+
member, members_lower[member], mfa_deadline, members_no_mfa
|
|
383
|
+
)
|
|
384
|
+
if manage_members and member not in members_lower or (enforce_mfa and mfa_violation):
|
|
385
|
+
# Remove from team
|
|
386
|
+
member_change = True
|
|
387
|
+
extra_changes = " due to MFA violation" if mfa_violation else ""
|
|
388
|
+
member_removal = {
|
|
389
|
+
"old": f"User {member} is in team {name}",
|
|
390
|
+
"new": f"User {member} is not in team {name}{extra_changes}",
|
|
391
|
+
}
|
|
392
|
+
if __opts__["test"]:
|
|
393
|
+
if mfa_violation:
|
|
394
|
+
test_comments.append(
|
|
395
|
+
f"User {member} set to be removed from the team because they do not have MFA."
|
|
396
|
+
)
|
|
397
|
+
else:
|
|
398
|
+
test_comments.append(f"User {member} set to be removed from the team.")
|
|
399
|
+
ret["result"] = None
|
|
400
|
+
ret["changes"][member] = member_removal
|
|
401
|
+
else:
|
|
402
|
+
result = __salt__["github.remove_team_member"](
|
|
403
|
+
member, name, profile=profile, **kwargs
|
|
404
|
+
)
|
|
405
|
+
if result:
|
|
406
|
+
ret["changes"][member] = member_removal
|
|
407
|
+
else:
|
|
408
|
+
ret["result"] = False
|
|
409
|
+
ret["comment"] = f"Failed to remove user {member} from team {name}."
|
|
410
|
+
return ret
|
|
411
|
+
|
|
412
|
+
if member_change: # Refresh team cache
|
|
413
|
+
__salt__["github.list_team_members"](name, profile=profile, ignore_cache=False, **kwargs)
|
|
414
|
+
|
|
415
|
+
if len(test_comments) > 0:
|
|
416
|
+
ret["comment"] = "\n".join(test_comments)
|
|
417
|
+
return ret
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def _member_violates_mfa(member, member_info, mfa_deadline, members_without_mfa):
|
|
421
|
+
if member_info.get("mfa_exempt", False):
|
|
422
|
+
return False
|
|
423
|
+
enforce_mfa_from = datetime.datetime.strptime(
|
|
424
|
+
member_info.get("enforce_mfa_from", "1970/01/01"), "%Y/%m/%d"
|
|
425
|
+
).replace(tzinfo=datetime.UTC)
|
|
426
|
+
return member.lower() in members_without_mfa and (mfa_deadline > enforce_mfa_from)
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
@_catch_errors
|
|
430
|
+
def team_absent(name, profile="github", **kwargs):
|
|
431
|
+
"""
|
|
432
|
+
Ensure a team is absent.
|
|
433
|
+
|
|
434
|
+
.. deprecated:: 1.1.0
|
|
435
|
+
Use :py:func:`github_org.team_absent <saltext.github.states.github_org.team_absent>` instead.
|
|
436
|
+
|
|
437
|
+
Example:
|
|
438
|
+
|
|
439
|
+
.. code-block:: yaml
|
|
440
|
+
|
|
441
|
+
ensure team test is present in github:
|
|
442
|
+
github.team_absent:
|
|
443
|
+
- name: test
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
name
|
|
447
|
+
This is the name of the team in the organization.
|
|
448
|
+
"""
|
|
449
|
+
ret = {"name": name, "changes": {}, "result": None, "comment": ""}
|
|
450
|
+
|
|
451
|
+
target = __salt__["github.get_team"](name, profile=profile, **kwargs)
|
|
452
|
+
|
|
453
|
+
if not target:
|
|
454
|
+
ret["comment"] = f"Team {name} does not exist"
|
|
455
|
+
ret["result"] = True
|
|
456
|
+
return ret
|
|
457
|
+
if __opts__["test"]:
|
|
458
|
+
ret["comment"] = f"Team {name} will be deleted"
|
|
459
|
+
ret["changes"]["old"] = f"Team {name} exists"
|
|
460
|
+
ret["changes"]["new"] = f"Team {name} deleted"
|
|
461
|
+
ret["result"] = None
|
|
462
|
+
return ret
|
|
463
|
+
|
|
464
|
+
result = __salt__["github.remove_team"](name, profile=profile, **kwargs)
|
|
465
|
+
|
|
466
|
+
if result:
|
|
467
|
+
ret["comment"] = f"Deleted team {name}"
|
|
468
|
+
ret["changes"].setdefault("old", f"Team {name} exists")
|
|
469
|
+
ret["changes"].setdefault("new", f"Team {name} deleted")
|
|
470
|
+
ret["result"] = True
|
|
471
|
+
else:
|
|
472
|
+
ret["comment"] = f"Failed to delete {name}"
|
|
473
|
+
ret["result"] = False
|
|
474
|
+
return ret
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
@_catch_errors
|
|
478
|
+
def repo_present(
|
|
479
|
+
name,
|
|
480
|
+
description=None,
|
|
481
|
+
homepage=None,
|
|
482
|
+
private=None,
|
|
483
|
+
has_issues=None,
|
|
484
|
+
has_wiki=None,
|
|
485
|
+
has_downloads=None,
|
|
486
|
+
auto_init=False,
|
|
487
|
+
gitignore_template=None,
|
|
488
|
+
license_template=None,
|
|
489
|
+
teams=None,
|
|
490
|
+
profile="github",
|
|
491
|
+
**kwargs,
|
|
492
|
+
):
|
|
493
|
+
"""
|
|
494
|
+
Ensure a repository is present
|
|
495
|
+
|
|
496
|
+
.. deprecated:: 1.1.0
|
|
497
|
+
Use :py:func:`github_repo.present <saltext.github.states.github_repo.present>` instead.
|
|
498
|
+
|
|
499
|
+
name
|
|
500
|
+
This is the name of the repository.
|
|
501
|
+
|
|
502
|
+
description
|
|
503
|
+
The description of the repository.
|
|
504
|
+
|
|
505
|
+
homepage
|
|
506
|
+
The URL with more information about the repository.
|
|
507
|
+
|
|
508
|
+
private
|
|
509
|
+
The visiblity of the repository. Note that private repositories require
|
|
510
|
+
a paid GitHub account.
|
|
511
|
+
|
|
512
|
+
has_issues
|
|
513
|
+
Whether to enable issues for this repository.
|
|
514
|
+
|
|
515
|
+
has_wiki
|
|
516
|
+
Whether to enable the wiki for this repository.
|
|
517
|
+
|
|
518
|
+
has_downloads
|
|
519
|
+
Whether to enable downloads for this repository.
|
|
520
|
+
|
|
521
|
+
auto_init
|
|
522
|
+
Whether to create an initial commit with an empty README.
|
|
523
|
+
|
|
524
|
+
gitignore_template
|
|
525
|
+
The desired language or platform for a .gitignore, e.g "Haskell".
|
|
526
|
+
|
|
527
|
+
license_template
|
|
528
|
+
The desired LICENSE template to apply, e.g "mit" or "mozilla".
|
|
529
|
+
|
|
530
|
+
teams
|
|
531
|
+
The teams for which this repo should belong to, specified as a dict of
|
|
532
|
+
team name to permission (``pull``, ``push`` or ``admin``).
|
|
533
|
+
|
|
534
|
+
Example:
|
|
535
|
+
|
|
536
|
+
.. code-block:: yaml
|
|
537
|
+
|
|
538
|
+
Ensure repo my-repo is present in github:
|
|
539
|
+
github.repo_present:
|
|
540
|
+
- name: my-repo
|
|
541
|
+
- description: My very important repository
|
|
542
|
+
"""
|
|
543
|
+
ret = {"name": name, "changes": {}, "result": True, "comment": ""}
|
|
544
|
+
|
|
545
|
+
# This is an optimization to cache all repos in the organization up front.
|
|
546
|
+
# The first use of this state will collect all of the repos and save a bunch
|
|
547
|
+
# of API calls for future use.
|
|
548
|
+
__salt__["github.list_repos"](profile=profile)
|
|
549
|
+
|
|
550
|
+
try:
|
|
551
|
+
target = __salt__["github.get_repo_info"](name, profile=profile, **kwargs)
|
|
552
|
+
except CommandExecutionError:
|
|
553
|
+
target = None
|
|
554
|
+
|
|
555
|
+
given_params = {
|
|
556
|
+
"description": description,
|
|
557
|
+
"homepage": homepage,
|
|
558
|
+
"private": private,
|
|
559
|
+
"has_issues": has_issues,
|
|
560
|
+
"has_wiki": has_wiki,
|
|
561
|
+
"has_downloads": has_downloads,
|
|
562
|
+
"auto_init": auto_init,
|
|
563
|
+
"gitignore_template": gitignore_template,
|
|
564
|
+
"license_template": license_template,
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
# Keep track of current_teams if we've fetched them after creating a new repo
|
|
568
|
+
current_teams = None
|
|
569
|
+
|
|
570
|
+
if target: # Repo already exists
|
|
571
|
+
# Some params are only valid on repo creation
|
|
572
|
+
ignore_params = ["auto_init", "gitignore_template", "license_template"]
|
|
573
|
+
parameters = {}
|
|
574
|
+
old_parameters = {}
|
|
575
|
+
for param_name, param_value in given_params.items():
|
|
576
|
+
if (
|
|
577
|
+
param_value is not None
|
|
578
|
+
and param_name not in ignore_params
|
|
579
|
+
and target[param_name] is not param_value
|
|
580
|
+
and target[param_name] != param_value
|
|
581
|
+
):
|
|
582
|
+
parameters[param_name] = param_value
|
|
583
|
+
old_parameters[param_name] = target[param_name]
|
|
584
|
+
|
|
585
|
+
if len(parameters) > 0:
|
|
586
|
+
repo_change = {
|
|
587
|
+
"old": f"Repo properties were {old_parameters}",
|
|
588
|
+
"new": f"Repo properties (that changed) are {parameters}",
|
|
589
|
+
}
|
|
590
|
+
if __opts__["test"]:
|
|
591
|
+
ret["changes"]["repo"] = repo_change
|
|
592
|
+
ret["result"] = None
|
|
593
|
+
else:
|
|
594
|
+
result = __salt__["github.edit_repo"](name, profile=profile, **parameters)
|
|
595
|
+
if result:
|
|
596
|
+
ret["changes"]["repo"] = repo_change
|
|
597
|
+
else:
|
|
598
|
+
ret["result"] = False
|
|
599
|
+
ret["comment"] = "Failed to update repo properties."
|
|
600
|
+
return ret
|
|
601
|
+
|
|
602
|
+
else: # Repo does not exist - it will be created.
|
|
603
|
+
repo_change = {"old": None, "new": f"Repo {name} has been created"}
|
|
604
|
+
if __opts__["test"]:
|
|
605
|
+
ret["changes"]["repo"] = repo_change
|
|
606
|
+
ret["result"] = None
|
|
607
|
+
else:
|
|
608
|
+
add_params = dict(given_params)
|
|
609
|
+
add_params.update(kwargs)
|
|
610
|
+
result = __salt__["github.add_repo"](name, profile=profile, **add_params)
|
|
611
|
+
|
|
612
|
+
if not result:
|
|
613
|
+
ret["result"] = False
|
|
614
|
+
ret["comment"] = f"Failed to create repo {name}."
|
|
615
|
+
return ret
|
|
616
|
+
|
|
617
|
+
# Turns out that trying to fetch teams for a new repo can 404 immediately
|
|
618
|
+
# after repo creation, so this waits until we can fetch teams successfully
|
|
619
|
+
# before continuing.
|
|
620
|
+
for attempt in range(3):
|
|
621
|
+
time.sleep(1)
|
|
622
|
+
try:
|
|
623
|
+
current_teams = __salt__["github.get_repo_teams"](
|
|
624
|
+
name, profile=profile, **kwargs
|
|
625
|
+
)
|
|
626
|
+
break
|
|
627
|
+
except CommandExecutionError:
|
|
628
|
+
log.info("Attempt %s to fetch new repo %s failed", attempt, name)
|
|
629
|
+
|
|
630
|
+
if current_teams is None:
|
|
631
|
+
ret["result"] = False
|
|
632
|
+
ret["comment"] = f"Failed to verify repo {name} after creation."
|
|
633
|
+
return ret
|
|
634
|
+
|
|
635
|
+
ret["changes"]["repo"] = repo_change
|
|
636
|
+
|
|
637
|
+
if teams is not None:
|
|
638
|
+
if __opts__["test"] and not target:
|
|
639
|
+
# Assume no teams if we're in test mode and the repo doesn't exist
|
|
640
|
+
current_teams = []
|
|
641
|
+
elif current_teams is None:
|
|
642
|
+
current_teams = __salt__["github.get_repo_teams"](name, profile=profile)
|
|
643
|
+
current_team_names = {t["name"] for t in current_teams}
|
|
644
|
+
|
|
645
|
+
# First remove any teams that aren't present
|
|
646
|
+
for team_name in current_team_names:
|
|
647
|
+
if team_name in teams:
|
|
648
|
+
continue
|
|
649
|
+
team_change = {
|
|
650
|
+
"old": f"Repo {name} is in team {team_name}",
|
|
651
|
+
"new": f"Repo {name} is not in team {team_name}",
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
if __opts__["test"]:
|
|
655
|
+
ret["changes"][team_name] = team_change
|
|
656
|
+
ret["result"] = None
|
|
657
|
+
else:
|
|
658
|
+
result = __salt__["github.remove_team_repo"](name, team_name, profile=profile)
|
|
659
|
+
if not result:
|
|
660
|
+
ret["result"] = False
|
|
661
|
+
ret["comment"] = f"Failed to remove repo {name} from team {team_name}."
|
|
662
|
+
return ret
|
|
663
|
+
ret["changes"][team_name] = team_change
|
|
664
|
+
|
|
665
|
+
# Next add or modify any necessary teams
|
|
666
|
+
for team_name, permission in teams.items():
|
|
667
|
+
if team_name not in current_team_names: # Need to add repo to team
|
|
668
|
+
team_change = {
|
|
669
|
+
"old": f"Repo {name} is not in team {team_name}",
|
|
670
|
+
"new": f"Repo {name} is in team {team_name}",
|
|
671
|
+
}
|
|
672
|
+
if __opts__["test"]:
|
|
673
|
+
ret["changes"][team_name] = team_change
|
|
674
|
+
ret["result"] = None
|
|
675
|
+
else:
|
|
676
|
+
result = __salt__["github.add_team_repo"](
|
|
677
|
+
name, team_name, profile=profile, permission=permission
|
|
678
|
+
)
|
|
679
|
+
if not result:
|
|
680
|
+
ret["result"] = False
|
|
681
|
+
ret["comment"] = f"Failed to add repo {name} to team {team_name}."
|
|
682
|
+
return ret
|
|
683
|
+
ret["changes"][team_name] = team_change
|
|
684
|
+
else:
|
|
685
|
+
current_permission = (
|
|
686
|
+
__salt__["github.list_team_repos"](team_name, profile=profile)
|
|
687
|
+
.get(name.lower(), {})
|
|
688
|
+
.get("permission")
|
|
689
|
+
)
|
|
690
|
+
if not current_permission:
|
|
691
|
+
ret["result"] = False
|
|
692
|
+
ret["comment"] = (
|
|
693
|
+
f"Failed to determine current permission for team {team_name} in repo {name}"
|
|
694
|
+
)
|
|
695
|
+
return ret
|
|
696
|
+
if current_permission == permission:
|
|
697
|
+
continue
|
|
698
|
+
team_change = {
|
|
699
|
+
"old": f"Repo {name} in team {team_name} has permission {current_permission}",
|
|
700
|
+
"new": f"Repo {name} in team {team_name} has permission {permission}",
|
|
701
|
+
}
|
|
702
|
+
if __opts__["test"]:
|
|
703
|
+
ret["changes"][team_name] = team_change
|
|
704
|
+
ret["result"] = None
|
|
705
|
+
else:
|
|
706
|
+
result = __salt__["github.add_team_repo"](
|
|
707
|
+
name, team_name, profile=profile, permission=permission
|
|
708
|
+
)
|
|
709
|
+
if not result:
|
|
710
|
+
ret["result"] = False
|
|
711
|
+
ret["comment"] = (
|
|
712
|
+
f"Failed to set permission on repo {name} from team {team_name} to {permission}."
|
|
713
|
+
)
|
|
714
|
+
return ret
|
|
715
|
+
ret["changes"][team_name] = team_change
|
|
716
|
+
return ret
|
|
717
|
+
|
|
718
|
+
|
|
719
|
+
@_catch_errors
|
|
720
|
+
def repo_absent(name, profile="github", **kwargs):
|
|
721
|
+
"""
|
|
722
|
+
Ensure a repo is absent.
|
|
723
|
+
|
|
724
|
+
.. deprecated:: 1.1.0
|
|
725
|
+
Use :py:func:`github_repo.absent <saltext.github.states.github_repo.absent>` instead.
|
|
726
|
+
|
|
727
|
+
Example:
|
|
728
|
+
|
|
729
|
+
.. code-block:: yaml
|
|
730
|
+
|
|
731
|
+
ensure repo test is absent in github:
|
|
732
|
+
github.repo_absent:
|
|
733
|
+
- name: test
|
|
734
|
+
|
|
735
|
+
name
|
|
736
|
+
This is the name of the repository in the organization.
|
|
737
|
+
"""
|
|
738
|
+
ret = {"name": name, "changes": {}, "result": None, "comment": ""}
|
|
739
|
+
|
|
740
|
+
try:
|
|
741
|
+
target = __salt__["github.get_repo_info"](name, profile=profile, **kwargs)
|
|
742
|
+
except CommandExecutionError:
|
|
743
|
+
target = None
|
|
744
|
+
|
|
745
|
+
if not target:
|
|
746
|
+
ret["comment"] = f"Repo {name} does not exist"
|
|
747
|
+
ret["result"] = True
|
|
748
|
+
return ret
|
|
749
|
+
|
|
750
|
+
if __opts__["test"]:
|
|
751
|
+
ret["comment"] = f"Repo {name} will be deleted"
|
|
752
|
+
ret["changes"]["old"] = f"Repo {name} exists"
|
|
753
|
+
ret["changes"]["new"] = f"Repo {name} deleted"
|
|
754
|
+
ret["result"] = None
|
|
755
|
+
return ret
|
|
756
|
+
|
|
757
|
+
result = __salt__["github.remove_repo"](name, profile=profile, **kwargs)
|
|
758
|
+
|
|
759
|
+
if result:
|
|
760
|
+
ret["comment"] = f"Deleted repo {name}"
|
|
761
|
+
ret["changes"].setdefault("old", f"Repo {name} exists")
|
|
762
|
+
ret["changes"].setdefault("new", f"Repo {name} deleted")
|
|
763
|
+
ret["result"] = True
|
|
764
|
+
return ret
|
|
765
|
+
|
|
766
|
+
ret["comment"] = (
|
|
767
|
+
f"Failed to delete repo {name}. Ensure the delete_repo scope is enabled if using OAuth."
|
|
768
|
+
)
|
|
769
|
+
ret["result"] = False
|
|
770
|
+
return ret
|
|
771
|
+
|
|
772
|
+
|
|
773
|
+
@_catch_errors
|
|
774
|
+
def ruleset_absent(
|
|
775
|
+
name, ruleset_type, profile="github", owner=None, repo_name=None, org_name=None, **kwargs
|
|
776
|
+
):
|
|
777
|
+
"""
|
|
778
|
+
Ensure a ruleset is absent.
|
|
779
|
+
|
|
780
|
+
.. deprecated:: 1.1.0
|
|
781
|
+
Use :py:func:`github_org.ruleset_absent <saltext.github.states.github_org.ruleset_absent>` or
|
|
782
|
+
:py:func:`github_repo.ruleset_absent <saltext.github.states.github_repo.ruleset_absent>` instead.
|
|
783
|
+
|
|
784
|
+
Example:
|
|
785
|
+
|
|
786
|
+
.. code-block:: yaml
|
|
787
|
+
|
|
788
|
+
ensure rulset test is absent in github:
|
|
789
|
+
github.ruleset_absent:
|
|
790
|
+
- name: test
|
|
791
|
+
- ruleset_type: repo
|
|
792
|
+
- owner: test
|
|
793
|
+
- repo_name: test_repo
|
|
794
|
+
|
|
795
|
+
name
|
|
796
|
+
name of the ruleset.
|
|
797
|
+
|
|
798
|
+
ruleset_type
|
|
799
|
+
type of ruleset (``org`` or ``repo``)
|
|
800
|
+
"""
|
|
801
|
+
|
|
802
|
+
ret = {"name": name, "changes": {}, "result": None, "comment": ""}
|
|
803
|
+
|
|
804
|
+
if ruleset_type not in ["org", "repo"]:
|
|
805
|
+
ret["result"] = False
|
|
806
|
+
ret["comment"] = "ruleset_type not set to repo or org"
|
|
807
|
+
return ret
|
|
808
|
+
|
|
809
|
+
if ruleset_type == "repo":
|
|
810
|
+
kwargs.update({"owner": owner, "repo_name": repo_name, "ruleset_type": ruleset_type})
|
|
811
|
+
if ruleset_type == "org":
|
|
812
|
+
kwargs.update({"org_name": org_name, "ruleset_type": ruleset_type})
|
|
813
|
+
|
|
814
|
+
rulesets = __salt__["github.list_rulesets"](profile, **kwargs)
|
|
815
|
+
|
|
816
|
+
if rulesets.get("result") is False:
|
|
817
|
+
ret["result"] = False
|
|
818
|
+
ret["comment"] = "error listing rulesets"
|
|
819
|
+
return ret
|
|
820
|
+
|
|
821
|
+
ruleset = next(
|
|
822
|
+
(ruleset for ruleset in (rulesets.get("rulesets") or []) if ruleset["name"] == name), None
|
|
823
|
+
)
|
|
824
|
+
if ruleset is None:
|
|
825
|
+
ret["comment"] = f"Ruleset {name} does not exist"
|
|
826
|
+
ret["result"] = True
|
|
827
|
+
return ret
|
|
828
|
+
|
|
829
|
+
if __opts__["test"]:
|
|
830
|
+
ret["comment"] = f"Ruleset {name} will be deleted"
|
|
831
|
+
ret["changes"]["old"] = f"ruleset {name} exists"
|
|
832
|
+
ret["changes"]["new"] = f"ruleset {name} deleted"
|
|
833
|
+
ret["result"] = None
|
|
834
|
+
return ret
|
|
835
|
+
|
|
836
|
+
kwargs.update({"ruleset_id": ruleset["id"]})
|
|
837
|
+
result = __salt__["github.delete_ruleset"](profile, **kwargs)
|
|
838
|
+
|
|
839
|
+
# delete_ruleset returns a (truthy) dict with result: False on failure
|
|
840
|
+
if result.get("result") is not False:
|
|
841
|
+
ret["comment"] = f"Deleted ruleset {name}"
|
|
842
|
+
ret["changes"]["old"] = f"ruleset {name} exists"
|
|
843
|
+
ret["changes"]["new"] = f"ruleset {name} deleted"
|
|
844
|
+
ret["result"] = True
|
|
845
|
+
return ret
|
|
846
|
+
|
|
847
|
+
ret["comment"] = f"Failed to delete ruleset {name}"
|
|
848
|
+
ret["result"] = False
|
|
849
|
+
return ret
|
|
850
|
+
|
|
851
|
+
|
|
852
|
+
@_catch_errors
|
|
853
|
+
def ruleset_present(
|
|
854
|
+
name,
|
|
855
|
+
ruleset_type,
|
|
856
|
+
profile="github",
|
|
857
|
+
owner=None,
|
|
858
|
+
repo_name=None,
|
|
859
|
+
org_name=None,
|
|
860
|
+
ruleset_params=None,
|
|
861
|
+
**kwargs,
|
|
862
|
+
):
|
|
863
|
+
"""
|
|
864
|
+
Ensure a ruleset is present.
|
|
865
|
+
|
|
866
|
+
.. deprecated:: 1.1.0
|
|
867
|
+
Use :py:func:`github_org.ruleset_present <saltext.github.states.github_org.ruleset_present>` or
|
|
868
|
+
:py:func:`github_repo.ruleset_present <saltext.github.states.github_repo.ruleset_present>` instead.
|
|
869
|
+
|
|
870
|
+
Example:
|
|
871
|
+
|
|
872
|
+
.. code-block:: yaml
|
|
873
|
+
|
|
874
|
+
ensure rulset test is present in github:
|
|
875
|
+
github.ruleset_present:
|
|
876
|
+
- name: test
|
|
877
|
+
- ruleset_type: repo
|
|
878
|
+
- owner: test
|
|
879
|
+
- repo_name: test_repo
|
|
880
|
+
- ruleset_params:
|
|
881
|
+
target: branch
|
|
882
|
+
enforcement: disabled
|
|
883
|
+
|
|
884
|
+
name
|
|
885
|
+
name of the ruleset.
|
|
886
|
+
|
|
887
|
+
ruleset_type
|
|
888
|
+
type of ruleset (``org`` or ``repo``)
|
|
889
|
+
|
|
890
|
+
ruleset_params
|
|
891
|
+
parameters to set the ruleset rules. i.e ``{enforcement: disabled}``
|
|
892
|
+
"""
|
|
893
|
+
ret = {"name": name, "changes": {}, "result": True, "comment": ""}
|
|
894
|
+
|
|
895
|
+
if ruleset_type not in ["org", "repo"]:
|
|
896
|
+
ret["result"] = False
|
|
897
|
+
ret["comment"] = "ruleset_type not set to repo or org"
|
|
898
|
+
return ret
|
|
899
|
+
|
|
900
|
+
if ruleset_type == "repo":
|
|
901
|
+
kwargs.update({"owner": owner, "repo_name": repo_name, "ruleset_type": ruleset_type})
|
|
902
|
+
if ruleset_type == "org":
|
|
903
|
+
kwargs.update({"org_name": org_name, "ruleset_type": ruleset_type})
|
|
904
|
+
|
|
905
|
+
if ruleset_params:
|
|
906
|
+
ruleset_params["name"] = name
|
|
907
|
+
else:
|
|
908
|
+
ruleset_params = {}
|
|
909
|
+
|
|
910
|
+
rulesets = __salt__["github.list_rulesets"](profile, **kwargs)
|
|
911
|
+
|
|
912
|
+
if rulesets.get("result") is False:
|
|
913
|
+
ret["result"] = False
|
|
914
|
+
ret["comment"] = "error listing rulesets"
|
|
915
|
+
return ret
|
|
916
|
+
|
|
917
|
+
ruleset = next(
|
|
918
|
+
(ruleset for ruleset in (rulesets.get("rulesets") or []) if ruleset["name"] == name), None
|
|
919
|
+
)
|
|
920
|
+
if ruleset is None:
|
|
921
|
+
ret["changes"] = {"old": "No existing ruleset found", "new": "Ruleset created"}
|
|
922
|
+
if __opts__["test"]:
|
|
923
|
+
ret["result"] = None
|
|
924
|
+
ret["comment"] = "ruleset will be added"
|
|
925
|
+
return ret
|
|
926
|
+
result = __salt__["github.add_ruleset"](profile, ruleset_params=ruleset_params, **kwargs)
|
|
927
|
+
ret["comment"] = "ruleset added"
|
|
928
|
+
if not result.get("id"):
|
|
929
|
+
ret["changes"] = {}
|
|
930
|
+
ret["result"] = False
|
|
931
|
+
ret["comment"] = "ruleset could not be added"
|
|
932
|
+
return ret
|
|
933
|
+
|
|
934
|
+
kwargs.update({"ruleset_id": ruleset["id"]})
|
|
935
|
+
ruleset_info = __salt__["github.get_ruleset"](profile, **kwargs)
|
|
936
|
+
if not ruleset_info.get("id"):
|
|
937
|
+
ret["comment"] = "Could not get ruleset"
|
|
938
|
+
ret["result"] = False
|
|
939
|
+
return ret
|
|
940
|
+
|
|
941
|
+
# Only diff the parameters that are being managed. The server
|
|
942
|
+
# response carries additional server-populated fields (id,
|
|
943
|
+
# timestamps, source, ...) that must not register as drift.
|
|
944
|
+
diff = {key: value for key, value in ruleset_params.items() if ruleset_info.get(key) != value}
|
|
945
|
+
if not diff:
|
|
946
|
+
ret["comment"] = "ruleset present"
|
|
947
|
+
return ret
|
|
948
|
+
|
|
949
|
+
ret["changes"] = {
|
|
950
|
+
"old": {key: ruleset_info.get(key) for key in diff},
|
|
951
|
+
"new": diff,
|
|
952
|
+
}
|
|
953
|
+
if __opts__["test"]:
|
|
954
|
+
ret["result"] = None
|
|
955
|
+
ret["comment"] = "ruleset will be updated"
|
|
956
|
+
return ret
|
|
957
|
+
|
|
958
|
+
kwargs.update({"ruleset_params": ruleset_params})
|
|
959
|
+
result = __salt__["github.update_ruleset"](profile, **kwargs)
|
|
960
|
+
if result.get("id"):
|
|
961
|
+
ret["comment"] = "ruleset updated"
|
|
962
|
+
else:
|
|
963
|
+
ret["changes"] = {}
|
|
964
|
+
ret["result"] = False
|
|
965
|
+
ret["comment"] = "Could not update ruleset"
|
|
966
|
+
return ret
|