diff --git a/modules.go b/modules.go index b86ee7f..41b728b 100644 --- a/modules.go +++ b/modules.go @@ -2185,7 +2185,10 @@ func (e *Executor) moduleDockerCompose(ctx context.Context, client sshExecutorCl } // Heuristic for changed - changed := !contains(stdout, "Up to date") && !contains(stderr, "Up to date") + changed := true + if contains(stdout, "Up to date") || contains(stderr, "Up to date") { + changed = false + } return &TaskResult{Changed: changed, Stdout: stdout}, nil } diff --git a/modules_adv_test.go b/modules_adv_test.go index 702a39f..d0878ba 100644 --- a/modules_adv_test.go +++ b/modules_adv_test.go @@ -1128,6 +1128,22 @@ func TestModulesAdv_ModuleDockerCompose_Good_DefaultStateIsPresent(t *testing.T) assert.True(t, mock.hasExecuted(`docker compose up -d`)) } +func TestModulesAdv_ModuleDockerCompose_Production_Good_AlreadyUpToDate(t *testing.T) { + e := NewExecutor("/tmp") + mock := NewMockSSHClient() + mock.expectCommand(`docker compose up -d`, "Container myapp-web-1 Up to date\n", "", 0) + + result, err := e.moduleDockerCompose(context.Background(), mock, map[string]any{ + "project_src": "/opt/myapp", + "state": "present", + }) + + require.NoError(t, err) + assert.False(t, result.Changed) + assert.False(t, result.Failed) + assert.Equal(t, "Container myapp-web-1 Up to date\n", result.Stdout) +} + // --- Cross-module dispatch tests for advanced modules --- func TestModulesAdv_ExecuteModuleWithMock_Good_DispatchUser(t *testing.T) {