semaphoreui-client 0.1.18__tar.gz → 0.2.0__tar.gz
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.
- {semaphoreui_client-0.1.18 → semaphoreui_client-0.2.0}/PKG-INFO +1 -1
- semaphoreui_client-0.2.0/semaphoreui_client/__about__.py +1 -0
- {semaphoreui_client-0.1.18 → semaphoreui_client-0.2.0}/semaphoreui_client/client.py +317 -520
- semaphoreui_client-0.1.18/semaphoreui_client/__about__.py +0 -1
- {semaphoreui_client-0.1.18 → semaphoreui_client-0.2.0}/.github/workflows/checks.yml +0 -0
- {semaphoreui_client-0.1.18 → semaphoreui_client-0.2.0}/.github/workflows/pre-release.yml +0 -0
- {semaphoreui_client-0.1.18 → semaphoreui_client-0.2.0}/.github/workflows/release.yml +0 -0
- {semaphoreui_client-0.1.18 → semaphoreui_client-0.2.0}/.gitignore +0 -0
- {semaphoreui_client-0.1.18 → semaphoreui_client-0.2.0}/README.md +0 -0
- {semaphoreui_client-0.1.18 → semaphoreui_client-0.2.0}/pyproject.toml +0 -0
- {semaphoreui_client-0.1.18 → semaphoreui_client-0.2.0}/semaphoreui_client/__init__.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: semaphoreui-client
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.2.0
|
4
4
|
Summary: An api client for interacting with Semaphore UI
|
5
5
|
Project-URL: Documentation, https://github.com/rockstar/semaphoreui-client#readme
|
6
6
|
Project-URL: Issues, https://github.com/rockstar/semaphoreui-client/issues
|
@@ -0,0 +1 @@
|
|
1
|
+
__version__ = "0.2.0"
|
@@ -43,10 +43,6 @@ class SemaphoreUIClient:
|
|
43
43
|
assert response.status_code == 201
|
44
44
|
return Token(**response.json(), client=self)
|
45
45
|
|
46
|
-
def delete_token(self, id: str) -> None:
|
47
|
-
response = self.http.delete(f"{self.api_endpoint}/user/tokens/{id}")
|
48
|
-
assert response.status_code in (204, 404) # 404 if token was already expired
|
49
|
-
|
50
46
|
def projects(self) -> typing.List["Project"]:
|
51
47
|
response = self.http.get(f"{self.api_endpoint}/projects")
|
52
48
|
assert response.status_code == 200
|
@@ -80,118 +76,175 @@ class SemaphoreUIClient:
|
|
80
76
|
assert response.status_code == 201
|
81
77
|
return Project(**response.json(), client=self)
|
82
78
|
|
83
|
-
def delete_project(self, id: int) -> None:
|
84
|
-
response = self.http.delete(f"{self.api_endpoint}/project/{id}")
|
85
|
-
assert response.status_code == 204
|
86
79
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
80
|
+
@dataclass
|
81
|
+
class Integration:
|
82
|
+
"""A project integration"""
|
83
|
+
|
84
|
+
id: int
|
85
|
+
name: str
|
86
|
+
project_id: int
|
87
|
+
template_id: int
|
88
|
+
|
89
|
+
client: SemaphoreUIClient
|
90
|
+
|
91
|
+
def save(self) -> None:
|
92
|
+
response = self.client.http.put(
|
93
|
+
f"{self.client.api_endpoint}/project/{self.project_id}/integrations/{self.id}",
|
98
94
|
json={
|
99
|
-
"
|
100
|
-
"
|
101
|
-
"
|
102
|
-
"max_parallel_tasks": max_parallel_tasks,
|
103
|
-
"type": type,
|
95
|
+
"project_id": self.project_id,
|
96
|
+
"name": self.name,
|
97
|
+
"template_id": self.template_id,
|
104
98
|
},
|
105
99
|
)
|
106
100
|
assert response.status_code == 204
|
107
101
|
|
108
|
-
def
|
109
|
-
response = self.http.
|
110
|
-
|
111
|
-
|
102
|
+
def delete(self) -> None:
|
103
|
+
response = self.client.http.delete(
|
104
|
+
f"{self.client.api_endpoint}/project/{self.project_id}/integrations/{self.id}"
|
105
|
+
)
|
106
|
+
assert response.status_code == 204
|
112
107
|
|
113
|
-
def get_project_role(self, id: int) -> "Permissions":
|
114
|
-
response = self.http.get(f"{self.api_endpoint}/project/{id}/role")
|
115
|
-
assert response.status_code == 200
|
116
|
-
return Permissions(**response.json())
|
117
108
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
return [Event(**data) for data in response.json()]
|
109
|
+
@dataclass
|
110
|
+
class Token:
|
111
|
+
"""An authorization token."""
|
122
112
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
113
|
+
id: str
|
114
|
+
created: str
|
115
|
+
expired: bool
|
116
|
+
user_id: int
|
127
117
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
118
|
+
client: SemaphoreUIClient
|
119
|
+
|
120
|
+
def delete(self) -> None:
|
121
|
+
response = self.client.http.delete(
|
122
|
+
f"{self.client.api_endpoint}/user/tokens/{self.id}"
|
132
123
|
)
|
133
|
-
assert response.status_code
|
124
|
+
assert response.status_code in (204, 404) # 404 if token was already expired
|
134
125
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
126
|
+
|
127
|
+
@dataclass
|
128
|
+
class Project:
|
129
|
+
"""A Semaphore UI project."""
|
130
|
+
|
131
|
+
id: int
|
132
|
+
name: str
|
133
|
+
created: str
|
134
|
+
alert: bool
|
135
|
+
alert_chat: str
|
136
|
+
max_parallel_tasks: int
|
137
|
+
type: str # This might be an enum?
|
138
|
+
|
139
|
+
client: SemaphoreUIClient
|
140
|
+
|
141
|
+
@property
|
142
|
+
def url(self) -> str:
|
143
|
+
return f"{self.client.api_endpoint}/project/{self.id}"
|
144
|
+
|
145
|
+
def delete(self) -> None:
|
146
|
+
response = self.client.http.delete(
|
147
|
+
f"{self.client.api_endpoint}/project/{self.id}"
|
139
148
|
)
|
140
149
|
assert response.status_code == 204
|
141
150
|
|
142
|
-
def
|
143
|
-
response = self.http.
|
151
|
+
def save(self) -> None:
|
152
|
+
response = self.client.http.put(
|
153
|
+
f"{self.client.api_endpoint}/project/{self.id}",
|
154
|
+
json={
|
155
|
+
"name": self.name,
|
156
|
+
"alert": self.alert,
|
157
|
+
"alert_chat": self.alert_chat,
|
158
|
+
"max_parallel_tasks": self.max_parallel_tasks,
|
159
|
+
"type": self.type,
|
160
|
+
},
|
161
|
+
)
|
144
162
|
assert response.status_code == 204
|
145
163
|
|
146
|
-
def
|
147
|
-
response = self.http.get(
|
164
|
+
def backup(self) -> "ProjectBackup":
|
165
|
+
response = self.client.http.get(
|
166
|
+
f"{self.client.api_endpoint}/project/{self.id}/backup"
|
167
|
+
)
|
148
168
|
assert response.status_code == 200
|
149
|
-
return
|
169
|
+
return ProjectBackup(**response.json())
|
150
170
|
|
151
|
-
def
|
152
|
-
|
153
|
-
|
154
|
-
response = self.http.post(
|
155
|
-
f"{self.api_endpoint}/project/{id}integrations",
|
156
|
-
json={"project_id": project_id, "name": name, "template_id": template_id},
|
171
|
+
def role(self) -> "Permissions":
|
172
|
+
response = self.client.http.get(
|
173
|
+
f"{self.client.api_endpoint}/project/{self.id}/role"
|
157
174
|
)
|
158
175
|
assert response.status_code == 200
|
159
|
-
return
|
160
|
-
|
161
|
-
def
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
176
|
+
return Permissions(**response.json())
|
177
|
+
|
178
|
+
def events(self) -> typing.List["Event"]:
|
179
|
+
response = self.client.http.get(
|
180
|
+
f"{self.client.api_endpoint}/project/{self.id}/events"
|
181
|
+
)
|
182
|
+
assert response.status_code == 200
|
183
|
+
return [Event(**data) for data in response.json()]
|
184
|
+
|
185
|
+
def users(
|
186
|
+
self, sort: typing.Optional[str] = None, order: typing.Optional[str] = None
|
187
|
+
) -> typing.List["ProjectUser"]:
|
188
|
+
params = {}
|
189
|
+
if sort is not None:
|
190
|
+
params["sort"] = sort
|
191
|
+
if order is not None:
|
192
|
+
params["order"] = order
|
193
|
+
response = self.client.http.get(
|
194
|
+
f"{self.client.api_endpoint}/project/{self.id}/users", params=params
|
195
|
+
)
|
196
|
+
assert response.status_code == 200
|
197
|
+
return [
|
198
|
+
ProjectUser(**data, client=self.client, project_id=self.id)
|
199
|
+
for data in response.json()
|
200
|
+
]
|
201
|
+
|
202
|
+
def add_user(self, user: "ProjectUser") -> None:
|
203
|
+
response = self.client.http.post(
|
204
|
+
f"{self.client.api_endpoint}/project/{self.id}/users",
|
205
|
+
json=user.to_json(), # type: ignore
|
167
206
|
)
|
168
207
|
assert response.status_code == 204
|
169
208
|
|
170
|
-
def
|
171
|
-
response = self.http.delete(
|
172
|
-
f"{self.api_endpoint}/project/{
|
209
|
+
def remove_user(self, user_id: int) -> None:
|
210
|
+
response = self.client.http.delete(
|
211
|
+
f"{self.client.api_endpoint}/project/{self.id}/users/{user_id}"
|
173
212
|
)
|
174
213
|
assert response.status_code == 204
|
175
214
|
|
176
|
-
def
|
215
|
+
def update_user(self, user: "ProjectUser") -> None:
|
216
|
+
response = self.client.http.put(
|
217
|
+
f"{self.client.api_endpoint}/project/{self.id}/users/{user.id}",
|
218
|
+
json=user.to_json(), # type: ignore
|
219
|
+
)
|
220
|
+
assert response.status_code == 204
|
221
|
+
|
222
|
+
def keys(
|
177
223
|
self,
|
178
|
-
project_id: int,
|
179
224
|
key_type: typing.Optional[str] = None,
|
180
225
|
sort: typing.Optional[str] = None,
|
181
226
|
order: typing.Optional[str] = None,
|
182
227
|
) -> typing.List["Key"]:
|
183
|
-
|
228
|
+
params = {}
|
229
|
+
if key_type is not None:
|
230
|
+
params["key_type"] = key_type
|
231
|
+
if sort is not None:
|
232
|
+
params["sort"] = sort
|
233
|
+
if order is not None:
|
234
|
+
params["order"] = order
|
235
|
+
response = self.client.http.get(
|
236
|
+
f"{self.client.api_endpoint}/project/{self.id}/keys", params=params
|
237
|
+
)
|
184
238
|
assert response.status_code == 200
|
185
|
-
return [Key(**data, client=self) for data in response.json()]
|
239
|
+
return [Key(**data, client=self.client) for data in response.json()]
|
186
240
|
|
187
|
-
def
|
241
|
+
def create_key(
|
188
242
|
self,
|
189
|
-
project_id: int,
|
190
243
|
name: str,
|
191
244
|
key_type: str,
|
192
|
-
override_secret: bool,
|
193
|
-
login_password: typing.Optional[typing.Tuple[str, str]],
|
194
|
-
ssh: typing.Optional[typing.Tuple[str, str, str]],
|
245
|
+
override_secret: bool = False,
|
246
|
+
login_password: typing.Optional[typing.Tuple[str, str]] = None,
|
247
|
+
ssh: typing.Optional[typing.Tuple[str, str, str]] = None,
|
195
248
|
) -> "Key":
|
196
249
|
if key_type not in ("ssh", "login_password"):
|
197
250
|
raise ValueError(
|
@@ -202,7 +255,7 @@ class SemaphoreUIClient:
|
|
202
255
|
raise ValueError("ssh parameter must be set on key_type: ssh")
|
203
256
|
json_data = {
|
204
257
|
"id": 0,
|
205
|
-
"project_id":
|
258
|
+
"project_id": self.id,
|
206
259
|
"name": name,
|
207
260
|
"type": key_type,
|
208
261
|
"override_secret": override_secret,
|
@@ -223,7 +276,7 @@ class SemaphoreUIClient:
|
|
223
276
|
)
|
224
277
|
json_data = {
|
225
278
|
"id": 0,
|
226
|
-
"project_id":
|
279
|
+
"project_id": self.id,
|
227
280
|
"name": name,
|
228
281
|
"type": key_type,
|
229
282
|
"override_secret": override_secret,
|
@@ -233,40 +286,32 @@ class SemaphoreUIClient:
|
|
233
286
|
},
|
234
287
|
"ssh": {"login": "", "passphrase": "", "private_key": ""},
|
235
288
|
}
|
236
|
-
response = self.http.post(
|
237
|
-
f"{self.api_endpoint}/project/{
|
289
|
+
response = self.client.http.post(
|
290
|
+
f"{self.client.api_endpoint}/project/{self.id}/keys", json=json_data
|
238
291
|
)
|
239
292
|
assert response.status_code == 204
|
240
293
|
|
241
294
|
try:
|
242
|
-
return Key(**response.json(), client=self)
|
295
|
+
return Key(**response.json(), client=self.client)
|
243
296
|
except ValueError:
|
244
297
|
# Sporadically, the response is an empty string. Get the actual key from the API
|
245
|
-
return [
|
246
|
-
key for key in self.get_project_keys(project_id) if key.name == name
|
247
|
-
][0]
|
248
|
-
|
249
|
-
def delete_project_key(self, project_id: int, id: int) -> None:
|
250
|
-
response = self.http.delete(
|
251
|
-
f"{self.api_endpoint}/project/{project_id}/keys/{id}"
|
252
|
-
)
|
253
|
-
assert response.status_code == 204
|
298
|
+
return [key for key in self.keys() if key.name == name][0]
|
254
299
|
|
255
|
-
def
|
256
|
-
response = self.http.get(
|
257
|
-
f"{self.api_endpoint}/project/{
|
300
|
+
def repositories(self) -> typing.List["Repository"]:
|
301
|
+
response = self.client.http.get(
|
302
|
+
f"{self.client.api_endpoint}/project/{self.id}/repositories"
|
258
303
|
)
|
259
304
|
assert response.status_code == 200
|
260
|
-
return [Repository(**data, client=self) for data in response.json()]
|
305
|
+
return [Repository(**data, client=self.client) for data in response.json()]
|
261
306
|
|
262
|
-
def
|
263
|
-
self,
|
307
|
+
def create_repository(
|
308
|
+
self, name: str, git_url: str, git_branch: str, ssh_key_id: int
|
264
309
|
) -> "Repository":
|
265
|
-
response = self.http.post(
|
266
|
-
f"{self.api_endpoint}/project/{
|
310
|
+
response = self.client.http.post(
|
311
|
+
f"{self.client.api_endpoint}/project/{self.id}/repositories",
|
267
312
|
json={
|
268
313
|
"name": name,
|
269
|
-
"project_id":
|
314
|
+
"project_id": self.id,
|
270
315
|
"git_url": git_url,
|
271
316
|
"git_branch": git_branch,
|
272
317
|
"ssh_key_id": ssh_key_id,
|
@@ -274,41 +319,30 @@ class SemaphoreUIClient:
|
|
274
319
|
)
|
275
320
|
assert response.status_code == 204
|
276
321
|
try:
|
277
|
-
return Repository(**response.json(), client=self)
|
322
|
+
return Repository(**response.json(), client=self.client)
|
278
323
|
except ValueError:
|
279
|
-
return [
|
280
|
-
repo
|
281
|
-
for repo in self.get_project_repositories(project_id)
|
282
|
-
if repo.name == name
|
283
|
-
][0]
|
284
|
-
|
285
|
-
def delete_project_repository(self, project_id: int, id: int) -> None:
|
286
|
-
response = self.http.delete(
|
287
|
-
f"{self.api_endpoint}/project/{project_id}/repositories/{id}"
|
288
|
-
)
|
289
|
-
assert response.status_code == 204
|
324
|
+
return [repo for repo in self.repositories() if repo.name == name][0]
|
290
325
|
|
291
|
-
def
|
292
|
-
response = self.http.get(
|
293
|
-
f"{self.api_endpoint}/project/{
|
326
|
+
def environments(self) -> typing.List["Environment"]:
|
327
|
+
response = self.client.http.get(
|
328
|
+
f"{self.client.api_endpoint}/project/{self.id}/environment"
|
294
329
|
)
|
295
330
|
assert response.status_code == 200
|
296
|
-
return [Environment(**data, client=self) for data in response.json()]
|
331
|
+
return [Environment(**data, client=self.client) for data in response.json()]
|
297
332
|
|
298
|
-
def
|
333
|
+
def create_environment(
|
299
334
|
self,
|
300
|
-
project_id: int,
|
301
335
|
name: str,
|
302
336
|
password: str,
|
303
337
|
json: str,
|
304
338
|
env: str,
|
305
339
|
secrets: typing.List[typing.Dict[typing.Any, typing.Any]],
|
306
340
|
) -> "Environment":
|
307
|
-
response = self.http.post(
|
308
|
-
f"{self.
|
341
|
+
response = self.client.http.post(
|
342
|
+
f"{self.url}/environment",
|
309
343
|
json={
|
310
344
|
"name": name,
|
311
|
-
"project_id":
|
345
|
+
"project_id": self.id,
|
312
346
|
"password": None,
|
313
347
|
"json": json,
|
314
348
|
"env": env,
|
@@ -317,89 +351,67 @@ class SemaphoreUIClient:
|
|
317
351
|
)
|
318
352
|
assert response.status_code == 204
|
319
353
|
try:
|
320
|
-
return Environment(**response.json(), client=self)
|
354
|
+
return Environment(**response.json(), client=self.client)
|
321
355
|
except ValueError:
|
322
|
-
return [
|
323
|
-
env
|
324
|
-
for env in self.get_project_environments(project_id)
|
325
|
-
if env.name == name
|
326
|
-
][0]
|
327
|
-
|
328
|
-
def delete_project_environment(self, project_id: int, id: int) -> None:
|
329
|
-
response = self.http.delete(
|
330
|
-
f"{self.api_endpoint}/project/{project_id}/environment/{id}"
|
331
|
-
)
|
332
|
-
assert response.status_code == 204
|
356
|
+
return [env for env in self.environments() if env.name == name][0]
|
333
357
|
|
334
|
-
def
|
335
|
-
response = self.http.get(f"{self.
|
358
|
+
def views(self) -> typing.List["View"]:
|
359
|
+
response = self.client.http.get(f"{self.url}/views")
|
336
360
|
assert response.status_code == 200
|
337
|
-
return [View(**data, client=self) for data in response.json()]
|
361
|
+
return [View(**data, client=self.client) for data in response.json()]
|
338
362
|
|
339
|
-
def
|
340
|
-
response = self.http.post(
|
341
|
-
f"{self.
|
342
|
-
json={"position": position, "title": title, "project_id":
|
363
|
+
def create_view(self, title: str, position: int) -> "View":
|
364
|
+
response = self.client.http.post(
|
365
|
+
f"{self.url}/views",
|
366
|
+
json={"position": position, "title": title, "project_id": self.id},
|
343
367
|
)
|
344
368
|
assert response.status_code == 201
|
345
|
-
return View(**response.json(), client=self)
|
346
|
-
|
347
|
-
def delete_project_view(self, project_id: int, id: int) -> None:
|
348
|
-
response = self.http.delete(
|
349
|
-
f"{self.api_endpoint}/project/{project_id}/views/{id}"
|
350
|
-
)
|
351
|
-
assert response.status_code == 204
|
369
|
+
return View(**response.json(), client=self.client)
|
352
370
|
|
353
|
-
def
|
354
|
-
response = self.http.get(f"{self.
|
371
|
+
def inventories(self) -> typing.List["Inventory"]:
|
372
|
+
response = self.client.http.get(f"{self.url}/inventory")
|
355
373
|
assert response.status_code == 200
|
356
|
-
return [Inventory(**data, client=self) for data in response.json()]
|
374
|
+
return [Inventory(**data, client=self.client) for data in response.json()]
|
357
375
|
|
358
|
-
def
|
376
|
+
def create_inventory(
|
359
377
|
self,
|
360
|
-
project_id: int,
|
361
378
|
name: str,
|
362
379
|
inventory: str,
|
363
380
|
ssh_key_id: int,
|
364
381
|
become_key_id: int,
|
365
382
|
type: str,
|
366
|
-
|
383
|
+
repository_id: int,
|
367
384
|
) -> "Inventory":
|
368
|
-
response = self.http.post(
|
369
|
-
f"{self.
|
385
|
+
response = self.client.http.post(
|
386
|
+
f"{self.url}/inventory",
|
370
387
|
json={
|
371
388
|
"id": 0,
|
372
389
|
"name": name,
|
373
|
-
"project_id":
|
390
|
+
"project_id": self.id,
|
374
391
|
"inventory": inventory,
|
375
392
|
"ssh_key_id": ssh_key_id,
|
376
393
|
"become_key_id": become_key_id,
|
377
394
|
"type": type,
|
378
|
-
"repository_id":
|
395
|
+
"repository_id": repository_id,
|
379
396
|
},
|
380
397
|
)
|
381
398
|
assert response.status_code == 201
|
382
|
-
return Inventory(**response.json(), client=self)
|
383
|
-
|
384
|
-
def delete_project_inventory(self, project_id: int, id: int) -> None:
|
385
|
-
response = self.http.delete(
|
386
|
-
f"{self.api_endpoint}/project/{project_id}/inventory/{id}"
|
387
|
-
)
|
388
|
-
assert response.status_code == 204
|
399
|
+
return Inventory(**response.json(), client=self.client)
|
389
400
|
|
390
|
-
def
|
391
|
-
response = self.http.get(f"{self.
|
401
|
+
def templates(self) -> typing.List["Template"]:
|
402
|
+
response = self.client.http.get(f"{self.url}/templates")
|
392
403
|
assert response.status_code == 200
|
393
404
|
templates: typing.List["Template"] = []
|
394
405
|
for template in response.json():
|
395
406
|
if template["last_task"] is not None:
|
396
|
-
template["last_task"] = Task(
|
397
|
-
|
407
|
+
template["last_task"] = Task(
|
408
|
+
**template["last_task"], client=self.client
|
409
|
+
)
|
410
|
+
templates.append(Template(**template, client=self.client))
|
398
411
|
return templates
|
399
412
|
|
400
|
-
def
|
413
|
+
def create_template(
|
401
414
|
self,
|
402
|
-
project_id: int,
|
403
415
|
name: str,
|
404
416
|
repository_id: int,
|
405
417
|
inventory_id: int,
|
@@ -420,11 +432,11 @@ class SemaphoreUIClient:
|
|
420
432
|
autorun: bool,
|
421
433
|
build_template_id: typing.Optional[int] = None,
|
422
434
|
) -> "Template":
|
423
|
-
response = self.http.post(
|
424
|
-
f"{self.
|
435
|
+
response = self.client.http.post(
|
436
|
+
f"{self.url}/templates",
|
425
437
|
json={
|
426
438
|
"id": 0,
|
427
|
-
"project_id":
|
439
|
+
"project_id": self.id,
|
428
440
|
"inventory_id": inventory_id,
|
429
441
|
"repository_id": repository_id,
|
430
442
|
"environment_id": environment_id,
|
@@ -449,63 +461,23 @@ class SemaphoreUIClient:
|
|
449
461
|
assert response.status_code == 201, (
|
450
462
|
f"Expected response code 201, got {response.status_code}"
|
451
463
|
)
|
452
|
-
return Template(**response.json(), client=self)
|
453
|
-
|
454
|
-
def run_template(
|
455
|
-
self,
|
456
|
-
template_id: int,
|
457
|
-
project_id: int,
|
458
|
-
debug: bool,
|
459
|
-
dry_run: bool,
|
460
|
-
diff: bool,
|
461
|
-
message: str,
|
462
|
-
git_branch: str,
|
463
|
-
limit: str,
|
464
|
-
environment: str,
|
465
|
-
playbook: str,
|
466
|
-
) -> "Task":
|
467
|
-
response = self.http.post(
|
468
|
-
f"{self.api_endpoint}/project/{project_id}/tasks",
|
469
|
-
json={
|
470
|
-
"template_id": template_id,
|
471
|
-
"debug": debug,
|
472
|
-
"dry_run": dry_run,
|
473
|
-
"diff": diff,
|
474
|
-
"playbook": playbook,
|
475
|
-
"environment": environment,
|
476
|
-
"limit": limit,
|
477
|
-
"git_branch": git_branch,
|
478
|
-
"message": message,
|
479
|
-
},
|
480
|
-
)
|
481
|
-
assert response.status_code == 201
|
482
|
-
# The response is not quite a full task, so re-fetch it.
|
483
|
-
return self.get_project_task(project_id, response.json()["id"])
|
484
|
-
|
485
|
-
def delete_project_template(self, project_id: int, id: int) -> None:
|
486
|
-
response = self.http.delete(
|
487
|
-
f"{self.api_endpoint}/project/{project_id}/templates/{id}"
|
488
|
-
)
|
489
|
-
assert response.status_code == 204
|
464
|
+
return Template(**response.json(), client=self.client)
|
490
465
|
|
491
|
-
def
|
492
|
-
response = self.http.get(f"{self.
|
466
|
+
def schedules(self) -> typing.List["Schedule"]:
|
467
|
+
response = self.client.http.get(f"{self.url}/schedules")
|
493
468
|
assert response.status_code == 200
|
494
|
-
return [
|
469
|
+
return [
|
470
|
+
Schedule(**schedule, client=self.client) for schedule in response.json()
|
471
|
+
]
|
495
472
|
|
496
|
-
def
|
497
|
-
self,
|
498
|
-
project_id: int,
|
499
|
-
template_id: int,
|
500
|
-
name: str,
|
501
|
-
cron_format: str,
|
502
|
-
active: bool = True,
|
473
|
+
def create_schedule(
|
474
|
+
self, template_id: int, name: str, cron_format: str, active: bool = True
|
503
475
|
) -> "Schedule":
|
504
|
-
response = self.http.post(
|
505
|
-
f"{self.
|
476
|
+
response = self.client.http.post(
|
477
|
+
f"{self.url}/schedules",
|
506
478
|
json={
|
507
479
|
"id": 0,
|
508
|
-
"project_id":
|
480
|
+
"project_id": self.id,
|
509
481
|
"template_id": template_id,
|
510
482
|
"name": name,
|
511
483
|
"cron_format": cron_format,
|
@@ -513,274 +485,34 @@ class SemaphoreUIClient:
|
|
513
485
|
},
|
514
486
|
)
|
515
487
|
assert response.status_code == 201
|
516
|
-
return Schedule(**response.json(), client=self)
|
488
|
+
return Schedule(**response.json(), client=self.client)
|
517
489
|
|
518
|
-
def
|
519
|
-
self
|
520
|
-
project_id: int,
|
521
|
-
schedule_id: int,
|
522
|
-
template_id: int,
|
523
|
-
name: str,
|
524
|
-
cron_format: str,
|
525
|
-
active: bool,
|
526
|
-
) -> None:
|
527
|
-
response = self.http.post(
|
528
|
-
f"{self.api_endpoint}/project/{project_id}/schedules",
|
529
|
-
json={
|
530
|
-
"id": schedule_id,
|
531
|
-
"project_id": project_id,
|
532
|
-
"template_id": template_id,
|
533
|
-
"name": name,
|
534
|
-
"cron_format": cron_format,
|
535
|
-
"active": active,
|
536
|
-
},
|
537
|
-
)
|
538
|
-
assert response.status_code == 201
|
539
|
-
|
540
|
-
def delete_project_schedule(self, project_id: int, schedule_id: int) -> None:
|
541
|
-
response = self.http.delete(
|
542
|
-
f"{self.api_endpoint}/project/{project_id}/schedules/{schedule_id}"
|
543
|
-
)
|
544
|
-
assert response.status_code == 204
|
545
|
-
|
546
|
-
def get_project_tasks(self, project_id: int) -> typing.List["Task"]:
|
547
|
-
response = self.http.get(f"{self.api_endpoint}/project/{project_id}/tasks")
|
490
|
+
def tasks(self) -> typing.List["Task"]:
|
491
|
+
response = self.client.http.get(f"{self.url}/tasks")
|
548
492
|
assert response.status_code == 200
|
549
|
-
return [Task(**task, client=self) for task in response.json()]
|
493
|
+
return [Task(**task, client=self.client) for task in response.json()]
|
550
494
|
|
551
|
-
def
|
552
|
-
response = self.http.
|
553
|
-
f"{self.api_endpoint}/project/{
|
495
|
+
def get_task(self, task_id: int) -> "Task":
|
496
|
+
response = self.client.http.get(
|
497
|
+
f"{self.client.api_endpoint}/project/{self.id}/tasks/{task_id}"
|
554
498
|
)
|
555
|
-
assert response.status_code == 204
|
556
|
-
|
557
|
-
def get_project_task(self, project_id: int, id: int) -> "Task":
|
558
|
-
response = self.http.get(f"{self.api_endpoint}/project/{project_id}/tasks/{id}")
|
559
499
|
assert response.status_code == 200
|
560
|
-
return Task(**response.json(), client=self)
|
561
|
-
|
562
|
-
def delete_project_task(self, project_id: int, id: int) -> None:
|
563
|
-
response = self.http.delete(
|
564
|
-
f"{self.api_endpoint}/project/{project_id}/tasks/{id}"
|
565
|
-
)
|
566
|
-
assert response.status_code == 204
|
500
|
+
return Task(**response.json(), client=self.client)
|
567
501
|
|
568
|
-
def
|
569
|
-
response = self.http.get(
|
570
|
-
f"{self.api_endpoint}/project/{
|
502
|
+
def integrations(self) -> typing.List["Integration"]:
|
503
|
+
response = self.client.http.get(
|
504
|
+
f"{self.client.api_endpoint}/project/{self.id}/integrations"
|
571
505
|
)
|
572
506
|
assert response.status_code == 200
|
573
|
-
return
|
574
|
-
|
575
|
-
|
576
|
-
@dataclass
|
577
|
-
class Integration:
|
578
|
-
"""A project integration"""
|
579
|
-
|
580
|
-
id: int
|
581
|
-
name: str
|
582
|
-
project_id: int
|
583
|
-
template_id: int
|
584
|
-
|
585
|
-
client: SemaphoreUIClient
|
586
|
-
|
587
|
-
def save(self) -> None:
|
588
|
-
self.client.update_project_integration(
|
589
|
-
self.project_id, self.id, self.name, self.template_id
|
590
|
-
)
|
591
|
-
|
592
|
-
def delete(self) -> None:
|
593
|
-
self.client.delete_project_integration(self.project_id, self.id)
|
594
|
-
|
595
|
-
|
596
|
-
@dataclass
|
597
|
-
class Token:
|
598
|
-
"""An authorization token."""
|
599
|
-
|
600
|
-
id: str
|
601
|
-
created: str
|
602
|
-
expired: bool
|
603
|
-
user_id: int
|
604
|
-
|
605
|
-
client: SemaphoreUIClient
|
606
|
-
|
607
|
-
def delete(self) -> None:
|
608
|
-
self.client.delete_token(self.id)
|
609
|
-
|
610
|
-
|
611
|
-
@dataclass
|
612
|
-
class Project:
|
613
|
-
"""A Semaphore UI project."""
|
614
|
-
|
615
|
-
id: int
|
616
|
-
name: str
|
617
|
-
created: str
|
618
|
-
alert: bool
|
619
|
-
alert_chat: str
|
620
|
-
max_parallel_tasks: int
|
621
|
-
type: str # This might be an enum?
|
622
|
-
|
623
|
-
client: SemaphoreUIClient
|
624
|
-
|
625
|
-
def delete(self) -> None:
|
626
|
-
self.client.delete_project(self.id)
|
507
|
+
return [Integration(**data, client=self.client) for data in response.json()]
|
627
508
|
|
628
|
-
def
|
629
|
-
self.client.
|
630
|
-
self.id,
|
631
|
-
self.name,
|
632
|
-
self.alert,
|
633
|
-
self.alert_chat,
|
634
|
-
self.max_parallel_tasks,
|
635
|
-
self.type,
|
509
|
+
def create_integration(self, name: str, template_id: int) -> "Integration":
|
510
|
+
response = self.client.http.post(
|
511
|
+
f"{self.client.api_endpoint}/project/{self.id}integrations",
|
512
|
+
json={"project_id": self.id, "name": name, "template_id": template_id},
|
636
513
|
)
|
637
|
-
|
638
|
-
|
639
|
-
return self.client.backup_project(self.id)
|
640
|
-
|
641
|
-
def role(self) -> "Permissions":
|
642
|
-
return self.client.get_project_role(self.id)
|
643
|
-
|
644
|
-
def events(self) -> typing.List["Event"]:
|
645
|
-
return self.client.get_project_events(self.id)
|
646
|
-
|
647
|
-
def users(self, sort: str, order: str) -> typing.List["User"]:
|
648
|
-
return self.client.get_project_users(self.id, sort, order)
|
649
|
-
|
650
|
-
def add_user(self, user: "User") -> None:
|
651
|
-
return self.client.add_project_user(self.id, user)
|
652
|
-
|
653
|
-
def remove_user(self, user_id: int) -> None:
|
654
|
-
return self.client.remove_project_user(self.id, user_id)
|
655
|
-
|
656
|
-
def update_user(self, user: "User") -> None:
|
657
|
-
return self.client.update_project_user(self.id, user)
|
658
|
-
|
659
|
-
def keys(self) -> typing.List["Key"]:
|
660
|
-
return self.client.get_project_keys(self.id)
|
661
|
-
|
662
|
-
def create_key(
|
663
|
-
self,
|
664
|
-
name: str,
|
665
|
-
key_type: str,
|
666
|
-
override_secret: bool = False,
|
667
|
-
login_password: typing.Optional[typing.Tuple[str, str]] = None,
|
668
|
-
ssh: typing.Optional[typing.Tuple[str, str, str]] = None,
|
669
|
-
) -> "Key":
|
670
|
-
return self.client.create_project_key(
|
671
|
-
self.id, name, key_type, override_secret, login_password, ssh
|
672
|
-
)
|
673
|
-
|
674
|
-
def repositories(self) -> typing.List["Repository"]:
|
675
|
-
return self.client.get_project_repositories(self.id)
|
676
|
-
|
677
|
-
def create_repository(
|
678
|
-
self, name: str, git_url: str, git_branch: str, ssh_key_id: int
|
679
|
-
) -> "Repository":
|
680
|
-
return self.client.create_project_repository(
|
681
|
-
self.id, name, git_url, git_branch, ssh_key_id
|
682
|
-
)
|
683
|
-
|
684
|
-
def environments(self) -> typing.List["Environment"]:
|
685
|
-
return self.client.get_project_environments(self.id)
|
686
|
-
|
687
|
-
def create_environment(
|
688
|
-
self,
|
689
|
-
name: str,
|
690
|
-
password: str,
|
691
|
-
json: str,
|
692
|
-
env: str,
|
693
|
-
secrets: typing.List[typing.Dict[typing.Any, typing.Any]],
|
694
|
-
) -> "Environment":
|
695
|
-
return self.client.create_project_environment(
|
696
|
-
self.id, name, password, json, env, secrets
|
697
|
-
)
|
698
|
-
|
699
|
-
def views(self) -> typing.List["View"]:
|
700
|
-
return self.client.get_project_views(self.id)
|
701
|
-
|
702
|
-
def create_view(self, title: str, position: int) -> "View":
|
703
|
-
return self.client.create_project_view(self.id, title, position)
|
704
|
-
|
705
|
-
def inventories(self) -> typing.List["Inventory"]:
|
706
|
-
return self.client.get_project_inventories(self.id)
|
707
|
-
|
708
|
-
def create_inventory(
|
709
|
-
self,
|
710
|
-
name: str,
|
711
|
-
inventory: str,
|
712
|
-
ssh_key_id: int,
|
713
|
-
become_key_id: int,
|
714
|
-
type: str,
|
715
|
-
repository_id: int,
|
716
|
-
) -> "Inventory":
|
717
|
-
return self.client.create_project_inventory(
|
718
|
-
self.id, name, inventory, ssh_key_id, become_key_id, type, repository_id
|
719
|
-
)
|
720
|
-
|
721
|
-
def templates(self) -> typing.List["Template"]:
|
722
|
-
return self.client.get_project_templates(self.id)
|
723
|
-
|
724
|
-
def create_template(
|
725
|
-
self,
|
726
|
-
name: str,
|
727
|
-
repository_id: int,
|
728
|
-
inventory_id: int,
|
729
|
-
environment_id: int,
|
730
|
-
view_id: int,
|
731
|
-
vault_id: int,
|
732
|
-
playbook: str,
|
733
|
-
arguments: str,
|
734
|
-
description: str,
|
735
|
-
allow_override_args_in_task: bool,
|
736
|
-
limit: int,
|
737
|
-
suppress_success_alerts: bool,
|
738
|
-
app: str,
|
739
|
-
git_branch: str,
|
740
|
-
survey_vars: typing.List[typing.Dict[str, typing.Any]],
|
741
|
-
type: str,
|
742
|
-
start_version: str,
|
743
|
-
autorun: bool,
|
744
|
-
build_template_id: typing.Optional[int] = None,
|
745
|
-
) -> "Template":
|
746
|
-
return self.client.create_project_template(
|
747
|
-
self.id,
|
748
|
-
name,
|
749
|
-
repository_id,
|
750
|
-
inventory_id,
|
751
|
-
environment_id,
|
752
|
-
view_id,
|
753
|
-
vault_id,
|
754
|
-
playbook,
|
755
|
-
arguments,
|
756
|
-
description,
|
757
|
-
allow_override_args_in_task,
|
758
|
-
limit,
|
759
|
-
suppress_success_alerts,
|
760
|
-
app,
|
761
|
-
git_branch,
|
762
|
-
survey_vars,
|
763
|
-
type,
|
764
|
-
start_version,
|
765
|
-
autorun,
|
766
|
-
build_template_id,
|
767
|
-
)
|
768
|
-
|
769
|
-
def schedules(self) -> typing.List["Schedule"]:
|
770
|
-
return self.client.get_project_schedules(self.id)
|
771
|
-
|
772
|
-
def create_schedule(
|
773
|
-
self, template_id: int, name: str, cron_format: str, active: bool = True
|
774
|
-
) -> "Schedule":
|
775
|
-
return self.client.create_project_schedule(
|
776
|
-
self.id, template_id, name, cron_format, active
|
777
|
-
)
|
778
|
-
|
779
|
-
def tasks(self) -> typing.List["Task"]:
|
780
|
-
return self.client.get_project_tasks(self.id)
|
781
|
-
|
782
|
-
def get_task(self, task_id: int) -> "Task":
|
783
|
-
return self.client.get_project_task(self.id, task_id)
|
514
|
+
assert response.status_code == 200
|
515
|
+
return Integration(**response.json(), client=self.client)
|
784
516
|
|
785
517
|
|
786
518
|
@dataclass
|
@@ -804,13 +536,19 @@ class Event:
|
|
804
536
|
|
805
537
|
@dataclass_json
|
806
538
|
@dataclass
|
807
|
-
class
|
808
|
-
|
539
|
+
class ProjectUser:
|
540
|
+
id: int
|
541
|
+
name: str
|
542
|
+
username: str
|
809
543
|
role: str
|
810
544
|
|
545
|
+
project_id: int
|
546
|
+
|
547
|
+
client: SemaphoreUIClient
|
548
|
+
|
811
549
|
@property
|
812
|
-
def
|
813
|
-
return self.
|
550
|
+
def url(self) -> str:
|
551
|
+
return f"{self.client.api_endpoint}/project/{self.project_id}/users/{self.id}"
|
814
552
|
|
815
553
|
|
816
554
|
@dataclass
|
@@ -840,8 +578,13 @@ class Key:
|
|
840
578
|
|
841
579
|
client: SemaphoreUIClient
|
842
580
|
|
581
|
+
@property
|
582
|
+
def url(self) -> str:
|
583
|
+
return f"{self.client.api_endpoint}/project/{self.project_id}/keys/{self.id}"
|
584
|
+
|
843
585
|
def delete(self) -> None:
|
844
|
-
self.client.
|
586
|
+
response = self.client.http.delete(self.url)
|
587
|
+
assert response.status_code == 204
|
845
588
|
|
846
589
|
|
847
590
|
@dataclass
|
@@ -855,8 +598,13 @@ class Repository:
|
|
855
598
|
|
856
599
|
client: SemaphoreUIClient
|
857
600
|
|
601
|
+
@property
|
602
|
+
def url(self) -> str:
|
603
|
+
return f"{self.client.api_endpoint}/project/{self.project_id}/repositories/{self.id}"
|
604
|
+
|
858
605
|
def delete(self) -> None:
|
859
|
-
self.client.
|
606
|
+
response = self.client.http.delete(self.url)
|
607
|
+
assert response.status_code == 204
|
860
608
|
|
861
609
|
|
862
610
|
@dataclass
|
@@ -878,8 +626,13 @@ class Environment:
|
|
878
626
|
|
879
627
|
client: SemaphoreUIClient
|
880
628
|
|
629
|
+
@property
|
630
|
+
def url(self) -> str:
|
631
|
+
return f"{self.client.api_endpoint}/project/{self.project_id}/environment/{self.id}"
|
632
|
+
|
881
633
|
def delete(self) -> None:
|
882
|
-
self.client.
|
634
|
+
response = self.client.http.delete(self.url)
|
635
|
+
assert response.status_code == 204
|
883
636
|
|
884
637
|
|
885
638
|
@dataclass
|
@@ -891,8 +644,13 @@ class View:
|
|
891
644
|
|
892
645
|
client: SemaphoreUIClient
|
893
646
|
|
647
|
+
@property
|
648
|
+
def url(self) -> str:
|
649
|
+
return f"{self.client.api_endpoint}/project/{self.project_id}/views/{self.id}"
|
650
|
+
|
894
651
|
def delete(self) -> None:
|
895
|
-
self.client.
|
652
|
+
response = self.client.http.delete(self.url)
|
653
|
+
assert response.status_code == 204
|
896
654
|
|
897
655
|
|
898
656
|
@dataclass
|
@@ -910,8 +668,16 @@ class Inventory:
|
|
910
668
|
|
911
669
|
client: SemaphoreUIClient
|
912
670
|
|
671
|
+
def url(self) -> str:
|
672
|
+
return (
|
673
|
+
f"{self.client.api_endpoint}/project/{self.project_id}/inventory/{self.id}"
|
674
|
+
)
|
675
|
+
|
913
676
|
def delete(self) -> None:
|
914
|
-
self.client.
|
677
|
+
response = self.client.http.delete(
|
678
|
+
f"{self.client.api_endpoint}/project/{self.project_id}/inventory/{self.id}"
|
679
|
+
)
|
680
|
+
assert response.status_code == 204
|
915
681
|
|
916
682
|
|
917
683
|
@dataclass
|
@@ -940,6 +706,12 @@ class Template:
|
|
940
706
|
|
941
707
|
client: SemaphoreUIClient
|
942
708
|
|
709
|
+
@property
|
710
|
+
def url(self) -> str:
|
711
|
+
return (
|
712
|
+
f"{self.client.api_endpoint}/project/{self.project_id}/templates/{self.id}"
|
713
|
+
)
|
714
|
+
|
943
715
|
def run(
|
944
716
|
self,
|
945
717
|
debug: bool = False,
|
@@ -949,27 +721,33 @@ class Template:
|
|
949
721
|
limit: str = "",
|
950
722
|
environment: str = "",
|
951
723
|
) -> "Task":
|
724
|
+
project = self.client.get_project(self.project_id)
|
952
725
|
repo = [
|
953
|
-
repo
|
954
|
-
for repo in self.client.get_project_repositories(self.project_id)
|
955
|
-
if repo.id == self.repository_id
|
726
|
+
repo for repo in project.repositories() if repo.id == self.repository_id
|
956
727
|
][0]
|
957
728
|
git_branch = repo.git_branch
|
958
|
-
|
959
|
-
self.
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
729
|
+
response = self.client.http.post(
|
730
|
+
f"{self.client.api_endpoint}/project/{self.project_id}/tasks",
|
731
|
+
json={
|
732
|
+
"template_id": self.id,
|
733
|
+
"debug": debug,
|
734
|
+
"dry_run": dry_run,
|
735
|
+
"diff": diff,
|
736
|
+
"playbook": self.playbook,
|
737
|
+
"environment": environment,
|
738
|
+
"limit": limit,
|
739
|
+
"git_branch": git_branch,
|
740
|
+
"message": message,
|
741
|
+
},
|
969
742
|
)
|
743
|
+
assert response.status_code == 201
|
744
|
+
# The response is not quite a full task, so re-fetch it.
|
745
|
+
project = self.client.get_project(self.project_id)
|
746
|
+
return project.get_task(response.json()["id"])
|
970
747
|
|
971
748
|
def delete(self) -> None:
|
972
|
-
self.client.
|
749
|
+
response = self.client.http.delete(self.url)
|
750
|
+
assert response.status_code == 204
|
973
751
|
|
974
752
|
|
975
753
|
@dataclass
|
@@ -984,18 +762,29 @@ class Schedule:
|
|
984
762
|
|
985
763
|
client: SemaphoreUIClient
|
986
764
|
|
765
|
+
@property
|
766
|
+
def url(self) -> str:
|
767
|
+
return (
|
768
|
+
f"{self.client.api_endpoint}/project/{self.project_id}/schedules/{self.id}"
|
769
|
+
)
|
770
|
+
|
987
771
|
def save(self) -> None:
|
988
|
-
self.client.
|
989
|
-
self.
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
772
|
+
response = self.client.http.post(
|
773
|
+
self.url,
|
774
|
+
json={
|
775
|
+
"id": self.id,
|
776
|
+
"project_id": self.project_id,
|
777
|
+
"template_id": self.template_id,
|
778
|
+
"name": self.name,
|
779
|
+
"cron_format": self.cron_format,
|
780
|
+
"active": self.active,
|
781
|
+
},
|
995
782
|
)
|
783
|
+
assert response.status_code == 201
|
996
784
|
|
997
785
|
def delete(self) -> None:
|
998
|
-
self.client.
|
786
|
+
response = self.client.http.delete(self.url)
|
787
|
+
assert response.status_code == 204
|
999
788
|
|
1000
789
|
|
1001
790
|
@dataclass
|
@@ -1037,11 +826,19 @@ class Task:
|
|
1037
826
|
user_name: typing.Optional[str] = field(default=None)
|
1038
827
|
version: typing.Optional[str] = field(default=None)
|
1039
828
|
|
829
|
+
@property
|
830
|
+
def url(self) -> str:
|
831
|
+
return f"{self.client.api_endpoint}/project/{self.project_id}/tasks/{self.id}"
|
832
|
+
|
1040
833
|
def stop(self) -> None:
|
1041
|
-
self.client.
|
834
|
+
response = self.client.http.post(f"{self.url}/stop")
|
835
|
+
assert response.status_code == 204
|
1042
836
|
|
1043
837
|
def delete(self) -> None:
|
1044
|
-
self.client.
|
838
|
+
response = self.client.http.delete(self.url)
|
839
|
+
assert response.status_code == 204
|
1045
840
|
|
1046
|
-
def output(self) -> str:
|
1047
|
-
|
841
|
+
def output(self) -> typing.List[str]:
|
842
|
+
response = self.client.http.get(f"{self.url}/output")
|
843
|
+
assert response.status_code == 200
|
844
|
+
return [data["output"] for data in response.json()]
|
@@ -1 +0,0 @@
|
|
1
|
-
__version__ = "0.1.18"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|