11import os
2- from typing import List , Optional
2+ from typing import Dict , List , Optional
33
44import yaml
55from pydantic import BaseModel , Field
1313from morph .task .utils .morph import find_project_root_dir
1414
1515
16+ class BuildConfig (BaseModel ):
17+ runtime : Optional [str ] = None
18+ framework : Optional [str ] = "morph"
19+ package_manager : Optional [str ] = None
20+ context : Optional [str ] = None
21+ build_args : Optional [Dict [str , str ]] = None
22+
23+
24+ class DeploymentConfig (BaseModel ):
25+ provider : Optional [str ] = "aws"
26+ aws : Optional [Dict [str , Optional [str ]]] = None
27+ gcp : Optional [Dict [str , Optional [str ]]] = None
28+
29+
1630class MorphProject (BaseModel ):
1731 profile : Optional [str ] = "default"
1832 source_paths : List [str ] = Field (default_factory = lambda : ["src" ])
@@ -21,6 +35,8 @@ class MorphProject(BaseModel):
2135 package_manager : str = Field (
2236 default = "pip" , description = "Package manager to use, e.g., pip or poetry."
2337 )
38+ build : Optional [BuildConfig ] = Field (default_factory = BuildConfig )
39+ deployment : Optional [DeploymentConfig ] = Field (default_factory = DeploymentConfig )
2440
2541 class Config :
2642 arbitrary_types_allowed = True
@@ -72,9 +88,115 @@ def save_project(project_root: str, project: MorphProject) -> None:
7288 old_config_path = os .path .join (project_root , "morph_project.yaml" )
7389 if os .path .exists (old_config_path ):
7490 with open (old_config_path , "w" ) as f :
75- yaml . safe_dump ( project . model_dump (), f )
91+ f . write ( dump_project_yaml ( project ) )
7692 return
7793
7894 config_path = os .path .join (project_root , "morph_project.yml" )
7995 with open (config_path , "w" ) as f :
80- yaml .safe_dump (project .model_dump (), f )
96+ f .write (dump_project_yaml (project ))
97+
98+
99+ def dump_project_yaml (project : MorphProject ) -> str :
100+ source_paths = "\n - " .join (["" ] + project .source_paths )
101+
102+ # Default values
103+ build_runtime = ""
104+ build_framework = ""
105+ build_package_manager = ""
106+ build_context = "."
107+ build_args_str = "\n # - ARG_NAME=value\n # - ANOTHER_ARG=value"
108+ deployment_provider = "aws"
109+ deployment_aws_region = "us-east-1"
110+ deployment_aws_memory = "1024"
111+ deployment_aws_timeout = "300"
112+ deployment_aws_concurrency = "1"
113+ deployment_gcp_region = "us-central1"
114+ deployment_gcp_memory = "1Gi"
115+ deployment_gcp_cpu = "1"
116+ deployment_gcp_concurrency = "80"
117+ deployment_gcp_timeout = "300"
118+
119+ # Set values if build exists
120+ if project .build :
121+ if project .build .runtime :
122+ build_runtime = project .build .runtime or ""
123+ if project .build .framework :
124+ build_framework = project .build .framework or ""
125+ if project .build .package_manager :
126+ build_package_manager = project .build .package_manager or ""
127+ if project .build .context :
128+ build_context = f"{ project .build .context } " or "."
129+ if project .build .build_args :
130+ build_args_items = []
131+ for key , value in project .build .build_args .items ():
132+ build_args_items .append (f"{ key } ={ value } " )
133+ build_args_str = (
134+ "\n # - " .join (["" ] + build_args_items )
135+ if build_args_items
136+ else "\n # - ARG_NAME=value\n # - ANOTHER_ARG=value"
137+ )
138+
139+ # Set values if deployment exists
140+ if project .deployment :
141+ if project .deployment .provider :
142+ deployment_provider = project .deployment .provider or "aws"
143+ if project .deployment .aws :
144+ deployment_aws_region = project .deployment .aws .get ("region" ) or "us-east-1"
145+ deployment_aws_memory = project .deployment .aws .get ("memory" ) or "1024"
146+ deployment_aws_timeout = project .deployment .aws .get ("timeout" ) or "300"
147+ deployment_aws_concurrency = (
148+ project .deployment .aws .get ("concurrency" ) or "1"
149+ )
150+ if project .deployment .gcp :
151+ deployment_gcp_region = (
152+ project .deployment .gcp .get ("region" ) or "us-central1"
153+ )
154+ deployment_gcp_memory = project .deployment .gcp .get ("memory" ) or "1Gi"
155+ deployment_gcp_cpu = project .deployment .gcp .get ("cpu" ) or "1"
156+ deployment_gcp_concurrency = (
157+ project .deployment .gcp .get ("concurrency" ) or "80"
158+ )
159+ deployment_gcp_timeout = project .deployment .gcp .get ("timeout" ) or "300"
160+ else :
161+ # Use default DeploymentConfig
162+ deployment_provider = "aws"
163+
164+ return f"""
165+ version: '1'
166+
167+ # Framework Settings
168+ default_connection: { project .default_connection }
169+ source_paths:{ source_paths }
170+
171+ # Cloud Settings
172+ # profile: { project .profile } # Defined in the Profile Section in `~/.morph/credentials`
173+ # project_id: { project .project_id or "null" }
174+
175+ # Build Settings
176+ build:
177+ # These settings are required when there is no Dockerfile in the project root.
178+ # They define the environment in which the project will be built
179+ runtime: { build_runtime } # python3.9, python3.10, python3.11, python3.12
180+ framework: { build_framework }
181+ package_manager: { build_package_manager } # pip, poetry, uv
182+ # These settings are required when there is a Dockerfile in the project root.
183+ # They define how the Docker image will be built
184+ # context: { build_context }
185+ # build_args:{ build_args_str }
186+
187+ # Deployment Settings
188+ deployment:
189+ provider: { deployment_provider } # aws or gcp (default is aws)
190+ # These settings are used only when you want to customize the deployment settings
191+ # aws:
192+ # region: { deployment_aws_region }
193+ # memory: { deployment_aws_memory }
194+ # timeout: { deployment_aws_timeout }
195+ # concurrency: { deployment_aws_concurrency }
196+ # gcp:
197+ # region: { deployment_gcp_region }
198+ # memory: { deployment_gcp_memory }
199+ # cpu: { deployment_gcp_cpu }
200+ # concurrency: { deployment_gcp_concurrency }
201+ # timeout: { deployment_gcp_timeout }
202+ """
0 commit comments