1package action 2 3import ( 4 "context" 5 "errors" 6 "fmt" 7 "os" 8 9 "github.com/gopasspw/gopass/internal/backend" 10 "github.com/gopasspw/gopass/internal/cui" 11 "github.com/gopasspw/gopass/internal/out" 12 si "github.com/gopasspw/gopass/internal/store" 13 "github.com/gopasspw/gopass/pkg/ctxutil" 14 "github.com/gopasspw/gopass/pkg/debug" 15 "github.com/gopasspw/gopass/pkg/termio" 16 17 "github.com/fatih/color" 18 "github.com/urfave/cli/v2" 19) 20 21// RCSInit initializes a git repo including basic configuration 22func (s *Action) RCSInit(c *cli.Context) error { 23 ctx := ctxutil.WithGlobalFlags(c) 24 store := c.String("store") 25 un := termio.DetectName(c.Context, c) 26 ue := termio.DetectEmail(c.Context, c) 27 ctx = backend.WithStorageBackendString(ctx, c.String("storage")) 28 29 // default to git 30 if !backend.HasStorageBackend(ctx) { 31 ctx = backend.WithStorageBackend(ctx, backend.GitFS) 32 } 33 34 if err := s.rcsInit(ctx, store, un, ue); err != nil { 35 return ExitError(ExitGit, err, "failed to initialize git: %s", err) 36 } 37 return nil 38} 39 40func (s *Action) rcsInit(ctx context.Context, store, un, ue string) error { 41 bn := backend.StorageBackendName(backend.GetStorageBackend(ctx)) 42 userName, userEmail := s.getUserData(ctx, store, un, ue) 43 if err := s.Store.RCSInit(ctx, store, userName, userEmail); err != nil { 44 if errors.Is(err, backend.ErrNotSupported) { 45 debug.Log("RCSInit not supported for backend %s", bn) 46 return nil 47 } 48 if gtv := os.Getenv("GPG_TTY"); gtv == "" { 49 out.Printf(ctx, "Git initialization failed. You may want to try to 'export GPG_TTY=$(tty)' and start over.") 50 } 51 return fmt.Errorf("failed to run git init: %w", err) 52 } 53 54 out.Printf(ctx, "Initialized git repository (%s) for %s / %s...", bn, un, ue) 55 return nil 56} 57 58func (s *Action) getUserData(ctx context.Context, store, name, email string) (string, string) { 59 if name != "" && email != "" { 60 debug.Log("Username: %s, Email: %s (provided)", name, email) 61 return name, email 62 } 63 64 // for convenience, set defaults to user-selected values from available private keys 65 // NB: discarding returned error since this is merely a best-effort look-up for convenience 66 userName, userEmail, _ := cui.AskForGitConfigUser(ctx, s.Store.Crypto(ctx, store)) 67 68 if name == "" { 69 if userName == "" { 70 userName = termio.DetectName(ctx, nil) 71 } 72 var err error 73 name, err = termio.AskForString(ctx, color.CyanString("Please enter a user name for password store git config"), userName) 74 if err != nil { 75 out.Errorf(ctx, "Failed to ask for user input: %s", err) 76 } 77 } 78 if email == "" { 79 if userEmail == "" { 80 userEmail = termio.DetectEmail(ctx, nil) 81 } 82 var err error 83 email, err = termio.AskForString(ctx, color.CyanString("Please enter an email address for password store git config"), userEmail) 84 if err != nil { 85 out.Errorf(ctx, "Failed to ask for user input: %s", err) 86 } 87 } 88 89 debug.Log("Username: %s, Email: %s (detected)", name, email) 90 return name, email 91} 92 93// RCSAddRemote adds a new git remote 94func (s *Action) RCSAddRemote(c *cli.Context) error { 95 ctx := ctxutil.WithGlobalFlags(c) 96 store := c.String("store") 97 remote := c.Args().Get(0) 98 url := c.Args().Get(1) 99 100 if remote == "" || url == "" { 101 return ExitError(ExitUsage, nil, "Usage: %s git remote add <REMOTE> <URL>", s.Name) 102 } 103 104 return s.Store.RCSAddRemote(ctx, store, remote, url) 105} 106 107// RCSRemoveRemote removes a git remote 108func (s *Action) RCSRemoveRemote(c *cli.Context) error { 109 ctx := ctxutil.WithGlobalFlags(c) 110 store := c.String("store") 111 remote := c.Args().Get(0) 112 113 if remote == "" { 114 return ExitError(ExitUsage, nil, "Usage: %s git remote rm <REMOTE>", s.Name) 115 } 116 117 return s.Store.RCSRemoveRemote(ctx, store, remote) 118} 119 120// RCSPull pulls from a git remote 121func (s *Action) RCSPull(c *cli.Context) error { 122 ctx := ctxutil.WithGlobalFlags(c) 123 store := c.String("store") 124 origin := c.Args().Get(0) 125 branch := c.Args().Get(1) 126 127 return s.Store.RCSPull(ctx, store, origin, branch) 128} 129 130// RCSPush pushes to a git remote 131func (s *Action) RCSPush(c *cli.Context) error { 132 ctx := ctxutil.WithGlobalFlags(c) 133 store := c.String("store") 134 origin := c.Args().Get(0) 135 branch := c.Args().Get(1) 136 137 if err := s.Store.RCSPush(ctx, store, origin, branch); err != nil { 138 if errors.Is(err, si.ErrGitNoRemote) { 139 out.Noticef(ctx, "No Git remote. Not pushing") 140 return nil 141 } 142 return ExitError(ExitGit, err, "Failed to push to remote") 143 } 144 out.OKf(ctx, "Pushed to git remote") 145 return nil 146} 147 148// RCSStatus prints the rcs status 149func (s *Action) RCSStatus(c *cli.Context) error { 150 ctx := ctxutil.WithGlobalFlags(c) 151 store := c.String("store") 152 153 return s.Store.RCSStatus(ctx, store) 154} 155