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
}