fix(cfn-lang-ext): preserve inline-source intrinsics through LE-aware build merge (#9029)#9031
Merged
Conversation
… build merge (#9029) Under `Transform: AWS::LanguageExtensions`, `sam build` was overwriting `Code: {ZipFile: !Sub ...}` with the LE-resolved value, baking default pseudo-parameter substitutions (`us-east-1`, `123456789012`) into the built template. The same merge path covered three sites; gate all three on a single artifacts-lookup primitive matching the non-LE `ApplicationBuilder.update_template` early-skip: - Root-level merge (`_update_original_template_paths`): skip resources whose `get_full_path(stack_path, key)` is absent from `artifacts`. - ForEach static branch (`_merge_static_artifact_path`): same check per expanded key. - ForEach dynamic branch (`_collect_dynamic_mapping_entries` + nested): same check; reject with `UserException` naming the non-buildable property when no iteration produced an artifact, since Mappings hold only static strings and the loop variable cannot survive deploy. Adds case A/B/C integration tests + testdata templates and unit tests covering the new skip and refusal paths.
valerena
reviewed
May 19, 2026
- Hoist inline `get_full_path` imports to the top of build_context.py.
- Allow inline `Code: {ZipFile: !Sub ...}` inside Fn::ForEach bodies that
reference the loop variable: skip the merge instead of raising, since
CFN's LanguageExtensions transform expands the body at deploy time and
substitutes the loop variable per iteration. Drop the unused
`_raise_dynamic_no_artifacts_unsupported` helper and flip the related
integration test to assert pass-through preservation.
- Stop coercing `Optional[Dict[str, str]]` artifacts to `{}` in functions
that only forward the value. Where it's used directly, fold the None
guard into the existing membership check (`not artifacts or ...`).
`_merge_static_artifact_path` becomes Optional to match its callers.
Companion to the prior commit: the dynamic-branch path no longer raises when an inline-source Fn::ForEach iteration has no build artifact. Update the unit test to assert that no Mapping is generated and the original Code (with Fn::Sub intact) is left untouched, so CFN's LanguageExtensions transform can substitute the loop variable per-iteration at deploy time.
… {}`
Hoist every inline import in build_context.py to the module-level import
block (`itertools`, the cfn_language_extensions models/sam_integration
symbols, and all language_extensions_packaging helpers). None of these
introduce a circular dependency — language_extensions_packaging and
cfn_language_extensions only import from samcli.lib.* and samcli.commands.
validate, never from samcli.commands.build.build_context.
Drop the redundant `stack_output_template_path_by_stack_path or {}` at
the `_update_original_template_paths` call site: the receiver declares
the parameter Optional and already coerces to {} internally.
roger-zhangg
previously approved these changes
May 19, 2026
valerena
previously approved these changes
May 20, 2026
…nsub # Conflicts: # samcli/commands/build/build_context.py
After #9033, language extensions are opt-in. The two ForEach zipfile tests added by this PR omitted the flag, causing SAM transform plugins to choke on unexpanded Fn::ForEach blocks.
valerena
approved these changes
May 21, 2026
9 tasks
roger-zhangg
approved these changes
May 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #9029. Under
Transform: AWS::LanguageExtensions,sam buildwas overwritingCode: {ZipFile: !Sub ...}with the LE-resolved value, baking default pseudo-parameter substitutions (us-east-1,123456789012) into the built template.The merge had three sites that all need the same discipline as the non-LE
ApplicationBuilder.update_templateearly-skip — "if no build artifact, don't overwrite":_update_original_template_paths): skip whenget_full_path(stack_path, key)is absent fromartifacts._merge_static_artifact_path): same check per expanded key._collect_dynamic_mapping_entries+ nested): same check per expanded key.Test plan
pytest tests/unit/commands/buildcmd/test_build_context.py tests/unit/commands/buildcmd/test_build_context_language_extensions.py— 93 tests pass; new coverage for skip (cases A/B) and refusal (case C).test_build_zipfile_fnsub_preserves_intrinsic(case A: root-level)test_build_foreach_static_zipfile_fnsub_preserves_intrinsic(case B: ForEach static)test_build_foreach_dynamic_inline_zipfile_preserved(case C: ForEach dynamic,)black --checkclean on touched files.