Skip to content

exec: forward pty resize end-to-end (runtime + CLI)#83

Merged
bilby91 merged 2 commits into
mainfrom
cli/tty-resize
May 23, 2026
Merged

exec: forward pty resize end-to-end (runtime + CLI)#83
bilby91 merged 2 commits into
mainfrom
cli/tty-resize

Conversation

@bilby91
Copy link
Copy Markdown
Member

@bilby91 bilby91 commented May 23, 2026

Fixes terminal-resize misbehavior in `devcontainer exec`: interactive programs render at 80x24 until first redraw and ignore subsequent window resizes.

Two commits:

1. `runtime: forward pty resize through ExecContainer`

Adds `InitialTtySize` and `ResizeCh` to `runtime.ExecOptions` (mirrored on `devcontainer.ExecOptions`).

Docker backend:

  • `ConsoleSize` passed to `ExecCreate` so the pty starts at the right geometry
  • Small goroutine drains the resize channel into `ExecResize` daemon calls, coalescing bursts (keeps only the latest size when several updates arrive between API round-trips) so a fast drag doesn't back up

Apple-container backend: out of scope here; falls back to the no-resize behavior it already exhibits.

2. `cli: forward SIGWINCH into exec resize channel`

`setupTty` becomes a `ttyState` struct carrying:

  • `initial` — `term.GetSize` at exec start, passed as `InitialTtySize`
  • `resize` — channel fed by a `SIGWINCH` listener goroutine, passed as `ResizeCh`
  • `restore` — cancels the listener + restores cooked-mode stdin

The listener goroutine binds its lifetime to a context cancelled by `restore`, so it exits cleanly when `Exec` returns or the user `Ctrl-C`s.

Test plan

  • `go build ./...`, `golangci-lint run`, linux cross-build all clean
  • Rebuilt local binary, ran `devcontainer exec -- htop` (or any curses app), confirmed it renders at the host terminal size and reflows when the window is resized
  • Apple-container resize plumbing — follow-up (would mirror the docker goroutine through the Swift bridge)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Enhanced terminal support for command execution with automatic detection of terminal dimensions and dynamic resizing capabilities during execution.

Review Change Stack

bilby91 and others added 2 commits May 23, 2026 00:10
Add InitialTtySize and ResizeCh to runtime.ExecOptions (mirrored on
devcontainer.ExecOptions) so callers driving an interactive exec can:
- set the initial pty geometry at create time, matching the host
  terminal so curses apps don't render at 80x24 until first redraw
- stream subsequent size updates for the lifetime of the exec, so
  resizing the terminal window reflows the in-container application

Docker backend: pass ConsoleSize to ExecCreate and run a small
goroutine that drains the resize channel into ExecResize daemon
calls. The goroutine coalesces bursts (keeps only the latest size
when several updates arrive between API round-trips) so a fast drag
doesn't back up.

Apple-container backend: out of scope for this commit; falls back to
the no-resize behavior it already exhibits.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
\`devcontainer exec\` used to only put stdin into raw mode and forward
stdio — every interactive program kept rendering at 80x24 regardless
of the actual terminal size, and resizing the window did nothing.

Wire the runtime resize plumbing now that it exists. setupTty grows
into a ttyState struct carrying:
- initial: term.GetSize at exec start → passed as InitialTtySize so
  the pty is created at the right geometry
- resize: a channel fed by a SIGWINCH listener goroutine that emits
  the new size on every window-resize signal → passed as ResizeCh so
  the daemon resizes the pty in-flight
- restore: cancels the SIGWINCH listener and restores cooked-mode
  stdin; always safe to call (no-op when tty is false)

The goroutine bounds its lifetime on a context cancelled by restore,
so it exits cleanly when Exec returns or the user Ctrl-Cs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 23, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5e74604d-7436-4f1e-b744-656cc4529f93

📥 Commits

Reviewing files that changed from the base of the PR and between 3b9ff57 and 29c6b21.

📒 Files selected for processing (4)
  • cmd/devcontainer/exec.go
  • exec.go
  • runtime/docker/exec.go
  • runtime/runtime.go

📝 Walkthrough

Walkthrough

This PR adds end-to-end support for terminal size initialization and dynamic resizing in exec operations. The runtime API gains a TtySize type and ExecOptions fields for initial size and a resize channel. The CLI command captures terminal dimensions and SIGWINCH signals via a new setupTty helper, and the Docker runtime integrates those dimensions into exec creation and handles ongoing resize events forwarded through a channel.

Changes

TTY Size and Resize Support

Layer / File(s) Summary
Runtime TTY size types and options
runtime/runtime.go
Adds TtySize struct with Width and Height fields; extends ExecOptions with InitialTtySize and ResizeCh fields, both documented to apply only when Tty is true.
Engine exec option forwarding
exec.go
Extends ExecOptions with TTY size fields and forwards them into the runtime ExecOptions passed to ExecContainer.
CLI terminal resize handling
cmd/devcontainer/exec.go
Updates imports for signal and syscall support; initializes setupTty in the exec command's RunE for error handling and deferred cleanup; passes InitialTtySize and ResizeCh to the engine; implements setupTty to enter raw mode, capture initial terminal size, listen for SIGWINCH events, and forward size updates through a channel until context cancellation.
Docker runtime console size and resize
runtime/docker/exec.go
Computes a Docker ConsoleSize from InitialTtySize when TTY is enabled and dimensions are non-zero, wires it into ExecCreateOptions, and starts a resize-forwarding goroutine that coalesces updates from the resize channel and applies them via ExecResize for the lifetime of the exec. Includes forwardExecResize helper to drain and apply the latest resize event while respecting context cancellation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

A rabbit hops through signals bright,
SIGWINCH whispers in the night—
Terminal sizes flow like streams,
Resize channels realize dreams,
TTY tamed, the shell feels right! 🐰✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: implementing end-to-end PTY resize forwarding across the runtime and CLI components, which aligns directly with the PR's core objective of fixing terminal-resize behavior.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cli/tty-resize

Comment @coderabbitai help to get the list of available commands and usage tips.

@bilby91 bilby91 merged commit 45f1a06 into main May 23, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant