4.4.2 RunEnvironmentInfo

recommended

RunEnvironmentInfo is the provider a custom executable or test rule returns when the launched program needs environment variables. It is part of the runtime contract introduced in 4.2.6 Executable & Test Rules: DefaultInfo tells Bazel what to launch and which runfiles to stage, while RunEnvironmentInfo tells Bazel which environment entries to add when that executable runs under bazel run or bazel test.1

Which environment reaches the launched program?
RunEnvironmentInfo affects bazel run or test launch — not the action that builds the executable.
ANALYSIS
Rule returns launch contracts
DefaultInfoexecutable + runfiles
RunEnvironmentInfoenvironment + inherited_environment
BAZEL LAUNCH
Build, stage runfiles, start process
The launch boundary is after action execution.
bazel run / bazel test
LAUNCHED PROCESS
Process receives the merged result
environmentexact rule-defined values
inherited_environmentselected values copied from the outer environment
WHEN THE SAME VARIABLE IS SET MORE THAN ONCE
Later launch controls override provider values
1 · base
environment
2 · wins if set
inherited_environment
3 · final
--run_env / --test_env
For tests, explicit test-runner values override inherited ones. For one-off user injection, prefer the command-line launch flag.
RunEnvironmentInfo configures the final program. Action env and --action_env configure build tools separately.

The Launch Environment Is A Provider Contract

The provider has two fields. environment is a dictionary of exact variable names and values. Those values are made available when the target is executed as a test or through bazel run.2 inherited_environment is a list of variable names whose values Bazel takes from the shell environment at launch time.3

def _tool_test_impl(ctx):
    executable = ctx.actions.declare_file(ctx.label.name)
    runfiles = ctx.runfiles(files = [ctx.file.config])

    ctx.actions.write(
        output = executable,
        content = "#!/usr/bin/env bash\nexec \"$@\"\n",
        is_executable = True,
    )

    return [
        DefaultInfo(executable = executable, runfiles = runfiles),
        RunEnvironmentInfo(
            environment = {
                "MY_RULE_MODE": ctx.attr.mode,
                "MY_RULE_CONFIG": ctx.file.config.short_path,
            },
            inherited_environment = [
                "MY_RULE_DEBUG_TOKEN",
            ],
        ),
    ]

This is not a way to pass inputs to the action that creates the executable. The implementation function still runs during analysis, and build actions still need declared inputs, tools, arguments, and action environment on their own contract. Keep RunEnvironmentInfo for the process Bazel launches after the target has been built. Keep action execution settings in 4.4.3 Action Execution Contract.

Prefer Explicit Values

For production rules, environment should be the normal path. It makes the rule's runtime promise visible in analysis: the variable name is stable, the value comes from rule attributes or other declared analysis-time data, and downstream users can reason about the behavior without knowing the developer's laptop state.

Good examples are toggles derived by the rule, paths that the launcher understands, and mode names derived from attributes. If the value names a runtime file, pass a runfiles-relative path and make sure the file is also present in DefaultInfo(runfiles = ...). An environment variable that points at a missing file is just a delayed runfiles bug. 4.2.5 DefaultInfo & Runfiles explains how the runtime file enters runfiles.

inherited_environment is different. If a variable appears in both fields, the inherited shell value wins when it is set.4 That makes inheritance a deliberate escape hatch, not a convenience default. It is most useful for tests that truly need a host-specific value, because bazel test normally runs tests in a hermetic environment. The provider documentation warns that hardcoding this kind of host dependency can surprise users and can expose sensitive information.5

Tests Are More Sensitive Than Run Targets

bazel run already forwards the outer environment, so inheriting variables through RunEnvironmentInfo is mainly about test rules.6 Tests have a much stricter launch contract: the Test Encyclopedia specifies an initial environment block with variables such as TEST_TMPDIR, TEST_SRCDIR, TEST_WORKSPACE, HOME, PATH, and LC_CTYPE (which may be unset or C.UTF-8), and it treats writable locations and runfiles access as part of the test's hermetic execution model.7

That is why a test rule should not casually inherit HOME, PATH, cloud credentials, or locale variables. Each inherited variable becomes part of what the test observes but not necessarily part of what a reviewer sees in the BUILD file. If the test result can change when the caller's shell changes, the test caching model from 1.2.1 Test Caching becomes harder to trust, and the hermeticity principle from 2.3.1 Hermeticity is no longer just a build-action concern.

When a developer needs to inject a value for one invocation, prefer the CLI mechanism. --test_env=NAME=value injects an explicit value into each test environment. --test_env=NAME inherits the value from the shell used to start bazel test.8 The provider documentation still recommends avoiding that flag when possible and populating the environment explicitly, but it is better as an invocation-level override than baking host inheritance into a reusable rule.9

Do Not Reconstruct The Whole Environment

Rule authors can inspect ctx.configuration.test_env, which is the dictionary of user-specified test variables from --test_env options.10 The API reference marks it with a blunt warning against relying on it, because it is not the complete environment.11 Treat that field as a narrow compatibility signal, not as a source from which to rebuild the test process environment.

The same boundary matters for bazel run. The user manual says bazel run is similar to building and then invoking the binary, but not identical. Non-test binaries run with their current working directory in the binary's runfiles tree, and Bazel also provides BUILD_WORKSPACE_DIRECTORY, BUILD_WORKING_DIRECTORY, BUILD_ID, and BUILD_EXECROOT.12 If bazel run executes a test binary, Bazel makes a good-faith attempt to approximate the test environment, but the manual explicitly notes that the emulation is not perfect.13

Those details are reasons to avoid clever environment synthesis inside the rule. The rule should declare only the environment entries it defines. Bazel and the test runner provide the rest of the launch environment.

The mini-ruleset's glyph_test is a complete narrow use: it returns one explicit RunEnvironmentInfo(environment = ...) value for the test runner and keeps the binary plus runtime files in DefaultInfo.runfiles.

key takeaway

Return RunEnvironmentInfo only for environment variables that belong to the launched executable or test. Use environment for values derived by the rule, and treat inherited_environment as a documented non-hermetic escape hatch.

Do not confuse launch environment with action environment. RunEnvironmentInfo affects bazel run and bazel test. Action environment belongs to the actions that build the target.

extra

Migrating From TestEnvironment

Older Starlark code may still mention testing.TestEnvironment. The testing API marks it deprecated and says to use RunEnvironmentInfo instead.14 The old provider was test-specific. The replacement is the common provider for executable and test rules. If you are migrating, keep the old field mapping simple: environment remains the explicit variable map, and inherited_environment remains the list of shell variable names to pass through.15

The migration is a good time to make inheritance visible in the rule API. If an old rule always inherited a host variable, either replace it with a constant, derive the value from declared attributes, or expose an intentionally named escape hatch such as inherit_debug_token instead of silently preserving ambient shell behavior.

Check your understanding · 3 questions

1.What does RunEnvironmentInfo configure for a custom executable or test rule?

Select one answer

2.Which statements about environment and inherited_environment are correct?

Select all that apply

3.True or false: runtime environment design for custom test rules.

Choose True or False for each sentence

--test_env=NAME=value is an invocation-level way to inject a test environment variable.
ctx.configuration.test_env is the complete environment that Bazel will pass to the test process.
bazel run and bazel test have identical launch environments for test binaries.
A test rule that inherits host variables can become harder to cache and reason about hermetically.
0 of 3 answered

Footnotes

  1. RunEnvironmentInfo — provider purpose for executable rules and launched environments.

  2. RunEnvironmentInfoenvironment map semantics.

  3. RunEnvironmentInfoinherited_environment field semantics.

  4. RunEnvironmentInfo — inherited value precedence when a variable appears in both fields.

  5. RunEnvironmentInfo — warning about non-hermetic tests and sensitive information.

  6. RunEnvironmentInfo — contrast between hermetic bazel test environments and bazel run forwarding.

  7. Test encyclopedia — initial test environment variables including LC_CTYPE, writable directories, and runfiles access rules.

  8. Commands and Options--test_env syntax and inheritance behavior.

  9. RunEnvironmentInfo — preference for explicit population over --test_env.

  10. configurationctx.configuration.test_env contains variables from --test_env.

  11. configuration — warning that test_env is not the complete environment.

  12. Commands and Optionsbazel run working directory and extra BUILD_* environment variables.

  13. Commands and Optionsbazel run behavior for test binaries and imperfect test-environment emulation.

  14. testingtesting.TestEnvironment deprecation notice.

  15. testing — deprecated provider fields matching explicit and inherited environment variables.