diff --git a/cmd/entire/cli/settings/settings.go b/cmd/entire/cli/settings/settings.go index 3654f5ccde..a7f41b8615 100644 --- a/cmd/entire/cli/settings/settings.go +++ b/cmd/entire/cli/settings/settings.go @@ -631,10 +631,13 @@ func IsSetUpAny(ctx context.Context) bool { } // IsSetUpAndEnabled returns true if Entire is both set up and enabled. -// This checks if .entire/settings.json exists AND has enabled: true. +// This checks if either .entire/settings.json or .entire/settings.local.json +// exists AND the merged settings have enabled: true. Using IsSetUpAny here +// ensures hooks fire for `entire enable --local` setups, where only the +// local settings file exists. // Use this for hooks that should be no-ops when Entire is not active. func IsSetUpAndEnabled(ctx context.Context) bool { - if !IsSetUp(ctx) { + if !IsSetUpAny(ctx) { return false } s, err := Load(ctx) diff --git a/cmd/entire/cli/settings/settings_test.go b/cmd/entire/cli/settings/settings_test.go index bf8dc2a37a..9067af2096 100644 --- a/cmd/entire/cli/settings/settings_test.go +++ b/cmd/entire/cli/settings/settings_test.go @@ -8,6 +8,8 @@ import ( "strings" "testing" "time" + + "github.com/entireio/cli/cmd/entire/cli/testutil" ) const ( @@ -1043,3 +1045,63 @@ func TestReviewConfig_IsZero(t *testing.T) { }) } } + +// setupSettingsRepo initializes a real git repository in a temp dir, writes +// optional base/local settings files into .entire/, and chdirs into the +// repo. Unlike setupSettingsDir it creates a real repo so that +// paths.WorktreeRoot() (which calls `git rev-parse`) succeeds — required by +// IsSetUp / IsSetUpAny / IsSetUpAndEnabled. +func setupSettingsRepo(t *testing.T, base, local string) { + t.Helper() + tmpDir := t.TempDir() + testutil.InitRepo(t, tmpDir) + entireDir := filepath.Join(tmpDir, ".entire") + if err := os.MkdirAll(entireDir, 0o755); err != nil { + t.Fatalf("failed to create .entire directory: %v", err) + } + if base != "" { + if err := os.WriteFile(filepath.Join(entireDir, "settings.json"), []byte(base), 0o644); err != nil { + t.Fatalf("failed to write settings file: %v", err) + } + } + if local != "" { + if err := os.WriteFile(filepath.Join(entireDir, "settings.local.json"), []byte(local), 0o644); err != nil { + t.Fatalf("failed to write local settings file: %v", err) + } + } + t.Chdir(tmpDir) +} + +// TestIsSetUpAndEnabled_LocalOnly is a regression test for issue #1123: +// `entire enable --local` creates only .entire/settings.local.json, but +// IsSetUpAndEnabled previously called IsSetUp which only checks for the +// shared settings.json. Hooks calling IsSetUpAndEnabled then silently +// no-op on every commit. Switching to IsSetUpAny fixes this; this test +// locks the behavior in. +func TestIsSetUpAndEnabled_LocalOnly(t *testing.T) { + setupSettingsRepo(t, "", `{"enabled": true}`) + if !IsSetUpAndEnabled(context.Background()) { + t.Fatal("IsSetUpAndEnabled() = false; want true when only settings.local.json exists with enabled: true") + } +} + +func TestIsSetUpAndEnabled_LocalOnlyDisabled(t *testing.T) { + setupSettingsRepo(t, "", `{"enabled": false}`) + if IsSetUpAndEnabled(context.Background()) { + t.Fatal("IsSetUpAndEnabled() = true; want false when local-only settings have enabled: false") + } +} + +func TestIsSetUpAndEnabled_NoFiles(t *testing.T) { + setupSettingsRepo(t, "", "") + if IsSetUpAndEnabled(context.Background()) { + t.Fatal("IsSetUpAndEnabled() = true; want false when no settings files exist") + } +} + +func TestIsSetUpAndEnabled_BaseOnlyEnabled(t *testing.T) { + setupSettingsRepo(t, `{"enabled": true}`, "") + if !IsSetUpAndEnabled(context.Background()) { + t.Fatal("IsSetUpAndEnabled() = false; want true when settings.json has enabled: true") + } +}