in samcli/commands/_utils/template.py [0:0]
def _update_relative_paths(template_dict, original_root, new_root):
"""
SAM/CloudFormation template can contain certain properties whose value is a relative path to a local file/folder.
This path is usually relative to the template's location. If the template is being moved from original location
``original_root`` to new location ``new_root``, use this method to update these paths to be
relative to ``new_root``.
After this method is complete, it is safe to write the template to ``new_root`` without
breaking any relative paths.
This methods updates resource properties supported by ``aws cloudformation package`` command:
https://docs.aws.amazon.com/cli/latest/reference/cloudformation/package.html
If a property is either an absolute path or a S3 URI, this method will not update them.
Parameters
----------
template_dict : dict
Dictionary containing template contents. This dictionary will be updated & written to ``dest`` location.
original_root : str
Path to the directory where all paths were originally set relative to. This is usually the directory
containing the template originally
new_root : str
Path to the new directory that all paths set relative to after this method completes.
Returns
-------
Updated dictionary
"""
for resource_type, properties in template_dict.get("Metadata", {}).items():
if resource_type not in METADATA_WITH_LOCAL_PATHS:
# Unknown resource. Skipping
continue
for path_prop_name in METADATA_WITH_LOCAL_PATHS[resource_type]:
path = properties.get(path_prop_name)
updated_path = _resolve_relative_to(path, original_root, new_root)
if not updated_path:
# This path does not need to get updated
continue
properties[path_prop_name] = updated_path
for _, resource in template_dict.get("Resources", {}).items():
resource_type = resource.get("Type")
if resource_type not in RESOURCES_WITH_LOCAL_PATHS:
# Unknown resource. Skipping
continue
for path_prop_name in RESOURCES_WITH_LOCAL_PATHS[resource_type]:
properties = resource.get("Properties", {})
if (
resource_type in [AWS_SERVERLESS_FUNCTION, AWS_LAMBDA_FUNCTION]
and properties.get("PackageType", ZIP) == IMAGE
):
if not properties.get("ImageUri"):
continue
resolved_image_archive_path = _resolve_relative_to(properties.get("ImageUri"), original_root, new_root)
if not resolved_image_archive_path or not pathlib.Path(resolved_image_archive_path).is_file():
continue
# SAM GraphQLApi has many instances of CODE_ARTIFACT_PROPERTY and all of them must be updated
if resource_type == AWS_SERVERLESS_GRAPHQLAPI and path_prop_name == graphql_api.CODE_ARTIFACT_PROPERTY:
# to be able to set different nested properties to S3 uri, paths are necessary
# jmespath doesn't provide that functionality, thus custom implementation
paths_values = graphql_api.find_all_paths_and_values(path_prop_name, properties)
for property_path, property_value in paths_values:
updated_path = _resolve_relative_to(property_value, original_root, new_root)
if not updated_path:
# This path does not need to get updated
continue
set_value_from_jmespath(properties, property_path, updated_path)
path = jmespath.search(path_prop_name, properties)
updated_path = _resolve_relative_to(path, original_root, new_root)
if not updated_path:
# This path does not need to get updated
continue
set_value_from_jmespath(properties, path_prop_name, updated_path)
metadata = resource.get("Metadata", {})
if ASSET_PATH_METADATA_KEY in metadata:
path = metadata.get(ASSET_PATH_METADATA_KEY, "")
updated_path = _resolve_relative_to(path, original_root, new_root)
if not updated_path:
# This path does not need to get updated
continue
metadata[ASSET_PATH_METADATA_KEY] = updated_path
# AWS::Includes can be anywhere within the template dictionary. Hence we need to recurse through the
# dictionary in a separate method to find and update relative paths in there
template_dict = _update_aws_include_relative_path(template_dict, original_root, new_root)
return template_dict