fix: include npm global bin in service path (#3571) (#3573)

This commit is contained in:
Carlos Florêncio
2026-09-10 16:35:51 +01:00
committed by GitHub
parent d7e337ff47
commit adbe4c2ef7
3 changed files with 81 additions and 1 deletions
+1 -1
View File
@@ -480,7 +480,7 @@ type nativeServiceUnitInput struct {
}
const (
systemdServicePath = "/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin:/home/linuxbrew/.linuxbrew/bin:%h/.local/bin:%h/.bun/bin:%h/.opencode/bin"
systemdServicePath = "/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin:/home/linuxbrew/.linuxbrew/bin:%h/.local/bin:%h/.bun/bin:%h/.opencode/bin:%h/.npm-global/bin"
launchdServicePath = "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
)
@@ -0,0 +1,67 @@
package launcher
import (
"context"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
"time"
)
func TestSystemdServicePathExecutesNPMGlobalCLI(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("systemd service PATH is POSIX-only")
}
home := t.TempDir()
binDir := filepath.Join(home, ".npm-global", "bin")
if err := os.MkdirAll(binDir, 0o755); err != nil {
t.Fatal(err)
}
const cli = "kandev-test-npm-global-agent"
wantPath := filepath.Join(binDir, cli)
if err := os.WriteFile(wantPath, []byte("#!/bin/sh\nprintf 'npm-global-agent-ok'\n"), 0o755); err != nil {
t.Fatal(err)
}
unit := renderSystemdUnit(nativeServiceUnitInput{
Executable: "/opt/kandev/bin/kandev",
HomeDir: filepath.Join(home, ".kandev"),
})
var servicePath string
for _, line := range strings.Split(unit, "\n") {
value, ok := strings.CutPrefix(line, "Environment=")
if !ok {
continue
}
value = strings.Trim(value, `"`)
value, ok = strings.CutPrefix(value, "PATH=")
if !ok {
continue
}
// %h is the systemd home-dir specifier; it differs from input.HomeDir (kandev data dir).
servicePath = strings.ReplaceAll(value, "%h", home)
break
}
if servicePath == "" {
t.Fatal("generated unit has no PATH environment entry")
}
t.Setenv("PATH", servicePath)
resolved, err := exec.LookPath(cli)
if err != nil {
t.Fatalf("global npm CLI is not discoverable with generated service PATH: %v", err)
}
if resolved != wantPath {
t.Fatalf("resolved CLI = %q, want %q", resolved, wantPath)
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
output, err := exec.CommandContext(ctx, cli).CombinedOutput()
if err != nil {
t.Fatalf("execute global npm CLI: %v: %s", err, output)
}
if string(output) != "npm-global-agent-ok" {
t.Fatalf("CLI output = %q", output)
}
}
+13
View File
@@ -340,3 +340,16 @@ Reinstall using the upgraded `kandev` binary, preserve the original `--system` a
### Service starts but agents fail
Read service logs first. A service has a smaller `PATH` and no interactive shell environment, so tools or credentials visible in a terminal may be absent. Configure executor credentials through Kandev's profile/settings paths, use stable executable paths, and verify Docker/SSH/Sprites connectivity as described in [Executors](executors.md#troubleshooting).
Linux user services include `~/.npm-global/bin` in their generated `PATH` for
agent CLIs installed with that npm prefix. After upgrading an existing user
service, regenerate its unit and restart it to apply the updated `PATH`:
```bash
# Repeat your original install-time flags when used.
kandev service install
kandev service restart
```
Restarting alone does not update an older unit's `PATH`. Other custom npm
prefixes are not automatically added to the service environment.