startWithNodeEnvironment method

Future<ShellSession> startWithNodeEnvironment(
  1. ShellRequest request,
  2. Map<String, String> nodeEnvironment
)

start, given the node side of the session's environment — the live environment with the profile over it — already computed (by a decorating backend falling back to this one), so it is not read a second time.

Implementation

Future<ShellSession> startWithNodeEnvironment(
  ShellRequest request,
  Map<String, String> nodeEnvironment,
) async {
  if (allowCommand != null && !allowCommand!(request)) {
    throw ProcessException(
      request.command ?? defaultShell,
      request.args,
      'Command not permitted by node policy',
    );
  }

  // The shell and the start directory are the node's choices, so they are
  // looked up in the node's environment only: a client-sent `PATH` in
  // [ShellRequest.env] must not pick which `bash.exe` the node probes and
  // runs (a node policy vets the request's command, not its environment).
  final (executable, args) = resolveShellInvocation(
    request,
    defaultShell,
    environment: nodeEnvironment,
  );
  // Client's choice, then the node's, then the user's home (a profile `HOME`
  // first) — and only then wherever this process happens to be standing.
  // See `resolveStartDirectory`.
  final cwd = resolveStartDirectory(
    requested: request.cwd,
    configured: workingDirectory,
    environment: nodeEnvironment,
  );
  // The session's whole environment, built here rather than merged with
  // this process's by `Process.start`: that merge matches names by exact
  // case, so a profile `PATH` next to the inherited `Path` reached the child
  // as two variables, the first (stale) one winning.
  final env = mergeEnvironment([
    nodeEnvironment,
    _ptyEnv(request),
    request.env,
  ]);
  final process = await Process.start(
    executable,
    args,
    workingDirectory: cwd,
    // `withUserHome` only fills in `HOME` when nothing supplies one — a node
    // run as a service is handed no `HOME`, and a shell without one is
    // subtly broken rather than obviously so. `withPubCacheBin` does the same
    // for the pub cache `bin` directory, so `dart pub global
    // activate`-installed CLIs (`omnyshell` among them) are runnable in a
    // session that sources no rc. [env] is complete, so it is also what
    // both consult instead of this process's environment.
    environment: mergeEnvironment([
      withPubCacheBin(
        withUserHome(env, processEnvironment: env),
        processEnvironment: env,
      ),
    ]),
    includeParentEnvironment: false,
  );
  return ProcessShellSession(
    process,
    shellFamily: classifyShellFamily(executable),
  );
}