-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat: Add .env file support for local Lambda invocations #8297
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
- Added python-dotenv dependency to requirements/base.txt - Implemented --dotenv CLI option for sam local invoke, start-api, and start-lambda commands - Added _get_dotenv_values() method to load environment variables from .env files - Environment variables from .env files are merged with --env-vars JSON, with JSON taking precedence - Updated all relevant tests to include dotenv parameter - Updated schema documentation with dotenv parameter details This feature allows developers to use standard .env files for environment variables in local Lambda invocations, providing a more convenient alternative to JSON format while maintaining backward compatibility with existing --env-vars functionality.
Updated reproducible-linux.txt and reproducible-mac.txt to include python-dotenv~=1.0.0 dependency that was added in the .env file support feature. Windows requirements will be updated via CI workflow.
|
Two questions before I review the code:
Also it seems whatever changes you made to the reproducible files aren't working, it's failing those tests. |
|
Hi @reedham-aws, Thank you for reviewing! Let me address your questions: 1. Issue Link RelevanceYou're absolutely right to question this - I linked the wrong issue in my comment 😅 Issue #2151 is about Go executable permissions on Windows/Linux, which is not related to this PR. The actual issue this PR addresses is #1355:
I mistakenly announced this PR in issue #2151 when I should have referenced #1355. I'll update the PR description to link to the correct issue. 2. --env-vars + --dotenv behavior✅ Already answered in the PR description:
Example: # Base config in .env, specific overrides in JSON
sam local invoke MyFunction --dotenv .env --env-vars env.json3. Reproducible Requirements✅ Already fixed in commit
The failing checks shown are from the old CI run (before the fix). The next CI run should pass all checks. Thanks again for the review! |
PR #8297: Add .env file support for local Lambda invocationsWhich issue(s) does this change fix?Fixes #1355 Note: The PR description originally linked issue #2151 by mistake. Issue #2151 is about Go executable permissions and is unrelated to this PR. This PR actually implements the feature request in issue #1355, which asks for .env file support for Why is this change necessary?Developers commonly use This creates friction in development workflows because:
Issue #1355 specifically requests this feature to eliminate dual maintenance of environment files and align with common development practices. How does it address the issue?This PR implements the Implementation Details:
Usage Examples:# Using .env file only
sam local invoke MyFunction --dotenv .env
# Combining both (JSON takes precedence)
sam local invoke MyFunction --dotenv .env --env-vars env.jsonWhat side effects does this change have?Positive Side Effects:
Considerations:
Mandatory ChecklistPRs will only be reviewed after checklist is complete
Notes on Checklist:Reproducible Requirements (✅ Complete):
Failing CI Checks: The failing CI checks shown are from the old run before commit 2fc00b1. The reproducible requirements have been properly updated and the next CI run should pass. Outstanding Items:
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your description mentions added unit tests, but the new functionality is not actually tested here (values are filled in as None everywhere). Please check the PRs for mistakes like this before they're sent in.
| else: | ||
| self._env_vars_value = env_vars | ||
|
|
||
| self._container_env_vars_value = self._get_env_vars_value(self._container_env_vars_file) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this work also not be done here for the _container_env_vars_value? Or is it a specific decision to not offer _container_dotenv_file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅ Implemented full container dotenv support. Added --container-dotenv CLI option that:
- Loads .env file for container environment variables (used for debugging)
- Works parallel to
--dotenv(which is for Lambda runtime env vars) - Added to all 3 commands:
invoke,start-api,start-lambda - Merges with JSON container env vars (JSON takes precedence)
- Keeps flat Dict[str, str] structure for debugging compatibility
I've tested 3 app using local invoke, local start-api and local lambda using --container-dotenv and it is working
| override_value = value | ||
| # Collect all variable names from all sources | ||
| all_var_names = ( | ||
| set(self.variables.keys()) | set(self.shell_env_values.keys()) | set(self.override_values.keys()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this change accomplishing? Why does the existing code not already take in the shell_env_values and override_values?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added detailed explanatory comment** in env_vars.py explaining that previously only variables defined in the template were processed. The change collects ALL variable names from all three sources (template, shell, overrides) to ensure variables from shell environment and override files (like .env) are included even if not declared in the template. This allows dotenv files to add new variables, not just override existing ones.
- Fixed DebugContext.__bool__ to check both debug_ports and container_env_vars - Fixed lambda_container._get_debug_settings to return env vars without debug ports - Added debug logging to invoke_context - All tests pass (5881 passed, 94.23% coverage) - Verified working with 3 test applications (invoke, start-api, start-lambda)
|
|
||
| def __bool__(self): | ||
| return bool(self.debug_ports) | ||
| # DebugContext is "truthy" if we have either debug ports OR container env vars |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this change for? Why is this needed now and wasn't needed previously?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The change was needed because --container-dotenv adds container env vars even without debug ports. Previously, __bool__() only checked debug_ports, making it false when only container env vars exist. Changed to return bool(self.debug_function) to properly handle warm containers with selective debugging.
tests/unit/commands/local/cli_common/test_invoke_context_dotenv.py
Outdated
Show resolved
Hide resolved
tests/unit/commands/local/cli_common/test_invoke_context_dotenv.py
Outdated
Show resolved
Hide resolved
- Refactor duplicated merging logic into _merge_env_vars() - Add 21 comprehensive tests (integration + unit + error) - Fix 17 failing tests with isinstance check for Mocks - Add file existence checks and empty file warnings - Improve comments explaining complex logic - Fix mypy unreachable code errors - All checks passing: 5894 tests, 94.24% coverage
Manual Testing Completed ✅Tested dotenv functionality with 4 comprehensive scenarios - All Passed: ✅ Basic .env loading Test commands: samdev local invoke TestFunction --dotenv .env
samdev local invoke TestFunction --dotenv .env --env-vars env.json
samdev local invoke TestFunction --dotenv missing.env
samdev local invoke TestFunction --dotenv empty.envFeature works as designed with proper error handling and excellent UX. Could you please approve CI validation? 🙏 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code is looking good, had a few more comments, sorry to nitpick!
tests/unit/commands/local/cli_common/test_invoke_context_dotenv.py
Outdated
Show resolved
Hide resolved
tests/unit/commands/local/cli_common/test_invoke_context_dotenv.py
Outdated
Show resolved
Hide resolved
Removed 3 tests that verify python-dotenv library behavior: - test_dotenv_with_special_characters - test_dotenv_with_empty_and_whitespace_values - test_get_dotenv_values_direct_with_real_file Keeping 18 tests that verify SAM CLI logic: - File existence checks - Empty file warnings - Merging behavior (dotenv + JSON) - Integration with InvokeContext Tests: 18/18 pass, cleaner test suite focused on SAM CLI logic.
|
Hi @reedham-aws, Thanks for the additional review! I've addressed all 4 comments: 1. Function-Specific Overrides ✅Great question! I've implemented this feature - .env files now support function-specific overrides using a naming pattern. Example: Implementation:
Commit: ef1bcdb2 2-4. Library Behavior & Duplicate Tests ✅You're absolutely right - these tests were:
Removed in commits: 3b941d1 + 8a56be1 (formatting) Result:
Final status:
Thanks for the thorough review! |
|
@dcabib I don't think you have pushed any commits adding support for function specific overrides. When you do that, it would also be helpful for future reviewers if this change is mentioned directly in the PR description as well. Once this PR is merged, I can get our official docs changed. |
Hi @reedham-aws, I was working in different features / issues and accidentally remove the code .... The code is pushed in commit |
| ) from ex | ||
|
|
||
| @staticmethod | ||
| def _parse_function_specific_env_vars(env_dict: Dict[str, str]) -> Dict: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My concern is the following cases for function name:
- Function name is
MyFunction - Function name is
myFunction - Function name is
my_function
For 1, it should work fine. For 2, it would not correctly pick up that it's Pascal case because it's not capitalized. For 3, it would take my as the function name incorrectly. I think the best way to do this might be to use a separator that can't be used in a function name (* for example). This way I think you wouldn't have to rely on the Pascal case and have the problem with the separator being possible in a function name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| # Function-specific variable: FunctionName_VAR | ||
| if prefix not in result: | ||
| result[prefix] = {} | ||
| result[prefix][var_name] = value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leaving a comment in case a future reviewer has a similar concern: I was wondering if we have a way of surfacing to customers that they have provided a function-specific var that doesn't actually correspond to a function. This could be in the case that they typo'd the function name or something. In the existing function specific env-vars code, there is no check for this, so I think it's fine to ignore here. Not sure how much that functionality adds anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
… env vars Addresses @reedham-aws review feedback (review #3377979259) Changed from underscore (_) with PascalCase detection to asterisk (*) separator for function-specific environment variables. This fixes the main blocker identified in the review where PascalCase detection failed for: - camelCase function names (myFunction) - snake_case function names (my_function) New pattern: FunctionName*VARIABLE_NAME Examples: - MyFunction*API_KEY (PascalCase) - myFunction*API_KEY (camelCase) - MySnakeCaseFunction*API_KEY (snake_case) - MYFUNCTION*API_KEY (UPPERCASE) Benefits: - Works with ALL naming conventions - Unambiguous (asterisk cannot be in CloudFormation logical IDs) - No case detection complexity needed - More robust and predictable Updated all 14 unit tests to use asterisk syntax. All tests passing.
|
@reedham-aws I've implemented your suggested asterisk (*) separator fix! Replace underscore with asterisk separator for function-specific env vars Changed from underscore (_) with PascalCase detection to asterisk (*) This fixes the main blocker identified in the review where PascalCase
New pattern: FunctionName*VARIABLE_NAME Examples:
Benefits:
Updated all 14 unit tests to use asterisk syntax. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for all your work on revisions! I will flag this to the team to get a second reviewer.
Description
This PR adds support for loading environment variables from .env files when running SAM local commands. This provides a more convenient alternative to JSON format while maintaining backward compatibility with existing --env-vars functionality.
Issue: #2151 (comment)
Motivation
Developers commonly use .env files for environment configuration. This feature allows SAM CLI to directly consume .env files without requiring conversion to JSON format.
Changes
python-dotenv~=1.0.0dependency to requirements/base.txt--dotenvCLI option for:sam local invokesam local start-apisam local start-lambda_get_dotenv_values()method ininvoke_context.pyto load .env filesBackward Compatibility
✅ This change is fully backward compatible:
--env-varsfunctionality remains unchanged--dotenvflag is optional--env-varsExample Usage
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.