Skip to content

Commit fc040b9

Browse files
authored
Merge pull request #1156 from MrBatschner/action-secrets
Add support for GitHub repository/organization secrets
2 parents dd71b98 + 25ca885 commit fc040b9

28 files changed

+6679
-12
lines changed

AUTHORS.rst

+2
Original file line numberDiff line numberDiff line change
@@ -223,4 +223,6 @@ Contributors
223223

224224
- Chris R (@offbyone)
225225

226+
- Thomas Buchner (@MrBatschner)
227+
226228
- Chris Cotter (@ccotter)

src/github3/actions/__init__.py

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"""
2+
github3.actions
3+
=============
4+
5+
Module which contains all GitHub Actions related material (only secrets
6+
so far).
7+
8+
See also: http://developer.github.com/v3/actions/
9+
"""
10+
11+
from .secrets import OrganizationSecret
12+
from .secrets import RepositorySecret
13+
from .secrets import SharedOrganizationSecret
14+
15+
__all__ = (
16+
"OrganizationSecret",
17+
"RepositorySecret",
18+
"SharedOrganizationSecret",
19+
)

src/github3/actions/secrets.py

+233
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
"""This module contains all the classes relating to GitHub Actions secrets."""
2+
3+
import typing
4+
5+
from .. import models
6+
7+
8+
class PublicKey(models.GitHubCore):
9+
"""Object representing a Public Key for GitHub Actions secrets.
10+
11+
See https://docs.github.com/en/rest/actions/secrets for more details.
12+
13+
.. attribute:: key_id
14+
15+
The ID of the public key
16+
17+
.. attribute:: key
18+
19+
The actual public key as a string
20+
"""
21+
22+
def _update_attributes(self, publickey):
23+
self.key_id = publickey["key_id"]
24+
self.key = publickey["key"]
25+
26+
def _repr(self):
27+
return f"<PublicKey [{self.key_id}]>"
28+
29+
def __str__(self):
30+
return self.key
31+
32+
33+
class _Secret(models.GitHubCore):
34+
"""Base class for all secrets for GitHub Actions.
35+
36+
See https://docs.github.com/en/rest/actions/secrets for more details.
37+
GitHub never reveals the secret value through its API, it is only accessible
38+
from within actions. Therefore, this object represents the secret's metadata
39+
but not its actual value.
40+
"""
41+
42+
class_name = "_Secret"
43+
44+
def _repr(self):
45+
return f"<{self.class_name} [{self.name}]>"
46+
47+
def __str__(self):
48+
return self.name
49+
50+
def _update_attributes(self, secret):
51+
self.name = secret["name"]
52+
self.created_at = self._strptime(secret["created_at"])
53+
self.updated_at = self._strptime(secret["updated_at"])
54+
55+
56+
class RepositorySecret(_Secret):
57+
"""An object representing a repository secret for GitHub Actions.
58+
59+
See https://docs.github.com/en/rest/actions/secrets for more details.
60+
GitHub never reveals the secret value through its API, it is only accessible
61+
from within actions. Therefore, this object represents the secret's metadata
62+
but not its actual value.
63+
64+
.. attribute:: name
65+
66+
The name of the secret
67+
68+
.. attribute:: created_at
69+
70+
The timestamp of when the secret was created
71+
72+
.. attribute:: updated_at
73+
74+
The timestamp of when the secret was last updated
75+
"""
76+
77+
class_name = "RepositorySecret"
78+
79+
80+
class SharedOrganizationSecret(_Secret):
81+
"""An object representing an organization secret for GitHub Actions that is
82+
shared with the repository.
83+
84+
See https://docs.github.com/en/rest/actions/secrets for more details.
85+
GitHub never reveals the secret value through its API, it is only accessible
86+
from within actions. Therefore, this object represents the secret's metadata
87+
but not its actual value.
88+
89+
.. attribute:: name
90+
91+
The name of the secret
92+
93+
.. attribute:: created_at
94+
95+
The timestamp of when the secret was created
96+
97+
.. attribute:: updated_at
98+
99+
The timestamp of when the secret was last updated
100+
"""
101+
102+
class_name = "SharedOrganizationSecret"
103+
104+
105+
class OrganizationSecret(_Secret):
106+
"""An object representing am organization secret for GitHub Actions.
107+
108+
See https://docs.github.com/en/rest/actions/secrets for more details.
109+
GitHub never reveals the secret value through its API, it is only accessible
110+
from within actions. Therefore, this object represents the secret's metadata
111+
but not its actual value.
112+
113+
.. attribute:: name
114+
115+
The name of the secret
116+
117+
.. attribute:: created_at
118+
119+
The timestamp of when the secret was created
120+
121+
.. attribute:: updated_at
122+
123+
The timestamp of when the secret was last updated
124+
125+
.. attribute:: visibility
126+
127+
Specifies which type of organization repositories have access to
128+
the secret. Can be one of all, private, selected.
129+
"""
130+
131+
class_name = "OrganizationSecret"
132+
133+
def _update_attributes(self, secret):
134+
super()._update_attributes(secret)
135+
self.visibility = secret["visibility"]
136+
if self.visibility == "selected":
137+
self.selected_repositories_url = secret[
138+
"selected_repositories_url"
139+
]
140+
141+
def selected_repositories(self, number=-1, etag=""):
142+
"""Iterates over all repositories this secret is visible to.
143+
144+
:param int number:
145+
(optional), number of repositories to return.
146+
Default: -1 returns all selected repositories.
147+
:param str etag:
148+
(optional), ETag from a previous request to the same endpoint
149+
:returns:
150+
Generator of selected repositories or None if the visibility of this
151+
secret is not set to 'selected'.
152+
:rtype:
153+
:class:`~github3.repos.ShortRepository`
154+
"""
155+
from .. import repos
156+
157+
if self.visibility != "selected":
158+
return None
159+
160+
return self._iter(
161+
int(number),
162+
self.selected_repositories_url,
163+
repos.ShortRepository,
164+
etag=etag,
165+
list_key="repositories",
166+
)
167+
168+
def set_selected_repositories(self, repository_ids: typing.Sequence[int]):
169+
"""Sets the selected repositories this secret is visible to.
170+
171+
:param list[int] repository_ids:
172+
A list of repository IDs which this secret should be visible to.
173+
:returns:
174+
A boolean indicating whether the update was successful.
175+
:rtype:
176+
bool
177+
"""
178+
if self.visibility != "selected":
179+
raise ValueError(
180+
"""cannot set a list of selected repositories when visibility
181+
is not 'selected'"""
182+
)
183+
184+
data = {"selected_repository_ids": repository_ids}
185+
186+
return self._boolean(
187+
self._put(self.selected_repositories_url, json=data), 204, 404
188+
)
189+
190+
def add_selected_repository(self, repository_id: int):
191+
"""Adds a repository to the list of repositories this secret is
192+
visible to.
193+
194+
:param int repository_id:
195+
The IDs of a repository this secret should be visible to.
196+
:raises:
197+
A ValueError if the visibility of this secret is not 'selected'.
198+
:returns:
199+
A boolean indicating if the repository was successfully added to
200+
the visible list.
201+
:rtype:
202+
bool
203+
"""
204+
if self.visibility != "selected":
205+
raise ValueError(
206+
"cannot add a repository when visibility is not 'selected'"
207+
)
208+
209+
url = "/".join([self.selected_repositories_url, str(repository_id)])
210+
return self._boolean(self._put(url), 204, 409)
211+
212+
def remove_selected_repository(self, repository_id: int):
213+
"""Deletes a repository from the list of repositories this secret is
214+
visible to.
215+
216+
:param int repository_id:
217+
The IDs of the repository this secret should no longer be
218+
visible to.
219+
:raises:
220+
A ValueError if the visibility of this secret is not 'selected'.
221+
:returns:
222+
A boolean indicating if the repository was successfully removed
223+
from the visible list.
224+
:rtype:
225+
bool
226+
"""
227+
if self.visibility != "selected":
228+
raise ValueError(
229+
"cannot delete a repository when visibility is not 'selected'"
230+
)
231+
232+
url = "/".join([self.selected_repositories_url, str(repository_id)])
233+
return self._boolean(self._delete(url), 204, 409)

0 commit comments

Comments
 (0)