Compare commits
4
Commits
db8ce90ee7
..
v0.1.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87141cb53b
|
||
|
|
e9366faa9b | ||
|
|
4377eb98bf | ||
|
|
2044341b20 |
No files matched your search
@@ -2,6 +2,13 @@
|
||||
|
||||
Every release of lazysecrets, newest first.
|
||||
|
||||
## v0.1.1 – 2026-10-13
|
||||
|
||||
### Added
|
||||
|
||||
- `e` on the status pane edits the config ([#55](https://git.kebcloud.no/keb/lazysecrets/pulls/55)) ([2044341](https://git.kebcloud.no/keb/lazysecrets/commit/2044341b20d795fa7cadcabb9514631990993e0a))
|
||||
- Share env files as encrypted links ([#54](https://git.kebcloud.no/keb/lazysecrets/pulls/54)) ([4377eb9](https://git.kebcloud.no/keb/lazysecrets/commit/4377eb98bf0dd523d5c4371bf84668412053290e))
|
||||
|
||||
## v0.1.0 – 2026-10-12
|
||||
|
||||
Hello world 👋
|
||||
|
||||
@@ -51,6 +51,8 @@ commit_preprocessors = [
|
||||
{ pattern = "^Merge pull request '(.+)' \\((#[0-9]+)\\) from .*", replace = "${1} (${2})" },
|
||||
# #11 was squash-merged with #10 (the status pane) in it: name both.
|
||||
{ pattern = "^feat: link each changelog entry to its PR and commit \\(#11\\)", replace = "feat: a status pane (0) with version, updates, accounts, files and links; changelog entries link to their PR and commit (#11)" },
|
||||
# #55's title starts with a key, which capitalised reads as another.
|
||||
{ pattern = "^feat: e on the status pane edits the config", replace = "feat: `e` on the status pane edits the config" },
|
||||
# Titles may be written "[feat] x", "[feat]: x" or "feat:x" (see
|
||||
# pr-title.yml); all become "feat: x".
|
||||
{ pattern = "^\\[(feat|fix|perf|refactor|docs|test|ci|chore|build|style|revert)(\\([^)]*\\))?(!?)\\](!?):?\\s*", replace = "${1}${2}${3}${4}: " },
|
||||
|
||||
+3
-1
@@ -2,7 +2,9 @@
|
||||
|
||||
Optional, at `~/.config/lazysecrets/config.yaml` (or
|
||||
`$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` on the
|
||||
status pane (`0`) opens it in your editor, as in lazygit (making it if
|
||||
it isn't there yet); changes apply when lazysecrets starts again.
|
||||
|
||||
```yaml
|
||||
icons: true # Nerd Font icons (v3+); also --icons=false or LAZYSECRETS_ICONS=0
|
||||
|
||||
+2
-1
@@ -122,7 +122,8 @@ Secrets. Going into it (`l`, or `4`) keeps it on that; there `j`/`k`,
|
||||
it's hidden.
|
||||
|
||||
On top, the status pane (`0`) shows the version, a newer release and how
|
||||
your accounts are doing. Focused, the right pane lists more under the
|
||||
your accounts are doing; `e` there opens your [config](config.md) in your
|
||||
editor, as in lazygit. Focused, the right pane lists more under the
|
||||
logo (when there's room): the version and updates (`U`), each account
|
||||
(`L` on one that needs it signs in), what's expiring (`enter` goes to
|
||||
it), this session's errors, newest first (what a pane showed, or the
|
||||
|
||||
+40
-4
@@ -3,6 +3,7 @@ package app
|
||||
import (
|
||||
"cmp"
|
||||
"errors"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -13,18 +14,20 @@ import (
|
||||
|
||||
"git.kebcloud.no/keb/lazysecrets/internal/browser"
|
||||
"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), or on the status pane the config: SOPS files with sops edit, which decrypts
|
||||
// them for the editor and encrypts them again after. Other files, inside
|
||||
// nvim: in that nvim, once lazysecrets has quit; else in $VISUAL or
|
||||
// $EDITOR, coming back after; else in the system's default app. On a
|
||||
// secret, editors that can open at a line start on its key.
|
||||
|
||||
type editDoneMsg struct {
|
||||
c provider.Container
|
||||
name string
|
||||
err error
|
||||
c provider.Container
|
||||
name string
|
||||
config bool // the config file: read at startup
|
||||
err error
|
||||
}
|
||||
|
||||
// sopsUnchanged is sops edit's exit code when the file wasn't changed.
|
||||
@@ -44,6 +47,8 @@ func (m *Model) edit() tea.Cmd {
|
||||
var c provider.Container
|
||||
var o *provider.Object
|
||||
switch m.focus {
|
||||
case paneStatus: // like lazygit's status panel
|
||||
return m.editConfig()
|
||||
case paneSources:
|
||||
m.flash = warning("select a file to open it")
|
||||
return nil
|
||||
@@ -93,6 +98,33 @@ func (m *Model) edit() tea.Cmd {
|
||||
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: e on the status pane.
|
||||
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.
|
||||
var plusLine = map[string]bool{
|
||||
"vi": true, "vim": true, "nvim": true, "nano": true, "emacs": true, "emacsclient": true,
|
||||
@@ -142,6 +174,10 @@ func (m *Model) handleEditDone(msg editDoneMsg) tea.Cmd {
|
||||
m.flash = flash{text: "open " + msg.name + ": " + msg.err.Error(), isErr: true}
|
||||
return nil
|
||||
}
|
||||
if msg.config {
|
||||
m.flash = done("closed " + msg.name + ": restart lazysecrets to use it")
|
||||
return nil
|
||||
}
|
||||
m.flash = done("closed " + msg.name)
|
||||
if c, ok := m.containers.Selected(); ok && containerKey(c) == containerKey(msg.c) {
|
||||
m.hideValue()
|
||||
|
||||
@@ -54,7 +54,8 @@ func (c contextHelp) ShortHelp() []key.Binding {
|
||||
switch {
|
||||
case c.focus == paneStatus:
|
||||
details := key.NewBinding(key.WithKeys(k.Enter.Keys()...), key.WithHelp(k.Enter.Help().Key, "details"))
|
||||
return append(b, details, k.Update, k.Help, k.Quit)
|
||||
config := key.NewBinding(key.WithKeys(k.Edit.Keys()...), key.WithHelp(k.Edit.Help().Key, "edit config"))
|
||||
return append(b, details, config, k.Update, k.Help, k.Quit)
|
||||
case c.focus == paneDetail && c.account && c.of == paneStatus:
|
||||
open := key.NewBinding(key.WithKeys(k.Open.Keys()...), key.WithHelp(k.Open.Help().Key, "open link"))
|
||||
return append(b, k.Up, k.Down, copyField, open, k.Edit, k.Filter, k.Help, k.Quit)
|
||||
|
||||
@@ -342,3 +342,53 @@ 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 "" }
|
||||
|
||||
// e on the status pane. No editor: the system's default app, on a
|
||||
// config made for it.
|
||||
d.keys("0", "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)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusFooterEditsConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
d := newDriver(t)
|
||||
d.keys("0")
|
||||
if !strings.Contains(d.screen(), "e edit config") {
|
||||
t.Errorf("no edit config in the footer:\n%s", d.screen())
|
||||
}
|
||||
}
|
||||
@@ -114,7 +114,7 @@ func (m *Map) Actions() []Action {
|
||||
a(Lists, "copyReference", &m.CopyRef, "copy reference", "secrets", "Copy the reference to use in config, e.g. @Microsoft.KeyVault(SecretUri=…) or {{secrets/scope/key}}."),
|
||||
a(Lists, "versions", &m.Versions, "versions", "secrets", "List the versions of the selected item (Azure). Reveal, copy and share then act on the selected version."),
|
||||
a(Lists, "openInBrowser", &m.Open, "open in browser", "secrets", "Open the selected subscription, vault or item in the browser (Azure portal or Databricks workspace)."),
|
||||
a(Lists, "edit", &m.Edit, "open file", "secrets", "Open the selected SOPS or .env file, or the file of the selected secret at its line. SOPS files open with sops edit. Others open in the nvim lazysecrets runs in (quitting lazysecrets), else in $VISUAL or $EDITOR, else in the system's default app."),
|
||||
a(Lists, "edit", &m.Edit, "open file", "secrets", "Open the selected SOPS or .env file, or the file of the selected secret at its line; on the status pane, your config (made if it isn't there yet). SOPS files open with sops edit. Others open in the nvim lazysecrets runs in (quitting lazysecrets), else in $VISUAL or $EDITOR, else in the system's default app."),
|
||||
|
||||
a(Lists, "share", &m.Share, "copy link", "share", "Copy a lazysecrets:// link to the selected subscription, workspace, vault, scope or secret, for sharing. From the versions view the link is pinned to that version."),
|
||||
a(Lists, "shareMenu", &m.ShareMenu, "share", "share", "Choose what to share: the lazysecrets link, the plain Key Vault URL, the config reference, the web page, or env files as an encrypted link."),
|
||||
|
||||
Reference in new issue
Block a user