From c6b7090316fd1cca373860954325c8eafc6d9684 Mon Sep 17 00:00:00 2001 From: jinhyuk9714 Date: Sat, 30 May 2026 14:18:34 +0900 Subject: [PATCH 1/2] fix(event): keep bounded consume alive after stdin EOF --- cmd/event/consume.go | 9 ++++++-- cmd/event/consume_stdin_test.go | 39 +++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/cmd/event/consume.go b/cmd/event/consume.go index 9fd4d234d..a38968185 100644 --- a/cmd/event/consume.go +++ b/cmd/event/consume.go @@ -184,8 +184,9 @@ func runConsume(cmd *cobra.Command, f *cmdutil.Factory, eventKey string, o consu errOut = io.Discard } - // Non-TTY only: stdin EOF is shutdown for subprocess callers; in TTY Ctrl-D must not exit. - if !f.IOStreams.IsTerminal { + // Non-TTY unbounded consumers use stdin EOF as shutdown for subprocess callers. + // Bounded runs already have --max-events/--timeout as their lifecycle control. + if shouldWatchStdinEOF(f.IOStreams.IsTerminal, o.maxEvents, o.timeout) { watchStdinEOF(os.Stdin, cancel, errOut) } @@ -370,3 +371,7 @@ func watchStdinEOF(r io.Reader, cancel context.CancelFunc, errOut io.Writer) { cancel() }() } + +func shouldWatchStdinEOF(isTerminal bool, maxEvents int, timeout time.Duration) bool { + return !isTerminal && maxEvents == 0 && timeout == 0 +} diff --git a/cmd/event/consume_stdin_test.go b/cmd/event/consume_stdin_test.go index c4391842c..eec1e70f1 100644 --- a/cmd/event/consume_stdin_test.go +++ b/cmd/event/consume_stdin_test.go @@ -61,3 +61,42 @@ func TestWatchStdinEOF_DiagnosticMessage(t *testing.T) { t.Fatal("watchStdinEOF did not cancel within 1s of EOF") } } + +func TestShouldWatchStdinEOF(t *testing.T) { + tests := []struct { + name string + isTerminal bool + maxEvents int + timeout time.Duration + want bool + }{ + { + name: "terminal", + isTerminal: true, + want: false, + }, + { + name: "non terminal unbounded", + want: true, + }, + { + name: "non terminal max events bounded", + maxEvents: 1, + want: false, + }, + { + name: "non terminal timeout bounded", + timeout: 10 * time.Minute, + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := shouldWatchStdinEOF(tt.isTerminal, tt.maxEvents, tt.timeout) + if got != tt.want { + t.Fatalf("shouldWatchStdinEOF() = %v, want %v", got, tt.want) + } + }) + } +} From db4a09c361f9eb13fdb8ebb7d73f27e18846a902 Mon Sep 17 00:00:00 2001 From: jinhyuk9714 Date: Sat, 30 May 2026 15:00:45 +0900 Subject: [PATCH 2/2] fix(event): align stdin EOF bounds semantics --- cmd/event/consume.go | 2 +- cmd/event/consume_stdin_test.go | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/cmd/event/consume.go b/cmd/event/consume.go index a38968185..fdc6fad49 100644 --- a/cmd/event/consume.go +++ b/cmd/event/consume.go @@ -373,5 +373,5 @@ func watchStdinEOF(r io.Reader, cancel context.CancelFunc, errOut io.Writer) { } func shouldWatchStdinEOF(isTerminal bool, maxEvents int, timeout time.Duration) bool { - return !isTerminal && maxEvents == 0 && timeout == 0 + return !isTerminal && maxEvents <= 0 && timeout <= 0 } diff --git a/cmd/event/consume_stdin_test.go b/cmd/event/consume_stdin_test.go index eec1e70f1..de2a2e2e1 100644 --- a/cmd/event/consume_stdin_test.go +++ b/cmd/event/consume_stdin_test.go @@ -79,6 +79,16 @@ func TestShouldWatchStdinEOF(t *testing.T) { name: "non terminal unbounded", want: true, }, + { + name: "non terminal negative max events is unbounded", + maxEvents: -1, + want: true, + }, + { + name: "non terminal negative timeout is unbounded", + timeout: -1 * time.Second, + want: true, + }, { name: "non terminal max events bounded", maxEvents: 1,