@@ -574,7 +574,8 @@ def build_and_package_template(self, directory, force_rebuild=False):
574574 self ._delete_checksum_file (directory )
575575 self .log_verbose (f"Exception in build_and_package_template: { e } " )
576576 self .log_verbose (f"Traceback: { traceback .format_exc ()} " )
577- return False
577+ self .console .print (f"[red]❌ Build failed for { directory } : { e } [/red]" )
578+ sys .exit (1 )
578579
579580 return True
580581
@@ -924,44 +925,91 @@ def _check_requirements_has_idp_common_pkg(self, func_dir):
924925
925926 return False , "No idp_common_pkg found in requirements.txt"
926927 except Exception as e :
927- return False , f"Error reading requirements.txt: { e } "
928+ self .console .print (
929+ f"[red]❌ Error reading requirements.txt in { func_dir } : { e } [/red]"
930+ )
931+ sys .exit (1 )
928932
929933 def _extract_function_name (self , dir_name , template_path ):
930934 """Extract CloudFormation function name from template by matching CodeUri."""
931935 try :
936+ try :
937+ import yaml
938+ except ImportError :
939+ self .console .print ("[yellow]PyYAML not found, installing...[/yellow]" )
940+ subprocess .run (
941+ [sys .executable , "-m" , "pip" , "install" , "PyYAML" ], check = True
942+ )
943+ import yaml
944+
945+ # Create a custom loader that ignores CloudFormation intrinsic functions
946+ class CFLoader (yaml .SafeLoader ):
947+ pass
948+
949+ def construct_unknown (loader , node ):
950+ if isinstance (node , yaml .ScalarNode ):
951+ return loader .construct_scalar (node )
952+ elif isinstance (node , yaml .SequenceNode ):
953+ return loader .construct_sequence (node )
954+ elif isinstance (node , yaml .MappingNode ):
955+ return loader .construct_mapping (node )
956+ return None
957+
958+ # Add constructors for CloudFormation intrinsic functions
959+ cf_functions = [
960+ "!Ref" ,
961+ "!GetAtt" ,
962+ "!Join" ,
963+ "!Sub" ,
964+ "!Select" ,
965+ "!Split" ,
966+ "!Base64" ,
967+ "!GetAZs" ,
968+ "!ImportValue" ,
969+ "!FindInMap" ,
970+ "!Equals" ,
971+ "!And" ,
972+ "!Or" ,
973+ "!Not" ,
974+ "!If" ,
975+ "!Condition" ,
976+ ]
977+
978+ for func in cf_functions :
979+ CFLoader .add_constructor (func , construct_unknown )
980+
932981 with open (template_path , "r" , encoding = "utf-8" ) as f :
933- lines = f . readlines ( )
982+ template = yaml . load ( f , Loader = CFLoader )
934983
935- for i , line in enumerate (lines ):
936- # Look for CodeUri that matches our directory
937- if "CodeUri:" in line :
938- code_uri = (
939- line .split ("CodeUri:" )[- 1 ].strip ().strip ("\" '" ).rstrip ("/" )
940- )
941- code_dir = code_uri .split ("/" )[- 1 ] if "/" in code_uri else code_uri
942-
943- if code_dir == dir_name :
944- # Found matching CodeUri, now look backwards for the resource name
945- # Look for AWS::Serverless::Function type first
946- for j in range (i - 1 , max (0 , i - 50 ), - 1 ):
947- if "Type: AWS::Serverless::Function" in lines [j ]:
948- # Found the function type, now look backwards for resource name
949- for k in range (j - 1 , max (0 , j - 10 ), - 1 ):
950- stripped = lines [k ].strip ()
951- # Resource names are at the start of line and end with ':'
952- if (
953- stripped
954- and not stripped .startswith (" " )
955- and stripped .endswith (":" )
956- ):
957- return stripped .rstrip (":" )
958- break
959-
960- return dir_name
984+ if not template or not isinstance (template , dict ):
985+ raise Exception (f"Failed to parse YAML template: { template_path } " )
986+
987+ resources = template .get ("Resources" , {})
988+ for resource_name , resource_config in resources .items ():
989+ if (
990+ resource_config
991+ and isinstance (resource_config , dict )
992+ and resource_config .get ("Type" ) == "AWS::Serverless::Function"
993+ ):
994+ properties = resource_config .get ("Properties" , {})
995+ if properties and isinstance (properties , dict ):
996+ code_uri = properties .get ("CodeUri" , "" )
997+ if isinstance (code_uri , str ):
998+ code_uri = code_uri .rstrip ("/" )
999+ code_dir = (
1000+ code_uri .split ("/" )[- 1 ] if "/" in code_uri else code_uri
1001+ )
1002+ if code_dir == dir_name :
1003+ return resource_name
1004+ raise Exception (
1005+ f"No CloudFormation function found for directory { dir_name } in template { template_path } "
1006+ )
9611007
9621008 except Exception as e :
963- self .log_verbose (f"Error reading template { template_path } : { e } " )
964- return dir_name
1009+ self .console .print (
1010+ f"[red]❌ Error extracting function name for { dir_name } from { template_path } : { e } [/red]"
1011+ )
1012+ sys .exit (1 )
9651013
9661014 def _validate_idp_common_in_build (self , template_dir , function_name , source_path ):
9671015 """Validate that idp_common package exists in the built Lambda function."""
0 commit comments