Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions internal/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ var rootCmd = &cobra.Command{
if cmd.CommandPath() == "flashduty update" {
return nil
}
if update.IsManagedByRunner() {
return nil
}
if !isTerminalFn(int(os.Stderr.Fd())) {
return nil
}
Expand Down
42 changes: 42 additions & 0 deletions internal/cli/root_managed_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package cli

import (
"runtime"
"testing"

"github.com/flashcatcloud/flashduty-cli/internal/update"
)

func TestRootSkipsAutoUpdateWhenManagedByRunner(t *testing.T) {
saveAndResetGlobals(t)
tmp := t.TempDir()
t.Setenv("HOME", tmp)
if runtime.GOOS == "windows" {
t.Setenv("USERPROFILE", tmp)
}
t.Setenv("CI", "")
t.Setenv("GITHUB_ACTIONS", "")
t.Setenv("JENKINS_URL", "")
t.Setenv("GITLAB_CI", "")
t.Setenv("FLASHDUTY_NO_UPDATE_CHECK", "")
t.Setenv("FLASHDUTY_MANAGED_BY_RUNNER", "1")

origIsTerminal := isTerminalFn
isTerminalFn = func(int) bool { return true }
t.Cleanup(func() { isTerminalFn = origIsTerminal })

called := false
origCheck := checkForUpdateAutoFn
checkForUpdateAutoFn = func(string) (*update.CheckResult, error) {
called = true
return &update.CheckResult{}, nil
}
t.Cleanup(func() { checkForUpdateAutoFn = origCheck })

if _, err := execCommand("version"); err != nil {
t.Fatalf("version command should run in runner-managed mode: %v", err)
}
if called {
t.Fatal("runner-managed CLI must not check for updates")
}
}
4 changes: 4 additions & 0 deletions internal/cli/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ func newUpdateCmd() *cobra.Command {
Use: "update",
Short: "Update flashduty to the latest version",
RunE: func(cmd *cobra.Command, _ []string) error {
if update.IsManagedByRunner() {
return fmt.Errorf("flashduty is managed by flashduty-runner; upgrade the runner instead")
}

w := cmd.OutOrStdout()
_, _ = fmt.Fprintf(w, "Current version: %s\n", versionStr)
_, _ = fmt.Fprintf(w, "Checking for updates...\n")
Expand Down
29 changes: 29 additions & 0 deletions internal/cli/update_managed_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package cli

import (
"net/http"
"net/http/httptest"
"strings"
"testing"
)

func TestUpdateRejectsRunnerManagedCLIWithoutChecking(t *testing.T) {
saveAndResetGlobals(t)
t.Setenv("FLASHDUTY_MANAGED_BY_RUNNER", "1")

checked := false
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
checked = true
_, _ = w.Write([]byte("v0.0.0\n"))
}))
t.Cleanup(server.Close)
t.Setenv("FLASHDUTY_UPDATE_BASE_URL", server.URL)

_, err := execCommand("update", "--check")
if err == nil || !strings.Contains(err.Error(), "managed by flashduty-runner") {
t.Fatalf("expected runner-managed update rejection, got %v", err)
}
if checked {
t.Fatal("runner-managed CLI must reject update before checking for releases")
}
}
10 changes: 10 additions & 0 deletions internal/update/managed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package update

import "os"

// IsManagedByRunner reports whether flashduty-runner owns this CLI binary.
// The marker is intentionally exact so a user environment cannot
// accidentally disable standalone CLI updates with a truthy-looking value.
func IsManagedByRunner() bool {
return os.Getenv("FLASHDUTY_MANAGED_BY_RUNNER") == "1"
}
15 changes: 15 additions & 0 deletions internal/update/managed_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package update

import "testing"

func TestIsManagedByRunner(t *testing.T) {
t.Setenv("FLASHDUTY_MANAGED_BY_RUNNER", "1")
if !IsManagedByRunner() {
t.Fatal("runner-managed CLI was not detected")
}

t.Setenv("FLASHDUTY_MANAGED_BY_RUNNER", "true")
if IsManagedByRunner() {
t.Fatal("only the runner's explicit marker value should enable managed mode")
}
}