From 46a3f9c7eef3b15faa5ee67e7d923a1971feab21 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 03:49:44 +0000 Subject: [PATCH 1/2] Initial plan From 84e84c4fd49c6e1df546c6bf6748fc6d0dd9e5a8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 04:03:05 +0000 Subject: [PATCH 2/2] Print ready message only once per ready transition Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> --- internal/run.go | 20 +++++++++----- internal/run_test.go | 64 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/internal/run.go b/internal/run.go index dd459a9..44d7e5a 100644 --- a/internal/run.go +++ b/internal/run.go @@ -200,6 +200,8 @@ func RunSubgraph(ctx context.Context, cancel context.CancelFunc, port int, openB }) } + allRunning := false + for { select { case <-ctx.Done(): @@ -275,15 +277,20 @@ func RunSubgraph(ctx context.Context, cancel context.CancelFunc, port int, openB logger.Println("✅ exiting because all requested tasks completed and none should be restarted") cancel() } else if len(remainingTasks) == 0 { - logger.Println("🔵 all requested tasks are running:") - // print a list of running tasks, and their ports - for _, node := range subgraph.Nodes { - if (node.Phase == "running" || node.Phase == "stalled") && node.Task.Ports != nil { - for _, port := range node.Task.Ports { - logger.Printf(" - %s: http://localhost:%d\n", node.Name, port.HostPort) + if !allRunning { + allRunning = true + logger.Println("🔵 all requested tasks are running:") + // print a list of running tasks, and their ports + for _, node := range subgraph.Nodes { + if (node.Phase == "running" || node.Phase == "stalled") && node.Task.Ports != nil { + for _, port := range node.Task.Ports { + logger.Printf(" - %s: http://localhost:%d\n", node.Name, port.HostPort) + } } } } + } else { + allRunning = false } // if the event is a string, it is the name of the task to run @@ -308,6 +315,7 @@ func RunSubgraph(ctx context.Context, cancel context.CancelFunc, port int, openB node := subgraph.Nodes[taskName] node.cancel() + allRunning = false // each task is executed in a separate goroutine wg.Add(1) diff --git a/internal/run_test.go b/internal/run_test.go index 7ef8eb7..5ef1602 100644 --- a/internal/run_test.go +++ b/internal/run_test.go @@ -437,6 +437,70 @@ sleep 30 err := RunSubgraph(ctx, cancel, 0, false, logger, wf, []string{"job"}, nil) assert.NoError(t, err) }) + + t.Run("Ready message printed only once when service stays running", func(t *testing.T) { + ctx, cancel, logger, buffer := setup(t) + defer cancel() + + wf := &types.Workflow{ + Tasks: map[string]types.Task{ + "service": {Command: []string{"sleep", "30"}, Type: types.TaskTypeService}, + }, + } + wg := &sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + err := RunSubgraph(ctx, cancel, 0, false, logger, wf, []string{"service"}, nil) + assert.NoError(t, err) + }() + + sleep(t) + cancel() + wg.Wait() + + // The ready message should appear exactly once + count := strings.Count(buffer.String(), "🔵 all requested tasks are running:") + assert.Equal(t, 1, count) + }) + + t.Run("Ready message printed again after service restarts", func(t *testing.T) { + ctx, cancel, logger, buffer := setup(t) + defer cancel() + + wf := &types.Workflow{ + Tasks: map[string]types.Task{ + "service": { + Command: []string{"sleep", "30"}, + Watch: []string{"testdata/marker"}, + Type: types.TaskTypeService, + }, + }, + } + + wg := &sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + err := RunSubgraph(ctx, cancel, 0, false, logger, wf, []string{"service"}, nil) + assert.NoError(t, err) + }() + + sleep(t) + + // Trigger a restart by modifying the watched file + err := os.WriteFile("testdata/marker", nil, 0644) + assert.NoError(t, err) + + sleep(t) + + cancel() + wg.Wait() + + // The ready message should appear twice: once initially, once after restart + count := strings.Count(buffer.String(), "🔵 all requested tasks are running:") + assert.Equal(t, 2, count) + }) } func sleep(t *testing.T) {