Skip to content

Commit 01f6ea4

Browse files
committed
feat: add execute_command example and utility functions for environment management
1 parent af22da1 commit 01f6ea4

File tree

2 files changed

+168
-0
lines changed

2 files changed

+168
-0
lines changed

examples/execute_command.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#!/usr/bin/env python
2+
3+
from gitpod import Gitpod
4+
from utils import get_context_url, get_authenticated_user, get_environment_class_id, get_environment_class, create_environment
5+
import time
6+
import requests
7+
8+
def check_environment_status(client, environment_id):
9+
environment = client.environments.retrieve(environment_id=environment_id).environment
10+
print(f"Environment status: {environment.status.phase}")
11+
if environment.status.phase in ["ENVIRONMENT_PHASE_STOPPING", "ENVIRONMENT_PHASE_STOPPED", "ENVIRONMENT_PHASE_DELETING", "ENVIRONMENT_PHASE_DELETED"]:
12+
raise RuntimeError(f"Environment {environment_id} is in an unexpected phase: {environment.status.phase}")
13+
elif environment.status.failure_message and len(environment.status.failure_message) > 0:
14+
raise RuntimeError(f"Environment {environment_id} failed: {'; '.join(environment.status.failure_message)}")
15+
16+
def main() -> None:
17+
client: Gitpod = Gitpod()
18+
context_url: str = get_context_url()
19+
user = get_authenticated_user(client)
20+
environment_class_id: str = get_environment_class_id(client, user)
21+
22+
if not environment_class_id:
23+
raise ValueError("no environment class found")
24+
25+
environment_class = get_environment_class(client, environment_class_id)
26+
27+
if not environment_class:
28+
raise ValueError("no environment class found")
29+
30+
print(f"Using repository: {context_url}")
31+
print(f"Using environment class: {environment_class.display_name} (ID: {environment_class.id})")
32+
33+
environment_id: str = create_environment(client, context_url, environment_class)
34+
print(f"Created environment: {environment_id}")
35+
36+
try:
37+
reference = "hello-world"
38+
task = client.environments.automations.tasks.create(
39+
spec={
40+
"command": "echo 'Hello, World!'"
41+
},
42+
environment_id=environment_id,
43+
metadata={
44+
"name": "Hello, World!",
45+
"description": "Prints 'Hello, World!' to the console.",
46+
"reference": reference,
47+
}
48+
).task
49+
print(f"Created task: {task.id}")
50+
51+
execution = client.environments.automations.tasks.start(
52+
id=task.id
53+
).task_execution
54+
print(f"Started task execution: {execution.id}")
55+
56+
logs_access_token = client.environments.create_logs_token(environment_id=environment_id).access_token
57+
print(f"Waiting for task logs to be available...")
58+
59+
event_stream = client.events.watch(
60+
environment_id=environment_id
61+
)
62+
63+
log_url = None
64+
for event in event_stream:
65+
print(f"Received event: {event}")
66+
if event.resource_type == "RESOURCE_TYPE_ENVIRONMENT" and event.resource_id == environment_id:
67+
check_environment_status(client, environment_id)
68+
elif event.resource_type == "RESOURCE_TYPE_TASK_EXECUTION" and event.resource_id == execution.id:
69+
execution = client.environments.automations.tasks.executions.retrieve(id=execution.id).task_execution
70+
print(f"Task execution status: {execution.status.phase}")
71+
if execution.status.log_url:
72+
log_url = execution.status.log_url
73+
break
74+
75+
if not log_url:
76+
raise RuntimeError("Task logs are not available.")
77+
78+
print(f"Task logs are available at: {log_url}")
79+
80+
# Stream the log URL and print the logs
81+
response = requests.get(log_url, stream=True, headers={"Authorization": f"Bearer {logs_access_token}"})
82+
for line in response.iter_lines():
83+
if line:
84+
print(line.decode('utf-8'))
85+
86+
finally:
87+
client.environments.delete(environment_id=environment_id)
88+
print(f"Deleted environment: {environment_id}")
89+
90+
if __name__ == "__main__":
91+
main()

examples/utils.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import os
2+
from typing import Optional
3+
from gitpod.types.environment_create_params import SpecContentInitializerSpecContextURL
4+
from gitpod import Gitpod
5+
from gitpod.types.environment_retrieve_response import Environment
6+
from gitpod.types.user_get_authenticated_user_response import User
7+
from gitpod.types.environments.class_list_response import ClassListResponse
8+
9+
def get_context_url() -> str:
10+
return os.environ.get("CONTEXT_URL", "https://github.com/gitpod-io/empty")
11+
12+
def get_authenticated_user(client: Gitpod) -> User:
13+
return client.users.get_authenticated_user(body={}).user
14+
15+
def get_environment_class_id(client: Gitpod, user: User) -> Optional[str]:
16+
environment_class_id = os.environ.get("ENVIRONMENT_CLASS_ID")
17+
if environment_class_id:
18+
return environment_class_id
19+
20+
class_usage = {}
21+
page = client.environments.list(organization_id=user.organization_id)
22+
while page:
23+
for env in page.environments:
24+
env_class = env.spec.machine.class_
25+
if env_class not in class_usage:
26+
class_usage[env_class] = 0
27+
class_usage[env_class] += 1
28+
if page.pagination and page.pagination.next_token:
29+
page = client.environments.list(token=page.pagination.next_token, organization_id=user.organization_id)
30+
else:
31+
break
32+
33+
sorted_classes = sorted(class_usage.items(), key=lambda item: -item[1])
34+
return sorted_classes[0][0] if sorted_classes else None
35+
36+
def get_environment_class(client: Gitpod, environment_class_id: str) -> Optional[ClassListResponse]:
37+
page = client.environments.classes.list(filter={"enabled": True})
38+
while page:
39+
for cls in page.environment_classes:
40+
if cls.id == environment_class_id:
41+
return cls
42+
if page.pagination and page.pagination.next_token:
43+
page = client.environments.classes.list(token=page.pagination.next_token)
44+
else:
45+
break
46+
return None
47+
48+
def create_environment(client: Gitpod, context_url: str, environment_class: ClassListResponse) -> str:
49+
resp = client.environments.create(
50+
spec={
51+
"desired_phase": "ENVIRONMENT_PHASE_RUNNING",
52+
"content": {
53+
"initializer": {
54+
"specs": [
55+
SpecContentInitializerSpecContextURL(
56+
context_url={
57+
"url": context_url
58+
}
59+
)
60+
]
61+
}
62+
},
63+
"machine": {
64+
"class": environment_class.id
65+
}
66+
}
67+
)
68+
return resp.environment.id
69+
70+
def check_environment_status(client: Gitpod, environment_id: str) -> None:
71+
environment = client.environments.retrieve(environment_id=environment_id).environment
72+
print(f"Environment status: {environment.status.phase}")
73+
if environment.status.phase in ["ENVIRONMENT_PHASE_STOPPING", "ENVIRONMENT_PHASE_STOPPED", "ENVIRONMENT_PHASE_DELETING", "ENVIRONMENT_PHASE_DELETED"]:
74+
raise RuntimeError(f"Environment {environment_id} is in an unexpected phase: {environment.status.phase}")
75+
elif environment.status.failure_message and len(environment.status.failure_message) > 0:
76+
raise RuntimeError(f"Environment {environment_id} failed: {'; '.join(environment.status.failure_message)}")
77+

0 commit comments

Comments
 (0)