charmbracelet / bubbletea

A powerful little TUI framework 🏗
MIT License
26.76k stars 771 forks source link

"open /dev/tty: no such device or address" when running in Docker with no tty #761

Open jonbretman opened 1 year ago

jonbretman commented 1 year ago

Describe the bug It seems that when running inside a Docker container with no tty Run() returns an error:

open /dev/tty: no such device or address

Setup Please complete the following information along with version numbers, if applicable.

To Reproduce Simple bubbletea program:

package main

import (
    tea "github.com/charmbracelet/bubbletea"
)

type Model struct{}

func (m Model) Init() tea.Cmd {
    return nil
}

func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    return m, tea.Quit
}

func (m Model) View() string {
    return ""
}

func main() {
    _, err := tea.NewProgram(Model{}).Run()
    if err != nil {
        panic(err)
    }
}

Build the binary for linux: GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o ./main ./main.go

Run in Docker: docker run -v "$(pwd)"/main:/usr/local/bin/my-cli public.ecr.aws/ubuntu/ubuntu:22.04 my-cli

panic: open /dev/tty: no such device or address

goroutine 1 [running]:
main.main()
    /Users/jonbretman/code/bubbletea-tty-issue/main.go:24 +0x53

I'm using AWS Ubuntu because I discovered this issue while using my CLI tool in AWS CodeBuild, but the issue is not specific to AWS or CodeBuild (as far as I can tell). Running with -it works e.g. docker run -it ...

Expected behavior I would expect the program to still run

meowgorithm commented 1 year ago

This came up in Discord recently as well. Based on this and that I don't think we should fatally error if we're not able to open a TTY. To help work around any resulting confusion we may want to consider:

  1. Exposing input on Program (i.e. Program.Input() io.Readernil would mean no input)
  2. Send something like a WarningNoInputMsg on program start if no input can be detected.

Development note: remember that internally, if input's not a TTY, we try and open a TTY for input at /dev/tty (or CONIN$ on Windows).

Fwiw, to some degree you can work around this in the meantime by detecting presence of a TTY prior to starting the program and passing tea.WithInput(nil) to NewProgram if no TTY is available.

DavidS-ovm commented 2 months ago

tea.WithInput(nil) does not work when using tea.Exec() on 0.26.5:

runtime error: invalid memory address or nil pointer dereference

Restoring terminal...

goroutine 1 [running]:
runtime/debug.Stack()
    /opt/hostedtoolcache/go/1.22.4/x64/src/runtime/debug/stack.go:24 +0x5e
runtime/debug.PrintStack()
    /opt/hostedtoolcache/go/1.22.4/x64/src/runtime/debug/stack.go:16 +0x13
github.com/charmbracelet/bubbletea.(*Program).Run.func1()
    /home/runner/go/pkg/mod/github.com/charmbracelet/bubbletea@v0.26.5/tea.go:479 +0x91
panic({0x3ffa560?, 0x68a0570?})
    /opt/hostedtoolcache/go/1.22.4/x64/src/runtime/panic.go:770 +0x132
github.com/charmbracelet/bubbletea.(*Program).ReleaseTerminal(0xc000018900)
    /home/runner/go/pkg/mod/github.com/charmbracelet/bubbletea@v0.26.5/tea.go:655 +0x30
github.com/charmbracelet/bubbletea.(*Program).exec(0xc000018900, {0x4f2c308, 0xc0009c2470}, 0xc00041ef90)
    /home/runner/go/pkg/mod/github.com/charmbracelet/bubbletea@v0.26.5/exec.go:103 +0x2b
github.com/charmbracelet/bubbletea.(*Program).eventLoop(0xc000018900, {0x4f2a370?, 0xc000018800?}, 0xc0001145a0)
    /home/runner/go/pkg/mod/github.com/charmbracelet/bubbletea@v0.26.5/tea.go:366 +0x4b0
github.com/charmbracelet/bubbletea.(*Program).Run(0xc000018900)
    /home/runner/go/pkg/mod/github.com/charmbracelet/bubbletea@v0.26.5/tea.go:550 +0xa0e