44import json
55import tempfile
66from pathlib import Path
7+
8+ from appwrite_lab .automations .models import BaseVarModel
79from ._state import State
810from dataclasses import dataclass
9- from .models import LabService , Automation
11+ from .models import LabService , Automation , SyncType
1012from dotenv import dotenv_values
1113from appwrite_lab .utils import console
12- from .utils import is_cli
13- from .config import PLAYWRIGHT_IMAGE
14+ from .utils import is_cli , load_config
15+ from .config import APPWRITE_CLI_IMAGE , APPWRITE_PLAYWRIGHT_IMAGE
1416from dataclasses import asdict
1517
1618
1719@dataclass
1820class Response :
1921 message : str
20- data : any
22+ data : any = None
2123 error : bool = False
2224
2325 def __post_init__ (self ):
@@ -43,7 +45,23 @@ def __init__(self, state: State, backend: str = "auto"):
4345 Path (__file__ ).parent / "templates" / "environment" / "dotenv"
4446 )
4547
46- def get_labs (self , collapsed : bool = False ):
48+ def get_labs (self ):
49+ """
50+ Get all labs.
51+ """
52+ labs : dict = self .state .get ("labs" , {})
53+ return [LabService (** lab ) for lab in labs .values ()]
54+
55+ def get_lab (self , name : str ) -> LabService | None :
56+ """
57+ Get a lab by name.
58+ """
59+ labs : dict = self .state .get ("labs" , {})
60+ if not (lab := labs .get (name , None )):
61+ return None
62+ return LabService (** lab )
63+
64+ def get_formatted_labs (self , collapsed : bool = False ):
4765 """
4866 Get all labs.
4967 """
@@ -192,7 +210,6 @@ def deploy_appwrite_lab(
192210 url = f"http://localhost:{ port } "
193211 else :
194212 url = ""
195- print ("url" , url )
196213 lab = LabService (
197214 name = name ,
198215 version = version ,
@@ -210,7 +227,7 @@ def deploy_appwrite_lab(
210227 f"Lab '{ name } ' deployed, but failed to create API key."
211228 )
212229 return api_key_res
213- lab .api_key = api_key_res
230+ lab .api_key = api_key_res . data
214231
215232 stored_labs : dict = self .state .get ("labs" , {}).copy ()
216233 stored_labs [name ] = asdict (lab )
@@ -223,7 +240,11 @@ def deploy_appwrite_lab(
223240 )
224241
225242 def deploy_playwright_automation (
226- self , lab : LabService , automation : str
243+ self ,
244+ lab : LabService ,
245+ automation : Automation ,
246+ model : BaseVarModel = None ,
247+ args : list [str ] = [],
227248 ) -> str | Response :
228249 """
229250 Deploy playwright automations on a lab (very few automations supported).
@@ -236,55 +257,69 @@ def deploy_playwright_automation(
236257 Args:
237258 lab: The lab to deploy the automations for.
238259 automation: The automation to deploy.
260+ model: The model to use for the automation.
239261 """
240- if automation == Automation .CREATE_USER_AND_API_KEY :
241- function = (
242- Path (__file__ ).parent / "playwright" / "functions" / f"{ automation } .py"
262+ automation = automation .value
263+ function = (
264+ Path (__file__ ).parent / "automations" / "functions" / f"{ automation } .py"
265+ )
266+ if not function .exists ():
267+ return Response (
268+ error = True ,
269+ message = f"Function { automation } not found. This should not happen." ,
270+ data = None ,
243271 )
244- if not function .exists ():
245- return Response (
246- error = True ,
247- message = f"Function { automation } not found. This should not happen." ,
248- data = None ,
272+ automation_dir = Path (__file__ ).parent / "automations"
273+ container_work_dir = "/work/automations"
274+ env_vars = {
275+ "APPWRITE_URL" : lab .url ,
276+ "APPWRITE_PROJECT_ID" : lab .project_id ,
277+ "APPWRITE_ADMIN_EMAIL" : lab .admin_email ,
278+ "APPWRITE_ADMIN_PASSWORD" : lab .admin_password ,
279+ "HOME" : container_work_dir ,
280+ ** (model .as_dict_with_prefix ("APPWRITE" ) if model else {}),
281+ }
282+ envs = " " .join ([f"{ key } ={ value } " for key , value in env_vars .items ()])
283+ docker_env_args = []
284+ for key , value in env_vars .items ():
285+ docker_env_args .extend (["-e" , f"{ key } ={ value } " ])
286+ with tempfile .TemporaryDirectory () as temp_dir :
287+ shutil .copytree (automation_dir , temp_dir , dirs_exist_ok = True )
288+ function = Path (temp_dir ) / "automations" / "functions" / f"{ automation } .py"
289+
290+ cmd = [
291+ self .util ,
292+ "run" ,
293+ "--network" ,
294+ "host" ,
295+ # "--rm",
296+ "-u" ,
297+ f"{ os .getuid ()} :{ os .getgid ()} " ,
298+ "-v" ,
299+ f"{ temp_dir } :{ container_work_dir } " ,
300+ * args ,
301+ * docker_env_args ,
302+ APPWRITE_PLAYWRIGHT_IMAGE ,
303+ "python" ,
304+ "-m" ,
305+ f"automations.functions.{ automation } " ,
306+ ]
307+ cmd_res = self ._run_cmd_safely (cmd )
308+ if type (cmd_res ) is Response and cmd_res .error :
309+ cmd_res .message = (
310+ f"Failed to deploy playwright automation { automation } ."
249311 )
250- env_vars = {
251- "APPWRITE_URL" : lab .url ,
252- "APPWRITE_PROJECT_ID" : lab .project_id ,
253- "APPWRITE_ADMIN_EMAIL" : lab .admin_email ,
254- "APPWRITE_ADMIN_PASSWORD" : lab .admin_password ,
255- }
256- envs = " " .join ([f"{ key } ={ value } " for key , value in env_vars .items ()])
257- with tempfile .TemporaryDirectory () as temp_dir :
258- temp_file = Path (temp_dir ) / "function.py"
259- shutil .copy (function , temp_file )
260- cmd = [
261- self .util ,
262- "run" ,
263- "--network" ,
264- "--rm" ,
265- "host" ,
266- "-v" ,
267- f"{ temp_dir } :/playwright" ,
268- * [f"-e { key } ={ value } " for key , value in env_vars .items ()],
269- PLAYWRIGHT_IMAGE ,
270- "bash" ,
271- "-c" ,
272- f"pip install playwright asyncio && { envs } python /playwright/function.py" ,
273- ]
274- cmd_res = self ._run_cmd_safely (cmd )
275- if type (cmd_res ) is Response and cmd_res .error :
276- cmd_res .message = (
277- f"Failed to deploy playwright automation { automation } ."
312+ return cmd_res
313+ # If successful, any data should be mounted as result.txt
314+ result_file = Path (temp_dir ) / "result.txt"
315+ if result_file .exists ():
316+ with open (result_file , "r" ) as f :
317+ data = f .read ()
318+ return Response (
319+ error = False ,
320+ message = f"Playwright automation{ automation } deployed successfully." ,
321+ data = data ,
278322 )
279- return cmd_res
280- # If successful, any data should be mounted as result.txt
281- result_file = Path (temp_dir ) / "result.txt"
282- if result_file .exists ():
283- with open (result_file , "r" ) as f :
284- data = f .read ()
285- return data
286- else :
287- return None
288323
289324 def teardown_service (self , name : str ):
290325 """
@@ -377,7 +412,7 @@ def run_cmd(cmd: list[str], envs: dict[str, str] | None = None):
377412 )
378413 if result .returncode != 0 :
379414 raise OrchestratorError (
380- f"An error occured running a command: { result .stdout } "
415+ f"An error occured running a command: { result .stderr } "
381416 )
382417 return result
383418 except Exception as e :
0 commit comments