From 72477684653f5f8e180017fc9fa813d15ce7520b Mon Sep 17 00:00:00 2001 From: codex-bot-andresmgsl <224985780+codex-bot-andresmgsl@users.noreply.github.com> Date: Sat, 25 Jul 2026 17:16:00 +0000 Subject: [PATCH] fix: preserve box exec command newlines --- CHANGELOG.md | 4 ++++ bin/box | 9 +++++++-- drill/drill.sh | 4 ++-- test/cli.sh | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c11272..f755c22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ which records not just what changed but what each drill run proved. ## Unreleased +### Fixed + +- `box exec` preserves newlines and command argv across its login-user boundary (#169) + ### Changed - Release and repository governance now use the shared ceremony pinned at `0.1.0` (heavy-duty/ceremony#14) diff --git a/bin/box b/bin/box index d4811f7..d50d8de 100755 --- a/bin/box +++ b/bin/box @@ -1845,13 +1845,18 @@ box_user() { } cmd_shell() { incus exec "$inst" -- sudo -u "$(box_user "$inst")" -i; } -cmd_exec() { incus exec "$inst" -- sudo -u "$(box_user "$inst")" -i "${args[@]:1}"; } +# sudo -i joins its command argv into one shell string. In that join, a +# backslash-newline becomes a shell continuation and silently deletes the +# newline from a multi-line `box exec` payload (#169). Keep the login +# environment explicitly, but let the inner shell exec the original argv. +cmd_exec() { incus exec "$inst" -- sudo -u "$(box_user "$inst")" -H bash -lc 'cd ~ && exec "$@"' _ "${args[@]:1}"; } # A shell is a child of the exec connection: drop the terminal and everything # in it is SIGHUP'd — a long Claude run dies with it. tmux 'new-session -A' # attaches when the session exists and creates it when it doesn't, so starting # work and reattaching after a disconnect are the same command. 'shell' stays -# bare on purpose — two verbs, two contracts. +# bare on purpose — two verbs, two contracts. Unlike cmd_exec, tmux has no +# caller-supplied command payload; its session name is validated below. cmd_tmux() { local session="${args[1]:-main}" case "$session" in diff --git a/drill/drill.sh b/drill/drill.sh index 66e1e64..cee0210 100755 --- a/drill/drill.sh +++ b/drill/drill.sh @@ -71,8 +71,8 @@ wait_box() { # poll until exec answers (the VM agent can take a while), ~4 min # Read from inside a box WITHOUT ever hanging the drill. # # Two traps, both hit for real: -# · 'box exec' becomes 'sudo -u