feat: e on the status pane edits the config #55
No files matched your search
+3
-1
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
Optional, at `~/.config/lazysecrets/config.yaml` (or
|
Optional, at `~/.config/lazysecrets/config.yaml` (or
|
||||||
`$XDG_CONFIG_HOME/lazysecrets/config.yaml`, or `--config`). Flags override
|
`$XDG_CONFIG_HOME/lazysecrets/config.yaml`, or `--config`). Flags override
|
||||||
it. Unknown keys are an error, so typos don't go unnoticed.
|
it. Unknown keys are an error, so typos don't go unnoticed. `E` in
|
||||||
|
lazysecrets opens it in your editor (making it if it isn't there yet);
|
||||||
|
changes apply when lazysecrets starts again.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
icons: true # Nerd Font icons (v3+); also --icons=false or LAZYSECRETS_ICONS=0
|
icons: true # Nerd Font icons (v3+); also --icons=false or LAZYSECRETS_ICONS=0
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ match case exactly.
|
|||||||
| `x` | [custom commands](custom-commands.md) |
|
| `x` | [custom commands](custom-commands.md) |
|
||||||
| `L` | sign in again (shown when a login has expired) |
|
| `L` | sign in again (shown when a login has expired) |
|
||||||
| `U` | a newer lazysecrets: its changelog, and update in place (restart to use it); or look for one |
|
| `U` | a newer lazysecrets: its changelog, and update in place (restart to use it); or look for one |
|
||||||
|
| `E` | edit your [config](config.md), like `e` opens a file (made if it isn't there yet; restart to use it) |
|
||||||
|
|
||||||
A revealed value is shown as it reads best: JSON indented, and PEM
|
A revealed value is shown as it reads best: JSON indented, and PEM
|
||||||
certificates as their subject, issuer, dates, names and thumbprint, with
|
certificates as their subject, issuer, dates, names and thumbprint, with
|
||||||
|
|||||||
@@ -965,6 +965,8 @@ func (m *Model) handleKey(msg tea.KeyPressMsg) (cmd tea.Cmd, quit bool) {
|
|||||||
return m.refresh(), false
|
return m.refresh(), false
|
||||||
case key.Matches(msg, k.Update):
|
case key.Matches(msg, k.Update):
|
||||||
return m.update(), false
|
return m.update(), false
|
||||||
|
case key.Matches(msg, k.EditConfig):
|
||||||
|
return m.editConfig(), false
|
||||||
}
|
}
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-3
@@ -3,6 +3,7 @@ package app
|
|||||||
import (
|
import (
|
||||||
"cmp"
|
"cmp"
|
||||||
"errors"
|
"errors"
|
||||||
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -13,6 +14,7 @@ import (
|
|||||||
|
|
||||||
"git.kebcloud.no/keb/lazysecrets/internal/browser"
|
"git.kebcloud.no/keb/lazysecrets/internal/browser"
|
||||||
"git.kebcloud.no/keb/lazysecrets/internal/provider"
|
"git.kebcloud.no/keb/lazysecrets/internal/provider"
|
||||||
|
"git.kebcloud.no/keb/lazysecrets/internal/update"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Opening a repo's file (e): SOPS files with sops edit, which decrypts
|
// Opening a repo's file (e): SOPS files with sops edit, which decrypts
|
||||||
@@ -22,9 +24,10 @@ import (
|
|||||||
// secret, editors that can open at a line start on its key.
|
// secret, editors that can open at a line start on its key.
|
||||||
|
|
||||||
type editDoneMsg struct {
|
type editDoneMsg struct {
|
||||||
c provider.Container
|
c provider.Container
|
||||||
name string
|
name string
|
||||||
err error
|
config bool // the config file: read at startup
|
||||||
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
// sopsUnchanged is sops edit's exit code when the file wasn't changed.
|
// sopsUnchanged is sops edit's exit code when the file wasn't changed.
|
||||||
@@ -93,6 +96,33 @@ func (m *Model) edit() tea.Cmd {
|
|||||||
return m.openInEditor(f, finished)
|
return m.openInEditor(f, finished)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// configHeader starts a config file made to be edited.
|
||||||
|
const configHeader = "# lazysecrets config: " + update.DefaultRepo + "/src/branch/main/docs/config.md\n"
|
||||||
|
|
||||||
|
// editConfig opens the user's config file (the first of them), as e
|
||||||
|
// opens a file, making it if it isn't there yet.
|
||||||
|
func (m *Model) editConfig() tea.Cmd {
|
||||||
|
if len(m.configFiles) == 0 {
|
||||||
|
m.flash = warning("no config file")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
path := m.configFiles[0]
|
||||||
|
if _, err := os.Stat(path); errors.Is(err, fs.ErrNotExist) {
|
||||||
|
err := os.MkdirAll(filepath.Dir(path), 0o755)
|
||||||
|
if err == nil {
|
||||||
|
err = os.WriteFile(path, []byte(configHeader), 0o644)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
m.flash = flash{text: "edit config: " + err.Error(), isErr: true}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
name := filepath.Base(path)
|
||||||
|
return m.openInEditor(provider.File{Path: path}, func(err error) tea.Msg {
|
||||||
|
return editDoneMsg{name: name, config: true, err: err}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// plusLine are editors that open at a line with +N before the file.
|
// plusLine are editors that open at a line with +N before the file.
|
||||||
var plusLine = map[string]bool{
|
var plusLine = map[string]bool{
|
||||||
"vi": true, "vim": true, "nvim": true, "nano": true, "emacs": true, "emacsclient": true,
|
"vi": true, "vim": true, "nvim": true, "nano": true, "emacs": true, "emacsclient": true,
|
||||||
@@ -142,6 +172,10 @@ func (m *Model) handleEditDone(msg editDoneMsg) tea.Cmd {
|
|||||||
m.flash = flash{text: "open " + msg.name + ": " + msg.err.Error(), isErr: true}
|
m.flash = flash{text: "open " + msg.name + ": " + msg.err.Error(), isErr: true}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if msg.config {
|
||||||
|
m.flash = done("closed " + msg.name + ": restart lazysecrets to use it")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
m.flash = done("closed " + msg.name)
|
m.flash = done("closed " + msg.name)
|
||||||
if c, ok := m.containers.Selected(); ok && containerKey(c) == containerKey(msg.c) {
|
if c, ok := m.containers.Selected(); ok && containerKey(c) == containerKey(msg.c) {
|
||||||
m.hideValue()
|
m.hideValue()
|
||||||
|
|||||||
@@ -342,3 +342,43 @@ func TestEditorAtLine(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEditConfig(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
path := filepath.Join(t.TempDir(), "lazysecrets", "config.yaml")
|
||||||
|
var opened []string
|
||||||
|
d := newDriverOpts(t, Options{ConfigFiles: []string{path}, OpenURL: func(u string) error { opened = append(opened, u); return nil }})
|
||||||
|
d.m.getenv = func(string) string { return "" }
|
||||||
|
|
||||||
|
// No editor: the system's default app, on a config made for it.
|
||||||
|
d.keys("E")
|
||||||
|
if !slices.Equal(opened, []string{path}) {
|
||||||
|
t.Errorf("opened %q", opened)
|
||||||
|
}
|
||||||
|
if b, err := os.ReadFile(path); err != nil || !strings.HasPrefix(string(b), "# lazysecrets config") {
|
||||||
|
t.Errorf("config made: %q, %v", b, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inside nvim: in that nvim; an existing config is left as it is.
|
||||||
|
os.WriteFile(path, []byte("icons: false\n"), 0o644)
|
||||||
|
d.m.getenv = func(k string) string { return map[string]string{"NVIM": "/tmp/nvim.sock"}[k] }
|
||||||
|
var nvim []string
|
||||||
|
d.m.nvimOpen = func(server, path string, line int) error {
|
||||||
|
nvim = append(nvim, server+" "+path)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if msg := d.m.editConfig()(); msg != (tea.QuitMsg{}) {
|
||||||
|
t.Errorf("didn't quit for nvim: %v", msg)
|
||||||
|
}
|
||||||
|
if want := []string{"/tmp/nvim.sock " + path}; !slices.Equal(nvim, want) {
|
||||||
|
t.Errorf("nvim got %q", nvim)
|
||||||
|
}
|
||||||
|
if b, _ := os.ReadFile(path); string(b) != "icons: false\n" {
|
||||||
|
t.Errorf("config changed: %q", b)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.send(editDoneMsg{name: "config.yaml", config: true})
|
||||||
|
if !strings.Contains(d.m.flash.text, "restart") {
|
||||||
|
t.Errorf("flash %q", d.m.flash.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -48,7 +48,7 @@ type Map struct {
|
|||||||
SplitLeft, SplitRight, SplitReset key.Binding
|
SplitLeft, SplitRight, SplitReset key.Binding
|
||||||
Taller, Shorter, ZoomIn, ZoomOut key.Binding
|
Taller, Shorter, ZoomIn, ZoomOut key.Binding
|
||||||
Login, Refresh, Help, Quit key.Binding
|
Login, Refresh, Help, Quit key.Binding
|
||||||
Update key.Binding
|
Update, EditConfig key.Binding
|
||||||
// filter
|
// filter
|
||||||
AcceptFilter, ClearFilter key.Binding
|
AcceptFilter, ClearFilter key.Binding
|
||||||
FilterUp, FilterDown key.Binding
|
FilterUp, FilterDown key.Binding
|
||||||
@@ -131,6 +131,7 @@ func (m *Map) Actions() []Action {
|
|||||||
a(Lists, "refresh", &m.Refresh, "refresh", "other", "Reload the focused pane, bypassing the cache."),
|
a(Lists, "refresh", &m.Refresh, "refresh", "other", "Reload the focused pane, bypassing the cache."),
|
||||||
a(Lists, "login", &m.Login, "sign in", "other", "Sign in again (az login or databricks auth login) when a login has expired."),
|
a(Lists, "login", &m.Login, "sign in", "other", "Sign in again (az login or databricks auth login) when a login has expired."),
|
||||||
a(Lists, "update", &m.Update, "update", "other", "Check for a newer lazysecrets: its changelog, and update to it."),
|
a(Lists, "update", &m.Update, "update", "other", "Check for a newer lazysecrets: its changelog, and update to it."),
|
||||||
|
a(Lists, "editConfig", &m.EditConfig, "edit config", "other", "Open your config file like e opens a file: in the nvim lazysecrets runs in, else in $VISUAL or $EDITOR, else in the system's default app. Made if it doesn't exist yet. Changes apply when lazysecrets starts again."),
|
||||||
a(Lists, "help", &m.Help, "help", "other", "Show this list. Also works in Go to while browsing, and in menus, showing their keys first."),
|
a(Lists, "help", &m.Help, "help", "other", "Show this list. Also works in Go to while browsing, and in menus, showing their keys first."),
|
||||||
a(Lists, "quit", &m.Quit, "quit", "other", "Quit lazysecrets. ctrl+c always quits too."),
|
a(Lists, "quit", &m.Quit, "quit", "other", "Quit lazysecrets. ctrl+c always quits too."),
|
||||||
|
|
||||||
@@ -194,7 +195,7 @@ var defaults = map[string][]string{
|
|||||||
"lists.dividerLeft": {"ctrl+left", "<"}, "lists.dividerRight": {"ctrl+right", ">"},
|
"lists.dividerLeft": {"ctrl+left", "<"}, "lists.dividerRight": {"ctrl+right", ">"},
|
||||||
"lists.taller": {"ctrl+down", "}"}, "lists.shorter": {"ctrl+up", "{"},
|
"lists.taller": {"ctrl+down", "}"}, "lists.shorter": {"ctrl+up", "{"},
|
||||||
"lists.zoomIn": {"+"}, "lists.zoomOut": {"_"}, "lists.resetLayout": {"="},
|
"lists.zoomIn": {"+"}, "lists.zoomOut": {"_"}, "lists.resetLayout": {"="},
|
||||||
"lists.refresh": {"r"}, "lists.login": {"L"}, "lists.update": {"U"}, "lists.help": {"?"}, "lists.quit": {"q"},
|
"lists.refresh": {"r"}, "lists.login": {"L"}, "lists.update": {"U"}, "lists.editConfig": {"E"}, "lists.help": {"?"}, "lists.quit": {"q"},
|
||||||
|
|
||||||
"filter.down": {"ctrl+j", "ctrl+n", "down"}, "filter.up": {"ctrl+k", "ctrl+p", "up"},
|
"filter.down": {"ctrl+j", "ctrl+n", "down"}, "filter.up": {"ctrl+k", "ctrl+p", "up"},
|
||||||
"filter.copyValue": {"ctrl+y"}, "filter.reveal": {"ctrl+r"}, "filter.star": {"ctrl+f"},
|
"filter.copyValue": {"ctrl+y"}, "filter.reveal": {"ctrl+r"}, "filter.star": {"ctrl+f"},
|
||||||
|
|||||||
Reference in new issue
Block a user