xenfra-sdk 0.1.3__py3-none-any.whl → 0.1.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import json
|
|
1
2
|
import logging
|
|
3
|
+
from typing import Iterator
|
|
2
4
|
|
|
3
5
|
# Import Deployment model when it's defined in models.py
|
|
4
6
|
# from ..models import Deployment
|
|
@@ -10,7 +12,7 @@ logger = logging.getLogger(__name__)
|
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
class DeploymentsManager(BaseManager):
|
|
13
|
-
def create(self, project_name: str, git_repo: str, branch: str, framework: str) -> dict:
|
|
15
|
+
def create(self, project_name: str, git_repo: str, branch: str, framework: str, region: str = None, size_slug: str = None) -> dict:
|
|
14
16
|
"""Creates a new deployment."""
|
|
15
17
|
try:
|
|
16
18
|
payload = {
|
|
@@ -19,6 +21,11 @@ class DeploymentsManager(BaseManager):
|
|
|
19
21
|
"branch": branch,
|
|
20
22
|
"framework": framework,
|
|
21
23
|
}
|
|
24
|
+
if region:
|
|
25
|
+
payload["region"] = region
|
|
26
|
+
if size_slug:
|
|
27
|
+
payload["size_slug"] = size_slug
|
|
28
|
+
|
|
22
29
|
response = self._client._request("POST", "/deployments", json=payload)
|
|
23
30
|
# Safe JSON parsing
|
|
24
31
|
return safe_json_parse(response)
|
|
@@ -87,3 +94,92 @@ class DeploymentsManager(BaseManager):
|
|
|
87
94
|
raise # Re-raise API errors
|
|
88
95
|
except Exception as e:
|
|
89
96
|
raise XenfraError(f"Failed to get logs for deployment {deployment_id}: {e}")
|
|
97
|
+
|
|
98
|
+
def create_stream(self, project_name: str, git_repo: str, branch: str, framework: str, region: str = None, size_slug: str = None) -> Iterator[dict]:
|
|
99
|
+
"""
|
|
100
|
+
Creates a new deployment with real-time SSE log streaming.
|
|
101
|
+
|
|
102
|
+
Yields SSE events as dictionaries with 'event' and 'data' keys.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
project_name: Name of the project
|
|
106
|
+
git_repo: Git repository URL
|
|
107
|
+
branch: Git branch to deploy
|
|
108
|
+
framework: Framework type (fastapi, flask, django)
|
|
109
|
+
region: DigitalOcean region (optional)
|
|
110
|
+
size_slug: DigitalOcean droplet size (optional)
|
|
111
|
+
|
|
112
|
+
Yields:
|
|
113
|
+
dict: SSE events with 'event' and 'data' fields
|
|
114
|
+
|
|
115
|
+
Example:
|
|
116
|
+
for event in client.deployments.create_stream(...):
|
|
117
|
+
if event['event'] == 'log':
|
|
118
|
+
print(event['data'])
|
|
119
|
+
elif event['event'] == 'deployment_complete':
|
|
120
|
+
print("Done!")
|
|
121
|
+
"""
|
|
122
|
+
payload = {
|
|
123
|
+
"project_name": project_name,
|
|
124
|
+
"git_repo": git_repo,
|
|
125
|
+
"branch": branch,
|
|
126
|
+
"framework": framework,
|
|
127
|
+
}
|
|
128
|
+
if region:
|
|
129
|
+
payload["region"] = region
|
|
130
|
+
if size_slug:
|
|
131
|
+
payload["size_slug"] = size_slug
|
|
132
|
+
|
|
133
|
+
try:
|
|
134
|
+
# Use httpx to stream the SSE response
|
|
135
|
+
import httpx
|
|
136
|
+
|
|
137
|
+
headers = {
|
|
138
|
+
"Authorization": f"Bearer {self._client.token}",
|
|
139
|
+
"Accept": "text/event-stream",
|
|
140
|
+
"Content-Type": "application/json",
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
url = f"{self._client.api_url}/deployments/stream"
|
|
144
|
+
|
|
145
|
+
with httpx.stream(
|
|
146
|
+
"POST",
|
|
147
|
+
url,
|
|
148
|
+
json=payload,
|
|
149
|
+
headers=headers,
|
|
150
|
+
timeout=600.0, # 10 minute timeout for deployments
|
|
151
|
+
) as response:
|
|
152
|
+
if response.status_code not in [200, 201, 202]:
|
|
153
|
+
error_text = response.text
|
|
154
|
+
raise XenfraAPIError(
|
|
155
|
+
status_code=response.status_code,
|
|
156
|
+
detail=f"Deployment failed: {error_text}"
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Parse SSE events
|
|
160
|
+
for line in response.iter_lines():
|
|
161
|
+
line = line.strip()
|
|
162
|
+
if not line:
|
|
163
|
+
continue
|
|
164
|
+
|
|
165
|
+
# SSE format: "event: eventname" or "data: eventdata"
|
|
166
|
+
if line.startswith("event:"):
|
|
167
|
+
current_event = line[6:].strip()
|
|
168
|
+
elif line.startswith("data:"):
|
|
169
|
+
data = line[5:].strip()
|
|
170
|
+
try:
|
|
171
|
+
# Try to parse as JSON
|
|
172
|
+
data_parsed = json.loads(data)
|
|
173
|
+
yield {"event": current_event if 'current_event' in locals() else "message", "data": data_parsed}
|
|
174
|
+
except json.JSONDecodeError:
|
|
175
|
+
# If not JSON, yield as plain text
|
|
176
|
+
yield {"event": current_event if 'current_event' in locals() else "message", "data": data}
|
|
177
|
+
|
|
178
|
+
# Reset current_event
|
|
179
|
+
if 'current_event' in locals():
|
|
180
|
+
del current_event
|
|
181
|
+
|
|
182
|
+
except httpx.HTTPError as e:
|
|
183
|
+
raise XenfraError(f"HTTP error during streaming deployment: {e}")
|
|
184
|
+
except Exception as e:
|
|
185
|
+
raise XenfraError(f"Failed to create streaming deployment: {e}")
|
|
@@ -18,7 +18,7 @@ xenfra_sdk/privacy.py,sha256=ksGf5L9PVtRP-xZS3T-Gj7MKfexTqIMgbFLoYkIESOE,5662
|
|
|
18
18
|
xenfra_sdk/recipes.py,sha256=g_UKQIcdSokYh7zn186mzDTr08P034-KZ1iiDNELyP4,877
|
|
19
19
|
xenfra_sdk/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
20
|
xenfra_sdk/resources/base.py,sha256=C6BuZfhR-oU5ecHSfkGG6ZLU6MHGXUyCWoy1F-yTIf8,84
|
|
21
|
-
xenfra_sdk/resources/deployments.py,sha256=
|
|
21
|
+
xenfra_sdk/resources/deployments.py,sha256=GmgX3F-mr3mDR_vJzidqDmkOhJKiFA9adQmSiBrSdzM,7272
|
|
22
22
|
xenfra_sdk/resources/intelligence.py,sha256=Y11K6_iXfm2QKTbH1vUmt45MifLoVtZtlHEkqbzmTzs,3418
|
|
23
23
|
xenfra_sdk/resources/projects.py,sha256=EsCVXmqkhWl_Guz_8WDQDi3kAm1Wyg1rjXcyAigPD6E,3712
|
|
24
24
|
xenfra_sdk/security.py,sha256=Px887RRb1BUDXaPUrxmQITJ1mHyOyupCJqEDZ78F7Tk,1240
|
|
@@ -26,6 +26,6 @@ xenfra_sdk/templates/Dockerfile.j2,sha256=GXc0JiaF-HsxTQS15Gs2fcvsIhA1EHnwapdFVi
|
|
|
26
26
|
xenfra_sdk/templates/cloud-init.sh.j2,sha256=NKIwtL9OgnlK2NnYRZI3gWC9aYl6wNPsS6r14g8eHQQ,2290
|
|
27
27
|
xenfra_sdk/templates/docker-compose.yml.j2,sha256=qMHiatuZlxiYZ1pE_g2ag1M798MvQbeq0cVTVK07jkM,893
|
|
28
28
|
xenfra_sdk/utils.py,sha256=d8eCjjV32QwqoJa759CEcETnnsjG5qVKDLQ84yYtlus,3898
|
|
29
|
-
xenfra_sdk-0.1.
|
|
30
|
-
xenfra_sdk-0.1.
|
|
31
|
-
xenfra_sdk-0.1.
|
|
29
|
+
xenfra_sdk-0.1.4.dist-info/WHEEL,sha256=KSLUh82mDPEPk0Bx0ScXlWL64bc8KmzIPNcpQZFV-6E,79
|
|
30
|
+
xenfra_sdk-0.1.4.dist-info/METADATA,sha256=H8i-Gv6K6KGaHbIkdBAqDtS8DXrVl8rD2HFa4IX4vPM,3980
|
|
31
|
+
xenfra_sdk-0.1.4.dist-info/RECORD,,
|
|
File without changes
|