fix: lazysecrets list puts the name first, and keeps errors out of fzf

This commit is contained in:
keb committed 2026-10-14 21:24:01 +02:00
1 parent e99462bd21
commit baee1dfe61
3 files changed
+35 -18

No files matched your search

+15 -7
View File
@@ -25,23 +25,31 @@ import (
// listCmd prints every source, vault and secret lazysecrets can find, one
// per line, for fzf, editor pickers and scripts:
//
// lazysecrets list | fzf --ansi --delimiter='\t' --with-nth=3 | cut -f1
// lazysecrets list | fzf --ansi --delimiter='\t' --with-nth=1 | cut -f2
//
// The name is coloured from the theme, with the provider's icon (as in
// the TUI: --icons, LAZYSECRETS_ICONS, ui.icons); the link never is.
// Each line is the name, then the link: the name coloured from the theme,
// with the provider's icon (as in the TUI: --icons, LAZYSECRETS_ICONS,
// ui.icons); the link never is.
//
// What's cached prints at once; what's missing or older than --max-age
// follows as it loads. Lists that can't be read are reported on stderr.
func listCmd(providers []provider.Provider, cachePath string, timeout func(provider.Kind) time.Duration, theme styles.Styles, withIcons bool, args []string, out, errOut io.Writer) error {
// follows as it loads. Lists that can't be read are reported to errOut:
// stderr when the output is on the terminal, nothing when it goes into
// fzf, whose screen they'd draw over; --errors reports them to stderr
// either way.
func listCmd(providers []provider.Provider, cachePath string, timeout func(provider.Kind) time.Duration, theme styles.Styles, withIcons bool, args []string, out, errOut, stderr io.Writer) error {
fs := flag.NewFlagSet("list", flag.ContinueOnError)
cached := fs.Bool("cached", false, "only what's cached, and repos: load nothing from Azure or Databricks")
maxAge := fs.Duration("max-age", 2*time.Minute, "load cached lists older than this again")
format := fs.String("format", "text", "text: link, kind and name, tab-separated; json: an object per line")
format := fs.String("format", "text", "text: name and link, tab-separated; json: an object per line")
color := fs.String("color", "always", "colour the names (text): always or never; fzf needs --ansi")
iconsOn := fs.Bool("icons", withIcons, "put the provider's Nerd Font icon before names (text)")
showErrors := fs.Bool("errors", false, "report lists that can't be read on stderr also when the output goes into a pipe")
if err := fs.Parse(args); err != nil {
return err
}
if *showErrors {
errOut = stderr
}
if fs.NArg() > 0 {
return fmt.Errorf("usage: lazysecrets list [--cached] [--max-age 2m] [--format text|json]")
}
@@ -56,7 +64,7 @@ func listCmd(providers []provider.Provider, cachePath string, timeout func(provi
switch *format {
case "text":
write = func(e listing.Entry) error {
_, err := fmt.Fprintf(out, "%s\t%s\t%s\n", e.Link, e.Kind, nameOf(e, theme, iconSet, *color == "always"))
_, err := fmt.Fprintf(out, "%s\t%s\n", nameOf(e, theme, iconSet, *color == "always"), e.Link)
return err
}
case "json":
+8 -2
View File
@@ -17,6 +17,7 @@ import (
"time"
tea "charm.land/bubbletea/v2"
"golang.org/x/term"
"git.kebcloud.no/keb/lazysecrets"
"git.kebcloud.no/keb/lazysecrets/internal/commands"
@@ -122,7 +123,7 @@ func run() error {
lazysecrets [flags] get <ref> print the value a lazysecrets:// link points at,
for scripts: VAR=$(lazysecrets get <ref>)
lazysecrets [flags] list [--cached] [--max-age 2m] [--format text|json]
[--color always|never] [--icons=false]
[--color always|never] [--icons=false] [--errors]
print every source, vault and secret it can
find, one per line, for fzf and pickers:
what's cached at once, then what loads
@@ -357,7 +358,12 @@ Flags:
if d, err := config.CacheDir(); err == nil && !*demo && config.On(cfg.Storage.Cache) {
cachePath = filepath.Join(d, "cache.json")
}
return listCmd(providers, cachePath, timeoutFor, theme, *icons, listArgs, os.Stdout, os.Stderr)
// Into a pipe (fzf), stderr would draw over what reads it.
errOut := io.Discard
if term.IsTerminal(int(os.Stdout.Fd())) {
errOut = os.Stderr
}
return listCmd(providers, cachePath, timeoutFor, theme, *icons, listArgs, os.Stdout, errOut, os.Stderr)
}
if shareArgs != nil {
// The files from a repo, the key from whichever provider it's
+12 -9
View File
@@ -139,22 +139,24 @@ a newline only on a terminal.
### Listing, for fzf and pickers
`lazysecrets list` prints every source, vault and secret, one per line:
its link, its kind (`source`, `vault`, `secret`, `key`, `certificate`)
and its name, tab-separated. It's what Go to searches, so fzf, or an
editor's picker, can search it instead:
its name (`platform-prod / kv-app-prod / db-password`), then its link,
tab-separated. It's what Go to searches, so fzf, or an editor's picker,
can search it instead, showing the names and acting on the links:
```sh
lazysecrets list | fzf --ansi --delimiter='\t' --with-nth=3 \
--bind 'enter:become(lazysecrets open {1})' \
--bind 'ctrl-y:execute-silent(lazysecrets get {1} | pbcopy)'
lazysecrets list | fzf --ansi --delimiter='\t' --with-nth=1 \
--bind 'enter:become(lazysecrets open {2})' \
--bind 'ctrl-y:execute-silent(lazysecrets get {2} | pbcopy)'
```
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
as it arrives: fzf shows it while you type. `--cached` loads nothing from
Azure or Databricks, for an answer at once. Repos are always read, from
disk. What it loads is cached for the TUI too. Lists it can't read are
reported on stderr; it stops when the reader goes, after a pick in fzf.
disk. What it loads is cached for the TUI too. It stops when the reader
goes, after a pick in fzf. Lists it can't read are reported on stderr when
it prints to the terminal; into a pipe they'd draw over fzf, so only with
`--errors`.
The name is coloured from your [theme](config.md#theme), with the
provider's icon when [icons](config.md#theme) are on (`ui.icons`); fzf
@@ -162,7 +164,8 @@ shows the colours with `--ansi`. `--color never` leaves them out, for
scripts, and `--icons=false` the icons. The link is never coloured.
`--format json` prints an object per line instead, for editor plugins,
without colours or icons:
without colours or icons, with each entry's kind (`source`, `vault`,
`secret`, `key` or `certificate`):
```json
{"link":"lazysecrets://azure/kv-app-prod.vault.azure.net/secrets/db-password","kind":"secret","provider":"azure","path":["platform-prod","kv-app-prod","db-password"],"account":"alice"}