From dec5802f298f2a4efe9dc128f176cb4064b6f844 Mon Sep 17 00:00:00 2001 From: pangeran-bottor Date: Tue, 24 Feb 2026 00:14:58 +0700 Subject: [PATCH 1/4] update installation method and instruction mapping on GetUpdateInstructions --- .../workflows/test-update-instructions.yml | 116 ++++++++++++++++++ packages/util/check-for-update.go | 29 ++++- packages/util/check-for-update_test.go | 93 ++++++++++++++ 3 files changed, 236 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/test-update-instructions.yml create mode 100644 packages/util/check-for-update_test.go diff --git a/.github/workflows/test-update-instructions.yml b/.github/workflows/test-update-instructions.yml new file mode 100644 index 00000000..27a59e37 --- /dev/null +++ b/.github/workflows/test-update-instructions.yml @@ -0,0 +1,116 @@ +name: Test Update Instructions + +on: + pull_request: + push: + +jobs: + unit-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v4 + with: + go-version: "1.24.13" + - name: Run unit tests + run: go test ./packages/util/ -run TestGetUpdateInstructions -v + + integration-windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v4 + with: + go-version: "1.24.13" + - name: Build CLI + run: go build -o infisical.exe . + + - name: Test scoop path detection + shell: pwsh + run: | + $scoopPath = "$env:USERPROFILE\scoop\apps\infisical\current" + New-Item -ItemType Directory -Force -Path $scoopPath | Out-Null + Copy-Item infisical.exe "$scoopPath\infisical.exe" + $output = & "$scoopPath\infisical.exe" --help 2>&1 | Out-String + Write-Host "Output: $output" + if ($output -notmatch "scoop update infisical") { + Write-Error "Expected 'scoop update infisical', got: $output" + exit 1 + } + + - name: Test npm path detection + shell: pwsh + run: | + $npmPath = "$env:APPDATA\npm\node_modules\@infisical\cli\bin" + New-Item -ItemType Directory -Force -Path $npmPath | Out-Null + Copy-Item infisical.exe "$npmPath\infisical.exe" + $output = & "$npmPath\infisical.exe" --help 2>&1 | Out-String + Write-Host "Output: $output" + if ($output -notmatch "npm update -g @infisical/cli") { + Write-Error "Expected 'npm update -g @infisical/cli', got: $output" + exit 1 + } + + - name: Test winget path detection + shell: pwsh + run: | + $wingetPath = "$env:LOCALAPPDATA\Microsoft\WinGet\Links" + New-Item -ItemType Directory -Force -Path $wingetPath | Out-Null + Copy-Item infisical.exe "$wingetPath\infisical.exe" + $output = & "$wingetPath\infisical.exe" --help 2>&1 | Out-String + Write-Host "Output: $output" + if ($output -notmatch "winget upgrade Infisical.Infisical") { + Write-Error "Expected 'winget upgrade Infisical.Infisical', got: $output" + exit 1 + } + + integration-macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v4 + with: + go-version: "1.24.13" + - name: Build CLI + run: go build -o infisical . + + - name: Test brew path detection + run: | + mkdir -p /tmp/homebrew/bin + cp infisical /tmp/homebrew/bin/infisical + output=$(/tmp/homebrew/bin/infisical --help 2>&1 || true) + echo "Output: $output" + echo "$output" | grep -q "brew update" || (echo "Expected brew instruction, got: $output" && exit 1) + + - name: Test npm path detection + run: | + mkdir -p /tmp/node_modules/@infisical/cli/bin + cp infisical /tmp/node_modules/@infisical/cli/bin/infisical + output=$(/tmp/node_modules/@infisical/cli/bin/infisical --help 2>&1 || true) + echo "Output: $output" + echo "$output" | grep -q "npm update -g @infisical/cli" || (echo "Expected npm instruction, got: $output" && exit 1) + + integration-linux: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v4 + with: + go-version: "1.24.13" + - name: Build CLI + run: go build -o infisical . + + - name: Test apt path detection + run: | + sudo cp infisical /usr/bin/infisical + output=$(/usr/bin/infisical --help 2>&1 || true) + echo "Output: $output" + echo "$output" | grep -q "apt-get" || (echo "Expected apt-get instruction, got: $output" && exit 1) + + - name: Test npm path detection + run: | + mkdir -p $HOME/.npm-global/lib/node_modules/@infisical/cli/bin + cp infisical $HOME/.npm-global/lib/node_modules/@infisical/cli/bin/infisical + output=$($HOME/.npm-global/lib/node_modules/@infisical/cli/bin/infisical --help 2>&1 || true) + echo "Output: $output" + echo "$output" | grep -q "npm update -g @infisical/cli" || (echo "Expected npm instruction, got: $output" && exit 1) diff --git a/packages/util/check-for-update.go b/packages/util/check-for-update.go index 3b7044e9..4eb15950 100644 --- a/packages/util/check-for-update.go +++ b/packages/util/check-for-update.go @@ -164,13 +164,38 @@ func getReleasePublishedAt(repoOwner string, repoName string, version string) (t } func GetUpdateInstructions() string { - os := runtime.GOOS - switch os { + execPath, err := os.Executable() + if err != nil { + execPath = "" + } + return getUpdateInstructions(runtime.GOOS, execPath) +} + +func getUpdateInstructions(goos string, execPath string) string { + p := strings.ToLower(execPath) + isNpm := strings.Contains(p, "node_modules") + + switch goos { case "darwin": + if isNpm { + return "To update, run: npm update -g @infisical/cli" + } return "To update, run: brew update && brew upgrade infisical" case "windows": + if isNpm { + return "To update, run: npm update -g @infisical/cli" + } + if strings.Contains(p, "scoop") { + return "To update, run: scoop update infisical" + } + if strings.Contains(p, "winget") { + return "To update, run: winget upgrade Infisical.Infisical" + } return "To update, run: scoop update infisical" case "linux": + if isNpm { + return "To update, run: npm update -g @infisical/cli" + } 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 new file mode 100644 index 00000000..802f7004 --- /dev/null +++ b/packages/util/check-for-update_test.go @@ -0,0 +1,93 @@ +package util + +import ( + "testing" +) + +func TestGetUpdateInstructions(t *testing.T) { + tests := []struct { + name string + goos string + execPath string + expected string + }{ + // darwin + { + name: "darwin brew", + goos: "darwin", + execPath: "/opt/homebrew/bin/infisical", + expected: "brew update && brew upgrade infisical", + }, + { + name: "darwin npm", + goos: "darwin", + execPath: "/opt/homebrew/lib/node_modules/@infisical/cli/bin/infisical", + expected: "npm update -g @infisical/cli", + }, + + // windows + { + name: "windows scoop", + goos: "windows", + execPath: `C:\Users\user\scoop\apps\infisical\current\infisical.exe`, + expected: "scoop update infisical", + }, + { + name: "windows npm", + goos: "windows", + execPath: `C:\Users\user\AppData\Roaming\npm\node_modules\@infisical\cli\bin\infisical.exe`, + expected: "npm update -g @infisical/cli", + }, + { + name: "windows winget", + goos: "windows", + execPath: `C:\Users\user\AppData\Local\Microsoft\WinGet\Links\infisical.exe`, + expected: "winget upgrade Infisical.Infisical", + }, + { + name: "windows unknown defaults to scoop", + goos: "windows", + execPath: `C:\infisical\infisical.exe`, + expected: "scoop update infisical", + }, + + // linux + { + name: "linux apt", + goos: "linux", + execPath: "/usr/bin/infisical", + expected: "", // apt detection is runtime — tested in integration + }, + { + name: "linux npm", + goos: "linux", + execPath: "/home/user/.npm-global/lib/node_modules/@infisical/cli/bin/infisical", + expected: "npm update -g @infisical/cli", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := getUpdateInstructions(tt.goos, tt.execPath) + if tt.expected == "" { + return // skip runtime-dependent cases + } + if !contains(result, tt.expected) { + t.Errorf("expected %q to contain %q", result, tt.expected) + } + }) + } +} + +func contains(s, substr string) bool { + return len(s) >= len(substr) && (s == substr || len(s) > 0 && containsStr(s, substr)) +} + +func containsStr(s, substr string) bool { + for i := 0; i <= len(s)-len(substr); i++ { + if s[i:i+len(substr)] == substr { + return true + } + } + return false +} From 5da406b7f2eb4a8b9cdd637a9b4aba60958c955a Mon Sep 17 00:00:00 2001 From: pangeran-bottor Date: Tue, 24 Feb 2026 00:20:28 +0700 Subject: [PATCH 2/4] use infisical vault command on CI --- .github/workflows/test-update-instructions.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test-update-instructions.yml b/.github/workflows/test-update-instructions.yml index 27a59e37..0ea2c992 100644 --- a/.github/workflows/test-update-instructions.yml +++ b/.github/workflows/test-update-instructions.yml @@ -31,7 +31,7 @@ jobs: $scoopPath = "$env:USERPROFILE\scoop\apps\infisical\current" New-Item -ItemType Directory -Force -Path $scoopPath | Out-Null Copy-Item infisical.exe "$scoopPath\infisical.exe" - $output = & "$scoopPath\infisical.exe" --help 2>&1 | Out-String + $output = & "$scoopPath\infisical.exe" vault 2>&1 | Out-String Write-Host "Output: $output" if ($output -notmatch "scoop update infisical") { Write-Error "Expected 'scoop update infisical', got: $output" @@ -44,7 +44,7 @@ jobs: $npmPath = "$env:APPDATA\npm\node_modules\@infisical\cli\bin" New-Item -ItemType Directory -Force -Path $npmPath | Out-Null Copy-Item infisical.exe "$npmPath\infisical.exe" - $output = & "$npmPath\infisical.exe" --help 2>&1 | Out-String + $output = & "$npmPath\infisical.exe" vault 2>&1 | Out-String Write-Host "Output: $output" if ($output -notmatch "npm update -g @infisical/cli") { Write-Error "Expected 'npm update -g @infisical/cli', got: $output" @@ -57,7 +57,7 @@ jobs: $wingetPath = "$env:LOCALAPPDATA\Microsoft\WinGet\Links" New-Item -ItemType Directory -Force -Path $wingetPath | Out-Null Copy-Item infisical.exe "$wingetPath\infisical.exe" - $output = & "$wingetPath\infisical.exe" --help 2>&1 | Out-String + $output = & "$wingetPath\infisical.exe" vault 2>&1 | Out-String Write-Host "Output: $output" if ($output -notmatch "winget upgrade Infisical.Infisical") { Write-Error "Expected 'winget upgrade Infisical.Infisical', got: $output" @@ -78,7 +78,7 @@ jobs: run: | mkdir -p /tmp/homebrew/bin cp infisical /tmp/homebrew/bin/infisical - output=$(/tmp/homebrew/bin/infisical --help 2>&1 || true) + output=$(/tmp/homebrew/bin/infisical vault 2>&1 || true) echo "Output: $output" echo "$output" | grep -q "brew update" || (echo "Expected brew instruction, got: $output" && exit 1) @@ -86,7 +86,7 @@ jobs: run: | mkdir -p /tmp/node_modules/@infisical/cli/bin cp infisical /tmp/node_modules/@infisical/cli/bin/infisical - output=$(/tmp/node_modules/@infisical/cli/bin/infisical --help 2>&1 || true) + output=$(/tmp/node_modules/@infisical/cli/bin/infisical vault 2>&1 || true) echo "Output: $output" echo "$output" | grep -q "npm update -g @infisical/cli" || (echo "Expected npm instruction, got: $output" && exit 1) @@ -103,7 +103,7 @@ jobs: - name: Test apt path detection run: | sudo cp infisical /usr/bin/infisical - output=$(/usr/bin/infisical --help 2>&1 || true) + output=$(/usr/bin/infisical vault 2>&1 || true) echo "Output: $output" echo "$output" | grep -q "apt-get" || (echo "Expected apt-get instruction, got: $output" && exit 1) @@ -111,6 +111,6 @@ jobs: run: | mkdir -p $HOME/.npm-global/lib/node_modules/@infisical/cli/bin cp infisical $HOME/.npm-global/lib/node_modules/@infisical/cli/bin/infisical - output=$($HOME/.npm-global/lib/node_modules/@infisical/cli/bin/infisical --help 2>&1 || true) + output=$($HOME/.npm-global/lib/node_modules/@infisical/cli/bin/infisical vault 2>&1 || true) echo "Output: $output" echo "$output" | grep -q "npm update -g @infisical/cli" || (echo "Expected npm instruction, got: $output" && exit 1) From 2434d53fb1bee1f011123ade8c4153cb0daa1adb Mon Sep 17 00:00:00 2001 From: pangeran-bottor Date: Tue, 24 Feb 2026 00:27:23 +0700 Subject: [PATCH 3/4] Pinning to 0.1.0 makes the flow deterministic on integration-macos --- .github/workflows/test-update-instructions.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-update-instructions.yml b/.github/workflows/test-update-instructions.yml index 0ea2c992..684c7b3d 100644 --- a/.github/workflows/test-update-instructions.yml +++ b/.github/workflows/test-update-instructions.yml @@ -23,7 +23,7 @@ jobs: with: go-version: "1.24.13" - name: Build CLI - run: go build -o infisical.exe . + run: go build -ldflags "-X github.com/Infisical/infisical-merge/packages/util.CLI_VERSION=0.1.0" -o infisical.exe . - name: Test scoop path detection shell: pwsh @@ -72,7 +72,7 @@ jobs: with: go-version: "1.24.13" - name: Build CLI - run: go build -o infisical . + run: go build -ldflags "-X github.com/Infisical/infisical-merge/packages/util.CLI_VERSION=0.1.0" -o infisical . - name: Test brew path detection run: | @@ -98,7 +98,7 @@ jobs: with: go-version: "1.24.13" - name: Build CLI - run: go build -o infisical . + run: go build -ldflags "-X github.com/Infisical/infisical-merge/packages/util.CLI_VERSION=0.1.0" -o infisical . - name: Test apt path detection run: | From fd824d37a87d48a9864371124ae875c8b1928b90 Mon Sep 17 00:00:00 2001 From: pangeran-bottor Date: Tue, 24 Feb 2026 00:43:01 +0700 Subject: [PATCH 4/4] fix: detect install method from executable path for update instructions --- .../workflows/test-update-instructions.yml | 45 ++++++++++--------- packages/util/check-for-update.go | 4 ++ test/update-hint/main.go | 14 ++++++ 3 files changed, 41 insertions(+), 22 deletions(-) create mode 100644 test/update-hint/main.go diff --git a/.github/workflows/test-update-instructions.yml b/.github/workflows/test-update-instructions.yml index 684c7b3d..6802adb1 100644 --- a/.github/workflows/test-update-instructions.yml +++ b/.github/workflows/test-update-instructions.yml @@ -22,16 +22,16 @@ jobs: - uses: actions/setup-go@v4 with: go-version: "1.24.13" - - name: Build CLI - run: go build -ldflags "-X github.com/Infisical/infisical-merge/packages/util.CLI_VERSION=0.1.0" -o infisical.exe . + - name: Build test helper + run: go build -o update-hint.exe ./test/update-hint - name: Test scoop path detection shell: pwsh run: | $scoopPath = "$env:USERPROFILE\scoop\apps\infisical\current" New-Item -ItemType Directory -Force -Path $scoopPath | Out-Null - Copy-Item infisical.exe "$scoopPath\infisical.exe" - $output = & "$scoopPath\infisical.exe" vault 2>&1 | Out-String + Copy-Item update-hint.exe "$scoopPath\update-hint.exe" + $output = & "$scoopPath\update-hint.exe" Write-Host "Output: $output" if ($output -notmatch "scoop update infisical") { Write-Error "Expected 'scoop update infisical', got: $output" @@ -43,8 +43,8 @@ jobs: run: | $npmPath = "$env:APPDATA\npm\node_modules\@infisical\cli\bin" New-Item -ItemType Directory -Force -Path $npmPath | Out-Null - Copy-Item infisical.exe "$npmPath\infisical.exe" - $output = & "$npmPath\infisical.exe" vault 2>&1 | Out-String + Copy-Item update-hint.exe "$npmPath\update-hint.exe" + $output = & "$npmPath\update-hint.exe" Write-Host "Output: $output" if ($output -notmatch "npm update -g @infisical/cli") { Write-Error "Expected 'npm update -g @infisical/cli', got: $output" @@ -56,8 +56,8 @@ jobs: run: | $wingetPath = "$env:LOCALAPPDATA\Microsoft\WinGet\Links" New-Item -ItemType Directory -Force -Path $wingetPath | Out-Null - Copy-Item infisical.exe "$wingetPath\infisical.exe" - $output = & "$wingetPath\infisical.exe" vault 2>&1 | Out-String + Copy-Item update-hint.exe "$wingetPath\update-hint.exe" + $output = & "$wingetPath\update-hint.exe" Write-Host "Output: $output" if ($output -notmatch "winget upgrade Infisical.Infisical") { Write-Error "Expected 'winget upgrade Infisical.Infisical', got: $output" @@ -71,22 +71,23 @@ jobs: - uses: actions/setup-go@v4 with: go-version: "1.24.13" - - name: Build CLI - run: go build -ldflags "-X github.com/Infisical/infisical-merge/packages/util.CLI_VERSION=0.1.0" -o infisical . + - name: Build test helper + run: go build -o update-hint ./test/update-hint - name: Test brew path detection run: | mkdir -p /tmp/homebrew/bin - cp infisical /tmp/homebrew/bin/infisical - output=$(/tmp/homebrew/bin/infisical vault 2>&1 || true) + cp update-hint /tmp/homebrew/bin/update-hint + output=$(/tmp/homebrew/bin/update-hint) echo "Output: $output" echo "$output" | grep -q "brew update" || (echo "Expected brew instruction, got: $output" && exit 1) - - name: Test npm path detection + - name: Test npm path detection (via symlink, as npm installs it) run: | - mkdir -p /tmp/node_modules/@infisical/cli/bin - cp infisical /tmp/node_modules/@infisical/cli/bin/infisical - output=$(/tmp/node_modules/@infisical/cli/bin/infisical vault 2>&1 || true) + mkdir -p /tmp/node_modules/@infisical/cli/bin /tmp/bin + cp update-hint /tmp/node_modules/@infisical/cli/bin/update-hint + ln -sf /tmp/node_modules/@infisical/cli/bin/update-hint /tmp/bin/update-hint + output=$(/tmp/bin/update-hint) echo "Output: $output" echo "$output" | grep -q "npm update -g @infisical/cli" || (echo "Expected npm instruction, got: $output" && exit 1) @@ -97,20 +98,20 @@ jobs: - uses: actions/setup-go@v4 with: go-version: "1.24.13" - - name: Build CLI - run: go build -ldflags "-X github.com/Infisical/infisical-merge/packages/util.CLI_VERSION=0.1.0" -o infisical . + - name: Build test helper + run: go build -o update-hint ./test/update-hint - name: Test apt path detection run: | - sudo cp infisical /usr/bin/infisical - output=$(/usr/bin/infisical vault 2>&1 || true) + sudo cp update-hint /usr/bin/update-hint + output=$(/usr/bin/update-hint) echo "Output: $output" echo "$output" | grep -q "apt-get" || (echo "Expected apt-get instruction, got: $output" && exit 1) - name: Test npm path detection run: | mkdir -p $HOME/.npm-global/lib/node_modules/@infisical/cli/bin - cp infisical $HOME/.npm-global/lib/node_modules/@infisical/cli/bin/infisical - output=$($HOME/.npm-global/lib/node_modules/@infisical/cli/bin/infisical vault 2>&1 || true) + cp update-hint $HOME/.npm-global/lib/node_modules/@infisical/cli/bin/update-hint + 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) diff --git a/packages/util/check-for-update.go b/packages/util/check-for-update.go index 4eb15950..989841e8 100644 --- a/packages/util/check-for-update.go +++ b/packages/util/check-for-update.go @@ -9,6 +9,7 @@ import ( "net/http" "os" "os/exec" + "path/filepath" "runtime" "strings" "time" @@ -168,6 +169,9 @@ func GetUpdateInstructions() string { if err != nil { execPath = "" } + if resolved, err := filepath.EvalSymlinks(execPath); err == nil { + execPath = resolved + } return getUpdateInstructions(runtime.GOOS, execPath) } diff --git a/test/update-hint/main.go b/test/update-hint/main.go new file mode 100644 index 00000000..eede100a --- /dev/null +++ b/test/update-hint/main.go @@ -0,0 +1,14 @@ +// Helper binary for integration testing of update hint path detection. +// Prints the update instruction for the current executable path and OS, +// without making any network calls. +package main + +import ( + "fmt" + + "github.com/Infisical/infisical-merge/packages/util" +) + +func main() { + fmt.Println(util.GetUpdateInstructions()) +}