Specify the notebook directory explicitly (#14)

pull/15/head
Mickaël Menu 3 years ago committed by GitHub
parent be8b2d6289
commit e653c71356
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -11,6 +11,10 @@ All notable changes to this project will be documented in this file.
* This is the same format as a notebook [configuration file](docs/config.md). * This is the same format as a notebook [configuration file](docs/config.md).
* Shared templates can be stored in `~/.config/zk/templates/`. * Shared templates can be stored in `~/.config/zk/templates/`.
* `XDG_CONFIG_HOME` is taken into account. * `XDG_CONFIG_HOME` is taken into account.
* Use `--notebook-dir` or set `ZK_NOTEBOOK_DIR` to run `zk` as if it was started from this path instead of the current working directory.
* This allows running `zk` without being in a notebook.
* By setting `ZK_NOTEBOOK_DIR` in your shell configuration file (e.g. `~/.profile`), you are declaring a default global notebook which will be used when `zk` is not in a notebook.
* When the notebook directory is set explicitly, any path given as argument will be relative to it instead of the actual working directory.
## 0.2.1 ## 0.2.1

@ -26,6 +26,7 @@ type Container struct {
Date date.Provider Date date.Provider
Logger util.Logger Logger util.Logger
Terminal *term.Terminal Terminal *term.Terminal
WorkingDir string
templateLoader *handlebars.Loader templateLoader *handlebars.Loader
zk *zk.Zk zk *zk.Zk
zkErr error zkErr error
@ -48,24 +49,15 @@ func NewContainer() (*Container, error) {
} }
} }
// Open current notebook
zk, zkErr := zk.Open(".", config)
if zkErr == nil {
config = zk.Config
os.Setenv("ZK_PATH", zk.Path)
}
date := date.NewFrozenNow() date := date.NewFrozenNow()
return &Container{ return &Container{
Config: config, Config: config,
Logger: util.NewStdLogger("zk: ", 0),
// zk is short-lived, so we freeze the current date to use the same // zk is short-lived, so we freeze the current date to use the same
// date for any rendering during the execution. // date for any template rendering during the execution.
Date: &date, Date: &date,
Logger: util.NewStdLogger("zk: ", 0),
Terminal: term.New(), Terminal: term.New(),
zk: zk,
zkErr: zkErr,
}, nil }, nil
} }
@ -94,6 +86,24 @@ func locateGlobalConfig() (string, error) {
} }
} }
// OpenNotebook resolves and loads the first notebook found in the given
// searchPaths.
func (c *Container) OpenNotebook(searchPaths []string) {
if len(searchPaths) == 0 {
panic("no notebook search paths provided")
}
for _, path := range searchPaths {
c.zk, c.zkErr = zk.Open(path, c.Config)
if c.zkErr == nil {
c.WorkingDir = path
c.Config = c.zk.Config
os.Setenv("ZK_NOTEBOOK_DIR", c.zk.Path)
return
}
}
}
func (c *Container) Zk() (*zk.Zk, error) { func (c *Container) Zk() (*zk.Zk, error) {
return c.zk, c.zkErr return c.zk, c.zkErr
} }
@ -106,11 +116,11 @@ func (c *Container) TemplateLoader(lang string) *handlebars.Loader {
return c.templateLoader return c.templateLoader
} }
func (c *Container) Parser(zk *zk.Zk) *markdown.Parser { func (c *Container) Parser() *markdown.Parser {
return markdown.NewParser(markdown.ParserOpts{ return markdown.NewParser(markdown.ParserOpts{
HashtagEnabled: zk.Config.Format.Markdown.Hashtags, HashtagEnabled: c.Config.Format.Markdown.Hashtags,
MultiWordTagEnabled: zk.Config.Format.Markdown.MultiwordTags, MultiWordTagEnabled: c.Config.Format.Markdown.MultiwordTags,
ColontagEnabled: zk.Config.Format.Markdown.ColonTags, ColontagEnabled: c.Config.Format.Markdown.ColonTags,
}) })
} }
@ -127,10 +137,14 @@ func (c *Container) NoteIndexer(tx sqlite.Transaction) *sqlite.NoteIndexer {
// Database returns the DB instance for the given notebook, after executing any // Database returns the DB instance for the given notebook, after executing any
// pending migration and indexing the notes if needed. // pending migration and indexing the notes if needed.
func (c *Container) Database(zk *zk.Zk, forceIndexing bool) (*sqlite.DB, note.IndexingStats, error) { func (c *Container) Database(forceIndexing bool) (*sqlite.DB, note.IndexingStats, error) {
var stats note.IndexingStats var stats note.IndexingStats
db, err := sqlite.Open(zk.DBPath()) if c.zkErr != nil {
return nil, stats, c.zkErr
}
db, err := sqlite.Open(c.zk.DBPath())
if err != nil { if err != nil {
return nil, stats, err return nil, stats, err
} }
@ -139,7 +153,7 @@ func (c *Container) Database(zk *zk.Zk, forceIndexing bool) (*sqlite.DB, note.In
return nil, stats, errors.Wrap(err, "failed to migrate the database") return nil, stats, errors.Wrap(err, "failed to migrate the database")
} }
stats, err = c.index(zk, db, forceIndexing || needsReindexing) stats, err = c.index(db, forceIndexing || needsReindexing)
if err != nil { if err != nil {
return nil, stats, err return nil, stats, err
} }
@ -147,7 +161,7 @@ func (c *Container) Database(zk *zk.Zk, forceIndexing bool) (*sqlite.DB, note.In
return db, stats, err return db, stats, err
} }
func (c *Container) index(zk *zk.Zk, db *sqlite.DB, force bool) (note.IndexingStats, error) { func (c *Container) index(db *sqlite.DB, force bool) (note.IndexingStats, error) {
var bar = progressbar.NewOptions(-1, var bar = progressbar.NewOptions(-1,
progressbar.OptionSetWriter(os.Stderr), progressbar.OptionSetWriter(os.Stderr),
progressbar.OptionThrottle(100*time.Millisecond), progressbar.OptionThrottle(100*time.Millisecond),
@ -156,11 +170,16 @@ func (c *Container) index(zk *zk.Zk, db *sqlite.DB, force bool) (note.IndexingSt
var err error var err error
var stats note.IndexingStats var stats note.IndexingStats
if c.zkErr != nil {
return stats, c.zkErr
}
err = db.WithTransaction(func(tx sqlite.Transaction) error { err = db.WithTransaction(func(tx sqlite.Transaction) error {
stats, err = note.Index( stats, err = note.Index(
zk, c.zk,
force, force,
c.Parser(zk), c.Parser(),
c.NoteIndexer(tx), c.NoteIndexer(tx),
c.Logger, c.Logger,
func(change paths.DiffChange) { func(change paths.DiffChange) {
@ -179,8 +198,8 @@ func (c *Container) index(zk *zk.Zk, db *sqlite.DB, force bool) (note.IndexingSt
// paginated if noPager is false, using the user's pager. // paginated if noPager is false, using the user's pager.
// //
// You can write to the pager only in the run callback. // You can write to the pager only in the run callback.
func (c *Container) Paginate(noPager bool, config zk.Config, run func(out io.Writer) error) error { func (c *Container) Paginate(noPager bool, run func(out io.Writer) error) error {
pager, err := c.pager(noPager || config.Tool.Pager.IsEmpty(), config) pager, err := c.pager(noPager || c.Config.Tool.Pager.IsEmpty())
if err != nil { if err != nil {
return err return err
} }
@ -189,10 +208,10 @@ func (c *Container) Paginate(noPager bool, config zk.Config, run func(out io.Wri
return err return err
} }
func (c *Container) pager(noPager bool, config zk.Config) (*pager.Pager, error) { func (c *Container) pager(noPager bool) (*pager.Pager, error) {
if noPager || !c.Terminal.IsInteractive() { if noPager || !c.Terminal.IsInteractive() {
return pager.PassthroughPager, nil return pager.PassthroughPager, nil
} else { } else {
return pager.New(config.Tool.Pager, c.Logger) return pager.New(c.Config.Tool.Pager, c.Logger)
} }
} }

@ -2,7 +2,6 @@ package cmd
import ( import (
"fmt" "fmt"
"os"
"path/filepath" "path/filepath"
"github.com/mickael-menu/zk/adapter/fzf" "github.com/mickael-menu/zk/adapter/fzf"
@ -26,17 +25,12 @@ func (cmd *Edit) Run(container *Container) error {
return err return err
} }
wd, err := os.Getwd()
if err != nil {
return err
}
opts, err := NewFinderOpts(zk, cmd.Filtering, cmd.Sorting) opts, err := NewFinderOpts(zk, cmd.Filtering, cmd.Sorting)
if err != nil { if err != nil {
return errors.Wrapf(err, "incorrect criteria") return errors.Wrapf(err, "incorrect criteria")
} }
db, _, err := container.Database(zk, false) db, _, err := container.Database(false)
if err != nil { if err != nil {
return err return err
} }
@ -45,10 +39,10 @@ func (cmd *Edit) Run(container *Container) error {
err = db.WithTransaction(func(tx sqlite.Transaction) error { err = db.WithTransaction(func(tx sqlite.Transaction) error {
finder := container.NoteFinder(tx, fzf.NoteFinderOpts{ finder := container.NoteFinder(tx, fzf.NoteFinderOpts{
AlwaysFilter: true, AlwaysFilter: true,
PreviewCmd: zk.Config.Tool.FzfPreview, PreviewCmd: container.Config.Tool.FzfPreview,
NewNoteDir: cmd.newNoteDir(zk), NewNoteDir: cmd.newNoteDir(zk),
BasePath: zk.Path, BasePath: zk.Path,
CurrentPath: wd, CurrentPath: container.WorkingDir,
}) })
notes, err = finder.Find(*opts) notes, err = finder.Find(*opts)
return err return err

@ -15,12 +15,7 @@ func (cmd *Index) Help() string {
} }
func (cmd *Index) Run(container *Container) error { func (cmd *Index) Run(container *Container) error {
zk, err := container.Zk() _, stats, err := container.Database(cmd.Force)
if err != nil {
return err
}
_, stats, err := container.Database(zk, cmd.Force)
if err != nil { if err != nil {
return err return err
} }

@ -39,20 +39,15 @@ func (cmd *List) Run(container *Container) error {
return err return err
} }
db, _, err := container.Database(zk, false) db, _, err := container.Database(false)
if err != nil { if err != nil {
return err return err
} }
wd, err := os.Getwd() templates := container.TemplateLoader(container.Config.Note.Lang)
if err != nil {
return err
}
templates := container.TemplateLoader(zk.Config.Note.Lang)
styler := container.Terminal styler := container.Terminal
format := opt.NewNotEmptyString(cmd.Format) format := opt.NewNotEmptyString(cmd.Format)
formatter, err := note.NewFormatter(zk.Path, wd, format, templates, styler) formatter, err := note.NewFormatter(zk.Path, container.WorkingDir, format, templates, styler)
if err != nil { if err != nil {
return err return err
} }
@ -61,9 +56,9 @@ func (cmd *List) Run(container *Container) error {
err = db.WithTransaction(func(tx sqlite.Transaction) error { err = db.WithTransaction(func(tx sqlite.Transaction) error {
finder := container.NoteFinder(tx, fzf.NoteFinderOpts{ finder := container.NoteFinder(tx, fzf.NoteFinderOpts{
AlwaysFilter: false, AlwaysFilter: false,
PreviewCmd: zk.Config.Tool.FzfPreview, PreviewCmd: container.Config.Tool.FzfPreview,
BasePath: zk.Path, BasePath: zk.Path,
CurrentPath: wd, CurrentPath: container.WorkingDir,
}) })
notes, err = finder.Find(*opts) notes, err = finder.Find(*opts)
return err return err
@ -77,7 +72,7 @@ func (cmd *List) Run(container *Container) error {
count := len(notes) count := len(notes)
if count > 0 { if count > 0 {
err = container.Paginate(cmd.NoPager, zk.Config, func(out io.Writer) error { err = container.Paginate(cmd.NoPager, func(out io.Writer) error {
for i, note := range notes { for i, note := range notes {
if i > 0 { if i > 0 {
fmt.Fprint(out, cmd.Delimiter) fmt.Fprint(out, cmd.Delimiter)

@ -12,7 +12,7 @@ import (
// New adds a new note to the notebook. // New adds a new note to the notebook.
type New struct { type New struct {
Directory string `arg optional type:"path" default:"." help:"Directory in which to create the note."` Directory string `arg optional default:"." help:"Directory in which to create the note."`
Title string `short:t placeholder:TITLE help:"Title of the new note."` Title string `short:t placeholder:TITLE help:"Title of the new note."`
Group string `short:g placeholder:NAME help:"Name of the config group this note belongs to. Takes precedence over the config of the directory."` Group string `short:g placeholder:NAME help:"Name of the config group this note belongs to. Takes precedence over the config of the directory."`
@ -46,7 +46,7 @@ func (cmd *New) Run(container *Container) error {
} }
opts := note.CreateOpts{ opts := note.CreateOpts{
Config: zk.Config, Config: container.Config,
Dir: *dir, Dir: *dir,
Title: opt.NewNotEmptyString(cmd.Title), Title: opt.NewNotEmptyString(cmd.Title),
Content: content, Content: content,

@ -9,6 +9,13 @@ import (
"github.com/mickael-menu/zk/util/paths" "github.com/mickael-menu/zk/util/paths"
) )
// ErrNotebookNotFound is an error returned when a notebook cannot be found at the given path or its parents.
type ErrNotebookNotFound string
func (e ErrNotebookNotFound) Error() string {
return fmt.Sprintf("no notebook found in %s or a parent directory", string(e))
}
const defaultConfig = `# zk configuration file const defaultConfig = `# zk configuration file
# #
# Uncomment the properties you want to customize. # Uncomment the properties you want to customize.
@ -150,7 +157,7 @@ hashtags = true
#hist = "zk list --format path --delimiter0 --quiet $@ | xargs -t -0 git log --patch --" #hist = "zk list --format path --delimiter0 --quiet $@ | xargs -t -0 git log --patch --"
# Edit this configuration file. # Edit this configuration file.
#conf = '$EDITOR "$ZK_PATH/.zk/config.toml"' #conf = '$EDITOR "$ZK_NOTEBOOK_DIR/.zk/config.toml"'
` `
const defaultTemplate = `# {{title}} const defaultTemplate = `# {{title}}
@ -164,6 +171,8 @@ type Zk struct {
Path string Path string
// Global user configuration. // Global user configuration.
Config Config Config Config
// Working directory from which paths are relative.
workingDir string
} }
// Dir represents a directory inside a notebook. // Dir represents a directory inside a notebook.
@ -177,10 +186,10 @@ type Dir struct {
} }
// Open locates a notebook at the given path and parses its configuration. // Open locates a notebook at the given path and parses its configuration.
func Open(path string, parentConfig Config) (*Zk, error) { func Open(originalPath string, parentConfig Config) (*Zk, error) {
wrap := errors.Wrapper("open failed") wrap := errors.Wrapper("open failed")
path, err := filepath.Abs(path) path, err := filepath.Abs(originalPath)
if err != nil { if err != nil {
return nil, wrap(err) return nil, wrap(err)
} }
@ -195,8 +204,9 @@ func Open(path string, parentConfig Config) (*Zk, error) {
} }
return &Zk{ return &Zk{
Path: path, Path: path,
Config: config, Config: config,
workingDir: originalPath,
}, nil }, nil
} }
@ -237,7 +247,7 @@ func locateRoot(path string) (string, error) {
var locate func(string) (string, error) var locate func(string) (string, error)
locate = func(currentPath string) (string, error) { locate = func(currentPath string) (string, error) {
if currentPath == "/" || currentPath == "." { if currentPath == "/" || currentPath == "." {
return "", fmt.Errorf("no notebook found in %v or a parent directory", path) return "", ErrNotebookNotFound(path)
} }
exists, err := paths.DirExists(filepath.Join(currentPath, ".zk")) exists, err := paths.DirExists(filepath.Join(currentPath, ".zk"))
switch { switch {
@ -259,19 +269,20 @@ func (zk *Zk) DBPath() string {
} }
// RelPath returns the path relative to the notebook root to the given path. // RelPath returns the path relative to the notebook root to the given path.
func (zk *Zk) RelPath(absPath string) (string, error) { func (zk *Zk) RelPath(originalPath string) (string, error) {
wrap := errors.Wrapperf("%v: not a valid notebook path", absPath) wrap := errors.Wrapperf("%v: not a valid notebook path", originalPath)
path, err := filepath.Abs(absPath) path, err := zk.absPath(originalPath)
if err != nil { if err != nil {
return path, wrap(err) return path, wrap(err)
} }
path, err = filepath.Rel(zk.Path, path) path, err = filepath.Rel(zk.Path, path)
if err != nil { if err != nil {
return path, wrap(err) return path, wrap(err)
} }
if strings.HasPrefix(path, "..") { if strings.HasPrefix(path, "..") {
return path, fmt.Errorf("%s: path is outside the notebook", absPath) return path, fmt.Errorf("%s: path is outside the notebook", originalPath)
} }
if path == "." { if path == "." {
path = "" path = ""
@ -279,6 +290,23 @@ func (zk *Zk) RelPath(absPath string) (string, error) {
return path, nil return path, nil
} }
// AbsPath makes the given path absolute, using the current working directory
// as reference.
func (zk *Zk) absPath(originalPath string) (string, error) {
var err error
path := originalPath
if !filepath.IsAbs(path) {
path = filepath.Join(zk.workingDir, path)
path, err = filepath.Abs(path)
if err != nil {
return path, err
}
}
return path, nil
}
// RootDir returns the root Dir for this notebook. // RootDir returns the root Dir for this notebook.
func (zk *Zk) RootDir() Dir { func (zk *Zk) RootDir() Dir {
return Dir{ return Dir{
@ -290,7 +318,7 @@ func (zk *Zk) RootDir() Dir {
// DirAt returns a Dir representation of the notebook directory at the given path. // DirAt returns a Dir representation of the notebook directory at the given path.
func (zk *Zk) DirAt(path string, overrides ...ConfigOverrides) (*Dir, error) { func (zk *Zk) DirAt(path string, overrides ...ConfigOverrides) (*Dir, error) {
path, err := filepath.Abs(path) path, err := zk.absPath(path)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "%v: not a valid notebook directory", path) return nil, errors.Wrapf(err, "%v: not a valid notebook directory", path)
} }

@ -20,10 +20,10 @@ An alias can call other aliases but cannot call itself. This enables you to over
edit = "zk edit --interactive $@" edit = "zk edit --interactive $@"
``` ```
When running an alias, the `ZK_PATH` environment variable is set to the absolute path of the current notebook. You can use it to run commands working no matter the location of the working directory. When running an alias, the `ZK_NOTEBOOK_DIR` environment variable is set to the absolute path of the current notebook. You can use it to run commands working no matter the location of the working directory.
```toml ```toml
journal = 'zk new "$ZK_PATH/journal"' journal = 'zk new "$ZK_NOTEBOOK_DIR/journal"'
``` ```
If you need to surround the path with quotes, make sure you use double quotes, otherwise environment variables will not be expanded. If you need to surround the path with quotes, make sure you use double quotes, otherwise environment variables will not be expanded.
@ -70,10 +70,10 @@ recent = "zk edit --sort created- --created-after 'last two weeks' --interactive
### Edit the configuration file ### Edit the configuration file
Here's a concrete example using environment variables, in particular `ZK_PATH`. Note the double quotes around the path. Here's a concrete example using environment variables, in particular `ZK_NOTEBOOK_DIR`. Note the double quotes around the path.
```toml ```toml
conf = '$EDITOR "$ZK_PATH/.zk/config.toml"' conf = '$EDITOR "$ZK_NOTEBOOK_DIR/.zk/config.toml"'
``` ```
### List paths in a command-line friendly fashion ### List paths in a command-line friendly fashion

@ -34,12 +34,12 @@ That is a bit of a mouthful for a command called every day. Would it not be bett
```toml ```toml
[alias] [alias]
daily = 'zk new --no-input "$ZK_PATH/journal/daily"' daily = 'zk new --no-input "$ZK_NOTEBOOK_DIR/journal/daily"'
``` ```
Let's unpack this alias: Let's unpack this alias:
* `zk new` will refuse to overwrite notes. If you already created today's note, it will instead ask you if you wish to edit it. Using `--no-input` skips the prompt and edit the existing note right away. * `zk new` will refuse to overwrite notes. If you already created today's note, it will instead ask you if you wish to edit it. Using `--no-input` skips the prompt and edit the existing note right away.
* `$ZK_PATH` is set to the absolute path of the current [notebook](notebook.md) when running an alias. Using it allows you to run `zk daily` no matter where you are in the notebook folder hierarchy. * `$ZK_NOTEBOOK_DIR` is set to the absolute path of the current [notebook](notebook.md) when running an alias. Using it allows you to run `zk daily` no matter where you are in the notebook folder hierarchy.
* We need to use double quotes around `$ZK_PATH`, otherwise it will not be expanded. * We need to use double quotes around `$ZK_NOTEBOOK_DIR`, otherwise it will not be expanded.

@ -4,7 +4,7 @@ A *notebook* is a directory containing a collection of notes managed by `zk`. No
To create a new notebook, simply run `zk init [<directory>]`. To create a new notebook, simply run `zk init [<directory>]`.
Most `zk` commands are operating "Git-style" on the notebook containing the current working directory (or one of its parents). Most `zk` commands are operating "Git-style" on the notebook containing the current working directory (or one of its parents). However, you can explicitly set which notebook to use with `--notebook-dir` or the `ZK_NOTEBOOK_DIR` environment variable. Setting `ZK_NOTEBOOK_DIR` in your shell configuration (e.g. `~/.profile`) can be used to define a default notebook which `zk` commands will use when the working directory is not in another notebook.
## Anatomy of a notebook ## Anatomy of a notebook

@ -1,10 +1,10 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"strings"
"github.com/alecthomas/kong" "github.com/alecthomas/kong"
"github.com/mickael-menu/zk/cmd" "github.com/mickael-menu/zk/cmd"
@ -23,7 +23,8 @@ var cli struct {
List cmd.List `cmd group:"notes" help:"List notes matching the given criteria."` List cmd.List `cmd group:"notes" help:"List notes matching the given criteria."`
Edit cmd.Edit `cmd group:"notes" help:"Edit notes matching the given criteria."` Edit cmd.Edit `cmd group:"notes" help:"Edit notes matching the given criteria."`
NoInput NoInput `help:"Never prompt or ask for confirmation."` NoInput NoInput `help:"Never prompt or ask for confirmation."`
NotebookDir string `placeholder:"PATH" help:"Run as if zk was started in <PATH> instead of the current working directory."`
ShowHelp ShowHelp `cmd default:"1" hidden:true` ShowHelp ShowHelp `cmd default:"1" hidden:true`
Version kong.VersionFlag `help:"Print zk version." hidden:true` Version kong.VersionFlag `help:"Print zk version." hidden:true`
@ -57,9 +58,14 @@ func main() {
container, err := cmd.NewContainer() container, err := cmd.NewContainer()
fatalIfError(err) fatalIfError(err)
// Open the notebook if there's any.
searchPaths, err := notebookSearchPaths()
fatalIfError(err)
container.OpenNotebook(searchPaths)
// Run the alias or command.
if isAlias, err := runAlias(container, os.Args[1:]); isAlias { if isAlias, err := runAlias(container, os.Args[1:]); isAlias {
fatalIfError(err) fatalIfError(err)
} else { } else {
ctx := kong.Parse(&cli, options(container)...) ctx := kong.Parse(&cli, options(container)...)
err := ctx.Run(container) err := ctx.Run(container)
@ -112,6 +118,10 @@ func runAlias(container *cmd.Container, args []string) (bool, error) {
// Prevent infinite loop if an alias calls itself. // Prevent infinite loop if an alias calls itself.
os.Setenv("ZK_RUNNING_ALIAS", alias) os.Setenv("ZK_RUNNING_ALIAS", alias)
// Move to the provided working directory if it is not the current one,
// before running the alias.
cmdStr = `cd "` + container.WorkingDir + `" && ` + cmdStr
cmd := executil.CommandFromString(cmdStr, args[1:]...) cmd := executil.CommandFromString(cmdStr, args[1:]...)
cmd.Stdin = os.Stdin cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
@ -130,3 +140,59 @@ func runAlias(container *cmd.Container, args []string) (bool, error) {
return false, nil return false, nil
} }
// notebookSearchPaths returns the places where zk will look for a notebook.
// The first successful candidate will be used as the working directory from
// which path arguments are relative from.
//
// By order of precedence:
// 1. --notebook-dir flag
// 2. current working directory
// 3. ZK_NOTEBOOK_DIR environment variable
func notebookSearchPaths() ([]string, error) {
// 1. --notebook-dir flag
notebookDir, err := parseNotebookDirFlag()
if err != nil {
return []string{}, err
}
if notebookDir != "" {
// If --notebook-dir is used, we want to only check there to report errors.
return []string{notebookDir}, nil
}
candidates := []string{}
// 2. current working directory
wd, err := os.Getwd()
if err != nil {
return nil, err
}
candidates = append(candidates, wd)
// 3. ZK_NOTEBOOK_DIR environment variable
if notebookDir, ok := os.LookupEnv("ZK_NOTEBOOK_DIR"); ok {
candidates = append(candidates, notebookDir)
}
return candidates, nil
}
// parseNotebookDir returns the path to the notebook specified with the
// --notebook-dir flag.
//
// We need to parse the --notebook-dir flag before Kong, because we might need
// it to resolve zk command aliases before parsing the CLI.
func parseNotebookDirFlag() (string, error) {
foundFlag := false
for _, arg := range os.Args {
if arg == "--notebook-dir" {
foundFlag = true
} else if foundFlag {
return arg, nil
}
}
if foundFlag {
return "", errors.New("--notebook-dir requires an argument")
}
return "", nil
}

Loading…
Cancel
Save