commit 2939d55749ec4f5a2708e5880406abf8387be6c2
parent bfce88a82fc81a85dfcee77233a8d82f284d74b3
Author: Sean Enck <sean@ttypty.com>
Date: Wed, 14 Sep 2022 19:43:37 -0400
git repo integration
Diffstat:
5 files changed, 75 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
@@ -95,6 +95,14 @@ Setup the `.gitattributes` for the repository to include
*.lb diff=lb
```
+The `insert` and `rm` actions will attempt to perform a git operation (if the
+lockbox directory has a `.git/` directory), this
+functionality can be disabled via
+
+```
+export LOCKBOX_GIT="no"
+```
+
## build
Requires `make`
diff --git a/cmd/main.go b/cmd/main.go
@@ -96,7 +96,8 @@ func main() {
die("too many arguments", errors.New("insert can only perform one operation"))
}
isPipe := inputs.IsInputFromPipe()
- entry := getEntry(store.NewFileSystemStore(), args, idx)
+ s := store.NewFileSystemStore()
+ entry := getEntry(s, args, idx)
if store.PathExists(entry) {
if !isPipe {
if !confirm("overwrite existing") {
@@ -118,10 +119,14 @@ func main() {
if err := encrypt.ToFile(entry, password); err != nil {
die("unable to encrypt object", err)
}
+ if err := s.GitCommit(entry); err != nil {
+ die("failed to git commit changed", err)
+ }
fmt.Println("")
hooks.Run(hooks.Insert, hooks.PostStep)
case "rm":
- entry := getEntry(store.NewFileSystemStore(), args, 2)
+ s := store.NewFileSystemStore()
+ entry := getEntry(s, args, 2)
if !store.PathExists(entry) {
die("does not exists", errors.New("can not delete unknown entry"))
}
@@ -129,6 +134,9 @@ func main() {
if err := os.Remove(entry); err != nil {
die("unable to remove entry", err)
}
+ if err := s.GitRemove(entry); err != nil {
+ die("failed to git remove", err)
+ }
hooks.Run(hooks.Remove, hooks.PostStep)
}
case "show", "clip", "dump":
diff --git a/internal/inputs/env.go b/internal/inputs/env.go
@@ -12,6 +12,8 @@ const (
noClipEnv = prefixKey + "NOCLIP"
noColorEnv = prefixKey + "NOCOLOR"
interactiveEnv = prefixKey + "INTERACTIVE"
+ gitEnabledEnv = prefixKey + "GIT"
+ gitQuietEnv = gitEnabledEnv + "_QUIET"
// TotpEnv allows for overriding of the special name for totp entries.
TotpEnv = prefixKey + "TOTP"
// KeyModeEnv indicates what the KEY value is (e.g. command, plaintext).
@@ -58,6 +60,16 @@ func IsNoClipEnabled() (bool, error) {
return isYesNoEnv(false, noClipEnv)
}
+// IsGitQuiet indicates if git operations should be 'quiet' (no stdout/stderr)
+func IsGitQuiet() (bool, error) {
+ return isYesNoEnv(true, gitQuietEnv)
+}
+
+// IsGitEnabled indicates if the filesystem store is a git repo
+func IsGitEnabled() (bool, error) {
+ return isYesNoEnv(true, gitEnabledEnv)
+}
+
// IsNoColorEnabled indicates if the flag is set to disable color.
func IsNoColorEnabled() (bool, error) {
return isYesNoEnv(false, noColorEnv)
diff --git a/internal/store/filesystem.go b/internal/store/filesystem.go
@@ -3,8 +3,10 @@ package store
import (
"errors"
+ "fmt"
"io/fs"
"os"
+ "os/exec"
"path/filepath"
"sort"
"strings"
@@ -107,3 +109,45 @@ func PathExists(path string) bool {
}
return true
}
+
+// GitCommit is for adding/changing entities
+func (s FileSystem) GitCommit(entry string) error {
+ return s.gitAction("add", entry)
+}
+
+// GitRemove is for removing entities
+func (s FileSystem) GitRemove(entry string) error {
+ return s.gitAction("rm", entry)
+}
+
+func (s FileSystem) gitAction(action, entry string) error {
+ ok, err := inputs.IsGitEnabled()
+ if err != nil {
+ return err
+ }
+ if !ok {
+ return nil
+ }
+ if !PathExists(filepath.Join(s.path, ".git")) {
+ return nil
+ }
+ if err := s.gitRun(action, entry); err != nil {
+ return err
+ }
+ return s.gitRun("commit", "-m", fmt.Sprintf("lb %s: %s", action, entry))
+}
+
+func (s FileSystem) gitRun(args ...string) error {
+ arguments := []string{"-C", s.path}
+ arguments = append(arguments, args...)
+ cmd := exec.Command("git", arguments...)
+ ok, err := inputs.IsGitQuiet()
+ if err != nil {
+ return err
+ }
+ if !ok {
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ }
+ return cmd.Run()
+}
diff --git a/tests/run.sh b/tests/run.sh
@@ -8,6 +8,7 @@ export LOCKBOX_KEY="plaintextkey"
export LOCKBOX_TOTP="totp"
export LOCKBOX_INTERACTIVE="no"
export LOCKBOX_HOOKDIR="$TESTS/hooks"
+export LOCKBOX_GIT="no"
rm -rf $TESTS
mkdir -p $LOCKBOX_STORE