Skip to content

Commit 35eabfe

Browse files
author
Taniya Mathur
committed
Replace fallback logic with sys.exit(1) and add PyYAML auto-install
1 parent fddd516 commit 35eabfe

File tree

1 file changed

+79
-31
lines changed

1 file changed

+79
-31
lines changed

publish.py

Lines changed: 79 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)