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,116 @@
|
|
|
1
|
+
"""
|
|
2
|
+
State core managing repository labels.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
from saltext.github.utils.gh import labels as gh_labels
|
|
8
|
+
from saltext.github.utils.gh.exceptions import GitHubNotFoundError
|
|
9
|
+
|
|
10
|
+
log = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def present(conn, name, repo_name, *, color=None, description=None, owner=None, test=False):
|
|
14
|
+
"""
|
|
15
|
+
Ensure a label is present in a repository and configured as
|
|
16
|
+
specified. Unspecified settings are left unmanaged.
|
|
17
|
+
|
|
18
|
+
conn
|
|
19
|
+
:class:`~saltext.github.utils.gh.client.GitHubConnection` to use.
|
|
20
|
+
|
|
21
|
+
name
|
|
22
|
+
Name of the label.
|
|
23
|
+
|
|
24
|
+
repo_name
|
|
25
|
+
Name of the repository.
|
|
26
|
+
|
|
27
|
+
color
|
|
28
|
+
Hexadecimal color code for the label. A leading ``#``
|
|
29
|
+
is accepted.
|
|
30
|
+
|
|
31
|
+
description
|
|
32
|
+
Short description of the label.
|
|
33
|
+
|
|
34
|
+
owner
|
|
35
|
+
Owner of the repository. Defaults to the connection's owner.
|
|
36
|
+
|
|
37
|
+
test
|
|
38
|
+
Only project the changes without applying them (``test`` mode).
|
|
39
|
+
"""
|
|
40
|
+
ret = {"name": name, "changes": {}, "result": True, "comment": ""}
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
current = gh_labels.get(conn, repo_name, name, owner=owner)
|
|
44
|
+
except GitHubNotFoundError:
|
|
45
|
+
current = None
|
|
46
|
+
|
|
47
|
+
if current is None:
|
|
48
|
+
ret["changes"]["created"] = name
|
|
49
|
+
if test:
|
|
50
|
+
ret["result"] = None
|
|
51
|
+
ret["comment"] = f"Label {name} would have been created"
|
|
52
|
+
return ret
|
|
53
|
+
gh_labels.create(conn, repo_name, name, color=color, description=description, owner=owner)
|
|
54
|
+
ret["comment"] = f"Label {name} has been created"
|
|
55
|
+
return ret
|
|
56
|
+
|
|
57
|
+
diff = {}
|
|
58
|
+
if color is not None and gh_labels.normalize_color(
|
|
59
|
+
current["color"]
|
|
60
|
+
) != gh_labels.normalize_color(color):
|
|
61
|
+
diff["color"] = color
|
|
62
|
+
if description is not None and current.get("description") != description:
|
|
63
|
+
diff["description"] = description
|
|
64
|
+
|
|
65
|
+
if not diff:
|
|
66
|
+
ret["comment"] = f"Label {name} is already configured as specified"
|
|
67
|
+
return ret
|
|
68
|
+
|
|
69
|
+
ret["changes"]["old"] = {key: current.get(key) for key in diff}
|
|
70
|
+
ret["changes"]["new"] = diff
|
|
71
|
+
if test:
|
|
72
|
+
ret["result"] = None
|
|
73
|
+
ret["comment"] = f"Label {name} would have been updated"
|
|
74
|
+
return ret
|
|
75
|
+
|
|
76
|
+
gh_labels.update(conn, repo_name, name, owner=owner, **diff)
|
|
77
|
+
ret["comment"] = f"Label {name} has been updated"
|
|
78
|
+
return ret
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def absent(conn, name, repo_name, *, owner=None, test=False):
|
|
82
|
+
"""
|
|
83
|
+
Ensure a label is absent from a repository.
|
|
84
|
+
|
|
85
|
+
conn
|
|
86
|
+
:class:`~saltext.github.utils.gh.client.GitHubConnection` to use.
|
|
87
|
+
|
|
88
|
+
name
|
|
89
|
+
Name of the label.
|
|
90
|
+
|
|
91
|
+
repo_name
|
|
92
|
+
Name of the repository.
|
|
93
|
+
|
|
94
|
+
owner
|
|
95
|
+
Owner of the repository. Defaults to the connection's owner.
|
|
96
|
+
|
|
97
|
+
test
|
|
98
|
+
Only project the changes without applying them (``test`` mode).
|
|
99
|
+
"""
|
|
100
|
+
ret = {"name": name, "changes": {}, "result": True, "comment": ""}
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
gh_labels.get(conn, repo_name, name, owner=owner)
|
|
104
|
+
except GitHubNotFoundError:
|
|
105
|
+
ret["comment"] = f"Label {name} is already absent"
|
|
106
|
+
return ret
|
|
107
|
+
|
|
108
|
+
ret["changes"]["deleted"] = name
|
|
109
|
+
if test:
|
|
110
|
+
ret["result"] = None
|
|
111
|
+
ret["comment"] = f"Label {name} would have been removed"
|
|
112
|
+
return ret
|
|
113
|
+
|
|
114
|
+
gh_labels.delete(conn, repo_name, name, owner=owner)
|
|
115
|
+
ret["comment"] = f"Label {name} has been removed"
|
|
116
|
+
return ret
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"""
|
|
2
|
+
State core managing a repository's GitHub Pages site.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
from saltext.github.utils import filter_unset
|
|
8
|
+
from saltext.github.utils.gh import pages as gh_pages
|
|
9
|
+
from saltext.github.utils.gh.exceptions import GitHubNotFoundError
|
|
10
|
+
|
|
11
|
+
log = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def present(
|
|
15
|
+
conn,
|
|
16
|
+
name,
|
|
17
|
+
repo_name,
|
|
18
|
+
*,
|
|
19
|
+
build_type=None,
|
|
20
|
+
source_branch=None,
|
|
21
|
+
source_path=None,
|
|
22
|
+
cname=None,
|
|
23
|
+
https_enforced=None,
|
|
24
|
+
owner=None,
|
|
25
|
+
test=False,
|
|
26
|
+
):
|
|
27
|
+
"""
|
|
28
|
+
Ensure GitHub Pages are enabled for a repository and configured as
|
|
29
|
+
specified. Unspecified settings are left unmanaged.
|
|
30
|
+
|
|
31
|
+
conn
|
|
32
|
+
:class:`~saltext.github.utils.gh.client.GitHubConnection` to use.
|
|
33
|
+
|
|
34
|
+
name
|
|
35
|
+
State name (reported back unchanged).
|
|
36
|
+
|
|
37
|
+
repo_name
|
|
38
|
+
Name of the repository.
|
|
39
|
+
|
|
40
|
+
build_type
|
|
41
|
+
Process by which the site is built, either ``legacy``
|
|
42
|
+
(deploy from a branch) or ``workflow`` (GitHub Actions).
|
|
43
|
+
|
|
44
|
+
source_branch
|
|
45
|
+
For ``legacy`` builds, the repository branch to publish from.
|
|
46
|
+
|
|
47
|
+
source_path
|
|
48
|
+
For ``legacy`` builds, the repository directory to publish from,
|
|
49
|
+
either ``/`` or ``/docs``.
|
|
50
|
+
|
|
51
|
+
cname
|
|
52
|
+
Custom domain for the site.
|
|
53
|
+
|
|
54
|
+
https_enforced
|
|
55
|
+
Enforce HTTPS for the site.
|
|
56
|
+
|
|
57
|
+
owner
|
|
58
|
+
Owner of the repository. Defaults to the connection's owner.
|
|
59
|
+
|
|
60
|
+
test
|
|
61
|
+
Only project the changes without applying them (``test`` mode).
|
|
62
|
+
"""
|
|
63
|
+
ret = {"name": name, "changes": {}, "result": True, "comment": ""}
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
current = gh_pages.get(conn, repo_name, owner=owner)
|
|
67
|
+
except GitHubNotFoundError:
|
|
68
|
+
current = None
|
|
69
|
+
|
|
70
|
+
if current is None:
|
|
71
|
+
ret["changes"]["created"] = repo_name
|
|
72
|
+
if test:
|
|
73
|
+
ret["result"] = None
|
|
74
|
+
ret["comment"] = f"Pages would have been enabled for {repo_name}"
|
|
75
|
+
return ret
|
|
76
|
+
gh_pages.create(
|
|
77
|
+
conn,
|
|
78
|
+
repo_name,
|
|
79
|
+
build_type=build_type,
|
|
80
|
+
source=gh_pages.build_source(source_branch, source_path),
|
|
81
|
+
owner=owner,
|
|
82
|
+
)
|
|
83
|
+
if cname is not None or https_enforced is not None:
|
|
84
|
+
extra = {"https_enforced": https_enforced}
|
|
85
|
+
if cname is not None:
|
|
86
|
+
extra["cname"] = cname
|
|
87
|
+
gh_pages.update(conn, repo_name, owner=owner, **extra)
|
|
88
|
+
ret["comment"] = f"Pages have been enabled for {repo_name}"
|
|
89
|
+
return ret
|
|
90
|
+
|
|
91
|
+
current_source = current.get("source") or {}
|
|
92
|
+
desired = filter_unset(
|
|
93
|
+
{
|
|
94
|
+
"build_type": build_type,
|
|
95
|
+
"source_branch": source_branch,
|
|
96
|
+
"source_path": source_path,
|
|
97
|
+
"cname": cname,
|
|
98
|
+
"https_enforced": https_enforced,
|
|
99
|
+
}
|
|
100
|
+
)
|
|
101
|
+
current_flat = {
|
|
102
|
+
"build_type": current.get("build_type"),
|
|
103
|
+
"source_branch": current_source.get("branch"),
|
|
104
|
+
"source_path": current_source.get("path"),
|
|
105
|
+
"cname": current.get("cname"),
|
|
106
|
+
"https_enforced": current.get("https_enforced"),
|
|
107
|
+
}
|
|
108
|
+
diff = {key: val for key, val in desired.items() if current_flat[key] != val}
|
|
109
|
+
|
|
110
|
+
if not diff:
|
|
111
|
+
ret["comment"] = f"Pages for {repo_name} are already configured as specified"
|
|
112
|
+
return ret
|
|
113
|
+
|
|
114
|
+
ret["changes"]["old"] = {key: current_flat[key] for key in diff}
|
|
115
|
+
ret["changes"]["new"] = diff
|
|
116
|
+
if test:
|
|
117
|
+
ret["result"] = None
|
|
118
|
+
ret["comment"] = f"Pages configuration for {repo_name} would have been updated"
|
|
119
|
+
return ret
|
|
120
|
+
|
|
121
|
+
update_kwargs = dict(diff)
|
|
122
|
+
if "source_branch" in diff or "source_path" in diff:
|
|
123
|
+
# The source is submitted as a single object, carry over the
|
|
124
|
+
# unchanged half
|
|
125
|
+
update_kwargs.setdefault("source_branch", current_flat["source_branch"])
|
|
126
|
+
update_kwargs.setdefault("source_path", current_flat["source_path"])
|
|
127
|
+
gh_pages.update(
|
|
128
|
+
conn,
|
|
129
|
+
repo_name,
|
|
130
|
+
build_type=update_kwargs.get("build_type"),
|
|
131
|
+
source=gh_pages.build_source(
|
|
132
|
+
update_kwargs.get("source_branch"), update_kwargs.get("source_path")
|
|
133
|
+
),
|
|
134
|
+
cname=update_kwargs.get("cname", ...),
|
|
135
|
+
https_enforced=update_kwargs.get("https_enforced"),
|
|
136
|
+
owner=owner,
|
|
137
|
+
)
|
|
138
|
+
ret["comment"] = f"Pages configuration for {repo_name} has been updated"
|
|
139
|
+
return ret
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def absent(conn, name, repo_name, *, owner=None, test=False):
|
|
143
|
+
"""
|
|
144
|
+
Ensure GitHub Pages are disabled for a repository.
|
|
145
|
+
|
|
146
|
+
conn
|
|
147
|
+
:class:`~saltext.github.utils.gh.client.GitHubConnection` to use.
|
|
148
|
+
|
|
149
|
+
name
|
|
150
|
+
State name (reported back unchanged).
|
|
151
|
+
|
|
152
|
+
repo_name
|
|
153
|
+
Name of the repository.
|
|
154
|
+
|
|
155
|
+
owner
|
|
156
|
+
Owner of the repository. Defaults to the connection's owner.
|
|
157
|
+
|
|
158
|
+
test
|
|
159
|
+
Only project the changes without applying them (``test`` mode).
|
|
160
|
+
"""
|
|
161
|
+
ret = {"name": name, "changes": {}, "result": True, "comment": ""}
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
gh_pages.get(conn, repo_name, owner=owner)
|
|
165
|
+
except GitHubNotFoundError:
|
|
166
|
+
ret["comment"] = f"Pages are already disabled for {repo_name}"
|
|
167
|
+
return ret
|
|
168
|
+
|
|
169
|
+
ret["changes"]["deleted"] = repo_name
|
|
170
|
+
if test:
|
|
171
|
+
ret["result"] = None
|
|
172
|
+
ret["comment"] = f"Pages would have been disabled for {repo_name}"
|
|
173
|
+
return ret
|
|
174
|
+
|
|
175
|
+
gh_pages.delete(conn, repo_name, owner=owner)
|
|
176
|
+
ret["comment"] = f"Pages have been disabled for {repo_name}"
|
|
177
|
+
return ret
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"""
|
|
2
|
+
State core managing a repository's settings and team associations.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
from salt.exceptions import CommandExecutionError
|
|
8
|
+
|
|
9
|
+
from saltext.github.utils.gh import repos as gh_repos
|
|
10
|
+
from saltext.github.utils.gh import teams as gh_teams
|
|
11
|
+
from saltext.github.utils.gh.exceptions import GitHubNotFoundError
|
|
12
|
+
from saltext.github.utils.gh.statecores import StateError
|
|
13
|
+
from saltext.github.utils.gh.statecores import differs
|
|
14
|
+
|
|
15
|
+
log = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _repo_settings_diff(ret, target, managed):
|
|
19
|
+
"""
|
|
20
|
+
Record the settings of an existing repository that need updating
|
|
21
|
+
in ``ret`` and return them.
|
|
22
|
+
"""
|
|
23
|
+
update_params = {
|
|
24
|
+
param: value
|
|
25
|
+
for param, value in managed.items()
|
|
26
|
+
if value is not None and differs(target[param], value)
|
|
27
|
+
}
|
|
28
|
+
if update_params:
|
|
29
|
+
ret["changes"]["old"] = {param: target[param] for param in update_params}
|
|
30
|
+
ret["changes"]["new"] = update_params
|
|
31
|
+
return update_params
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _repo_teams_diff(ret, conn, name, teams, owner):
|
|
35
|
+
"""
|
|
36
|
+
Record the team association changes of an existing repository in
|
|
37
|
+
``ret`` and return them.
|
|
38
|
+
"""
|
|
39
|
+
current_teams = {team["name"] for team in gh_repos.get_teams(conn, name, owner=owner)}
|
|
40
|
+
teams_added = sorted(set(teams) - current_teams)
|
|
41
|
+
teams_removed = sorted(current_teams - set(teams))
|
|
42
|
+
team_permissions = {}
|
|
43
|
+
for team_name in sorted(set(teams) & current_teams):
|
|
44
|
+
team_repos = gh_teams.list_repos(
|
|
45
|
+
conn, gh_teams.find(conn, team_name, owner=owner), owner=owner
|
|
46
|
+
)
|
|
47
|
+
current_permission = team_repos.get(name.lower(), {}).get("permission")
|
|
48
|
+
if current_permission != teams[team_name]:
|
|
49
|
+
team_permissions[team_name] = {"old": current_permission, "new": teams[team_name]}
|
|
50
|
+
if teams_added:
|
|
51
|
+
ret["changes"]["teams_added"] = teams_added
|
|
52
|
+
if teams_removed:
|
|
53
|
+
ret["changes"]["teams_removed"] = teams_removed
|
|
54
|
+
if team_permissions:
|
|
55
|
+
ret["changes"]["team_permissions"] = team_permissions
|
|
56
|
+
return teams_added, teams_removed, team_permissions
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _set_team_repo(conn, name, team_name, permission, owner):
|
|
60
|
+
"""
|
|
61
|
+
Add a repository to a team or update the team's permission on it.
|
|
62
|
+
"""
|
|
63
|
+
gh_teams.add_repo(
|
|
64
|
+
conn, gh_teams.find(conn, team_name, owner=owner), name, permission=permission, owner=owner
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def present(
|
|
69
|
+
conn,
|
|
70
|
+
name,
|
|
71
|
+
*,
|
|
72
|
+
owner=None,
|
|
73
|
+
description=None,
|
|
74
|
+
homepage=None,
|
|
75
|
+
private=None,
|
|
76
|
+
has_issues=None,
|
|
77
|
+
has_wiki=None,
|
|
78
|
+
has_downloads=None,
|
|
79
|
+
auto_init=False,
|
|
80
|
+
gitignore_template=None,
|
|
81
|
+
license_template=None,
|
|
82
|
+
default_branch=None,
|
|
83
|
+
teams=None,
|
|
84
|
+
allow_privacy_change=False,
|
|
85
|
+
allow_create=True,
|
|
86
|
+
test=False,
|
|
87
|
+
):
|
|
88
|
+
"""
|
|
89
|
+
Ensure a repository is present and configured as specified.
|
|
90
|
+
Unspecified settings are left unmanaged.
|
|
91
|
+
|
|
92
|
+
conn
|
|
93
|
+
:class:`~saltext.github.utils.gh.client.GitHubConnection` to use.
|
|
94
|
+
|
|
95
|
+
name
|
|
96
|
+
Name of the repository.
|
|
97
|
+
|
|
98
|
+
owner
|
|
99
|
+
Owner of the repository. Defaults to the connection's owner.
|
|
100
|
+
|
|
101
|
+
allow_privacy_change
|
|
102
|
+
Allow changing an existing repository's visibility (the
|
|
103
|
+
profile's :ghconf:`allow_repo_privacy_changes`). Defaults to false.
|
|
104
|
+
|
|
105
|
+
allow_create
|
|
106
|
+
Create the repository when it is missing. When disabled, a
|
|
107
|
+
missing repository raises instead (resource context, where the
|
|
108
|
+
repository's existence is a given). Defaults to true.
|
|
109
|
+
|
|
110
|
+
test
|
|
111
|
+
Only project the changes without applying them (``test`` mode).
|
|
112
|
+
|
|
113
|
+
For the remaining parameters, see the ``github_repo.present`` state.
|
|
114
|
+
"""
|
|
115
|
+
ret = {
|
|
116
|
+
"name": name,
|
|
117
|
+
"changes": {},
|
|
118
|
+
"result": True,
|
|
119
|
+
"comment": f"Repo {name} is present as specified",
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
# This is an optimization to cache all of the owner's repos up front.
|
|
123
|
+
# The first use of this state will collect all of the repos and save a bunch
|
|
124
|
+
# of API calls for future use.
|
|
125
|
+
gh_repos.list_all(conn, owner=owner)
|
|
126
|
+
|
|
127
|
+
try:
|
|
128
|
+
target = gh_repos.get_info(conn, name, owner=owner)
|
|
129
|
+
except GitHubNotFoundError:
|
|
130
|
+
if not allow_create:
|
|
131
|
+
raise CommandExecutionError(f"Repo {name} does not exist") from None
|
|
132
|
+
target = None
|
|
133
|
+
|
|
134
|
+
update_params = {}
|
|
135
|
+
teams_added, teams_removed, team_permissions = [], [], {}
|
|
136
|
+
if target is None:
|
|
137
|
+
ret["changes"]["created"] = name
|
|
138
|
+
if teams:
|
|
139
|
+
ret["changes"]["teams_added"] = sorted(teams)
|
|
140
|
+
else:
|
|
141
|
+
update_params = _repo_settings_diff(
|
|
142
|
+
ret,
|
|
143
|
+
target,
|
|
144
|
+
{
|
|
145
|
+
"description": description,
|
|
146
|
+
"homepage": homepage,
|
|
147
|
+
"private": private,
|
|
148
|
+
"has_issues": has_issues,
|
|
149
|
+
"has_wiki": has_wiki,
|
|
150
|
+
"has_downloads": has_downloads,
|
|
151
|
+
"default_branch": default_branch,
|
|
152
|
+
},
|
|
153
|
+
)
|
|
154
|
+
if teams is not None:
|
|
155
|
+
teams_added, teams_removed, team_permissions = _repo_teams_diff(
|
|
156
|
+
ret, conn, name, teams, owner
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
if not ret["changes"]:
|
|
160
|
+
return ret
|
|
161
|
+
|
|
162
|
+
verb = "created" if target is None else "updated"
|
|
163
|
+
if test:
|
|
164
|
+
ret["result"] = None
|
|
165
|
+
ret["comment"] = f"Repo {name} would have been {verb}"
|
|
166
|
+
return ret
|
|
167
|
+
|
|
168
|
+
applied = {}
|
|
169
|
+
try:
|
|
170
|
+
if target is None:
|
|
171
|
+
gh_repos.create(
|
|
172
|
+
conn,
|
|
173
|
+
name,
|
|
174
|
+
owner=owner,
|
|
175
|
+
description=description,
|
|
176
|
+
homepage=homepage,
|
|
177
|
+
private=private,
|
|
178
|
+
has_issues=has_issues,
|
|
179
|
+
has_wiki=has_wiki,
|
|
180
|
+
has_downloads=has_downloads,
|
|
181
|
+
auto_init=auto_init,
|
|
182
|
+
gitignore_template=gitignore_template,
|
|
183
|
+
license_template=license_template,
|
|
184
|
+
)
|
|
185
|
+
applied["created"] = name
|
|
186
|
+
for team_name in sorted(teams or {}):
|
|
187
|
+
_set_team_repo(conn, name, team_name, teams[team_name], owner)
|
|
188
|
+
applied.setdefault("teams_added", []).append(team_name)
|
|
189
|
+
else:
|
|
190
|
+
if update_params:
|
|
191
|
+
gh_repos.update(
|
|
192
|
+
conn,
|
|
193
|
+
name,
|
|
194
|
+
owner=owner,
|
|
195
|
+
allow_privacy_change=allow_privacy_change,
|
|
196
|
+
**update_params,
|
|
197
|
+
)
|
|
198
|
+
applied["old"] = ret["changes"]["old"]
|
|
199
|
+
applied["new"] = ret["changes"]["new"]
|
|
200
|
+
for team_name in teams_added:
|
|
201
|
+
_set_team_repo(conn, name, team_name, teams[team_name], owner)
|
|
202
|
+
applied.setdefault("teams_added", []).append(team_name)
|
|
203
|
+
for team_name in teams_removed:
|
|
204
|
+
gh_teams.remove_repo(
|
|
205
|
+
conn, gh_teams.find(conn, team_name, owner=owner), name, owner=owner
|
|
206
|
+
)
|
|
207
|
+
applied.setdefault("teams_removed", []).append(team_name)
|
|
208
|
+
for team_name, permission in team_permissions.items():
|
|
209
|
+
_set_team_repo(conn, name, team_name, teams[team_name], owner)
|
|
210
|
+
applied.setdefault("team_permissions", {})[team_name] = permission
|
|
211
|
+
except CommandExecutionError as err:
|
|
212
|
+
raise StateError(str(err), changes=applied) from err
|
|
213
|
+
ret["comment"] = f"Repo {name} has been {verb}"
|
|
214
|
+
return ret
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def absent(conn, name, *, owner=None, test=False):
|
|
218
|
+
"""
|
|
219
|
+
Ensure a repository is absent.
|
|
220
|
+
|
|
221
|
+
conn
|
|
222
|
+
:class:`~saltext.github.utils.gh.client.GitHubConnection` to use.
|
|
223
|
+
|
|
224
|
+
name
|
|
225
|
+
Name of the repository.
|
|
226
|
+
|
|
227
|
+
owner
|
|
228
|
+
Owner of the repository. Defaults to the connection's owner.
|
|
229
|
+
|
|
230
|
+
test
|
|
231
|
+
Only project the changes without applying them (``test`` mode).
|
|
232
|
+
"""
|
|
233
|
+
ret = {"name": name, "changes": {}, "result": True, "comment": ""}
|
|
234
|
+
|
|
235
|
+
try:
|
|
236
|
+
gh_repos.get_info(conn, name, owner=owner)
|
|
237
|
+
except GitHubNotFoundError:
|
|
238
|
+
ret["comment"] = f"Repo {name} does not exist"
|
|
239
|
+
return ret
|
|
240
|
+
|
|
241
|
+
ret["changes"]["deleted"] = name
|
|
242
|
+
if test:
|
|
243
|
+
ret["result"] = None
|
|
244
|
+
ret["comment"] = f"Repo {name} would have been deleted"
|
|
245
|
+
return ret
|
|
246
|
+
|
|
247
|
+
gh_repos.delete(conn, name, owner=owner)
|
|
248
|
+
ret["comment"] = f"Repo {name} has been deleted"
|
|
249
|
+
return ret
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""
|
|
2
|
+
State core managing repository rulesets.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
from saltext.github.utils.gh import rulesets as gh_rulesets
|
|
8
|
+
|
|
9
|
+
log = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def present(conn, name, repo_name, *, ruleset_params=None, owner=None, test=False):
|
|
13
|
+
"""
|
|
14
|
+
Ensure a repository ruleset is present and configured as specified.
|
|
15
|
+
Only the parameters included in ``ruleset_params`` are compared,
|
|
16
|
+
server-populated fields are ignored.
|
|
17
|
+
|
|
18
|
+
conn
|
|
19
|
+
:class:`~saltext.github.utils.gh.client.GitHubConnection` to use.
|
|
20
|
+
|
|
21
|
+
name
|
|
22
|
+
Ruleset name to manage.
|
|
23
|
+
|
|
24
|
+
repo_name
|
|
25
|
+
Name of the repository.
|
|
26
|
+
|
|
27
|
+
ruleset_params
|
|
28
|
+
Ruleset definition. Must contain at least ``enforcement``
|
|
29
|
+
when the ruleset needs to be created. ``name`` is pulled from
|
|
30
|
+
this state's ``name``.
|
|
31
|
+
|
|
32
|
+
owner
|
|
33
|
+
Owner of the repository. Defaults to the connection's owner.
|
|
34
|
+
|
|
35
|
+
test
|
|
36
|
+
Only project the changes without applying them (``test`` mode).
|
|
37
|
+
"""
|
|
38
|
+
ret = {"name": name, "changes": {}, "result": True, "comment": ""}
|
|
39
|
+
|
|
40
|
+
ruleset_params = dict(ruleset_params or {})
|
|
41
|
+
ruleset_params["name"] = name
|
|
42
|
+
|
|
43
|
+
rulesets = gh_rulesets.list_all(conn, "repo", owner=owner, repo_name=repo_name)
|
|
44
|
+
ruleset = next((rls for rls in rulesets or [] if rls["name"] == name), None)
|
|
45
|
+
|
|
46
|
+
if ruleset is None:
|
|
47
|
+
ret["changes"]["created"] = name
|
|
48
|
+
if test:
|
|
49
|
+
ret["result"] = None
|
|
50
|
+
ret["comment"] = f"Ruleset {name} would have been created"
|
|
51
|
+
return ret
|
|
52
|
+
gh_rulesets.create(conn, ruleset_params, "repo", owner=owner, repo_name=repo_name)
|
|
53
|
+
ret["comment"] = f"Ruleset {name} has been created"
|
|
54
|
+
return ret
|
|
55
|
+
|
|
56
|
+
ruleset_info = gh_rulesets.get(conn, ruleset["id"], "repo", owner=owner, repo_name=repo_name)
|
|
57
|
+
|
|
58
|
+
# Only diff the parameters that are being managed. The server
|
|
59
|
+
# response carries additional server-populated fields (id,
|
|
60
|
+
# timestamps, source, ...) that must not register as drift.
|
|
61
|
+
diff = {key: val for key, val in ruleset_params.items() if ruleset_info.get(key) != val}
|
|
62
|
+
if not diff:
|
|
63
|
+
ret["comment"] = f"Ruleset {name} is already configured as specified"
|
|
64
|
+
return ret
|
|
65
|
+
|
|
66
|
+
ret["changes"]["old"] = {key: ruleset_info.get(key) for key in diff}
|
|
67
|
+
ret["changes"]["new"] = diff
|
|
68
|
+
if test:
|
|
69
|
+
ret["result"] = None
|
|
70
|
+
ret["comment"] = f"Ruleset {name} would have been updated"
|
|
71
|
+
return ret
|
|
72
|
+
|
|
73
|
+
gh_rulesets.update(
|
|
74
|
+
conn, ruleset["id"], ruleset_params, "repo", owner=owner, repo_name=repo_name
|
|
75
|
+
)
|
|
76
|
+
ret["comment"] = f"Ruleset {name} has been updated"
|
|
77
|
+
return ret
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def absent(conn, name, repo_name, *, owner=None, test=False):
|
|
81
|
+
"""
|
|
82
|
+
Ensure a repository ruleset is absent.
|
|
83
|
+
|
|
84
|
+
conn
|
|
85
|
+
:class:`~saltext.github.utils.gh.client.GitHubConnection` to use.
|
|
86
|
+
|
|
87
|
+
name
|
|
88
|
+
Ruleset name to manage.
|
|
89
|
+
|
|
90
|
+
repo_name
|
|
91
|
+
Name of the repository.
|
|
92
|
+
|
|
93
|
+
owner
|
|
94
|
+
Owner of the repository. Defaults to the connection's owner.
|
|
95
|
+
|
|
96
|
+
test
|
|
97
|
+
Only project the changes without applying them (``test`` mode).
|
|
98
|
+
"""
|
|
99
|
+
ret = {"name": name, "changes": {}, "result": True, "comment": ""}
|
|
100
|
+
|
|
101
|
+
rulesets = gh_rulesets.list_all(conn, "repo", owner=owner, repo_name=repo_name)
|
|
102
|
+
ruleset = next((rls for rls in rulesets or [] if rls["name"] == name), None)
|
|
103
|
+
if ruleset is None:
|
|
104
|
+
ret["comment"] = f"Ruleset {name} does not exist"
|
|
105
|
+
return ret
|
|
106
|
+
|
|
107
|
+
ret["changes"]["deleted"] = name
|
|
108
|
+
if test:
|
|
109
|
+
ret["result"] = None
|
|
110
|
+
ret["comment"] = f"Ruleset {name} would have been deleted"
|
|
111
|
+
return ret
|
|
112
|
+
|
|
113
|
+
gh_rulesets.delete(conn, ruleset["id"], "repo", owner=owner, repo_name=repo_name)
|
|
114
|
+
ret["comment"] = f"Ruleset {name} has been deleted"
|
|
115
|
+
return ret
|