google-labs-jules[bot] ed9d0799d2
feat: Allow to customize commit messages (#3231)
* feat: Allow to customize commit messages

This change introduces the ability for users to customize the commit
message when performing actions that modify the secret store.

It adds two new flags to the `edit`, `insert`, `generate`, `copy`,
`move`, and `delete` commands:
- `--commit-message` (`-m`): to specify the commit message directly.
- `--interactive-commit` (`-i`): to open an editor for the commit
  message.

The default behavior of using a pre-defined commit message is
preserved.

* fix: Use correct commit message from context

This change fixes a bug where the commit message from the context was
not being used correctly in the `delete` function.

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2025-09-19 09:30:19 +02:00

93 lines
2.3 KiB
Go

package action
import (
"context"
"fmt"
"path/filepath"
"strings"
"github.com/gopasspw/gopass/internal/action/exit"
"github.com/gopasspw/gopass/internal/tree"
"github.com/gopasspw/gopass/pkg/ctxutil"
"github.com/gopasspw/gopass/pkg/termio"
"github.com/urfave/cli/v2"
)
// Copy the contents of a file to another one.
func (s *Action) Copy(c *cli.Context) error {
ctx := ctxutil.WithGlobalFlags(c)
force := c.Bool("force")
if c.Args().Len() != 2 {
return exit.Error(exit.Usage, nil, "Usage: %s cp <FROM> <TO>", s.Name)
}
from := c.Args().Get(0)
to := c.Args().Get(1)
// Check for custom commit message
commitMsg := fmt.Sprintf("Copied %s to %s", from, to)
if c.IsSet("commit-message") {
commitMsg = c.String("commit-message")
}
if c.Bool("interactive-commit") {
commitMsg = ""
}
ctx = ctxutil.WithCommitMessage(ctx, commitMsg)
return s.copy(ctx, from, to, force)
}
func (s *Action) copy(ctx context.Context, from, to string, force bool) error {
if !s.Store.Exists(ctx, from) && !s.Store.IsDir(ctx, from) {
return exit.Error(exit.NotFound, nil, "%s does not exist", from)
}
isSourceDir := s.Store.IsDir(ctx, from)
hasTrailingSlash := strings.HasSuffix(to, "/")
if isSourceDir && hasTrailingSlash {
return s.copyFlattenDir(ctx, from, to, force)
}
return s.copyRegular(ctx, from, to, force)
}
func (s *Action) copyFlattenDir(ctx context.Context, from, to string, force bool) error {
entries, err := s.Store.List(ctx, tree.INF)
if err != nil {
return exit.Error(exit.List, err, "failed to list entries in %q", from)
}
fromPrefix := from
if !strings.HasSuffix(fromPrefix, "/") {
fromPrefix += "/"
}
for _, entry := range entries {
if strings.HasPrefix(entry, fromPrefix) {
toPath := filepath.Join(to, filepath.Base(entry))
if err := s.copyRegular(ctx, entry, toPath, force); err != nil {
return err
}
}
}
return nil
}
func (s *Action) copyRegular(ctx context.Context, from, to string, force bool) error {
if !force {
if s.Store.Exists(ctx, to) && !termio.AskForConfirmation(ctx, fmt.Sprintf("%s already exists. Overwrite it?", to)) {
return exit.Error(exit.Aborted, nil, "not overwriting your current secret")
}
}
if err := s.Store.Copy(ctx, from, to); err != nil {
return exit.Error(exit.IO, err, "failed to copy from %q to %q", from, to)
}
return nil
}