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