feat: lazysecrets list prints everything lazysecrets can find, for fzf and editor pickers #70
No files matched your search
+22
-10
@@ -90,19 +90,29 @@ func cmpOr(a, b string) string {
|
||||
}
|
||||
|
||||
// refArg parses get's and open's arguments: a reference, and optionally
|
||||
// the account to use when several could (lazysecrets list's column).
|
||||
func refArg(cmd string, args []string) (ref.Ref, error) {
|
||||
// the account to use when several could (lazysecrets list's column) and,
|
||||
// for open, what to do with it rather than asking (--action).
|
||||
func refArg(cmd string, args []string) (r ref.Ref, action string, err error) {
|
||||
fs := flag.NewFlagSet(cmd, flag.ContinueOnError)
|
||||
account := fs.String("account", "", "the account to use when several could, as lazysecrets list prints it")
|
||||
usage := "usage: lazysecrets get [--account <account>] <ref>"
|
||||
act := new(string)
|
||||
if cmd == "open" {
|
||||
act = fs.String("action", "", "do this rather than ask: go (to it), copy (go, and copy its value) or show (go, and show it)")
|
||||
usage = "usage: lazysecrets open [--account <account>] [--action go|copy|show] <ref>"
|
||||
}
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return ref.Ref{}, err
|
||||
return r, "", err
|
||||
}
|
||||
if fs.NArg() != 1 {
|
||||
return ref.Ref{}, fmt.Errorf("usage: lazysecrets %s [--account <account>] <ref>", cmd)
|
||||
return r, "", errors.New(usage)
|
||||
}
|
||||
r, err := ref.Parse(fs.Arg(0))
|
||||
if _, ok := app.OpenActions[*act]; *act != "" && !ok {
|
||||
return r, "", fmt.Errorf("--action: %q is not go, copy or show", *act)
|
||||
}
|
||||
r, err = ref.Parse(fs.Arg(0))
|
||||
r.Account = *account
|
||||
return r, err
|
||||
return r, *act, err
|
||||
}
|
||||
|
||||
// defaultConfigPath is $LAZYSECRETS_CONFIG_FILE, else config.yaml in the
|
||||
@@ -131,7 +141,7 @@ func run() error {
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(flag.CommandLine.Output(), `Usage:
|
||||
lazysecrets [flags] browse secrets
|
||||
lazysecrets [flags] [open [--account <account>]] <ref>
|
||||
lazysecrets [flags] [open [--account <account>] [--action go|copy|show]] <ref>
|
||||
open a lazysecrets:// link (also a share link),
|
||||
Key Vault URL, @Microsoft.KeyVault(...) or
|
||||
{{secrets/scope/key}}
|
||||
@@ -168,6 +178,7 @@ Flags:
|
||||
}
|
||||
|
||||
var open, getRef *ref.Ref
|
||||
var openAction string
|
||||
var shareArgs, listArgs []string
|
||||
printKeys := false
|
||||
switch args := flag.Args(); {
|
||||
@@ -181,7 +192,7 @@ Flags:
|
||||
case len(args) == 1 && args[0] == "keybindings":
|
||||
printKeys = true
|
||||
case len(args) > 0 && args[0] == "get":
|
||||
r, err := refArg("get", args[1:])
|
||||
r, _, err := refArg("get", args[1:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -194,11 +205,11 @@ Flags:
|
||||
return updateCmd(*configPath, args[1:])
|
||||
case len(args) == 0:
|
||||
case args[0] == "open" && len(args) >= 2:
|
||||
r, err := refArg("open", args[1:])
|
||||
r, act, err := refArg("open", args[1:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
open = &r
|
||||
open, openAction = &r, act
|
||||
case len(args) == 1 && looksLikeRef(args[0]):
|
||||
// lazysecrets <link>, like lazysecrets open <link>
|
||||
r, err := ref.Parse(args[0])
|
||||
@@ -464,6 +475,7 @@ Flags:
|
||||
ShareKey: cfg.Sharing.Key,
|
||||
ShareRules: shareRules(cfg),
|
||||
Open: open,
|
||||
OpenAction: openAction,
|
||||
Styles: &theme,
|
||||
DefaultFg: defaultFg,
|
||||
})
|
||||
|
||||
+7
-2
@@ -121,6 +121,9 @@ account) and jumps to it, asking which account to use when several can see
|
||||
it, ending on a secret's details as `enter` on it would. Links say what,
|
||||
not what to do: opening a link to a secret asks whether to just go to it,
|
||||
also copy or show its value, or (`y`) copy its value without going there.
|
||||
`lazysecrets open --action go|copy|show <link>` does one without asking,
|
||||
and `--account <account>` picks the account (as [`list`](#listing-for-fzf-and-pickers)
|
||||
prints it) when several can see it.
|
||||
|
||||
## In scripts
|
||||
|
||||
@@ -153,7 +156,7 @@ instead, showing the names and acting on the links:
|
||||
|
||||
```sh
|
||||
lazysecrets list | fzf --ansi --with-nth='2..-3' --accept-nth=-1 \
|
||||
--bind 'ctrl-o:become(lazysecrets open --account {-2} {-1})' \
|
||||
--bind 'ctrl-o:become(lazysecrets open --action go --account {-2} {-1})' \
|
||||
--bind 'ctrl-y:execute-silent(lazysecrets get --account {-2} {-1} | pbcopy)'
|
||||
```
|
||||
|
||||
@@ -170,7 +173,9 @@ clipboard after `values.clipboardClear`, as one copied in lazysecrets is.
|
||||
`--account` makes `open` and `get` use the account it was listed through,
|
||||
when several of yours can see it: without, `open` asks which, and `get`
|
||||
tries the one that last read it first. The link stays as it is, to copy
|
||||
and share; the account isn't part of it.
|
||||
and share; the account isn't part of it. `--action` tells `open` what to
|
||||
do rather than asking, as it does for a link clicked elsewhere: `go` to
|
||||
it, `copy` its value too, or `show` it.
|
||||
|
||||
What's [cached](config.md#cache) prints at once. Then, like Go to, it loads
|
||||
what's missing or older than `--max-age` (2m), and what's new in it follows
|
||||
|
||||
+22
-6
@@ -133,6 +133,9 @@ type Options struct {
|
||||
NoHistory bool
|
||||
// Open is a reference to open at startup (lazysecrets open <ref>).
|
||||
Open *ref.Ref
|
||||
// OpenAction, if set, is what to do with Open, rather than asking:
|
||||
// go to it, and maybe copy or show its value (OpenActions).
|
||||
OpenAction string
|
||||
// Styles, if set, replaces the default theme (styles.FromTheme), and
|
||||
// DefaultFg the terminal's default text colour.
|
||||
Styles *styles.Styles
|
||||
@@ -283,6 +286,7 @@ type Model struct {
|
||||
prompt *modal // popup that has the keyboard
|
||||
linked []provider.Container // vaults opened from links, this session
|
||||
open *ref.Ref // reference to open at startup
|
||||
openAction string // and what to do with it, or ask
|
||||
|
||||
shareKey string // the config's sharing.key
|
||||
shareRules share.Rules // and sharing.keyRules
|
||||
@@ -447,7 +451,7 @@ func New(providers []provider.Provider, opts Options) Model {
|
||||
}
|
||||
m.defaultFg = opts.DefaultFg
|
||||
m.help = newHelp(m.st)
|
||||
m.open = opts.Open
|
||||
m.open, m.openAction = opts.Open, opts.OpenAction
|
||||
saved := m.store.Layout()
|
||||
m.split, m.heights = saved.Split, saved.Heights
|
||||
|
||||
@@ -521,18 +525,26 @@ func (m Model) Init() tea.Cmd {
|
||||
cmds = append(cmds, m.fetchLatest(false))
|
||||
}
|
||||
if m.open != nil {
|
||||
r := *m.open
|
||||
cmds = append(cmds, func() tea.Msg { return openRefMsg{r} })
|
||||
msg := openRefMsg{ref: *m.open, action: m.openAction}
|
||||
cmds = append(cmds, func() tea.Msg { return msg })
|
||||
}
|
||||
return tea.Batch(cmds...)
|
||||
}
|
||||
|
||||
// openRefMsg starts opening a reference from outside the key handler.
|
||||
type openRefMsg struct{ ref ref.Ref }
|
||||
// openRefMsg starts opening a reference from outside the key handler:
|
||||
// asking what to do with it, or doing action (OpenActions).
|
||||
type openRefMsg struct {
|
||||
ref ref.Ref
|
||||
action string
|
||||
}
|
||||
|
||||
// OpenActions are what lazysecrets open --action takes, the Open link
|
||||
// menu's choices that go there.
|
||||
var OpenActions = map[string]linkAction{"go": actGo, "copy": actCopy, "show": actShow}
|
||||
|
||||
// OpenLink is a message offering r in the Open link popup, for links
|
||||
// handed over by the link handler (Program.Send).
|
||||
func OpenLink(r ref.Ref) tea.Msg { return openRefMsg{r} }
|
||||
func OpenLink(r ref.Ref) tea.Msg { return openRefMsg{ref: r} }
|
||||
|
||||
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
var cmds []tea.Cmd
|
||||
@@ -589,7 +601,11 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
|
||||
case openRefMsg:
|
||||
m.deferHandlerSetup() // the link first
|
||||
if act, ok := OpenActions[msg.action]; ok && !msg.ref.IsShare() {
|
||||
cmds = append(cmds, m.openRef(msg.ref, act))
|
||||
} else {
|
||||
cmds = append(cmds, m.offerRef("Open link", msg.ref, false))
|
||||
}
|
||||
|
||||
case resolvedMsg:
|
||||
m.handleResolved(msg)
|
||||
|
||||
@@ -206,6 +206,19 @@ func TestOpenPromptAndStartupOpen(t *testing.T) {
|
||||
if _, _, s := d2.selected(); s != "api-key" {
|
||||
t.Errorf("open at startup: selected %q", s)
|
||||
}
|
||||
|
||||
// --action: no question, it does that.
|
||||
d3 := newDriverOpts(t, Options{Open: &r, OpenAction: "go"})
|
||||
if d3.m.prompt != nil {
|
||||
t.Fatalf("--action go asked: %q", d3.m.prompt.title)
|
||||
}
|
||||
if _, _, s := d3.selected(); s != "api-key" {
|
||||
t.Errorf("--action go: selected %q", s)
|
||||
}
|
||||
d4 := newDriverOpts(t, Options{Open: &r, OpenAction: "copy"})
|
||||
if _, _, s := d4.selected(); s != "api-key" || d4.clip().content == "" {
|
||||
t.Errorf("--action copy: selected %q, copied %q", s, d4.clip().content)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShareLinks(t *testing.T) {
|
||||
|
||||
Reference in new issue
Block a user