From 2bc4b90f8eb28eb2a289d78d2a4928379d8b221e Mon Sep 17 00:00:00 2001 From: Beny Dishon K Date: Fri, 31 Jul 2026 15:38:03 +0530 Subject: [PATCH 1/2] fix(update-check): detect nix and linuxbrew installs on linux The linux branch of getUpdateInstructions never inspected execPath outside the npm check, falling through to getLinuxPackageManager(), which only probes which package managers exist on the machine. A nix install on Debian or Ubuntu was told to run 'apt-get install infisical' for a binary in the immutable nix store. Homebrew-on-Linux hit the same gap, since /home/linuxbrew/.linuxbrew/ does not match the /homebrew/ substring the darwin branch checks. Mirrors the existing darwin behaviour: nix returns no instruction rather than a wrong one. Adds unit cases and integration steps to the integration-linux job. Partially addresses Infisical/infisical#226 --- .../workflows/test-update-instructions.yml | 19 +++++++++++++++++++ packages/util/check-for-update.go | 9 +++++++++ packages/util/check-for-update_test.go | 12 ++++++++++++ 3 files changed, 40 insertions(+) diff --git a/.github/workflows/test-update-instructions.yml b/.github/workflows/test-update-instructions.yml index b969535d..ef82306d 100644 --- a/.github/workflows/test-update-instructions.yml +++ b/.github/workflows/test-update-instructions.yml @@ -115,3 +115,22 @@ jobs: output=$($HOME/.npm-global/lib/node_modules/@infisical/cli/bin/update-hint) echo "Output: $output" echo "$output" | grep -q "npm update -g @infisical/cli" || (echo "Expected npm instruction, got: $output" && exit 1) + + # The runner has apt-get, so without install-source detection this falls + # through to the apt instruction. Asserting empty output is what makes + # this a real regression test for the nix case. + - name: Test nix path detection + run: | + mkdir -p $HOME/nix/store/abc123-infisical-0.0.1/bin + cp update-hint $HOME/nix/store/abc123-infisical-0.0.1/bin/update-hint + output=$($HOME/nix/store/abc123-infisical-0.0.1/bin/update-hint) + echo "Output: $output" + [ -z "$output" ] || (echo "Expected no instruction for nix, got: $output" && exit 1) + + - name: Test linuxbrew path detection + run: | + mkdir -p $HOME/linuxbrew/.linuxbrew/bin + cp update-hint $HOME/linuxbrew/.linuxbrew/bin/update-hint + output=$($HOME/linuxbrew/.linuxbrew/bin/update-hint) + echo "Output: $output" + echo "$output" | grep -q "brew update" || (echo "Expected brew instruction, got: $output" && exit 1) diff --git a/packages/util/check-for-update.go b/packages/util/check-for-update.go index dc90713b..f20d08d4 100644 --- a/packages/util/check-for-update.go +++ b/packages/util/check-for-update.go @@ -407,6 +407,15 @@ func getUpdateInstructions(goos string, execPath string) string { if isNpm { return "To update, run: npm update -g @infisical/cli" } + // Nix-managed binaries live in the immutable store. The system package + // manager did not install them and cannot update them, so stay quiet + // rather than print a command that will not work. + if strings.Contains(p, "/nix/store/") { + return "" + } + if strings.Contains(p, "linuxbrew") { + return "To update, run: brew update && brew upgrade infisical" + } pkgManager := getLinuxPackageManager() switch pkgManager { case "apt-get": diff --git a/packages/util/check-for-update_test.go b/packages/util/check-for-update_test.go index f460118f..028e68ad 100644 --- a/packages/util/check-for-update_test.go +++ b/packages/util/check-for-update_test.go @@ -96,6 +96,18 @@ func TestGetUpdateInstructions(t *testing.T) { execPath: "/home/user/.npm-global/lib/node_modules/@infisical/cli/bin/infisical", expected: "npm update -g @infisical/cli", }, + { + name: "linux nix returns empty", + goos: "linux", + execPath: "/nix/store/abc123-infisical-0.41.90/bin/infisical", + expectEmpty: true, + }, + { + name: "linux linuxbrew", + goos: "linux", + execPath: "/home/linuxbrew/.linuxbrew/bin/infisical", + expected: "brew update && brew upgrade infisical", + }, } for _, tt := range tests { From 60fbb3aa0dd726b2211618b4439d205e41222c0a Mon Sep 17 00:00:00 2001 From: Beny Dishon K Date: Fri, 31 Jul 2026 15:45:34 +0530 Subject: [PATCH 2/2] fix(update-check): bound the linuxbrew path match Review feedback: a bare "linuxbrew" substring also classified unrelated paths such as /opt/linuxbrew-tools as Homebrew installs. Uses "linuxbrew/" rather than a fully bounded "/linuxbrew/" so both real layouts still match: the multi-user default (/home/linuxbrew/.linuxbrew) and the single-user prefix (~/.linuxbrew), where the component is preceded by a dot rather than a slash. Adds a single-user case and a negative case for /opt/linuxbrew-tools, plus a notExpected field on the test table to express the negative assertion. --- packages/util/check-for-update.go | 6 +++++- packages/util/check-for-update_test.go | 21 ++++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/packages/util/check-for-update.go b/packages/util/check-for-update.go index f20d08d4..01ba0702 100644 --- a/packages/util/check-for-update.go +++ b/packages/util/check-for-update.go @@ -413,7 +413,11 @@ func getUpdateInstructions(goos string, execPath string) string { if strings.Contains(p, "/nix/store/") { return "" } - if strings.Contains(p, "linuxbrew") { + // Trailing slash so this matches the linuxbrew prefix directory in both + // the multi-user (/home/linuxbrew/.linuxbrew) and single-user + // (~/.linuxbrew) layouts, without classifying unrelated paths such as + // /opt/linuxbrew-tools as a Homebrew install. + if strings.Contains(p, "linuxbrew/") { return "To update, run: brew update && brew upgrade infisical" } pkgManager := getLinuxPackageManager() diff --git a/packages/util/check-for-update_test.go b/packages/util/check-for-update_test.go index 028e68ad..8a09e8e9 100644 --- a/packages/util/check-for-update_test.go +++ b/packages/util/check-for-update_test.go @@ -23,7 +23,8 @@ func TestGetUpdateInstructions(t *testing.T) { goos string execPath string expected string - expectEmpty bool // true means assert empty result (vs. skipping runtime-dependent cases) + expectEmpty bool // true means assert empty result (vs. skipping runtime-dependent cases) + notExpected string // assert the result does NOT contain this, for paths that must not match a detector }{ // darwin { @@ -108,6 +109,18 @@ func TestGetUpdateInstructions(t *testing.T) { execPath: "/home/linuxbrew/.linuxbrew/bin/infisical", expected: "brew update && brew upgrade infisical", }, + { + name: "linux linuxbrew single-user prefix", + goos: "linux", + execPath: "/home/user/.linuxbrew/bin/infisical", + expected: "brew update && brew upgrade infisical", + }, + { + name: "linux linuxbrew-tools is not a brew install", + goos: "linux", + execPath: "/opt/linuxbrew-tools/bin/infisical", + notExpected: "brew", + }, } for _, tt := range tests { @@ -119,6 +132,12 @@ func TestGetUpdateInstructions(t *testing.T) { } return } + if tt.notExpected != "" { + if strings.Contains(result, tt.notExpected) { + t.Errorf("expected %q not to contain %q", result, tt.notExpected) + } + return + } if tt.expected == "" { return // skip runtime-dependent cases }