Skip to content

Conversation

@CalebBarnes
Copy link

@CalebBarnes CalebBarnes commented Jun 3, 2025

Summary

Fixes environment variables from build event handlers not being passed to functions during local development. Static config environment variables from netlify.toml were already working via global process environment injection, but variables added by plugins via build event handlers were not.

Problem

Build event handlers (like onPreDev) could successfully mutate the config to add environment variables to config.build.environment, but these mutated environment variables were not available to functions in process.env during local development.

What was working:

  • Static environment variables from netlify.tomlcachedConfig.env → global process.env → functions ✅
  • Build event handler mutations → config.build.environment
  • Process env override behavior ✅

What was broken:

  • Mutated config environment variables → functions ❌

Root Cause

Static config environment variables work because they're included in cachedConfig.env and injected into the global process.env via injectEnvVariables(). However, mutated config environment variables from build event handlers are only stored in config.build.environment and were never being passed to functions.

The NetlifyFunction.invoke() method was passing an empty environment object to the runtime, so functions only had access to the global process.env (which contained static config vars) but not the mutated config environment variables.

Solution

Instead of injecting mutated config into the global process.env (which could affect other parts of the system), we:

  • Extract environment variables from config.build.environment in NetlifyFunction.invoke()
  • Pass them directly to the function runtime via the environment parameter
  • The runtime injects them into the function's specific process.env
  • Maintain existing precedence: process env variables override config env variables

Key Changes

Core Implementation

  • src/lib/functions/netlify-function.ts: Extract environment variables from mutated config and pass to runtime
  • src/lib/functions/runtimes/js/index.ts: Update V1 function path to accept and use environment variables

Testing

  • tests/integration/commands/dev/functions.test.ts: Significantly expanded test coverage:
    • NEW: Static config environment variables for V1 functions
    • NEW: Build event handler environment variables for V1 functions
    • NEW: Static config environment variables for V2 functions (first V2 function env var tests)
    • NEW: Build event handler environment variables for V2 functions (first V2 function env var tests)
    • NEW: Environment variable precedence for both V1 and V2 functions (first build event handler mutation tests)

Note: While static config environment variables were already tested in dev.config.test.ts for V1 functions, this PR adds the first comprehensive test coverage for V2 functions and build event handler mutations.

Technical Details

This approach is better than global environment injection because:

  • It doesn't pollute the global process environment
  • It only affects the specific function being invoked
  • It maintains proper precedence handling
  • It works for both V1 and V2 function runtimes

Use Case

This enables plugins to add environment variables via build event handlers:

// Plugin build event handler
export const onPreDev = ({ netlifyConfig }) => {
  netlifyConfig.build.environment.MY_PLUGIN_VAR = 'value'
}

// Function can now access the mutated config env var during local dev
export const handler = async (event, context) => {
  console.log(process.env.MY_PLUGIN_VAR) // 'value' ✅ (was undefined ❌)
  return { statusCode: 200, body: 'OK' }
}

Test Coverage

✅ V1 Functions - Static config and build event handler environment variables
✅ V2 Functions - Static config and build event handler environment variables
✅ Environment variable precedence - Process env overrides config env for both function types
✅ Backwards compatibility - All existing functionality preserved (16 existing tests still pass)

Backwards Compatibility

✅ No breaking changes - all existing functionality preserved
✅ Static config environment variables continue to work via global process.env
✅ Process environment variables continue to take precedence
✅ Functions without config environment variables work unchanged


For us to review and ship your PR efficiently, please perform the following steps:

  • Open a bug/issue before writing your code 🧑‍💻. This ensures we can discuss the changes and get feedback from everyone that should be involved. If you`re fixing a typo or something that`s on fire 🔥 (e.g. incident related), you can skip this step.
  • Read the contribution guidelines 📖. This ensures your code follows our style guide and
    passes our tests.
  • Update or add tests (if any source code was changed or added) 🧪
  • Update or add documentation (if features were changed or added) 📝
  • Make sure the status checks below are successful ✅

A picture of a cute animal (not mandatory, but encouraged)

@github-actions
Copy link

github-actions bot commented Jun 3, 2025

📊 Benchmark results

Comparing with 813a2e2

  • Dependency count: 1,148 (no change)
  • Package size: 271 MB (no change)
  • Number of ts-expect-error directives: 397 (no change)

- Extract environment variables from config.build.environment in NetlifyFunction.invoke()
- Pass environment variables to both V1 and V2 function runtimes
- Add comprehensive test coverage for V1 and V2 functions
- Maintain environment variable precedence (process env overrides config env)
@CalebBarnes CalebBarnes force-pushed the feat/pass-mutated-config-envs-to-local-dev-functions-server branch from eca1775 to f7960e1 Compare June 3, 2025 21:44
@CalebBarnes CalebBarnes marked this pull request as ready for review June 3, 2025 21:51
@CalebBarnes CalebBarnes requested a review from a team as a code owner June 3, 2025 21:51
@CalebBarnes
Copy link
Author

CalebBarnes commented Jun 3, 2025

I think it is confusing that values from netlify.toml#build.environment are already populated in the local function server. But since this already happens, it makes sense that when build event handlers mutate these config values that those mutations are also populated in the functions.

@CalebBarnes CalebBarnes requested review from ndhoule and serhalp June 3, 2025 21:56
Copy link
Contributor

@ndhoule ndhoule left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, agree that this behavior is super confusing and is actually something we don't want to happen, but I definitely think it's better to be consistently wrong.

@CalebBarnes CalebBarnes merged commit 17cb26b into main Jun 3, 2025
52 checks passed
@CalebBarnes CalebBarnes deleted the feat/pass-mutated-config-envs-to-local-dev-functions-server branch June 3, 2025 22:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants