diff --git a/cli/lakeflags/datadir.go b/cli/lakeflags/datadir.go new file mode 100644 index 0000000000..d88020276d --- /dev/null +++ b/cli/lakeflags/datadir.go @@ -0,0 +1,36 @@ +package lakeflags + +import ( + "os" + "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") + } + if runtime.GOOS == "windows" { + if appData := os.Getenv("LOCALAPPDATA"); appData != "" { + return filepath.Join(appData, "zed") + } + } + if homeDir, _ := os.UserHomeDir(); homeDir != "" { + // Follow the XDG spec which states: + // If $XDG_DATA_HOME is either not set or empty, a default equal to + // $HOME/.local/share should be used. + return filepath.Join(homeDir, ".local", "share", "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 b4678fba3c..727cb36f5f 100644 --- a/cli/lakeflags/flags.go +++ b/cli/lakeflags/flags.go @@ -5,6 +5,7 @@ import ( "errors" "flag" "fmt" + "net" "os" "path/filepath" "strings" @@ -21,11 +22,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) { @@ -35,14 +33,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 }) } @@ -62,7 +59,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