feat(config): pass listed host env vars to app.command with inheritEnv

Merged
OK
12d ago
+288-53

What and why

  • app.command.inheritEnv copies host variables into the app process, by exact name or trailing-* prefix. A host that sets per-run values (a per-task DATABASE_URL, check secrets) no longer has to list each one in e2e.config.ts or spread process.env, which also hands the app the model keys.
  • A variable is copied only when it is set. command.env wins over an inherited value. Without inheritEnv, nothing changes.
  • Validation at config load (INVALID_CONFIG): a bare * (it would undo the isolation), a * anywhere but the end, an empty entry, =, or whitespace.
  • Inherited values are redacted from a quoted startup failure the same way command.env values are (packages/e2e/src/run/managed-process.ts).
  • openHost and the runner copy from the env they resolve against, not always process.env, so a programmatic host passing env gets its own values.
  • The E2E_APP_INHERIT_ENV env var form from the issue is left out on purpose: a host that controls the environment can already set the listed variables.
app: {
  url: 'http://localhost:3000',
  command: {
    executable: 'npm',
    args: ['run', 'dev'],
    inheritEnv: ['DATABASE_URL', 'STRIPE_*'],
  },
},

Closes #1119

Verified

Ran it locally: yes

  • Scratch project in /tmp with the built e2e and @e2e-dev/web linked in, and an app.command (a node server) that prints the variables it sees and serves them as a page. Config: inheritEnv: ['DATABASE_URL', 'STRIPE_*'], env: { STRIPE_HOOK: 'from-config' }.

DATABASE_URL=postgres://host/db STRIPE_KEY=sk_test_host STRIPE_HOOK=from-host OPENAI_API_KEY=sk-fake-model-key AI_GATEWAY_API_KEY=fake-gateway-key e2e run tests/env.e2e.ts

 ✓ target "web" command ready 95ms
 ✓ |web| tests/env.e2e.ts (1 test) 252ms
   ✓ app sees only the listed host env 252ms

App log: the listed variables arrive, command.env beats the host value, and the model keys stay out:

DATABASE_URL=postgres://host/db STRIPE_KEY=sk_test_host STRIPE_HOOK=from-config OPENAI_API_KEY=(unset) AI_GATEWAY_API_KEY=(unset)
  • The same config without inheritEnv behaves as before: DATABASE_URL=(unset) STRIPE_KEY=(unset) ....
  • openHost({ env: { ...process.env, DATABASE_URL: 'postgres://per-task/db' } }) with DATABASE_URL=postgres://orchestrator/db in the shell: the app logged DATABASE_URL=postgres://per-task/db.
  • Invalid entries:
 ERROR  configuration error INVALID_CONFIG (config)
target "web" app.command.inheritEnv[1] cannot be "*": it would pass every variable to the app, model keys included; list the names or prefixes it needs
target "web" app.command.inheritEnv[0] must be a variable name or a prefix ending in * (such as "STRIPE_*"), got "STR*PE"
  • Unit tests cover matching, precedence, the passed env, redaction, and validation. The precedence test fails when the merge order is swapped. Testbed suite not run (its fixed port could clash with other local runs); the change only applies when inheritEnv is set.

Checklist

  • Changeset added (pnpm changeset) for any change to a published package, or the change is testbed, docs, or CI only.
  • Breaking change: title carries !, the changeset body names what breaks and what replaces it, and the deprecation warning landed a window earlier (stability policy in CONTRIBUTING.md).
  • Docs updated in the same PR: the docs/**/*.mdx page for the behavior, and skills/e2e/ if the skill describes it.
  • Contract change: emitted .d.ts reviewed, tests/types/sdk-types.ts updated; wire change edits the schema and both fixtures.
  • Engine contract change (e2e/engine): changesets for e2e, @e2e-dev/web, and @e2e-dev/mobile.