diff options
author | Michael Muré <batolettre@gmail.com> | 2018-08-31 13:18:03 +0200 |
---|---|---|
committer | Michael Muré <batolettre@gmail.com> | 2018-08-31 17:22:10 +0200 |
commit | 7397c94d993541b33e555b758ebdb8f61ff33c6c (patch) | |
tree | dbdc52bb6efa03791e5ca84bc8695da5103524d2 | |
parent | 116a94401f0d3fbf79f7e20716b1c7b739e33246 (diff) | |
download | git-bug-7397c94d993541b33e555b758ebdb8f61ff33c6c.tar.gz |
make CLI commands use the cache to lock the repo properly
-rw-r--r-- | bug/bug_actions.go | 1 | ||||
-rw-r--r-- | cache/bug_cache.go | 10 | ||||
-rw-r--r-- | cache/cache.go | 105 | ||||
-rw-r--r-- | cache/repo_cache.go | 100 | ||||
-rw-r--r-- | commands/close.go | 17 | ||||
-rw-r--r-- | commands/comment.go | 19 | ||||
-rw-r--r-- | commands/label.go | 21 | ||||
-rw-r--r-- | commands/ls.go | 7 | ||||
-rw-r--r-- | commands/new.go | 26 | ||||
-rw-r--r-- | commands/open.go | 17 | ||||
-rw-r--r-- | commands/pull.go | 10 | ||||
-rw-r--r-- | commands/push.go | 10 | ||||
-rw-r--r-- | commands/root.go | 13 | ||||
-rw-r--r-- | commands/show.go | 12 | ||||
-rw-r--r-- | commands/termui.go | 9 | ||||
-rw-r--r-- | graphql/resolvers/mutation.go | 2 | ||||
-rw-r--r-- | termui/show_bug.go | 4 | ||||
-rw-r--r-- | termui/termui.go | 17 |
18 files changed, 208 insertions, 192 deletions
diff --git a/bug/bug_actions.go b/bug/bug_actions.go index 784397b0..31e9c07d 100644 --- a/bug/bug_actions.go +++ b/bug/bug_actions.go @@ -25,6 +25,7 @@ func Push(repo repository.Repo, remote string) (string, error) { return repo.PushRefs(remote, bugsRefPattern+"*") } +// TODO: return a chan of changes for the cache to be updated properly func Pull(repo repository.Repo, out io.Writer, remote string) error { if out == nil { out = ioutil.Discard diff --git a/cache/bug_cache.go b/cache/bug_cache.go index 7df76efe..c9e1f2f5 100644 --- a/cache/bug_cache.go +++ b/cache/bug_cache.go @@ -1,6 +1,8 @@ package cache import ( + "io" + "github.com/MichaelMure/git-bug/bug" "github.com/MichaelMure/git-bug/bug/operations" "github.com/MichaelMure/git-bug/util" @@ -22,6 +24,10 @@ func (c *BugCache) Snapshot() *bug.Snapshot { return c.bug.Snapshot() } +func (c *BugCache) HumanId() string { + return c.bug.HumanId() +} + func (c *BugCache) notifyUpdated() error { return c.repoCache.bugUpdated(c.bug.Id()) } @@ -45,13 +51,13 @@ func (c *BugCache) AddCommentWithFiles(message string, files []util.Hash) error return c.notifyUpdated() } -func (c *BugCache) ChangeLabels(added []string, removed []string) error { +func (c *BugCache) ChangeLabels(out io.Writer, added []string, removed []string) error { author, err := bug.GetUser(c.repoCache.repo) if err != nil { return err } - err = operations.ChangeLabels(nil, c.bug, author, added, removed) + err = operations.ChangeLabels(out, c.bug, author, added, removed) if err != nil { return err } diff --git a/cache/cache.go b/cache/cache.go index 6dbafba1..751467b2 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -2,14 +2,8 @@ package cache import ( "fmt" - "io" - "io/ioutil" - "os" - "path" - "strconv" "github.com/MichaelMure/git-bug/repository" - "github.com/MichaelMure/git-bug/util" ) const lockfile = "lock" @@ -27,11 +21,6 @@ func NewCache() RootCache { // RegisterRepository register a named repository. Use this for multi-repo setup func (c *RootCache) RegisterRepository(ref string, repo repository.Repo) error { - err := c.lockRepository(repo) - if err != nil { - return err - } - r, err := NewRepoCache(repo) if err != nil { return err @@ -43,11 +32,6 @@ func (c *RootCache) RegisterRepository(ref string, repo repository.Repo) error { // RegisterDefaultRepository register a unnamed repository. Use this for mono-repo setup func (c *RootCache) RegisterDefaultRepository(repo repository.Repo) error { - err := c.lockRepository(repo) - if err != nil { - return err - } - r, err := NewRepoCache(repo) if err != nil { return err @@ -57,28 +41,6 @@ func (c *RootCache) RegisterDefaultRepository(repo repository.Repo) error { return nil } -func (c *RootCache) lockRepository(repo repository.Repo) error { - lockPath := repoLockFilePath(repo) - - err := RepoIsAvailable(repo) - if err != nil { - return err - } - - f, err := os.Create(lockPath) - if err != nil { - return err - } - - pid := fmt.Sprintf("%d", os.Getpid()) - _, err = f.WriteString(pid) - if err != nil { - return err - } - - return f.Close() -} - // ResolveRepo retrieve a repository by name func (c *RootCache) DefaultRepo() (*RepoCache, error) { if len(c.repos) != 1 { @@ -104,75 +66,10 @@ func (c *RootCache) ResolveRepo(ref string) (*RepoCache, error) { // Close will do anything that is needed to close the cache properly func (c *RootCache) Close() error { for _, cachedRepo := range c.repos { - lockPath := repoLockFilePath(cachedRepo.repo) - err := os.Remove(lockPath) - if err != nil { - return err - } - } - return nil -} - -// RepoIsAvailable check is the given repository is locked by a Cache. -// Note: this is a smart function that will cleanup the lock file if the -// corresponding process is not there anymore. -// If no error is returned, the repo is free to edit. -func RepoIsAvailable(repo repository.Repo) error { - lockPath := repoLockFilePath(repo) - - // Todo: this leave way for a racey access to the repo between the test - // if the file exist and the actual write. It's probably not a problem in - // practice because using a repository will be done from user interaction - // or in a context where a single instance of git-bug is already guaranteed - // (say, a server with the web UI running). But still, that might be nice to - // have a mutex or something to guard that. - - // Todo: this will fail if somehow the filesystem is shared with another - // computer. Should add a configuration that prevent the cleaning of the - // lock file - - f, err := os.Open(lockPath) - - if err != nil && !os.IsNotExist(err) { - return err - } - - if err == nil { - // lock file already exist - buf, err := ioutil.ReadAll(io.LimitReader(f, 10)) - if err != nil { - return err - } - if len(buf) == 10 { - return fmt.Errorf("The lock file should be < 10 bytes") - } - - pid, err := strconv.Atoi(string(buf)) - if err != nil { - return err - } - - if util.ProcessIsRunning(pid) { - return fmt.Errorf("The repository you want to access is already locked by the process pid %d", pid) - } - - // The lock file is just laying there after a crash, clean it - - fmt.Println("A lock file is present but the corresponding process is not, removing it.") - err = f.Close() - if err != nil { - return err - } - - os.Remove(lockPath) + err := cachedRepo.Close() if err != nil { return err } } - return nil } - -func repoLockFilePath(repo repository.Repo) string { - return path.Join(repo.GetPath(), ".git", "git-bug", lockfile) -} diff --git a/cache/repo_cache.go b/cache/repo_cache.go index c73dbe9f..3d98806c 100644 --- a/cache/repo_cache.go +++ b/cache/repo_cache.go @@ -5,8 +5,10 @@ import ( "encoding/gob" "fmt" "io" + "io/ioutil" "os" "path" + "strconv" "strings" "github.com/MichaelMure/git-bug/bug" @@ -27,8 +29,12 @@ func NewRepoCache(r repository.Repo) (*RepoCache, error) { bugs: make(map[string]*BugCache), } - err := c.loadExcerpts() + err := c.lock() + if err != nil { + return &RepoCache{}, err + } + err = c.loadExcerpts() if err == nil { return c, nil } @@ -42,6 +48,33 @@ func (c *RepoCache) Repository() repository.Repo { return c.repo } +func (c *RepoCache) lock() error { + lockPath := repoLockFilePath(c.repo) + + err := repoIsAvailable(c.repo) + if err != nil { + return err + } + + f, err := os.Create(lockPath) + if err != nil { + return err + } + + pid := fmt.Sprintf("%d", os.Getpid()) + _, err = f.WriteString(pid) + if err != nil { + return err + } + + return f.Close() +} + +func (c *RepoCache) Close() error { + lockPath := repoLockFilePath(c.repo) + return os.Remove(lockPath) +} + // bugUpdated is a callback to trigger when the excerpt of a bug changed, // that is each time a bug is updated func (c *RepoCache) bugUpdated(id string) error { @@ -217,3 +250,68 @@ func (c *RepoCache) Pull(remote string, out io.Writer) error { func (c *RepoCache) Push(remote string) (string, error) { return bug.Push(c.repo, remote) } + +func repoLockFilePath(repo repository.Repo) string { + return path.Join(repo.GetPath(), ".git", "git-bug", lockfile) +} + +// repoIsAvailable check is the given repository is locked by a Cache. +// Note: this is a smart function that will cleanup the lock file if the +// corresponding process is not there anymore. +// If no error is returned, the repo is free to edit. +// @Deprecated +func repoIsAvailable(repo repository.Repo) error { + lockPath := repoLockFilePath(repo) + + // Todo: this leave way for a racey access to the repo between the test + // if the file exist and the actual write. It's probably not a problem in + // practice because using a repository will be done from user interaction + // or in a context where a single instance of git-bug is already guaranteed + // (say, a server with the web UI running). But still, that might be nice to + // have a mutex or something to guard that. + + // Todo: this will fail if somehow the filesystem is shared with another + // computer. Should add a configuration that prevent the cleaning of the + // lock file + + f, err := os.Open(lockPath) + + if err != nil && !os.IsNotExist(err) { + return err + } + + if err == nil { + // lock file already exist + buf, err := ioutil.ReadAll(io.LimitReader(f, 10)) + if err != nil { + return err + } + if len(buf) == 10 { + return fmt.Errorf("The lock file should be < 10 bytes") + } + + pid, err := strconv.Atoi(string(buf)) + if err != nil { + return err + } + + if util.ProcessIsRunning(pid) { + return fmt.Errorf("The repository you want to access is already locked by the process pid %d", pid) + } + + // The lock file is just laying there after a crash, clean it + + fmt.Println("A lock file is present but the corresponding process is not, removing it.") + err = f.Close() + if err != nil { + return err + } + + os.Remove(lockPath) + if err != nil { + return err + } + } + + return nil +} diff --git a/commands/close.go b/commands/close.go index 10420a4e..890702dd 100644 --- a/commands/close.go +++ b/commands/close.go @@ -3,8 +3,7 @@ package commands import ( "errors" - "github.com/MichaelMure/git-bug/bug" - "github.com/MichaelMure/git-bug/bug/operations" + "github.com/MichaelMure/git-bug/cache" "github.com/spf13/cobra" ) @@ -17,21 +16,25 @@ func runCloseBug(cmd *cobra.Command, args []string) error { return errors.New("You must provide a bug id") } + backend, err := cache.NewRepoCache(repo) + if err != nil { + return err + } + defer backend.Close() + prefix := args[0] - b, err := bug.FindLocalBug(repo, prefix) + b, err := backend.ResolveBugPrefix(prefix) if err != nil { return err } - author, err := bug.GetUser(repo) + err = b.Close() if err != nil { return err } - operations.Close(b, author) - - return b.Commit(repo) + return b.Commit() } var closeCmd = &cobra.Command{ diff --git a/commands/comment.go b/commands/comment.go index 97aa18aa..0f45c18d 100644 --- a/commands/comment.go +++ b/commands/comment.go @@ -4,8 +4,7 @@ import ( "errors" "fmt" - "github.com/MichaelMure/git-bug/bug" - "github.com/MichaelMure/git-bug/bug/operations" + "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/input" "github.com/spf13/cobra" ) @@ -26,6 +25,12 @@ func runComment(cmd *cobra.Command, args []string) error { return errors.New("You must provide a bug id") } + backend, err := cache.NewRepoCache(repo) + if err != nil { + return err + } + defer backend.Close() + prefix := args[0] if commentMessageFile != "" && commentMessage == "" { @@ -36,7 +41,7 @@ func runComment(cmd *cobra.Command, args []string) error { } if commentMessage == "" { - commentMessage, err = input.BugCommentEditorInput(repo) + commentMessage, err = input.BugCommentEditorInput(backend.Repository()) if err == input.ErrEmptyMessage { fmt.Println("Empty message, aborting.") return nil @@ -46,19 +51,17 @@ func runComment(cmd *cobra.Command, args []string) error { } } - author, err := bug.GetUser(repo) + b, err := backend.ResolveBugPrefix(prefix) if err != nil { return err } - b, err := bug.FindLocalBug(repo, prefix) + err = b.AddComment(commentMessage) if err != nil { return err } - operations.Comment(b, author, commentMessage) - - return b.Commit(repo) + return b.Commit() } var commentCmd = &cobra.Command{ diff --git a/commands/label.go b/commands/label.go index 6c729ff5..b545852f 100644 --- a/commands/label.go +++ b/commands/label.go @@ -4,8 +4,7 @@ import ( "errors" "os" - "github.com/MichaelMure/git-bug/bug" - "github.com/MichaelMure/git-bug/bug/operations" + "github.com/MichaelMure/git-bug/cache" "github.com/spf13/cobra" ) @@ -20,6 +19,12 @@ func runLabel(cmd *cobra.Command, args []string) error { return errors.New("You must provide a label") } + backend, err := cache.NewRepoCache(repo) + if err != nil { + return err + } + defer backend.Close() + prefix := args[0] var add, remove []string @@ -30,23 +35,17 @@ func runLabel(cmd *cobra.Command, args []string) error { add = args[1:] } - b, err := bug.FindLocalBug(repo, prefix) + b, err := backend.ResolveBugPrefix(prefix) if err != nil { return err } - author, err := bug.GetUser(repo) - if err != nil { - return err - } - - err = operations.ChangeLabels(os.Stdout, b, author, add, remove) - + err = b.ChangeLabels(os.Stdout, add, remove) if err != nil { return err } - return b.Commit(repo) + return b.Commit() } var labelCmd = &cobra.Command{ diff --git a/commands/ls.go b/commands/ls.go index 30aa7bdc..533f3d1e 100644 --- a/commands/ls.go +++ b/commands/ls.go @@ -9,6 +9,13 @@ import ( ) func runLsBug(cmd *cobra.Command, args []string) error { + //backend, err := cache.NewRepoCache(repo) + //if err != nil { + // return err + //} + + // Todo: read bugs from backend + bugs := bug.ReadAllLocalBugs(repo) for b := range bugs { diff --git a/commands/new.go b/commands/new.go index bd0966e5..ce6479f1 100644 --- a/commands/new.go +++ b/commands/new.go @@ -3,8 +3,7 @@ package commands import ( "fmt" - "github.com/MichaelMure/git-bug/bug" - "github.com/MichaelMure/git-bug/bug/operations" + "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/input" "github.com/spf13/cobra" ) @@ -25,8 +24,14 @@ func runNewBug(cmd *cobra.Command, args []string) error { } } + backend, err := cache.NewRepoCache(repo) + if err != nil { + return err + } + defer backend.Close() + if newMessage == "" || newTitle == "" { - newTitle, newMessage, err = input.BugCreateEditorInput(repo, newTitle, newMessage) + newTitle, newMessage, err = input.BugCreateEditorInput(backend.Repository(), newTitle, newMessage) if err == input.ErrEmptyTitle { fmt.Println("Empty title, aborting.") @@ -37,23 +42,12 @@ func runNewBug(cmd *cobra.Command, args []string) error { } } - author, err := bug.GetUser(repo) - if err != nil { - return err - } - - newBug, err := operations.Create(author, newTitle, newMessage) - if err != nil { - return err - } - - err = newBug.Commit(repo) - + b, err := backend.NewBug(newTitle, newMessage) if err != nil { return err } - fmt.Printf("%s created\n", newBug.HumanId()) + fmt.Printf("%s created\n", b.HumanId()) return nil } diff --git a/commands/open.go b/commands/open.go index 8a5ede3d..db6a5909 100644 --- a/commands/open.go +++ b/commands/open.go @@ -3,8 +3,7 @@ package commands import ( "errors" - "github.com/MichaelMure/git-bug/bug" - "github.com/MichaelMure/git-bug/bug/operations" + "github.com/MichaelMure/git-bug/cache" "github.com/spf13/cobra" ) @@ -17,21 +16,25 @@ func runOpenBug(cmd *cobra.Command, args []string) error { return errors.New("You must provide a bug id") } + backend, err := cache.NewRepoCache(repo) + if err != nil { + return err + } + defer backend.Close() + prefix := args[0] - b, err := bug.FindLocalBug(repo, prefix) + b, err := backend.ResolveBugPrefix(prefix) if err != nil { return err } - author, err := bug.GetUser(repo) + err = b.Open() if err != nil { return err } - operations.Open(b, author) - - return b.Commit(repo) + return b.Commit() } var openCmd = &cobra.Command{ diff --git a/commands/pull.go b/commands/pull.go index ff07912a..e9f0ad3e 100644 --- a/commands/pull.go +++ b/commands/pull.go @@ -4,7 +4,7 @@ import ( "errors" "os" - "github.com/MichaelMure/git-bug/bug" + "github.com/MichaelMure/git-bug/cache" "github.com/spf13/cobra" ) @@ -18,7 +18,13 @@ func runPull(cmd *cobra.Command, args []string) error { remote = args[0] } - return bug.Pull(repo, os.Stdout, remote) + backend, err := cache.NewRepoCache(repo) + if err != nil { + return err + } + defer backend.Close() + + return backend.Pull(remote, os.Stdout) } // showCmd defines the "push" subcommand. diff --git a/commands/push.go b/commands/push.go index 86c37b46..06a4044b 100644 --- a/commands/push.go +++ b/commands/push.go @@ -4,7 +4,7 @@ import ( "errors" "fmt" - "github.com/MichaelMure/git-bug/bug" + "github.com/MichaelMure/git-bug/cache" "github.com/spf13/cobra" ) @@ -18,7 +18,13 @@ func runPush(cmd *cobra.Command, args []string) error { remote = args[0] } - stdout, err := bug.Push(repo, remote) + backend, err := cache.NewRepoCache(repo) + if err != nil { + return err + } + defer backend.Close() + + stdout, err := backend.Push(remote) if err != nil { return err } diff --git a/commands/root.go b/commands/root.go index 9435ce64..62351055 100644 --- a/commands/root.go +++ b/commands/root.go @@ -5,15 +5,10 @@ import ( "os" "github.com/MichaelMure/git-bug/bug" - "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/repository" "github.com/spf13/cobra" ) -// Will display "git bug" -// \u00A0 is a non-breaking space -// It's used to avoid cobra to split the Use string at the first space to get the root command name -//const rootCommandName = "git\u00A0bug" const rootCommandName = "git-bug" // package scoped var to hold the repo after the PreRun execution @@ -69,13 +64,5 @@ func loadRepo(cmd *cobra.Command, args []string) error { return err } - // Prevent the command from running when the cache has locked the repo - // Todo: make it more fine-grained at first - // Todo: make the running cache available for other processes - err = cache.RepoIsAvailable(repo) - if err != nil { - return err - } - return nil } diff --git a/commands/show.go b/commands/show.go index fe7581a7..71ec1b57 100644 --- a/commands/show.go +++ b/commands/show.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - "github.com/MichaelMure/git-bug/bug" + "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/util" "github.com/spf13/cobra" ) @@ -19,14 +19,20 @@ func runShowBug(cmd *cobra.Command, args []string) error { return errors.New("You must provide a bug id") } + backend, err := cache.NewRepoCache(repo) + if err != nil { + return err + } + defer backend.Close() + prefix := args[0] - b, err := bug.FindLocalBug(repo, prefix) + b, err := backend.ResolveBugPrefix(prefix) if err != nil { return err } - snapshot := b.Compile() + snapshot := b.Snapshot() if len(snapshot.Comments) == 0 { return errors.New("Invalid bug: no comment") diff --git a/commands/termui.go b/commands/termui.go index 460770a6..5d600710 100644 --- a/commands/termui.go +++ b/commands/termui.go @@ -1,12 +1,19 @@ package commands import ( + "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/termui" "github.com/spf13/cobra" ) func runTermUI(cmd *cobra.Command, args []string) error { - return termui.Run(repo) + backend, err := cache.NewRepoCache(repo) + if err != nil { + return err + } + defer backend.Close() + + return termui.Run(backend) } var termUICmd = &cobra.Command{ diff --git a/graphql/resolvers/mutation.go b/graphql/resolvers/mutation.go index 9c5a589d..674c1775 100644 --- a/graphql/resolvers/mutation.go +++ b/graphql/resolvers/mutation.go @@ -89,7 +89,7 @@ func (r mutationResolver) ChangeLabels(ctx context.Context, repoRef *string, pre return bug.Snapshot{}, err } - err = b.ChangeLabels(added, removed) + err = b.ChangeLabels(nil, added, removed) if err != nil { return bug.Snapshot{}, err } diff --git a/termui/show_bug.go b/termui/show_bug.go index 48592fc4..84337c8b 100644 --- a/termui/show_bug.go +++ b/termui/show_bug.go @@ -599,7 +599,7 @@ func (sb *showBug) addLabel(g *gocui.Gui, v *gocui.View) error { return r == ' ' || r == ',' }) - err := sb.bug.ChangeLabels(trimLabels(labels), nil) + err := sb.bug.ChangeLabels(nil, trimLabels(labels), nil) if err != nil { ui.msgPopup.Activate(msgPopupErrorTitle, err.Error()) } @@ -622,7 +622,7 @@ func (sb *showBug) removeLabel(g *gocui.Gui, v *gocui.View) error { return r == ' ' || r == ',' }) - err := sb.bug.ChangeLabels(nil, trimLabels(labels)) + err := sb.bug.ChangeLabels(nil, nil, trimLabels(labels)) if err != nil { ui.msgPopup.Activate(msgPopupErrorTitle, err.Error()) } diff --git a/termui/termui.go b/termui/termui.go index 91e92f00..581db670 100644 --- a/termui/termui.go +++ b/termui/termui.go @@ -3,7 +3,6 @@ package termui import ( "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/input" - "github.com/MichaelMure/git-bug/repository" "github.com/jroimartin/gocui" "github.com/pkg/errors" ) @@ -42,18 +41,12 @@ type window interface { } // Run will launch the termUI in the terminal -func Run(repo repository.Repo) error { - c, err := cache.NewRepoCache(repo) - - if err != nil { - return err - } - +func Run(cache *cache.RepoCache) error { ui = &termUI{ gError: make(chan error, 1), - cache: c, - bugTable: newBugTable(c), - showBug: newShowBug(c), + cache: cache, + bugTable: newBugTable(cache), + showBug: newShowBug(cache), msgPopup: newMsgPopup(), inputPopup: newInputPopup(), } @@ -62,7 +55,7 @@ func Run(repo repository.Repo) error { initGui(nil) - err = <-ui.gError + err := <-ui.gError if err != nil && err != gocui.ErrQuit { return err |