From e4103dd9314d667059e4cf5441771368cb28bd83 Mon Sep 17 00:00:00 2001 From: Matthew Nibecker Date: Wed, 6 Sep 2023 14:28:08 -0700 Subject: [PATCH] zed: Default app data location If no lake location is specified, zed will use a per-os default location. Also if the default location is used, zed will first check if there's a service on 9867 and opt to connect to that instead of operating locally. --- cli/lakeflags/datadir.go | 59 ++++++++++++++++++++++++++++ cli/lakeflags/flags.go | 27 +++++++++---- cmd/zed/serve/command.go | 3 -- cmd/zed/ztests/no-lake-location.yaml | 2 +- compiler/ztests/from-error.yaml | 2 +- 5 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 cli/lakeflags/datadir.go diff --git a/cli/lakeflags/datadir.go b/cli/lakeflags/datadir.go new file mode 100644 index 0000000000..9ac5662d26 --- /dev/null +++ b/cli/lakeflags/datadir.go @@ -0,0 +1,59 @@ +package lakeflags + +import ( + "os" + "os/user" + "path/filepath" + "runtime" +) + +var defaultDataDir string + +func init() { + defaultDataDir = getDefaultDataDir() +} + +// getDefaultDataDir returns the default data directory for the current user. +// Derived from https://github.com/btcsuite/btcd/blob/master/btcutil/appdata.go +func getDefaultDataDir() string { + // Resolve the XDG data home directory if set. + if xdgDataHome := os.Getenv("XDG_DATA_HOME"); xdgDataHome != "" { + return filepath.Join(xdgDataHome, "zed") + } + // Get the OS specific home directory via the Go standard lib. + var homeDir string + usr, err := user.Current() + if err == nil { + homeDir = usr.HomeDir + } + // Fall back to standard HOME environment variable that works + // for most POSIX OSes if the directory from the Go standard + // lib failed. + if err != nil || homeDir == "" { + homeDir = os.Getenv("HOME") + } + switch runtime.GOOS { + case "windows": + // Windows XP and before didn't have a LOCALAPPDATA, so fallback + // to regular APPDATA when LOCALAPPDATA is not set. + appData := os.Getenv("LOCALAPPDATA") + if appData == "" { + appData = os.Getenv("APPDATA") + } + if appData != "" { + return filepath.Join(appData, "zed") + } + case "darwin": + if homeDir != "" { + return filepath.Join(homeDir, "Library", + "Application Support", "zed") + } + default: + if homeDir != "" { + return filepath.Join(homeDir, ".zed") + } + } + // Return an empty string which will cause an error if a default data + // directory cannot be found. + return "" +} diff --git a/cli/lakeflags/flags.go b/cli/lakeflags/flags.go index 11566e78eb..454ce250d8 100644 --- a/cli/lakeflags/flags.go +++ b/cli/lakeflags/flags.go @@ -5,6 +5,7 @@ import ( "errors" "flag" "fmt" + "net" "os" "path/filepath" "strings" @@ -20,11 +21,8 @@ var ErrNoHEAD = errors.New("HEAD not specified: indicate with -use or run the \" type Flags struct { ConfigDir string - // LakeSpecified is set to true if the lake is explicitly set via either - // command line flag or environment variable. - LakeSpecified bool - Lake string - Quiet bool + Lake string + Quiet bool } func (l *Flags) SetFlags(fs *flag.FlagSet) { @@ -34,14 +32,13 @@ func (l *Flags) SetFlags(fs *flag.FlagSet) { dir = filepath.Join(dir, ".zed") } fs.StringVar(&l.ConfigDir, "configdir", dir, "configuration and credentials directory") - l.Lake = "http://localhost:9867" if s, ok := os.LookupEnv("ZED_LAKE"); ok { l.Lake = strings.TrimRight(s, "/") - l.LakeSpecified = true + } else { + l.Lake = defaultDataDir } fs.Func("lake", fmt.Sprintf("lake location (env ZED_LAKE) (default %s)", l.Lake), func(s string) error { l.Lake = strings.TrimRight(s, "/") - l.LakeSpecified = true return nil }) } @@ -61,7 +58,21 @@ func (l *Flags) Connection() (*client.Connection, error) { return conn, nil } +func portInUse(port string) bool { + ln, err := net.Listen("tcp", port) + if err != nil { + return true + } + ln.Close() + return false +} + func (l *Flags) Open(ctx context.Context) (api.Interface, error) { + // If the lake is the defaultDataDir, first check if a service is running + // on port 9867 and if so, use this as the lake location. + if l.Lake == defaultDataDir && !portInUse("9867") { + l.Lake = "http://localhost:9867" + } uri, err := l.URI() if err != nil { return nil, err diff --git a/cmd/zed/serve/command.go b/cmd/zed/serve/command.go index 4caf08458c..e31bd2c7a3 100644 --- a/cmd/zed/serve/command.go +++ b/cmd/zed/serve/command.go @@ -73,9 +73,6 @@ func (c *Command) Run(args []string) error { return err } defer cleanup() - if !c.LakeFlags.LakeSpecified { - c.LakeFlags.Lake = "" - } uri, err := c.LakeFlags.URI() if err != nil { return err diff --git a/cmd/zed/ztests/no-lake-location.yaml b/cmd/zed/ztests/no-lake-location.yaml index cedc088f87..0fade08661 100644 --- a/cmd/zed/ztests/no-lake-location.yaml +++ b/cmd/zed/ztests/no-lake-location.yaml @@ -1,6 +1,6 @@ script: | ! zed ls -lake '' - ! zed serve + ! zed serve -lake '' outputs: - name: stderr diff --git a/compiler/ztests/from-error.yaml b/compiler/ztests/from-error.yaml index 209544a444..c40e85b392 100644 --- a/compiler/ztests/from-error.yaml +++ b/compiler/ztests/from-error.yaml @@ -1,5 +1,5 @@ script: | - ! zc -C -s 'from p' + ! zc -lake='' -C -s 'from p' echo === >&2 export ZED_LAKE=test zed init