vertracloud-api 0.3.0__tar.gz → 0.3.2__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.
- {vertracloud_api-0.3.0 → vertracloud_api-0.3.2}/CHANGELOG.md +15 -0
- {vertracloud_api-0.3.0 → vertracloud_api-0.3.2}/PKG-INFO +66 -5
- {vertracloud_api-0.3.0 → vertracloud_api-0.3.2}/README.md +65 -4
- {vertracloud_api-0.3.0 → vertracloud_api-0.3.2}/pyproject.toml +1 -1
- {vertracloud_api-0.3.0 → vertracloud_api-0.3.2}/src/vertracloud/client.py +3 -1
- {vertracloud_api-0.3.0 → vertracloud_api-0.3.2}/src/vertracloud/errors.py +13 -0
- {vertracloud_api-0.3.0 → vertracloud_api-0.3.2}/src/vertracloud/resources/apps.py +21 -6
- {vertracloud_api-0.3.0 → vertracloud_api-0.3.2}/src/vertracloud/resources/databases.py +11 -3
- vertracloud_api-0.3.2/src/vertracloud/resources/images.py +239 -0
- {vertracloud_api-0.3.0 → vertracloud_api-0.3.2}/src/vertracloud/resources/snapshots.py +17 -3
- {vertracloud_api-0.3.0 → vertracloud_api-0.3.2}/src/vertracloud/types.py +242 -10
- {vertracloud_api-0.3.0 → vertracloud_api-0.3.2}/.gitignore +0 -0
- {vertracloud_api-0.3.0 → vertracloud_api-0.3.2}/LICENSE +0 -0
- {vertracloud_api-0.3.0 → vertracloud_api-0.3.2}/src/vertracloud/__init__.py +0 -0
- {vertracloud_api-0.3.0 → vertracloud_api-0.3.2}/src/vertracloud/py.typed +0 -0
- {vertracloud_api-0.3.0 → vertracloud_api-0.3.2}/src/vertracloud/resources/__init__.py +0 -0
- {vertracloud_api-0.3.0 → vertracloud_api-0.3.2}/src/vertracloud/resources/account.py +0 -0
- {vertracloud_api-0.3.0 → vertracloud_api-0.3.2}/src/vertracloud/resources/billing.py +0 -0
- {vertracloud_api-0.3.0 → vertracloud_api-0.3.2}/src/vertracloud/resources/blob.py +0 -0
- {vertracloud_api-0.3.0 → vertracloud_api-0.3.2}/src/vertracloud/resources/workspaces.py +0 -0
- {vertracloud_api-0.3.0 → vertracloud_api-0.3.2}/src/vertracloud/sse.py +0 -0
- {vertracloud_api-0.3.0 → vertracloud_api-0.3.2}/src/vertracloud/transport.py +0 -0
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.2](https://github.com/vertracloud/sdk-api-py/compare/v0.3.1...v0.3.2) (2026-10-25)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add images resource and database engine versions ([2136b86](https://github.com/vertracloud/sdk-api-py/commit/2136b86739608096c9e4590589b3aca2f42d1777))
|
|
9
|
+
|
|
10
|
+
## [0.3.1](https://github.com/vertracloud/sdk-api-py/compare/v0.3.0...v0.3.1) (2026-10-22)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* **apps:** Deno, .NET and Elixir and language on create ([c7a7bb3](https://github.com/vertracloud/sdk-api-py/commit/c7a7bb3e4141d8f0c45853807acfd49719841100))
|
|
16
|
+
* **apps:** remove cleanup_old_runtime_language from the restart body ([e8aa75d](https://github.com/vertracloud/sdk-api-py/commit/e8aa75daf69752af8ea16048e84e85857f613ff4))
|
|
17
|
+
|
|
3
18
|
## [0.3.0](https://github.com/vertracloud/sdk-api-py/compare/v0.2.2...v0.3.0) (2026-10-22)
|
|
4
19
|
|
|
5
20
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: vertracloud-api
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Official Python SDK for the public API of Vertra Cloud, a Brazilian application hosting platform (PaaS).
|
|
5
5
|
Project-URL: Homepage, https://vertracloud.app
|
|
6
6
|
Project-URL: Documentation, https://docs.vertracloud.app/sdks
|
|
@@ -76,7 +76,7 @@ client = VertraClient(
|
|
|
76
76
|
|
|
77
77
|
### Per-call options
|
|
78
78
|
|
|
79
|
-
Methods on apps and
|
|
79
|
+
Methods on apps, databases and images take a keyword-only `workspace_id`; downloads, uploads and streams also take `timeout`:
|
|
80
80
|
|
|
81
81
|
```python
|
|
82
82
|
# Act on a resource that belongs to a workspace
|
|
@@ -177,7 +177,7 @@ except vertracloud.WaitTimeoutError: # still building after `timeout` seconds
|
|
|
177
177
|
...
|
|
178
178
|
```
|
|
179
179
|
|
|
180
|
-
|
|
180
|
+
While the build runs, calls that need a stopped or running app can fail with `APP_BUILDING` (409).
|
|
181
181
|
|
|
182
182
|
### Realtime logs (SSE)
|
|
183
183
|
|
|
@@ -214,6 +214,66 @@ client.blob.delete_many(prefix="tmp/")
|
|
|
214
214
|
|
|
215
215
|
`data` can be `bytes`, a file path, or a seekable binary file. Up to 5 MB it is one request; larger files go straight to storage in parts, with a retry per part. If the process dies halfway, `client.blob.resume(upload_id, data)` sends only the missing parts.
|
|
216
216
|
|
|
217
|
+
### Custom Docker images
|
|
218
|
+
|
|
219
|
+
An image is a build context (a ZIP of up to 100 MB, or a GitHub repository) with a `Dockerfile` at its root. Create and rebuild reply right away with `status: "building"`; the build then ends as `success` or `failed`, and `error` says why. Whether your plan can create images is decided by the API (`PLAN_DOES_NOT_SUPPORT_CUSTOM_IMAGES`).
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
# From a ZIP: the file is passed as bytes, like apps.create
|
|
223
|
+
with open("projeto.zip", "rb") as f:
|
|
224
|
+
image = client.images.create({"name": "meu-projeto"}, file=f.read())
|
|
225
|
+
|
|
226
|
+
# Or from GitHub: `github_branch` is optional (default: the repository's default branch). A repository in your
|
|
227
|
+
# GitHub App installations is linked and each push to the branch rebuilds the image; a public repository outside
|
|
228
|
+
# them is built once, with no link.
|
|
229
|
+
site = client.images.create({"name": "site", "github_repository": "acme/site", "github_branch": "main"})
|
|
230
|
+
|
|
231
|
+
# Check the Dockerfile before building; `refusals` block the build, `warnings` do not
|
|
232
|
+
report = client.images.check({"dockerfile": dockerfile_text, "files": ["Dockerfile", "app.py"]})
|
|
233
|
+
|
|
234
|
+
# Follow the build as lines of text; the stream repeats the history, then goes live
|
|
235
|
+
for line in client.images.logs(image["id"]):
|
|
236
|
+
print(line)
|
|
237
|
+
|
|
238
|
+
image = client.images.get(image["id"]) # then status is "success" or "failed"
|
|
239
|
+
|
|
240
|
+
app = client.apps.create_from_image(
|
|
241
|
+
{"image_id": image["id"], "name": "meu-app", "memory": 512, "web": True, "subdomain": "random"}
|
|
242
|
+
)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Images can belong to a workspace, like apps. `list` and every method that acts on an existing image take a keyword-only `workspace_id`; `create` and `apps.create_from_image` take it in the body (`{"name": "site", "workspace_id": workspace_id, ...}`). `check` only reads the `Dockerfile` you send, so it has no workspace.
|
|
246
|
+
|
|
247
|
+
```python
|
|
248
|
+
images = client.images.list(workspace_id=workspace_id)
|
|
249
|
+
client.images.rebuild(image_id, file=zip_bytes, workspace_id=workspace_id)
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
`create` takes an optional `description` (up to 128 characters). `update` renames the image and/or changes its description without building anything; the apps keep running. Send only what changes, and `None` removes the description:
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
client.images.update(image["id"], {"name": "meu-projeto-base", "description": "Base Node 22"})
|
|
256
|
+
client.images.update(image["id"], {"description": None})
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
`images.logs` returns an iterator with `close()`, like `apps.realtime`. `rebuild(image_id, file=...)` replaces the whole build context; it never restarts the apps that run the image. `link_github` and `unlink_github` change the GitHub link, and `versions` lists the successful builds, newest first.
|
|
260
|
+
|
|
261
|
+
`delete` removes the image and **every app created from it**. The name must be typed back as `confirm_name`:
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
client.images.delete(image["id"], confirm_name="meu-projeto") # {"deleted_apps": [...]}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Database versions
|
|
268
|
+
|
|
269
|
+
```python
|
|
270
|
+
catalog = client.databases.versions() # keyed by engine type, as a string
|
|
271
|
+
db = client.databases.create({"name": "meu-db", "type": 1, "ram": 512, "version": catalog["1"]["recommended"]})
|
|
272
|
+
print(db["version"]) # major line, e.g. "18"
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Omit `version` to get the recommended one; with `snapshot_id`, omit it to keep the snapshot's line (an older one fails with `SNAPSHOT_VERSION_NEWER`, an unknown one with `INVALID_DATABASE_VERSION`). A database's version cannot be changed after creation; security updates are applied when it is stopped and started.
|
|
276
|
+
|
|
217
277
|
### Errors
|
|
218
278
|
|
|
219
279
|
Every non-2xx response raises a `VertraAPIError` carrying the HTTP `status`, the API `code` (e.g. `APP_NOT_FOUND`) and `details`; `str(err)` includes the message. The API key is never part of the error, even when it is printed:
|
|
@@ -245,8 +305,9 @@ client = VertraClient(api_key="test", transport=my_fake_transport)
|
|
|
245
305
|
|
|
246
306
|
| Domain | Resource | Routes |
|
|
247
307
|
|---|---|---|
|
|
248
|
-
| Apps | `client.apps` (+ `.deploys`, `.releases`, `.domains`, `.network`, `.envs`, `.files`) |
|
|
249
|
-
| Databases | `client.databases` (+ `.credentials`) |
|
|
308
|
+
| Apps | `client.apps` (+ `.deploys`, `.releases`, `.domains`, `.network`, `.envs`, `.files`) | 43 |
|
|
309
|
+
| Databases | `client.databases` (+ `.credentials`) | 14 |
|
|
310
|
+
| Custom images | `client.images` | 12 |
|
|
250
311
|
| Snapshots | `client.snapshots` | 5 |
|
|
251
312
|
| Account | `client.account` (+ `.sessions`, `.folders`, `.favorites`) | 10 |
|
|
252
313
|
| Workspaces | `client.workspaces` (+ `.members`, `.roles`, `.invites`, `.action_requests`, `.apps`, `.databases`, `.folders`, `.favorites`) | 30 |
|
|
@@ -57,7 +57,7 @@ client = VertraClient(
|
|
|
57
57
|
|
|
58
58
|
### Per-call options
|
|
59
59
|
|
|
60
|
-
Methods on apps and
|
|
60
|
+
Methods on apps, databases and images take a keyword-only `workspace_id`; downloads, uploads and streams also take `timeout`:
|
|
61
61
|
|
|
62
62
|
```python
|
|
63
63
|
# Act on a resource that belongs to a workspace
|
|
@@ -158,7 +158,7 @@ except vertracloud.WaitTimeoutError: # still building after `timeout` seconds
|
|
|
158
158
|
...
|
|
159
159
|
```
|
|
160
160
|
|
|
161
|
-
|
|
161
|
+
While the build runs, calls that need a stopped or running app can fail with `APP_BUILDING` (409).
|
|
162
162
|
|
|
163
163
|
### Realtime logs (SSE)
|
|
164
164
|
|
|
@@ -195,6 +195,66 @@ client.blob.delete_many(prefix="tmp/")
|
|
|
195
195
|
|
|
196
196
|
`data` can be `bytes`, a file path, or a seekable binary file. Up to 5 MB it is one request; larger files go straight to storage in parts, with a retry per part. If the process dies halfway, `client.blob.resume(upload_id, data)` sends only the missing parts.
|
|
197
197
|
|
|
198
|
+
### Custom Docker images
|
|
199
|
+
|
|
200
|
+
An image is a build context (a ZIP of up to 100 MB, or a GitHub repository) with a `Dockerfile` at its root. Create and rebuild reply right away with `status: "building"`; the build then ends as `success` or `failed`, and `error` says why. Whether your plan can create images is decided by the API (`PLAN_DOES_NOT_SUPPORT_CUSTOM_IMAGES`).
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
# From a ZIP: the file is passed as bytes, like apps.create
|
|
204
|
+
with open("projeto.zip", "rb") as f:
|
|
205
|
+
image = client.images.create({"name": "meu-projeto"}, file=f.read())
|
|
206
|
+
|
|
207
|
+
# Or from GitHub: `github_branch` is optional (default: the repository's default branch). A repository in your
|
|
208
|
+
# GitHub App installations is linked and each push to the branch rebuilds the image; a public repository outside
|
|
209
|
+
# them is built once, with no link.
|
|
210
|
+
site = client.images.create({"name": "site", "github_repository": "acme/site", "github_branch": "main"})
|
|
211
|
+
|
|
212
|
+
# Check the Dockerfile before building; `refusals` block the build, `warnings` do not
|
|
213
|
+
report = client.images.check({"dockerfile": dockerfile_text, "files": ["Dockerfile", "app.py"]})
|
|
214
|
+
|
|
215
|
+
# Follow the build as lines of text; the stream repeats the history, then goes live
|
|
216
|
+
for line in client.images.logs(image["id"]):
|
|
217
|
+
print(line)
|
|
218
|
+
|
|
219
|
+
image = client.images.get(image["id"]) # then status is "success" or "failed"
|
|
220
|
+
|
|
221
|
+
app = client.apps.create_from_image(
|
|
222
|
+
{"image_id": image["id"], "name": "meu-app", "memory": 512, "web": True, "subdomain": "random"}
|
|
223
|
+
)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Images can belong to a workspace, like apps. `list` and every method that acts on an existing image take a keyword-only `workspace_id`; `create` and `apps.create_from_image` take it in the body (`{"name": "site", "workspace_id": workspace_id, ...}`). `check` only reads the `Dockerfile` you send, so it has no workspace.
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
images = client.images.list(workspace_id=workspace_id)
|
|
230
|
+
client.images.rebuild(image_id, file=zip_bytes, workspace_id=workspace_id)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`create` takes an optional `description` (up to 128 characters). `update` renames the image and/or changes its description without building anything; the apps keep running. Send only what changes, and `None` removes the description:
|
|
234
|
+
|
|
235
|
+
```python
|
|
236
|
+
client.images.update(image["id"], {"name": "meu-projeto-base", "description": "Base Node 22"})
|
|
237
|
+
client.images.update(image["id"], {"description": None})
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
`images.logs` returns an iterator with `close()`, like `apps.realtime`. `rebuild(image_id, file=...)` replaces the whole build context; it never restarts the apps that run the image. `link_github` and `unlink_github` change the GitHub link, and `versions` lists the successful builds, newest first.
|
|
241
|
+
|
|
242
|
+
`delete` removes the image and **every app created from it**. The name must be typed back as `confirm_name`:
|
|
243
|
+
|
|
244
|
+
```python
|
|
245
|
+
client.images.delete(image["id"], confirm_name="meu-projeto") # {"deleted_apps": [...]}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Database versions
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
catalog = client.databases.versions() # keyed by engine type, as a string
|
|
252
|
+
db = client.databases.create({"name": "meu-db", "type": 1, "ram": 512, "version": catalog["1"]["recommended"]})
|
|
253
|
+
print(db["version"]) # major line, e.g. "18"
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Omit `version` to get the recommended one; with `snapshot_id`, omit it to keep the snapshot's line (an older one fails with `SNAPSHOT_VERSION_NEWER`, an unknown one with `INVALID_DATABASE_VERSION`). A database's version cannot be changed after creation; security updates are applied when it is stopped and started.
|
|
257
|
+
|
|
198
258
|
### Errors
|
|
199
259
|
|
|
200
260
|
Every non-2xx response raises a `VertraAPIError` carrying the HTTP `status`, the API `code` (e.g. `APP_NOT_FOUND`) and `details`; `str(err)` includes the message. The API key is never part of the error, even when it is printed:
|
|
@@ -226,8 +286,9 @@ client = VertraClient(api_key="test", transport=my_fake_transport)
|
|
|
226
286
|
|
|
227
287
|
| Domain | Resource | Routes |
|
|
228
288
|
|---|---|---|
|
|
229
|
-
| Apps | `client.apps` (+ `.deploys`, `.releases`, `.domains`, `.network`, `.envs`, `.files`) |
|
|
230
|
-
| Databases | `client.databases` (+ `.credentials`) |
|
|
289
|
+
| Apps | `client.apps` (+ `.deploys`, `.releases`, `.domains`, `.network`, `.envs`, `.files`) | 43 |
|
|
290
|
+
| Databases | `client.databases` (+ `.credentials`) | 14 |
|
|
291
|
+
| Custom images | `client.images` | 12 |
|
|
231
292
|
| Snapshots | `client.snapshots` | 5 |
|
|
232
293
|
| Account | `client.account` (+ `.sessions`, `.folders`, `.favorites`) | 10 |
|
|
233
294
|
| Workspaces | `client.workspaces` (+ `.members`, `.roles`, `.invites`, `.action_requests`, `.apps`, `.databases`, `.folders`, `.favorites`) | 30 |
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "vertracloud-api"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.2"
|
|
8
8
|
description = "Official Python SDK for the public API of Vertra Cloud, a Brazilian application hosting platform (PaaS)."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -16,7 +16,7 @@ from .sse import SseStream
|
|
|
16
16
|
from .transport import Response, Transport, UrllibTransport
|
|
17
17
|
from .types import JsonValue
|
|
18
18
|
|
|
19
|
-
__version__ = "0.3.
|
|
19
|
+
__version__ = "0.3.2" # x-release-please-version
|
|
20
20
|
|
|
21
21
|
DEFAULT_BASE_URL = "https://api.vertracloud.app"
|
|
22
22
|
DEFAULT_BLOB_BASE_URL = "https://blob.vertracloud.app/v1"
|
|
@@ -136,11 +136,13 @@ class VertraClient:
|
|
|
136
136
|
from .resources.billing import BillingResource
|
|
137
137
|
from .resources.blob import BlobResource
|
|
138
138
|
from .resources.databases import DatabasesResource
|
|
139
|
+
from .resources.images import ImagesResource
|
|
139
140
|
from .resources.snapshots import SnapshotsResource
|
|
140
141
|
from .resources.workspaces import WorkspacesResource
|
|
141
142
|
|
|
142
143
|
self.apps = AppsResource(self)
|
|
143
144
|
self.databases = DatabasesResource(self)
|
|
145
|
+
self.images = ImagesResource(self)
|
|
144
146
|
self.snapshots = SnapshotsResource(self)
|
|
145
147
|
self.account = AccountResource(self)
|
|
146
148
|
self.workspaces = WorkspacesResource(self)
|
|
@@ -32,6 +32,19 @@ class VertraAPIError(Exception):
|
|
|
32
32
|
def __repr__(self) -> str:
|
|
33
33
|
return f"{self.__class__.__name__}(status={self.status!r}, code={self.code!r})"
|
|
34
34
|
|
|
35
|
+
@property
|
|
36
|
+
def issues(self) -> list[dict[str, Any]]:
|
|
37
|
+
"""Findings of a `DOCKERFILE_REFUSED` error: the objects in `details.issues`, as the API sent them
|
|
38
|
+
(`{code, line}`). Tolerant on purpose: any other error, or a `details` of another shape, gives `[]`
|
|
39
|
+
and never raises; entries that are not objects are skipped."""
|
|
40
|
+
details = self.details
|
|
41
|
+
if not isinstance(details, dict):
|
|
42
|
+
return []
|
|
43
|
+
raw = details.get("issues")
|
|
44
|
+
if not isinstance(raw, list):
|
|
45
|
+
return []
|
|
46
|
+
return [item for item in raw if isinstance(item, dict)]
|
|
47
|
+
|
|
35
48
|
|
|
36
49
|
class AuthenticationError(VertraAPIError):
|
|
37
50
|
"""401 — invalid key or user not found (`API_KEY_INVALID`, `USER_NOT_FOUND`)."""
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""`apps` domain —
|
|
1
|
+
"""`apps` domain — 43 routes (scopes `apps:read/write/delete/envs/files`).
|
|
2
2
|
|
|
3
3
|
`workspace_id` is accepted as an optional query parameter on almost every method.
|
|
4
4
|
"""
|
|
@@ -15,6 +15,7 @@ from ..types import (
|
|
|
15
15
|
AddDomainBody,
|
|
16
16
|
APIAppFileUploadResponse,
|
|
17
17
|
APIApplication,
|
|
18
|
+
APIApplicationCreated,
|
|
18
19
|
APIApplicationDeployment,
|
|
19
20
|
APIApplicationDomain,
|
|
20
21
|
APIApplicationDomainConnect,
|
|
@@ -29,11 +30,13 @@ from ..types import (
|
|
|
29
30
|
APIApplicationRollbackPreview,
|
|
30
31
|
APIApplicationStatus,
|
|
31
32
|
APIApplicationStatusShort,
|
|
33
|
+
APIApplicationWebhook,
|
|
32
34
|
APIApplicationWebPublish,
|
|
33
35
|
APIRuntimesResponse,
|
|
34
36
|
APISubdomainResponse,
|
|
35
37
|
APIWebhookUrl,
|
|
36
38
|
CreateAppBody,
|
|
39
|
+
CreateAppFromImageBody,
|
|
37
40
|
CreateWebhookBody,
|
|
38
41
|
EnvironmentSetBody,
|
|
39
42
|
JsonValue,
|
|
@@ -61,7 +64,7 @@ class AppDeploysWebhookResource:
|
|
|
61
64
|
def __init__(self, client: VertraClient) -> None:
|
|
62
65
|
self._c = client
|
|
63
66
|
|
|
64
|
-
def get(self, app_id: str, *, workspace_id: str | None = None) ->
|
|
67
|
+
def get(self, app_id: str, *, workspace_id: str | None = None) -> APIApplicationWebhook:
|
|
65
68
|
"""GET /v1/apps/:id/deploys/webhook — scope `apps:read`.
|
|
66
69
|
Doc: https://docs.vertracloud.app/api-reference/endpoint/apps/deploys/webhookurl
|
|
67
70
|
"""
|
|
@@ -255,7 +258,9 @@ class AppNetworkResource:
|
|
|
255
258
|
self, app_id: str, body: PublishAppBody | None = None, *, workspace_id: str | None = None
|
|
256
259
|
) -> APIApplicationWebPublish:
|
|
257
260
|
"""POST /v1/apps/:id/network/publish — scope `apps:write`. The Pro plan gets a random
|
|
258
|
-
subdomain, Scale picks its own; an empty `{}` body is valid.
|
|
261
|
+
subdomain, Scale picks its own; an empty `{}` body is valid. `port` (only for an app on a
|
|
262
|
+
custom image) is the port the image listens on; it is required when the app has no web
|
|
263
|
+
port yet."""
|
|
259
264
|
return self._c.request_json(
|
|
260
265
|
"POST",
|
|
261
266
|
f"/v1/apps/{encode_path_param(app_id)}/network/publish",
|
|
@@ -467,9 +472,10 @@ class AppsResource:
|
|
|
467
472
|
file: bytes | None = None,
|
|
468
473
|
filename: str = "app.zip",
|
|
469
474
|
timeout: float | None = None,
|
|
470
|
-
) ->
|
|
475
|
+
) -> APIApplicationCreated:
|
|
471
476
|
"""POST /v1/apps — scope `apps:write`. Multipart: field `file` (zip) OR `snapshot_id`
|
|
472
|
-
in `body`, other fields as text parts.
|
|
477
|
+
in `body`, other fields as text parts. A snapshot of an app on a custom image can come with
|
|
478
|
+
`warning` (`IMAGE_VERSION_NOT_FOUND`). Rate limit 15/min, plus the plan's
|
|
473
479
|
deploys-per-hour cap. Replies 202 with `status: "building"`: the build continues in the
|
|
474
480
|
background — use `wait_until_ready`, `logs` or `realtime` to follow it.
|
|
475
481
|
Doc: https://docs.vertracloud.app/api-reference/endpoint/apps/create
|
|
@@ -492,6 +498,15 @@ class AppsResource:
|
|
|
492
498
|
"POST", "/v1/apps", fields=fields, timeout=timeout if timeout is not None else LARGE_PAYLOAD_TIMEOUT
|
|
493
499
|
)
|
|
494
500
|
|
|
501
|
+
def create_from_image(self, body: CreateAppFromImageBody) -> APIApplication:
|
|
502
|
+
"""POST /v1/apps/image — scope `apps:write`. JSON. Creates an app that runs from a custom
|
|
503
|
+
image, which must be ready (`images.get(...)["status"] == "success"`). Replies 202 with
|
|
504
|
+
`status: "building"` and `custom_image` filled in; follow the build as with `create`.
|
|
505
|
+
`web: False` makes an app without traffic; `subdomain` and `port` need `web: True`.
|
|
506
|
+
Doc: https://docs.vertracloud.app/api-reference/endpoint/apps/create-from-image
|
|
507
|
+
"""
|
|
508
|
+
return self._c.request_json("POST", "/v1/apps/image", json_body=body)
|
|
509
|
+
|
|
495
510
|
def wait_until_ready(
|
|
496
511
|
self,
|
|
497
512
|
app_id: str,
|
|
@@ -534,7 +549,7 @@ class AppsResource:
|
|
|
534
549
|
self, app_id: str, body: RestartAppBody | None = None, *, workspace_id: str | None = None
|
|
535
550
|
) -> APIApplicationOperationResponse:
|
|
536
551
|
"""POST /v1/apps/:id/restart — scope `apps:write`. Flags: `reinstall_dependencies`,
|
|
537
|
-
`force_build
|
|
552
|
+
`force_build`."""
|
|
538
553
|
return self._c.request_json(
|
|
539
554
|
"POST",
|
|
540
555
|
f"/v1/apps/{encode_path_param(app_id)}/restart",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""`databases` domain —
|
|
1
|
+
"""`databases` domain — 14 routes (scopes `databases:read/write/delete/credentials`)."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -11,6 +11,7 @@ from ..types import (
|
|
|
11
11
|
APIDatabasePasswordReset,
|
|
12
12
|
APIDatabaseStatus,
|
|
13
13
|
APIDatabaseStatusShort,
|
|
14
|
+
APIDatabaseVersions,
|
|
14
15
|
APIOperationResponse,
|
|
15
16
|
CreateDatabaseBody,
|
|
16
17
|
UpdateDatabaseBody,
|
|
@@ -63,7 +64,7 @@ class DatabaseCredentialsResource:
|
|
|
63
64
|
|
|
64
65
|
|
|
65
66
|
class DatabasesResource:
|
|
66
|
-
"""`databases.*` —
|
|
67
|
+
"""`databases.*` — status_all/versions/get/status/metrics/create/update/start/stop/reset/delete +
|
|
67
68
|
`credentials`."""
|
|
68
69
|
|
|
69
70
|
def __init__(self, client: VertraClient) -> None:
|
|
@@ -74,6 +75,12 @@ class DatabasesResource:
|
|
|
74
75
|
"""GET /v1/databases/status — scope `databases:read`."""
|
|
75
76
|
return self._c.request_json("GET", "/v1/databases/status", query={"workspace_id": workspace_id})
|
|
76
77
|
|
|
78
|
+
def versions(self) -> APIDatabaseVersions:
|
|
79
|
+
"""GET /v1/databases/versions — scope `databases:read`. Catalog keyed by engine `type`:
|
|
80
|
+
`recommended` and `available` lines for new databases. A database's version cannot be
|
|
81
|
+
changed after creation; security updates are applied when it is stopped and started."""
|
|
82
|
+
return self._c.request_json("GET", "/v1/databases/versions")
|
|
83
|
+
|
|
77
84
|
def get(self, db_id: str, *, workspace_id: str | None = None) -> APIDatabase:
|
|
78
85
|
"""GET /v1/databases/:id — scope `databases:read`."""
|
|
79
86
|
return self._c.request_json(
|
|
@@ -93,7 +100,8 @@ class DatabasesResource:
|
|
|
93
100
|
)
|
|
94
101
|
|
|
95
102
|
def create(self, body: CreateDatabaseBody) -> APIDatabase:
|
|
96
|
-
"""POST /v1/databases — scope `databases:write`.
|
|
103
|
+
"""POST /v1/databases — scope `databases:write`.
|
|
104
|
+
Errors: `INVALID_DATABASE_VERSION`, `SNAPSHOT_VERSION_NEWER`."""
|
|
97
105
|
return self._c.request_json("POST", "/v1/databases", json_body=body)
|
|
98
106
|
|
|
99
107
|
def update(self, db_id: str, body: UpdateDatabaseBody, *, workspace_id: str | None = None) -> APIDatabase:
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"""`images` domain — custom Docker images (scopes `images:read`, `images:write` and `images:delete`).
|
|
2
|
+
|
|
3
|
+
A custom image is a build context with a `Dockerfile`, built by the platform in isolation.
|
|
4
|
+
`apps.create_from_image` (scope `apps:write`) runs an app from a ready image. Create and rebuild
|
|
5
|
+
reply 202 with `status: "building"`: follow the build with `logs`, or poll `get` until `status`
|
|
6
|
+
is `success` or `failed`.
|
|
7
|
+
|
|
8
|
+
`workspace_id` is a keyword-only query parameter on the routes that act on an existing image and
|
|
9
|
+
on `list`. Without it the call acts on the caller's own images, as the apps routes do.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from collections.abc import Iterator
|
|
15
|
+
from typing import TYPE_CHECKING
|
|
16
|
+
|
|
17
|
+
from ..client import LARGE_PAYLOAD_TIMEOUT, MultipartField, encode_path_param
|
|
18
|
+
from ..sse import SseStream
|
|
19
|
+
from ..types import (
|
|
20
|
+
APICustomImage,
|
|
21
|
+
APICustomImageContext,
|
|
22
|
+
APICustomImageDeleteResult,
|
|
23
|
+
APICustomImageVersion,
|
|
24
|
+
APIDockerfileCheck,
|
|
25
|
+
CheckDockerfileBody,
|
|
26
|
+
CreateCustomImageBody,
|
|
27
|
+
CustomImageGithubBody,
|
|
28
|
+
UpdateCustomImageBody,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from ..client import VertraClient
|
|
33
|
+
|
|
34
|
+
# Aliases so `list[...]` doesn't resolve to the `list` method inside the class itself.
|
|
35
|
+
ImageList = list[APICustomImage]
|
|
36
|
+
ImageVersionList = list[APICustomImageVersion]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ImageLogStream:
|
|
40
|
+
"""Build log of an image, as lines. Iterate it for the text of each `log` event; `close()`
|
|
41
|
+
stops following a live build. Other events (`system`, `heartbeat`) are skipped."""
|
|
42
|
+
|
|
43
|
+
def __init__(self, stream: SseStream) -> None:
|
|
44
|
+
self._stream = stream
|
|
45
|
+
|
|
46
|
+
def __iter__(self) -> Iterator[str]:
|
|
47
|
+
for event in self._stream:
|
|
48
|
+
if event.event == "log":
|
|
49
|
+
yield event.data
|
|
50
|
+
|
|
51
|
+
def close(self) -> None:
|
|
52
|
+
self._stream.close()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class ImagesResource:
|
|
56
|
+
"""`images.*` — list/get/create/update/rebuild/delete/versions/logs/context/link_github/unlink_github/check."""
|
|
57
|
+
|
|
58
|
+
def __init__(self, client: VertraClient) -> None:
|
|
59
|
+
self._c = client
|
|
60
|
+
|
|
61
|
+
def list(self, *, workspace_id: str | None = None) -> ImageList:
|
|
62
|
+
"""GET /v1/images — scope `images:read`. With `workspace_id`, lists the images of that workspace.
|
|
63
|
+
Doc: https://docs.vertracloud.app/api-reference/endpoint/images/list
|
|
64
|
+
"""
|
|
65
|
+
return self._c.request_json("GET", "/v1/images", query={"workspace_id": workspace_id})
|
|
66
|
+
|
|
67
|
+
def get(self, image_id: str, *, workspace_id: str | None = None) -> APICustomImage:
|
|
68
|
+
"""GET /v1/images/:image_id — scope `images:read`. `status` is the state of the latest build;
|
|
69
|
+
`current_version_id` and `digest` are `None` until a build succeeds.
|
|
70
|
+
Doc: https://docs.vertracloud.app/api-reference/endpoint/images/get
|
|
71
|
+
"""
|
|
72
|
+
return self._c.request_json(
|
|
73
|
+
"GET", f"/v1/images/{encode_path_param(image_id)}", query={"workspace_id": workspace_id}
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
def create(
|
|
77
|
+
self,
|
|
78
|
+
body: CreateCustomImageBody,
|
|
79
|
+
*,
|
|
80
|
+
file: bytes | None = None,
|
|
81
|
+
filename: str = "image.zip",
|
|
82
|
+
timeout: float | None = None,
|
|
83
|
+
) -> APICustomImage:
|
|
84
|
+
"""POST /v1/images — scope `images:write`. Multipart. The source is either `file` (a ZIP
|
|
85
|
+
of up to 100 MB, with a `Dockerfile` at its root) or `body["github_repository"]`;
|
|
86
|
+
`body["github_branch"]` is optional (default: the repository's default branch). A repository
|
|
87
|
+
in your GitHub App installations is linked and each push to the branch rebuilds the image; a
|
|
88
|
+
public repository outside them is built once, with no link (`github` is `None`). Raises
|
|
89
|
+
`ValueError` before any request when the source is missing, doubled, or a branch comes without
|
|
90
|
+
a repository. Replies 202 with `status: "building"`.
|
|
91
|
+
Errors: `PLAN_DOES_NOT_SUPPORT_CUSTOM_IMAGES`, `MAX_IMAGES_REACHED`, `DOCKERFILE_NOT_FOUND`,
|
|
92
|
+
`DOCKERFILE_REFUSED` (its `issues` attribute lists the findings), `IMAGE_CONTEXT_TOO_LARGE`,
|
|
93
|
+
`IMAGE_BUILDER_UNAVAILABLE`, `INSTALLATION_NOT_FOUND` (a private or missing repository outside
|
|
94
|
+
your installations).
|
|
95
|
+
Doc: https://docs.vertracloud.app/api-reference/endpoint/images/create
|
|
96
|
+
"""
|
|
97
|
+
repository = body.get("github_repository")
|
|
98
|
+
branch = body.get("github_branch")
|
|
99
|
+
if file is not None and (repository is not None or branch is not None):
|
|
100
|
+
raise ValueError("pass either file or github_repository (with github_branch), not both")
|
|
101
|
+
if file is None and repository is None:
|
|
102
|
+
raise ValueError("pass file, or github_repository (with an optional github_branch)")
|
|
103
|
+
|
|
104
|
+
fields = [MultipartField("name", body["name"])]
|
|
105
|
+
description = body.get("description")
|
|
106
|
+
if description is not None:
|
|
107
|
+
fields.append(MultipartField("description", description))
|
|
108
|
+
if file is not None:
|
|
109
|
+
fields.append(MultipartField("file", file, filename=filename))
|
|
110
|
+
if repository is not None:
|
|
111
|
+
fields.append(MultipartField("github_repository", repository))
|
|
112
|
+
if branch is not None:
|
|
113
|
+
fields.append(MultipartField("github_branch", branch))
|
|
114
|
+
workspace_id = body.get("workspace_id")
|
|
115
|
+
if workspace_id is not None:
|
|
116
|
+
fields.append(MultipartField("workspace_id", workspace_id))
|
|
117
|
+
return self._c.request_multipart(
|
|
118
|
+
"POST", "/v1/images", fields=fields, timeout=timeout if timeout is not None else LARGE_PAYLOAD_TIMEOUT
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
def update(self, image_id: str, body: UpdateCustomImageBody, *, workspace_id: str | None = None) -> APICustomImage:
|
|
122
|
+
"""PATCH /v1/images/:image_id — scope `images:write`. Renames the image and/or changes its
|
|
123
|
+
description; send only what changes (`description: None` or an empty string removes it). Builds
|
|
124
|
+
nothing: the apps keep running and nothing restarts. Errors: `VALIDATION_ERROR` (empty body, name
|
|
125
|
+
out of range or already used by another of your images with `details.path == "name"`,
|
|
126
|
+
description too long), `IMAGE_NOT_FOUND`, `ACCESS_DENIED` (system images cannot be changed).
|
|
127
|
+
Doc: https://docs.vertracloud.app/api-reference/endpoint/images/update
|
|
128
|
+
"""
|
|
129
|
+
return self._c.request_json(
|
|
130
|
+
"PATCH",
|
|
131
|
+
f"/v1/images/{encode_path_param(image_id)}",
|
|
132
|
+
json_body=body,
|
|
133
|
+
query={"workspace_id": workspace_id},
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
def rebuild(
|
|
137
|
+
self,
|
|
138
|
+
image_id: str,
|
|
139
|
+
*,
|
|
140
|
+
file: bytes,
|
|
141
|
+
filename: str = "image.zip",
|
|
142
|
+
workspace_id: str | None = None,
|
|
143
|
+
timeout: float | None = None,
|
|
144
|
+
) -> APICustomImage:
|
|
145
|
+
"""PUT /v1/images/:image_id — scope `images:write`. Multipart `file` replaces the whole build
|
|
146
|
+
context. Replies 202 with `status: "building"`. Refused with `IMAGE_BUILD_IN_PROGRESS` while a
|
|
147
|
+
build runs. A rebuild never restarts the apps that run the image.
|
|
148
|
+
Doc: https://docs.vertracloud.app/api-reference/endpoint/images/rebuild
|
|
149
|
+
"""
|
|
150
|
+
return self._c.request_multipart(
|
|
151
|
+
"PUT",
|
|
152
|
+
f"/v1/images/{encode_path_param(image_id)}",
|
|
153
|
+
fields=[MultipartField("file", file, filename=filename)],
|
|
154
|
+
query={"workspace_id": workspace_id},
|
|
155
|
+
timeout=timeout if timeout is not None else LARGE_PAYLOAD_TIMEOUT,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
def delete(
|
|
159
|
+
self, image_id: str, *, confirm_name: str, workspace_id: str | None = None
|
|
160
|
+
) -> APICustomImageDeleteResult:
|
|
161
|
+
"""DELETE /v1/images/:image_id — scope `images:delete`, and also `apps:delete` when apps run the
|
|
162
|
+
image. Body `{confirm_name}` must equal the image name (`IMAGE_NAME_MISMATCH` otherwise).
|
|
163
|
+
Deletes the versions, the context and every app created from the image; `deleted_apps` lists
|
|
164
|
+
their ids. Refused with `IMAGE_BUILD_IN_PROGRESS` while a build runs.
|
|
165
|
+
Doc: https://docs.vertracloud.app/api-reference/endpoint/images/delete
|
|
166
|
+
"""
|
|
167
|
+
return self._c.request_json(
|
|
168
|
+
"DELETE",
|
|
169
|
+
f"/v1/images/{encode_path_param(image_id)}",
|
|
170
|
+
json_body={"confirm_name": confirm_name},
|
|
171
|
+
query={"workspace_id": workspace_id},
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
def versions(self, image_id: str, *, workspace_id: str | None = None) -> ImageVersionList:
|
|
175
|
+
"""GET /v1/images/:image_id/versions — scope `images:read`. Newest first.
|
|
176
|
+
Doc: https://docs.vertracloud.app/api-reference/endpoint/images/versions
|
|
177
|
+
"""
|
|
178
|
+
return self._c.request_json(
|
|
179
|
+
"GET", f"/v1/images/{encode_path_param(image_id)}/versions", query={"workspace_id": workspace_id}
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
def logs(
|
|
183
|
+
self,
|
|
184
|
+
image_id: str,
|
|
185
|
+
*,
|
|
186
|
+
version_id: str | None = None,
|
|
187
|
+
workspace_id: str | None = None,
|
|
188
|
+
timeout: float | None = None,
|
|
189
|
+
) -> ImageLogStream:
|
|
190
|
+
"""GET /v1/images/:image_id/logs — scope `images:read`. A Server-Sent Events stream: it repeats
|
|
191
|
+
the history of the build and then follows it live. `version_id` picks the build of one version;
|
|
192
|
+
by default it is the latest build. Iterate it for lines; `close()` or `break` ends it.
|
|
193
|
+
Doc: https://docs.vertracloud.app/api-reference/endpoint/images/logs
|
|
194
|
+
"""
|
|
195
|
+
stream = self._c.request_sse(
|
|
196
|
+
f"/v1/images/{encode_path_param(image_id)}/logs",
|
|
197
|
+
query={"version_id": version_id, "workspace_id": workspace_id},
|
|
198
|
+
timeout=timeout,
|
|
199
|
+
)
|
|
200
|
+
return ImageLogStream(stream)
|
|
201
|
+
|
|
202
|
+
def context(self, image_id: str, *, workspace_id: str | None = None) -> APICustomImageContext:
|
|
203
|
+
"""GET /v1/images/:image_id/context — scope `images:read`. `url` is a temporary download link
|
|
204
|
+
of the current build context.
|
|
205
|
+
Doc: https://docs.vertracloud.app/api-reference/endpoint/images/context
|
|
206
|
+
"""
|
|
207
|
+
return self._c.request_json(
|
|
208
|
+
"GET", f"/v1/images/{encode_path_param(image_id)}/context", query={"workspace_id": workspace_id}
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
def link_github(
|
|
212
|
+
self, image_id: str, body: CustomImageGithubBody, *, workspace_id: str | None = None
|
|
213
|
+
) -> APICustomImage:
|
|
214
|
+
"""PUT /v1/images/:image_id/github — scope `images:write`. Links a repository (`owner/name`) in
|
|
215
|
+
your GitHub App installations: each push to the branch rebuilds the image. `body["branch"]` is
|
|
216
|
+
optional (default: the repository's default branch).
|
|
217
|
+
Doc: https://docs.vertracloud.app/api-reference/endpoint/images/github-link
|
|
218
|
+
"""
|
|
219
|
+
return self._c.request_json(
|
|
220
|
+
"PUT",
|
|
221
|
+
f"/v1/images/{encode_path_param(image_id)}/github",
|
|
222
|
+
json_body=body,
|
|
223
|
+
query={"workspace_id": workspace_id},
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
def unlink_github(self, image_id: str, *, workspace_id: str | None = None) -> APICustomImage:
|
|
227
|
+
"""DELETE /v1/images/:image_id/github — scope `images:write`. Unlinks the repository.
|
|
228
|
+
Doc: https://docs.vertracloud.app/api-reference/endpoint/images/github-unlink
|
|
229
|
+
"""
|
|
230
|
+
return self._c.request_json(
|
|
231
|
+
"DELETE", f"/v1/images/{encode_path_param(image_id)}/github", query={"workspace_id": workspace_id}
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
def check(self, body: CheckDockerfileBody) -> APIDockerfileCheck:
|
|
235
|
+
"""POST /v1/images/check — scope `images:read`. Checks a `Dockerfile` and the paths of its
|
|
236
|
+
context before building; creates nothing. `refusals` would block the build, `warnings` would not.
|
|
237
|
+
Doc: https://docs.vertracloud.app/api-reference/endpoint/images/check
|
|
238
|
+
"""
|
|
239
|
+
return self._c.request_json("POST", "/v1/images/check", json_body=body)
|
|
@@ -56,11 +56,25 @@ class SnapshotsResource:
|
|
|
56
56
|
query={"scope": scope},
|
|
57
57
|
)
|
|
58
58
|
|
|
59
|
-
def restore(
|
|
60
|
-
|
|
59
|
+
def restore(
|
|
60
|
+
self,
|
|
61
|
+
resource_id: str,
|
|
62
|
+
snapshot_id: str,
|
|
63
|
+
scope: SnapshotScope,
|
|
64
|
+
*,
|
|
65
|
+
restore_image_version: bool | None = None,
|
|
66
|
+
) -> APISnapshotRestoreResponse:
|
|
67
|
+
"""POST /v1/users/:id/snapshots/:snapshot_id/restore — scope `snapshots:write`.
|
|
68
|
+
For database snapshots it can fail with `SNAPSHOT_VERSION_NEWER`.
|
|
69
|
+
`restore_image_version` applies to an app on a custom image: `True` also goes back to the image
|
|
70
|
+
version the app ran when the snapshot was taken, if it still exists (the response then carries
|
|
71
|
+
`warning` when it does not). Omitted, the API restores only the data."""
|
|
72
|
+
body: dict[str, bool] = (
|
|
73
|
+
{} if restore_image_version is None else {"restore_image_version": restore_image_version}
|
|
74
|
+
)
|
|
61
75
|
return self._c.request_json(
|
|
62
76
|
"POST",
|
|
63
77
|
f"/v1/users/{encode_path_param(resource_id)}/snapshots/{encode_path_param(snapshot_id)}/restore",
|
|
64
|
-
json_body=
|
|
78
|
+
json_body=body,
|
|
65
79
|
query={"scope": scope},
|
|
66
80
|
)
|
|
@@ -66,6 +66,7 @@ class CreateAppBody(TypedDict, total=False):
|
|
|
66
66
|
main: str
|
|
67
67
|
subdomain: str
|
|
68
68
|
version: str
|
|
69
|
+
language: ApplicationLanguage
|
|
69
70
|
name: str
|
|
70
71
|
description: str | None
|
|
71
72
|
workspace_id: str
|
|
@@ -103,7 +104,6 @@ class RollbackAppBody(TypedDict, total=False):
|
|
|
103
104
|
|
|
104
105
|
|
|
105
106
|
class RestartAppBody(TypedDict, total=False):
|
|
106
|
-
cleanup_old_runtime_language: str
|
|
107
107
|
reinstall_dependencies: bool
|
|
108
108
|
force_build: bool
|
|
109
109
|
|
|
@@ -145,6 +145,9 @@ class SetSubdomainBody(TypedDict):
|
|
|
145
145
|
|
|
146
146
|
class PublishAppBody(TypedDict, total=False):
|
|
147
147
|
subdomain: str
|
|
148
|
+
# Only for an app on a custom image: the port the image listens on (1-65535).
|
|
149
|
+
# Required when the app was created without web; ignored for other apps.
|
|
150
|
+
port: int
|
|
148
151
|
|
|
149
152
|
|
|
150
153
|
class PurgeCacheBody(TypedDict, total=False):
|
|
@@ -170,15 +173,23 @@ class MoveFileBody(TypedDict, total=False):
|
|
|
170
173
|
workspace_id: str
|
|
171
174
|
|
|
172
175
|
|
|
173
|
-
class
|
|
174
|
-
"""Body for `apps.deploys.webhook.create` — all 4 fields are required."""
|
|
175
|
-
|
|
176
|
+
class _CreateWebhookBodyRequired(TypedDict):
|
|
176
177
|
owner: str
|
|
177
178
|
repo_name: str
|
|
178
179
|
repo_id: str
|
|
179
180
|
account_id: str
|
|
180
181
|
|
|
181
182
|
|
|
183
|
+
class CreateWebhookBody(_CreateWebhookBodyRequired, total=False):
|
|
184
|
+
"""Body for `apps.deploys.webhook.create` — the first 4 fields are required.
|
|
185
|
+
|
|
186
|
+
`branch` is the only branch the auto-deploy reacts to. Without it, a push on `main` or `master`
|
|
187
|
+
deploys, as it always did. Linking again with another `branch` changes it; linking again
|
|
188
|
+
without one goes back to `main` or `master`."""
|
|
189
|
+
|
|
190
|
+
branch: str
|
|
191
|
+
|
|
192
|
+
|
|
182
193
|
class _CreateDatabaseBodyRequired(TypedDict):
|
|
183
194
|
name: str
|
|
184
195
|
ram: int
|
|
@@ -192,6 +203,10 @@ class CreateDatabaseBody(_CreateDatabaseBodyRequired, total=False):
|
|
|
192
203
|
type: int
|
|
193
204
|
workspace_id: str
|
|
194
205
|
snapshot_id: str
|
|
206
|
+
version: str
|
|
207
|
+
"""Engine major line (see `databases.versions()`). Omitted, the recommended one is used; with
|
|
208
|
+
`snapshot_id`, omit it to keep the snapshot's line (an older one fails with `SNAPSHOT_VERSION_NEWER`,
|
|
209
|
+
an unknown one with `INVALID_DATABASE_VERSION`)."""
|
|
195
210
|
|
|
196
211
|
|
|
197
212
|
class UpdateDatabaseBody(TypedDict, total=False):
|
|
@@ -295,7 +310,19 @@ class PutBillingDetailsBody(_PutBillingDetailsRequired, total=False):
|
|
|
295
310
|
# -- responses -------------------------------------------------------------
|
|
296
311
|
|
|
297
312
|
ApplicationLanguage = Literal[
|
|
298
|
-
"javascript",
|
|
313
|
+
"javascript",
|
|
314
|
+
"typescript",
|
|
315
|
+
"bun",
|
|
316
|
+
"python",
|
|
317
|
+
"static",
|
|
318
|
+
"php",
|
|
319
|
+
"go",
|
|
320
|
+
"ruby",
|
|
321
|
+
"java",
|
|
322
|
+
"rust",
|
|
323
|
+
"deno",
|
|
324
|
+
"dotnet",
|
|
325
|
+
"elixir",
|
|
299
326
|
]
|
|
300
327
|
ApplicationStatus = Literal["up", "down", "building"]
|
|
301
328
|
ApplicationBuildError = Literal[
|
|
@@ -381,12 +408,20 @@ class _APIApplicationRequired(TypedDict):
|
|
|
381
408
|
shield_cooldown: APIApplicationShieldCooldown | None
|
|
382
409
|
deploy: APIApplicationDeploySettings
|
|
383
410
|
last_build_error: ApplicationBuildError | None
|
|
411
|
+
# Custom Docker image the app runs from; `None` for a regular app.
|
|
412
|
+
custom_image: APIApplicationCustomImage | None
|
|
384
413
|
|
|
385
414
|
|
|
386
415
|
class APIApplication(_APIApplicationRequired, total=False):
|
|
387
416
|
description: str
|
|
388
417
|
github: APIApplicationGithub | None
|
|
389
|
-
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
class APIApplicationCreated(APIApplication, total=False):
|
|
421
|
+
"""Response of `apps.create`. `warning` is set when the app was created from the snapshot of an app
|
|
422
|
+
on a custom image and the recorded image version no longer exists: the app uses the current version."""
|
|
423
|
+
|
|
424
|
+
warning: SnapshotImageWarning
|
|
390
425
|
|
|
391
426
|
|
|
392
427
|
class APIApplicationNetwork(TypedDict):
|
|
@@ -427,8 +462,11 @@ class _APIAppFileUploadResponseRequired(TypedDict):
|
|
|
427
462
|
|
|
428
463
|
|
|
429
464
|
class APIAppFileUploadResponse(_APIAppFileUploadResponseRequired, total=False):
|
|
430
|
-
missing_dependencies: list[str]
|
|
431
465
|
removed_directories: list[str]
|
|
466
|
+
# Only for a commit to an app on a custom image: the image build the commit started. `None` otherwise.
|
|
467
|
+
image_build: APIApplicationImageBuild | None
|
|
468
|
+
# Readable summary for the CLI, e.g. that the image is rebuilding.
|
|
469
|
+
message: str
|
|
432
470
|
|
|
433
471
|
|
|
434
472
|
class APIApplicationMetric(TypedDict):
|
|
@@ -520,6 +558,16 @@ class APIWebhookUrl(TypedDict):
|
|
|
520
558
|
url: str
|
|
521
559
|
|
|
522
560
|
|
|
561
|
+
class APIApplicationWebhook(TypedDict):
|
|
562
|
+
"""Auto-deploy webhook of an application and the repository it is linked to.
|
|
563
|
+
`branch` is `None` for a link created without one (reacts to `main` or `master`)."""
|
|
564
|
+
|
|
565
|
+
webhook_url: str
|
|
566
|
+
repo_owner: str
|
|
567
|
+
repo_name: str
|
|
568
|
+
branch: str | None
|
|
569
|
+
|
|
570
|
+
|
|
523
571
|
class APISubdomainResponse(TypedDict):
|
|
524
572
|
subdomain: str
|
|
525
573
|
|
|
@@ -554,12 +602,19 @@ class APIApplicationFileContent(TypedDict):
|
|
|
554
602
|
last_modified: str
|
|
555
603
|
|
|
556
604
|
|
|
557
|
-
class
|
|
605
|
+
class _APIApplicationScanSuggestionsRequired(TypedDict):
|
|
606
|
+
language: ApplicationLanguage | None
|
|
607
|
+
main: str | None
|
|
608
|
+
version: str | None
|
|
609
|
+
has_manifest: bool
|
|
610
|
+
candidates: list[ApplicationLanguage]
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
class APIApplicationScanSuggestions(_APIApplicationScanSuggestionsRequired, total=False):
|
|
558
614
|
suggested_start_command: str
|
|
559
615
|
suggested_build_command: str
|
|
560
616
|
suggested_memory_mb: int
|
|
561
617
|
suggested_public_web: bool
|
|
562
|
-
missing_dependencies: list[str]
|
|
563
618
|
|
|
564
619
|
|
|
565
620
|
class APIRuntimeEntry(TypedDict):
|
|
@@ -587,6 +642,18 @@ class APIDatabase(TypedDict):
|
|
|
587
642
|
updated_at: str
|
|
588
643
|
last_snapshot: str | None
|
|
589
644
|
offline_since: str | None
|
|
645
|
+
version: str
|
|
646
|
+
"""Engine major line, e.g. `"18"` or `"8.0"`; the patch is never exposed. Not limited to the
|
|
647
|
+
catalog: older databases may report a line that is no longer offered for new ones."""
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
class APIDatabaseVersionCatalog(TypedDict):
|
|
651
|
+
recommended: str
|
|
652
|
+
available: list[str]
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
APIDatabaseVersions = dict[str, APIDatabaseVersionCatalog]
|
|
656
|
+
"""Catalog keyed by the engine `type` as a string (`"1"`, `"2"`, ...)."""
|
|
590
657
|
|
|
591
658
|
|
|
592
659
|
class APIDatabaseNetwork(TypedDict):
|
|
@@ -649,10 +716,15 @@ class APIGroupedResourceSnapshots(TypedDict):
|
|
|
649
716
|
snapshots: list[APIResourceSnapshot]
|
|
650
717
|
|
|
651
718
|
|
|
652
|
-
class
|
|
719
|
+
class _APISnapshotRestoreResponseRequired(TypedDict):
|
|
653
720
|
message: str
|
|
654
721
|
|
|
655
722
|
|
|
723
|
+
class APISnapshotRestoreResponse(_APISnapshotRestoreResponseRequired, total=False):
|
|
724
|
+
# Set when `restore_image_version` was asked but the recorded image version is gone: only data was restored.
|
|
725
|
+
warning: SnapshotImageWarning
|
|
726
|
+
|
|
727
|
+
|
|
656
728
|
class APIUser(TypedDict):
|
|
657
729
|
id: str
|
|
658
730
|
name: str
|
|
@@ -1116,3 +1188,163 @@ class _APIBlobCustomDomainRequired(TypedDict):
|
|
|
1116
1188
|
class APIBlobCustomDomain(_APIBlobCustomDomainRequired, total=False):
|
|
1117
1189
|
# Only while the domain is not active: the TXT record that proves ownership.
|
|
1118
1190
|
verification: APIBlobCustomDomainVerification | None
|
|
1191
|
+
|
|
1192
|
+
|
|
1193
|
+
# -- Custom Docker images: `images.*` and `apps.create_from_image` ---------------------------
|
|
1194
|
+
|
|
1195
|
+
CustomImageStatus = Literal["building", "success", "failed"]
|
|
1196
|
+
CustomImageBuildSource = Literal["upload", "commit", "github"]
|
|
1197
|
+
CustomImageOwner = Literal["account", "system"]
|
|
1198
|
+
DockerfileIssueCode = Literal[
|
|
1199
|
+
"missing_dockerfile",
|
|
1200
|
+
"syntax_frontend",
|
|
1201
|
+
"run_security_insecure",
|
|
1202
|
+
"run_network_host",
|
|
1203
|
+
"mount_ssh",
|
|
1204
|
+
"mount_secret",
|
|
1205
|
+
"workdir_app",
|
|
1206
|
+
"env_file_in_context",
|
|
1207
|
+
"copy_env_file",
|
|
1208
|
+
"volume_outside_app",
|
|
1209
|
+
"volume_refused",
|
|
1210
|
+
]
|
|
1211
|
+
|
|
1212
|
+
|
|
1213
|
+
class DockerfileIssue(TypedDict):
|
|
1214
|
+
"""Refusals block the build; warnings do not. `line` is `None` when no line applies."""
|
|
1215
|
+
|
|
1216
|
+
code: DockerfileIssueCode
|
|
1217
|
+
line: int | None
|
|
1218
|
+
|
|
1219
|
+
|
|
1220
|
+
class APIDockerfileCheck(TypedDict):
|
|
1221
|
+
refusals: list[DockerfileIssue]
|
|
1222
|
+
warnings: list[DockerfileIssue]
|
|
1223
|
+
suggested_port: int
|
|
1224
|
+
|
|
1225
|
+
|
|
1226
|
+
class APICustomImageApp(TypedDict):
|
|
1227
|
+
id: str
|
|
1228
|
+
name: str
|
|
1229
|
+
version_id: str | None
|
|
1230
|
+
outdated: bool
|
|
1231
|
+
|
|
1232
|
+
|
|
1233
|
+
class APIApplicationCustomImage(APICustomImageApp):
|
|
1234
|
+
"""`APIApplication.custom_image`: the image the app runs from and the port it listens on."""
|
|
1235
|
+
|
|
1236
|
+
image_id: str
|
|
1237
|
+
port: int | None
|
|
1238
|
+
|
|
1239
|
+
|
|
1240
|
+
class APICustomImageGithub(TypedDict):
|
|
1241
|
+
repository: str
|
|
1242
|
+
branch: str
|
|
1243
|
+
|
|
1244
|
+
|
|
1245
|
+
class APICustomImage(TypedDict):
|
|
1246
|
+
id: str
|
|
1247
|
+
name: str
|
|
1248
|
+
# Free-text description, up to 128 characters; None when the image has none.
|
|
1249
|
+
description: str | None
|
|
1250
|
+
owner: CustomImageOwner
|
|
1251
|
+
status: CustomImageStatus
|
|
1252
|
+
error: str | None
|
|
1253
|
+
digest: str | None
|
|
1254
|
+
uncompressed_bytes: int | None
|
|
1255
|
+
layer_count: int | None
|
|
1256
|
+
# Port suggested for a new app on this image (first EXPOSE, or 80); None for images created before the field.
|
|
1257
|
+
suggested_port: int | None
|
|
1258
|
+
apps: list[APICustomImageApp]
|
|
1259
|
+
github: APICustomImageGithub | None
|
|
1260
|
+
current_version_id: str | None
|
|
1261
|
+
created_at: str
|
|
1262
|
+
updated_at: str
|
|
1263
|
+
|
|
1264
|
+
|
|
1265
|
+
class APICustomImageVersion(TypedDict):
|
|
1266
|
+
id: str
|
|
1267
|
+
digest: str
|
|
1268
|
+
source: CustomImageBuildSource
|
|
1269
|
+
message: str | None
|
|
1270
|
+
created_at: str
|
|
1271
|
+
in_use: bool
|
|
1272
|
+
|
|
1273
|
+
|
|
1274
|
+
class APICustomImageContext(TypedDict):
|
|
1275
|
+
url: str
|
|
1276
|
+
|
|
1277
|
+
|
|
1278
|
+
class APICustomImageDeleteResult(TypedDict):
|
|
1279
|
+
deleted_apps: list[str]
|
|
1280
|
+
|
|
1281
|
+
|
|
1282
|
+
class _CreateCustomImageRequired(TypedDict):
|
|
1283
|
+
name: str
|
|
1284
|
+
|
|
1285
|
+
|
|
1286
|
+
class CreateCustomImageBody(_CreateCustomImageRequired, total=False):
|
|
1287
|
+
"""`POST /v1/images` fields. Exactly one source: a `file` passed to `images.create`, or
|
|
1288
|
+
`github_repository` (`owner/name`). `github_branch` is optional; without it the repository's default
|
|
1289
|
+
branch is used. A repository in your GitHub App installations is linked (each push to the branch
|
|
1290
|
+
rebuilds the image); a public repository outside them is built once, with no link.
|
|
1291
|
+
`description` is optional free text, up to 128 characters (an empty value means none)."""
|
|
1292
|
+
|
|
1293
|
+
description: str
|
|
1294
|
+
github_repository: str
|
|
1295
|
+
github_branch: str
|
|
1296
|
+
workspace_id: str
|
|
1297
|
+
|
|
1298
|
+
|
|
1299
|
+
class _CustomImageGithubRequired(TypedDict):
|
|
1300
|
+
repository: str
|
|
1301
|
+
|
|
1302
|
+
|
|
1303
|
+
class UpdateCustomImageBody(TypedDict, total=False):
|
|
1304
|
+
"""`PATCH /v1/images/:image_id` fields; send only what changes (at least one). `name` is 1 to 50
|
|
1305
|
+
characters, unique among your images. `description` is up to 128 characters; `None` or an empty
|
|
1306
|
+
string removes it."""
|
|
1307
|
+
|
|
1308
|
+
name: str
|
|
1309
|
+
description: str | None
|
|
1310
|
+
|
|
1311
|
+
|
|
1312
|
+
class CustomImageGithubBody(_CustomImageGithubRequired, total=False):
|
|
1313
|
+
"""`PUT /v1/images/:image_id/github` fields. `branch` is optional: without it the repository's
|
|
1314
|
+
default branch is used. The repository must be in one of your GitHub App installations."""
|
|
1315
|
+
|
|
1316
|
+
branch: str
|
|
1317
|
+
|
|
1318
|
+
|
|
1319
|
+
class CheckDockerfileBody(TypedDict):
|
|
1320
|
+
dockerfile: str
|
|
1321
|
+
files: list[str]
|
|
1322
|
+
|
|
1323
|
+
|
|
1324
|
+
class APIApplicationImageBuild(TypedDict):
|
|
1325
|
+
"""The image build started by a commit to an app on a custom image (`apps.files.upload`)."""
|
|
1326
|
+
|
|
1327
|
+
image_id: str
|
|
1328
|
+
build_id: str
|
|
1329
|
+
|
|
1330
|
+
|
|
1331
|
+
# Warning of a snapshot restore or of a create from a snapshot: the image version the snapshot recorded was
|
|
1332
|
+
# removed by retention, so the app stays on the image's current version.
|
|
1333
|
+
SnapshotImageWarning = Literal["IMAGE_VERSION_NOT_FOUND"]
|
|
1334
|
+
|
|
1335
|
+
|
|
1336
|
+
class _CreateAppFromImageRequired(TypedDict):
|
|
1337
|
+
image_id: str
|
|
1338
|
+
name: str
|
|
1339
|
+
memory: int
|
|
1340
|
+
web: bool
|
|
1341
|
+
|
|
1342
|
+
|
|
1343
|
+
class CreateAppFromImageBody(_CreateAppFromImageRequired, total=False):
|
|
1344
|
+
"""`POST /v1/apps/image`. `subdomain` and `port` apply only with `web: True`."""
|
|
1345
|
+
|
|
1346
|
+
description: str
|
|
1347
|
+
subdomain: str
|
|
1348
|
+
port: int
|
|
1349
|
+
envs: list[EnvEntry]
|
|
1350
|
+
workspace_id: str
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|