98 lines
2.9 KiB
Go
98 lines
2.9 KiB
Go
package windsurf
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
)
|
|
|
|
// dataDirStat reports whether a data directory path exists and is writable.
|
|
type dataDirStat struct {
|
|
Exists bool
|
|
Writable bool
|
|
}
|
|
|
|
// dataDirStatFn is replaced in tests to avoid touching the filesystem.
|
|
var dataDirStatFn = defaultDataDirStat
|
|
|
|
// userConfigDirFn is replaced in tests.
|
|
var userConfigDirFn = defaultUserConfigDir
|
|
|
|
func defaultDataDirStat(path string) dataDirStat {
|
|
info, err := os.Stat(path)
|
|
if err != nil || !info.IsDir() {
|
|
return dataDirStat{}
|
|
}
|
|
// Probe writability by creating a sentinel file. This catches root-owned
|
|
// directories on Linux that are readable but not user-writable.
|
|
tmp := filepath.Join(path, ".sub2api-writable-check")
|
|
f, err := os.Create(tmp)
|
|
if err != nil {
|
|
return dataDirStat{Exists: true}
|
|
}
|
|
_ = f.Close()
|
|
_ = os.Remove(tmp)
|
|
return dataDirStat{Exists: true, Writable: true}
|
|
}
|
|
|
|
func defaultUserConfigDir() string {
|
|
if dir, err := os.UserConfigDir(); err == nil {
|
|
return dir
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// LegacyLinuxDataDir is the original pre-cross-platform data directory.
|
|
// Preserved to keep existing Linux deployments functional after upgrade.
|
|
const LegacyLinuxDataDir = "/opt/windsurf/data"
|
|
|
|
// resolveDataDir picks the base directory under which each LS instance's
|
|
// per-key subdirectory will live.
|
|
//
|
|
// Priority:
|
|
// 1. cfg.DataDir — explicit caller wins
|
|
// 2. WINDSURF_DATA_DIR env var
|
|
// 3. /opt/windsurf/data if it exists AND is writable (Linux backward compat)
|
|
// 4. os.UserConfigDir()/windsurf/data (cross-platform default)
|
|
// 5. os.TempDir()/windsurf-data (last-resort fallback)
|
|
func resolveDataDir(cfg LSPoolConfig) string {
|
|
return resolveDataDirFor(cfg.DataDir, os.Getenv("WINDSURF_DATA_DIR"), runtime.GOOS)
|
|
}
|
|
|
|
func resolveDataDirFor(cfgDir, envDir, goos string) string {
|
|
if cfgDir != "" {
|
|
return cfgDir
|
|
}
|
|
if envDir != "" {
|
|
return envDir
|
|
}
|
|
if goos == "linux" {
|
|
if stat := dataDirStatFn(LegacyLinuxDataDir); stat.Exists && stat.Writable {
|
|
return LegacyLinuxDataDir
|
|
}
|
|
}
|
|
if cfgDir := userConfigDirFn(); cfgDir != "" {
|
|
return filepath.Join(cfgDir, "windsurf", "data")
|
|
}
|
|
return filepath.Join(os.TempDir(), "windsurf-data")
|
|
}
|
|
|
|
// instanceHomeDir returns the per-instance HOME sandbox path. The LS binary
|
|
// writes telemetry and caches under $HOME (Linux/macOS) or %USERPROFILE%
|
|
// (Windows), so isolating it per instance prevents one instance from leaking
|
|
// state into another or into the invoking user's real home directory.
|
|
func instanceHomeDir(instanceDataDir string) string {
|
|
return filepath.Join(instanceDataDir, "home")
|
|
}
|
|
|
|
// homeEnvForPlatform returns the set of environment assignments needed to
|
|
// direct the LS process at the given home directory. On Windows we also set
|
|
// USERPROFILE because most Windows programs check that first.
|
|
func homeEnvForPlatform(homeDir, goos string) []string {
|
|
envs := []string{"HOME=" + homeDir}
|
|
if goos == "windows" {
|
|
envs = append(envs, "USERPROFILE="+homeDir)
|
|
}
|
|
return envs
|
|
}
|