lib.ExecContext.run(), .sudo(): Rename env

The name of the env parameter to ExecContext.run() and .sudo() is not
descriptive enough for which environment is supposed to be modified
and how, so rename and split it up as follows:

  - .run(): env -> mod_env

  - .sudo(): env -> mod_env_sudo and mod_env_cmd

The parameters have the following meaning:

  - "mod_env*" means that the environment is modified, not replaced

  - "mod_env" and "mod_env_cmd" modify the environment "cmd" runs in

  - "mod_env_sudo" modifies the environment sudo runs in

Fix the fallout of the API change all over jw-pkg.

Signed-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
Jan Lindemann 2026-04-19 14:04:35 +02:00
commit 54aecff8e4
9 changed files with 164 additions and 159 deletions

View file

@ -25,7 +25,7 @@ class AsyncSSH(Base):
super().__init__(
uri,
caps=self.Caps.LogOutput | self.Caps.Wd | self.Caps.Interactive | self.Caps.Env,
caps = self.Caps.LogOutput | self.Caps.Wd | self.Caps.Interactive | self.Caps.ModEnv,
**kwargs
)
@ -34,7 +34,8 @@ class AsyncSSH(Base):
self.term_type = term_type or os.environ.get("TERM", "xterm")
self.connect_timeout = connect_timeout
def _connect_kwargs(self) -> dict:
def _connect_kwargs(self, hide_secrets: bool=False) -> dict:
kwargs: dict = {
"host": self.hostname,
"port": self.port,
@ -47,7 +48,10 @@ class AsyncSSH(Base):
if self.known_hosts is not _USE_DEFAULT_KNOWN_HOSTS:
kwargs["known_hosts"] = self.known_hosts
return {k: v for k, v in kwargs.items() if v is not None}
ret = {k: v for k, v in kwargs.items() if v is not None}
if hide_secrets and 'password' in kwargs:
kwargs['password'] = '<hidden>'
return ret
@staticmethod
def _build_remote_command(cmd: list[str], wd: str | None) -> str:
@ -61,29 +65,6 @@ class AsyncSSH(Base):
return f"/bin/sh -lc {shlex.quote(inner)}"
@staticmethod
def _merge_env_into_forwarded_args(
args: tuple,
kwargs: dict,
mod_env: dict[str, str],
) -> tuple[tuple, dict]:
args = list(args)
kwargs = dict(kwargs)
if "env" in kwargs:
base_env = kwargs["env"]
merged_env = dict(base_env or {})
merged_env.update(mod_env)
kwargs["env"] = merged_env or None
elif len(args) >= 4:
base_env = args[3]
merged_env = dict(base_env or {})
merged_env.update(mod_env)
args[3] = merged_env or None
else:
kwargs["env"] = dict(mod_env) if mod_env else None
return tuple(args), kwargs
@staticmethod
def _has_local_tty() -> bool:
try:
@ -143,14 +124,15 @@ class AsyncSSH(Base):
cmd: list[str],
wd: str | None,
cmd_input: bytes | None,
env: dict[str, str] | None,
mod_env: dict[str, str] | None,
) -> Result:
command = self._build_remote_command(cmd, wd)
stdout_parts: list[bytes] = []
proc = await conn.create_process(
command=command,
env=env,
env=mod_env,
stdin=asyncssh.PIPE,
stdout=asyncssh.PIPE,
stderr=asyncssh.STDOUT,
@ -301,7 +283,7 @@ class AsyncSSH(Base):
wd: str | None,
verbose: bool,
cmd_input: bytes | None,
env: dict[str, str] | None,
mod_env: dict[str, str] | None,
log_prefix: str,
) -> Result:
command = self._build_remote_command(cmd, wd)
@ -311,7 +293,7 @@ class AsyncSSH(Base):
proc = await conn.create_process(
command=command,
env=env,
env=mod_env,
stdin=asyncssh.PIPE if cmd_input is not None else asyncssh.DEVNULL,
stdout=asyncssh.PIPE,
stderr=asyncssh.STDOUT,
@ -353,7 +335,7 @@ class AsyncSSH(Base):
wd: str | None,
verbose: bool,
cmd_input: bytes | None,
env: dict[str, str] | None,
mod_env: dict[str, str] | None,
interactive: bool,
log_prefix: str,
) -> Result:
@ -364,7 +346,7 @@ class AsyncSSH(Base):
cmd=cmd,
wd=wd,
cmd_input=cmd_input,
env=env,
mod_env=mod_env,
)
return await self._run_captured_pty_on_conn(
@ -373,7 +355,7 @@ class AsyncSSH(Base):
wd=wd,
verbose=verbose,
cmd_input=cmd_input,
env=env,
mod_env=mod_env,
log_prefix=log_prefix,
)
@ -389,7 +371,7 @@ class AsyncSSH(Base):
proc = await conn.create_process(
command=command,
env=env,
env=mod_env,
stdin=stdin_mode,
stdout=asyncssh.PIPE,
stderr=asyncssh.PIPE,
@ -443,48 +425,26 @@ class AsyncSSH(Base):
wd: str | None,
verbose: bool,
cmd_input: str | None,
env: dict[str, str] | None,
mod_env: dict[str, str] | None,
interactive: bool,
log_prefix: str,
) -> Result:
async with asyncssh.connect(**self._connect_kwargs()) as conn:
return await self._run_on_conn(
conn=conn,
cmd=cmd,
wd=wd,
verbose=verbose,
cmd_input=cmd_input,
env=env,
interactive=interactive,
log_prefix=log_prefix,
)
async def _sudo(
self,
cmd: list[str],
mod_env: dict[str, str],
opts: list[str],
*args,
**kwargs,
) -> Result:
args, kwargs = self._merge_env_into_forwarded_args(args, kwargs, mod_env)
async with asyncssh.connect(**self._connect_kwargs()) as conn:
uid_result = await conn.run("id -u", check=False)
is_root = (
uid_result.exit_status == 0
and isinstance(uid_result.stdout, str)
and uid_result.stdout.strip() == "0"
)
cmdline: list[str] = []
if not is_root:
cmdline.append("/usr/bin/sudo")
if mod_env:
cmdline.append("--preserve-env=" + ",".join(mod_env.keys()))
cmdline.extend(opts)
cmdline.extend(cmd)
return await self._run_on_conn(conn, cmdline, *args, **kwargs)
try:
async with asyncssh.connect(**self._connect_kwargs()) as conn:
return await self._run_on_conn(
conn=conn,
cmd=cmd,
wd=wd,
verbose=verbose,
cmd_input=cmd_input,
mod_env=mod_env,
interactive=interactive,
log_prefix=log_prefix,
)
except Exception as e:
msg = f'-------------------- Failed to run command {" ".join(cmd)} ({e})'
log(ERR, ',', msg)
for key, val in self._connect_kwargs(hide_secrets=True).items():
log(ERR, f'| {key:<20} = {val}')
log(ERR, '`', msg)
raise