Remote control launch: Fix the --copy-env option not copying current environment variables

Fixes #6724
This commit is contained in:
Kovid Goyal 2023-10-16 22:32:51 +05:30
parent d2026574b8
commit ddb121b418
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
5 changed files with 63 additions and 11 deletions

View file

@ -62,6 +62,8 @@ Detailed list of changes
- Fix trailing bracket not ignored when detecting a multi-line URL with the trailing bracket as the first character on the last line (:iss:`6710`)
- Remote control launch: Fix the ``--copy-env`` option not copying current environment variables (:iss:`6724`)
0.30.1 [2023-10-05]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View file

@ -306,6 +306,7 @@ def generate_completions_for_kitty() -> None:
def go_field_type(json_field_type: str) -> str:
json_field_type = json_field_type.partition('=')[0]
q = json_field_types.get(json_field_type)
if q:
return q
@ -324,6 +325,7 @@ def __init__(self, line: str) -> None:
field_def = line.split(':', 1)[0]
self.required = False
self.field, self.field_type = field_def.split('/', 1)
self.field_type, self.special_parser = self.field_type.partition('=')[::2]
if self.field.endswith('+'):
self.required = True
self.field = self.field[:-1]
@ -370,14 +372,17 @@ def go_code_for_remote_command(name: str, cmd: RemoteCommand, template: str) ->
if oq in option_map:
o = option_map[oq]
used_options.add(oq)
optstring = f'options_{name}.{o.go_var_name}'
if field.special_parser:
optstring = f'{field.special_parser}({optstring})'
if field.field_type == 'str':
jc.append(f'payload.{field.struct_field_name} = escaped_string(options_{name}.{o.go_var_name})')
jc.append(f'payload.{field.struct_field_name} = escaped_string({optstring})')
elif field.field_type == 'list.str':
jc.append(f'payload.{field.struct_field_name} = escape_list_of_strings(options_{name}.{o.go_var_name})')
jc.append(f'payload.{field.struct_field_name} = escape_list_of_strings({optstring})')
elif field.field_type == 'dict.str':
jc.append(f'payload.{field.struct_field_name} = escape_dict_of_strings(options_{name}.{o.go_var_name})')
jc.append(f'payload.{field.struct_field_name} = escape_dict_of_strings({optstring})')
else:
jc.append(f'payload.{field.struct_field_name} = options_{name}.{o.go_var_name}')
jc.append(f'payload.{field.struct_field_name} = {optstring}')
elif field.field in handled_fields:
pass
else:

View file

@ -96,6 +96,8 @@ def options_spec() -> str:
oldest foreground process associated with the currently active window rather
than the newest foreground process. Finally, the special value :code:`root`
refers to the process that was originally started when the window was created.
When running via remote control a value of current uses the working directory
for the remote control invocation.
--env
@ -322,10 +324,12 @@ def parse_launch_args(args: Optional[Sequence[str]] = None) -> LaunchSpec:
return LaunchSpec(opts, args)
def get_env(opts: LaunchCLIOptions, active_child: Optional[Child] = None) -> Dict[str, str]:
def get_env(opts: LaunchCLIOptions, active_child: Optional[Child] = None, base_env: Optional[Dict[str,str]] = None) -> Dict[str, str]:
env: Dict[str, str] = {}
if opts.copy_env and active_child:
env.update(active_child.foreground_environ)
if base_env is not None:
env.update(base_env)
for x in opts.env:
for k, v in parse_env(x, env):
env[k] = v
@ -456,6 +460,7 @@ def _launch(
active: Optional[Window] = None,
is_clone_launch: str = '',
rc_from_window: Optional[Window] = None,
base_env: Optional[Dict[str, str]] = None,
) -> Optional[Window]:
active = active or boss.active_window_for_cwd
if active:
@ -470,7 +475,7 @@ def _launch(
if opts.os_window_title == 'current':
tm = boss.active_tab_manager
opts.os_window_title = get_os_window_title(tm.os_window_id) if tm else None
env = get_env(opts, active_child)
env = get_env(opts, active_child, base_env)
remote_control_restrictions: Optional[Dict[str, Sequence[str]]] = None
if opts.allow_remote_control and opts.remote_control_password:
from kitty.options.utils import remote_control_password
@ -633,12 +638,13 @@ def launch(
active: Optional[Window] = None,
is_clone_launch: str = '',
rc_from_window: Optional[Window] = None,
base_env: Optional[Dict[str, str]] = None,
) -> Optional[Window]:
active = active or boss.active_window_for_cwd
if opts.keep_focus and active:
orig, active.ignore_focus_changes = active.ignore_focus_changes, True
try:
return _launch(boss, opts, args, target_tab, force_target_tab, active, is_clone_launch, rc_from_window)
return _launch(boss, opts, args, target_tab, force_target_tab, active, is_clone_launch, rc_from_window, base_env)
finally:
if opts.keep_focus and active:
active.ignore_focus_changes = orig

View file

@ -2,7 +2,7 @@
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING, Dict, Optional
from kitty.cli_stub import LaunchCLIOptions
from kitty.launch import launch as do_launch
@ -21,7 +21,7 @@ class Launch(RemoteCommand):
args+/list.str: The command line to run in the new window, as a list, use an empty list to run the default shell
match/str: The tab to open the new window in
window_title/str: Title for the new window
cwd/str: Working directory for the new window
cwd/str=copy_local_cwd: Working directory for the new window
env/list.str: List of environment variables of the form NAME=VALUE
var/list.str: List of user variables of the form NAME=VALUE
tab_title/str: Title for the new tab
@ -29,7 +29,7 @@ class Launch(RemoteCommand):
keep_focus/bool: Boolean indicating whether the current window should retain focus or not
copy_colors/bool: Boolean indicating whether to copy the colors from the current window
copy_cmdline/bool: Boolean indicating whether to copy the cmdline from the current window
copy_env/bool: Boolean indicating whether to copy the environ from the current window
copy_env/list.str=copy_local_env: List of strings representing the local env vars
hold/bool: Boolean indicating whether to keep window open after cmd exits
location/choices.first.after.before.neighbor.last.vsplit.hsplit.split.default: Where in the tab to open the new window
allow_remote_control/bool: Boolean indicating whether to allow remote control from the new window
@ -83,17 +83,30 @@ def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get:
default_opts = parse_launch_args()[0]
opts = LaunchCLIOptions()
for key, default_value in default_opts.__dict__.items():
if key == 'copy_env':
continue
val = payload_get(key)
if val is None:
val = default_value
setattr(opts, key, val)
ceval = payload_get('copy_env')
opts.copy_env = False
base_env: Optional[Dict[str, str]] = None
if ceval:
if isinstance(ceval, list):
base_env = {}
for x in ceval:
k, v = x.partition('=')[::2]
base_env[k] = v
elif isinstance(ceval, bool):
opts.copy_env = ceval
target_tab = None
tabs = self.tabs_for_match_payload(boss, window, payload_get)
if tabs and tabs[0]:
target_tab = tabs[0]
elif payload_get('type') not in ('background', 'os-window', 'tab', 'window'):
return None
w = do_launch(boss, opts, payload_get('args') or [], target_tab=target_tab, rc_from_window=window)
w = do_launch(boss, opts, payload_get('args') or [], target_tab=target_tab, rc_from_window=window, base_env=base_env)
return None if payload_get('no_response') else str(getattr(w, 'id', 0))

26
tools/cmd/at/launch.go Normal file
View file

@ -0,0 +1,26 @@
// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
package at
import (
"fmt"
"os"
)
var _ = fmt.Print
func copy_local_env(copy_env bool) []string {
if copy_env {
return os.Environ()
}
return nil
}
func copy_local_cwd(copy_cwd string) string {
if copy_cwd == "current" {
if c, e := os.Getwd(); e == nil {
copy_cwd = c
}
}
return copy_cwd
}