aboutsummaryrefslogtreecommitdiffstats
path: root/cache/repo_cache.go
diff options
context:
space:
mode:
authorMichael Muré <batolettre@gmail.com>2018-08-31 13:18:03 +0200
committerMichael Muré <batolettre@gmail.com>2018-08-31 17:22:10 +0200
commit7397c94d993541b33e555b758ebdb8f61ff33c6c (patch)
treedbdc52bb6efa03791e5ca84bc8695da5103524d2 /cache/repo_cache.go
parent116a94401f0d3fbf79f7e20716b1c7b739e33246 (diff)
downloadgit-bug-7397c94d993541b33e555b758ebdb8f61ff33c6c.tar.gz
make CLI commands use the cache to lock the repo properly
Diffstat (limited to 'cache/repo_cache.go')
-rw-r--r--cache/repo_cache.go100
1 files changed, 99 insertions, 1 deletions
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
+}