From 46c702a27cca94dca7a1b2058594a190b3c6afa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl-Einar=20Bergstr=C3=B8m?= Date: Tue, 13 Oct 2026 19:49:39 +0200 Subject: [PATCH] feat: config settings grouped under ui, values, storage and providers icons, sourcesView, collapseGroups, loadDelay and expiryWarningDays move under ui; revealTimeout, hideOnBlur and clipboardClear under values; cache and history under storage; timeout under providers. share is now sharing, in the config and in a repo's .lazysecrets.yaml. --- README.md | 2 +- cmd/lazysecrets/main.go | 38 +++++------ cmd/lazysecrets/share.go | 2 +- docs/azure.md | 2 +- docs/config.md | 35 +++++----- docs/keys.md | 4 +- docs/links.md | 4 +- docs/repos.md | 2 +- internal/config/config.go | 105 ++++++++++++++++------------- internal/config/config_test.go | 51 ++++++++------ internal/provider/repo/repo.go | 6 +- internal/share/share_test.go | 2 +- internal/ui/app/sharefiles_test.go | 2 +- internal/ui/app/usage.go | 2 +- 14 files changed, 142 insertions(+), 115 deletions(-) diff --git a/README.md b/README.md index e321629..c83a79c 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ use most: lazysecrets never stores, caches or sends your secret values. It keeps the names and metadata of what you've loaded in a cache file readable only by -you, to show it at once next time (`cache: false` turns it off). It talks +you, to show it at once next time (`storage.cache: false` turns it off). It talks only to the services you use it with, and collects no usage data. The one call of its own is a daily check for a new release, on GitHub or else git.kebcloud.no, which `updates.autoCheck: false` turns off. diff --git a/cmd/lazysecrets/main.go b/cmd/lazysecrets/main.go index 496356c..61b602d 100644 --- a/cmd/lazysecrets/main.go +++ b/cmd/lazysecrets/main.go @@ -207,23 +207,23 @@ Flags: // Precedence: flag > environment > config file > default. set := map[string]bool{} flag.Visit(func(f *flag.Flag) { set[f.Name] = true }) - if !set["timeout"] && cfg.Timeout > 0 { - *timeout = cfg.Timeout + if !set["timeout"] && cfg.Providers.Timeout > 0 { + *timeout = cfg.Providers.Timeout } - if !set["clipboard-clear"] && cfg.ClipboardClear != nil { - *clipClear = *cfg.ClipboardClear + if !set["clipboard-clear"] && cfg.Values.ClipboardClear != nil { + *clipClear = *cfg.Values.ClipboardClear } expiryDays := 30 - if cfg.ExpiryWarningDays != nil { - expiryDays = *cfg.ExpiryWarningDays + if cfg.UI.ExpiryWarningDays != nil { + expiryDays = *cfg.UI.ExpiryWarningDays } revealTimeout := 60 * time.Second - if cfg.RevealTimeout != nil { - revealTimeout = *cfg.RevealTimeout + if cfg.Values.RevealTimeout != nil { + revealTimeout = *cfg.Values.RevealTimeout } loadDelay := 150 * time.Millisecond - if cfg.LoadDelay != nil { - loadDelay = *cfg.LoadDelay + if cfg.UI.LoadDelay != nil { + loadDelay = *cfg.UI.LoadDelay } var updateEvery time.Duration // 0: the app's default, a day if cfg.Updates.AutoCheckInterval != nil { @@ -233,8 +233,8 @@ Flags: switch { case os.Getenv("LAZYSECRETS_ICONS") != "": *icons = os.Getenv("LAZYSECRETS_ICONS") != "0" - case cfg.Icons != nil: - *icons = *cfg.Icons + case cfg.UI.Icons != nil: + *icons = *cfg.UI.Icons } } @@ -321,7 +321,7 @@ Flags: if shareArgs != nil { ctx, cancel := context.WithTimeout(context.Background(), *timeout) defer cancel() - return shareFiles(ctx, providers, st, cfg.Share.Key, shareRules(cfg), shareArgs, os.Stdout) + return shareFiles(ctx, providers, st, cfg.Sharing.Key, shareRules(cfg), shareArgs, os.Stdout) } // Links opened elsewhere (lazysecrets launch) can come here. @@ -358,7 +358,7 @@ Flags: // What was loaded last time, and where it was left in this folder. var cacheFile, placeDir string if !*demo { - if d, err := config.CacheDir(); err == nil && config.On(cfg.Cache) { + if d, err := config.CacheDir(); err == nil && config.On(cfg.Storage.Cache) { cacheFile = filepath.Join(d, "cache.json") } placeDir = *dir @@ -374,14 +374,14 @@ Flags: Timeout: *timeout, Icons: *icons, ClipboardClear: *clipClear, - FlatSources: cfg.SourcesView == "flat", - CollapseGroups: config.On(cfg.CollapseGroups), + FlatSources: cfg.UI.SourcesView == "flat", + CollapseGroups: config.On(cfg.UI.CollapseGroups), LoadDelay: loadDelay, - NoHistory: !config.On(cfg.History), + NoHistory: !config.On(cfg.Storage.History), OnActive: onActive, RevealTimeout: revealTimeout, ExpiryWarning: time.Duration(expiryDays) * 24 * time.Hour, - HideOnBlur: config.On(cfg.HideOnBlur), + HideOnBlur: config.On(cfg.Values.HideOnBlur), HandlerSetup: setup, Updater: updater(cfg.Updates.UpdateSources), UpdateCheck: config.On(cfg.Updates.AutoCheck), @@ -395,7 +395,7 @@ Flags: StatePath: statePath, Changelog: lazysecrets.Changelog, Dir: placeDir, - ShareKey: cfg.Share.Key, + ShareKey: cfg.Sharing.Key, ShareRules: shareRules(cfg), Open: open, Styles: &theme, diff --git a/cmd/lazysecrets/share.go b/cmd/lazysecrets/share.go index 0d851f8..866f20b 100644 --- a/cmd/lazysecrets/share.go +++ b/cmd/lazysecrets/share.go @@ -89,5 +89,5 @@ func shareFiles(ctx context.Context, providers []provider.Provider, st *store.St // shareRules are the config's share.keyRules. func shareRules(cfg config.Config) share.Rules { - return share.Rules{MinLength: cfg.Share.KeyRules.MinLength, MinClasses: cfg.Share.KeyRules.MinClasses} + return share.Rules{MinLength: cfg.Sharing.KeyRules.MinLength, MinClasses: cfg.Sharing.KeyRules.MinClasses} } diff --git a/docs/azure.md b/docs/azure.md index 855034b..6405b92 100644 --- a/docs/azure.md +++ b/docs/azure.md @@ -63,7 +63,7 @@ Secrets, keys and certificates, each on their own tab of the Secrets pane (`[`/`]`). `space` reveals a secret's value, or the public material of a key (PEM public key) or certificate; private key material is never fetched. `v` lists an item's versions. Items expiring within -`expiryWarningDays` (30 by default) are marked `⚠ 12d`, expired ones `✗`. +`ui.expiryWarningDays` (30 by default) are marked `⚠ 12d`, expired ones `✗`. The status pane (`0`) lists them across every vault loaded, opened or cached, soonest first, and its line counts them (`⚠ 3 expiring`); `enter` diff --git a/docs/config.md b/docs/config.md index e2c0686..7cf5927 100644 --- a/docs/config.md +++ b/docs/config.md @@ -7,24 +7,27 @@ 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 -clipboardClear: 30s # clear copied values from the clipboard after this long; 0s disables -revealTimeout: 60s # hide a revealed value after this long; 0s keeps it shown -hideOnBlur: true # hide a revealed value when the terminal loses focus (tmux: focus-events on) -expiryWarningDays: 30 # mark secrets, keys and certificates expiring this soon (⚠ 12d); expired ones get ✗ -timeout: 30s # per backend request -loadDelay: 150ms # how long the cursor rests on a row before it loads; 0s loads at once -sourcesView: grouped # or flat; ` toggles it -collapseGroups: true # start with the Sources pane's groups folded; enter or space unfolds one +ui: + icons: true # Nerd Font icons (v3+); also --icons=false or LAZYSECRETS_ICONS=0 + sourcesView: grouped # or flat; ` toggles it + collapseGroups: true # start with the Sources pane's groups folded; enter or space unfolds one + loadDelay: 150ms # how long the cursor rests on a row before it loads; 0s loads at once + expiryWarningDays: 30 # mark secrets, keys and certificates expiring this soon (⚠ 12d); expired ones get ✗ +values: + revealTimeout: 60s # hide a revealed value after this long; 0s keeps it shown + hideOnBlur: true # hide a revealed value when the terminal loses focus (tmux: focus-events on) + clipboardClear: 30s # clear copied values from the clipboard after this long; 0s disables +storage: + cache: true # keep what was loaded (never values) for the next start + history: true # count uses of items for Recent (R) updates: autoCheck: true # look for a newer release at startup (U and lazysecrets update work either way) autoCheckInterval: 24h # at most this often updateSources: # where updates come from, tried in order (GitHub, or a Gitea or Forgejo server) - https://github.com/karleinarb/lazysecrets - https://git.kebcloud.no/keb/lazysecrets -history: true # count uses of items for Recent (R) -cache: true # keep what was loaded (never values) for the next start providers: + timeout: 30s # per backend request azure: # applies to every az account enabled: true # default hideSubscriptions: [platform-mgmt] # by name or ID @@ -39,7 +42,7 @@ providers: paths: [~/work/infra] include: ["secrets/**"] exclude: ["**/testdata/**"] -share: # sharing env files as encrypted links +sharing: # sharing env files as encrypted links key: https://kv-team.vault.azure.net/secrets/env-share # or its lazysecrets:// link; for repos naming none keyRules: # what a key's value must be minLength: 32 # characters, 16 at least @@ -47,7 +50,7 @@ share: # sharing env files as encrypted links ``` More, each on its own page: [repos](repos.md) (`providers.repos`), -[sharing env files](links.md#sharing-env-files) (`share`), the +[sharing env files](links.md#sharing-env-files) (`sharing`), the [link handler](links.md#from-the-browser-and-chat) (`linkHandler`), [keybindings](keys.md#rebinding-keys) (`keybinding`) and [custom commands](custom-commands.md) (`customCommands`). @@ -86,7 +89,7 @@ theme: ``` Icons need a [Nerd Font](https://www.nerdfonts.com) (v3+). If they render -as boxes, turn them off with `icons: false`. +as boxes, turn them off with `ui.icons: false`. ## Layering @@ -114,7 +117,7 @@ lazysecrets windows can save to it without losing each other's changes. Revealing, copying, opening in the browser, sharing and jumping to an item count as using it: its record keeps when it was last used and how often, for Recent (`R`). Records kept only for usage are dropped after 90 days -unused, and beyond the 100 most recent. `history: false` stops counting. +unused, and beyond the 100 most recent. `storage.history: false` stops counting. It also keeps where you left lazysecrets in each folder (the selected source, vault or secret), and goes back there the next time it starts in @@ -134,7 +137,7 @@ and copying always fetch the value live. Lists not loaded for 30 days are dropped, and so is everything from an account you're no longer signed in to. Repos aren't cached: their files are read from disk anyway. Deleting the file is always safe, and -`cache: false` stops using it. +`storage.cache: false` stops using it. ## Updates diff --git a/docs/keys.md b/docs/keys.md index a3ad8fe..3bc1536 100644 --- a/docs/keys.md +++ b/docs/keys.md @@ -65,8 +65,8 @@ certificates as their subject, issuer, dates, names and thumbprint, with any other PEM block (a private key) named but not shown. `y` still copies it exactly as stored. -A revealed value hides again after `revealTimeout` (60s) and when the -terminal loses focus (`hideOnBlur`); see [config](config.md). +A revealed value hides again after `values.revealTimeout` (60s) and +when the terminal loses focus (`values.hideOnBlur`); see [config](config.md). ## Stars and notes diff --git a/docs/links.md b/docs/links.md index d6429d5..6b1ddf7 100644 --- a/docs/links.md +++ b/docs/links.md @@ -54,7 +54,7 @@ uses it) or your config (for repos that don't name one), by its reference: ```yaml -share: +sharing: key: https://kv-team.vault.azure.net/secrets/env-share ``` @@ -67,7 +67,7 @@ isn't long and varied enough, saying how (never showing it). By default that's 32 characters of two kinds; your config can ask for more, or less: ```yaml -share: +sharing: keyRules: minLength: 32 # characters; 16 at least minClasses: 2 # kinds of character: lowercase, uppercase, digits, symbols diff --git a/docs/repos.md b/docs/repos.md index 68c9790..96260e4 100644 --- a/docs/repos.md +++ b/docs/repos.md @@ -60,7 +60,7 @@ A repo's `.lazysecrets.yaml` can also name the secret its env files are [shared](links.md#sharing-env-files) with: ```yaml -share: +sharing: key: https://kv-team.vault.azure.net/secrets/env-share # or its lazysecrets:// link ``` diff --git a/internal/config/config.go b/internal/config/config.go index 6ec30cb..f415d3b 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -19,38 +19,18 @@ import ( ) type Config struct { - Icons *bool `yaml:"icons"` - ClipboardClear *time.Duration `yaml:"clipboardClear"` - // RevealTimeout hides a revealed value after this long; 0s never. - // Defaults to 60s. - RevealTimeout *time.Duration `yaml:"revealTimeout"` - // ExpiryWarningDays marks secrets, keys and certificates expiring - // within this many days; 0 marks only expired ones. Defaults to 30. - ExpiryWarningDays *int `yaml:"expiryWarningDays"` - // HideOnBlur hides a revealed value when the terminal loses focus. - // Defaults to true. - HideOnBlur *bool `yaml:"hideOnBlur"` - Timeout time.Duration `yaml:"timeout"` - // LoadDelay is how long the cursor must rest on a row before its - // contents load; 0s loads at once. Defaults to 150ms. - LoadDelay *time.Duration `yaml:"loadDelay"` - // SourcesView is "grouped" (default) or "flat"; ` + "`" + ` toggles it. - SourcesView string `yaml:"sourcesView"` - // History counts uses of items for the Recent list; defaults to true. - History *bool `yaml:"history"` + UI UI `yaml:"ui"` + // Values keeps revealed and copied values from lingering. + Values Values `yaml:"values"` + // Storage is what's kept between runs. + Storage Storage `yaml:"storage"` // Updates sets how lazysecrets looks for newer releases. - Updates Updates `yaml:"updates"` - // Cache keeps what was loaded (never values) between runs, so lists - // show at once and refresh in the background; defaults to true. - Cache *bool `yaml:"cache"` - // CollapseGroups starts the Sources pane with its groups folded; - // defaults to true. - CollapseGroups *bool `yaml:"collapseGroups"` - Providers Providers `yaml:"providers"` + Updates Updates `yaml:"updates"` + Providers Providers `yaml:"providers"` // LinkHandler sets how lazysecrets:// links open; see linkhandler. LinkHandler LinkHandler `yaml:"linkHandler"` - // Share sets how env files are shared; see package share. - Share Share `yaml:"share"` + // Sharing sets how env files are shared; see package share. + Sharing Sharing `yaml:"sharing"` // CustomCommands run shell commands on the selected item; see // package commands. CustomCommands []commands.Command `yaml:"customCommands"` @@ -62,6 +42,39 @@ type Config struct { Theme map[string][]string `yaml:"theme"` } +type UI struct { + Icons *bool `yaml:"icons"` + // SourcesView is "grouped" (default) or "flat"; ` + "`" + ` toggles it. + SourcesView string `yaml:"sourcesView"` + // CollapseGroups starts the Sources pane with its groups folded; + // defaults to true. + CollapseGroups *bool `yaml:"collapseGroups"` + // LoadDelay is how long the cursor must rest on a row before its + // contents load; 0s loads at once. Defaults to 150ms. + LoadDelay *time.Duration `yaml:"loadDelay"` + // ExpiryWarningDays marks secrets, keys and certificates expiring + // within this many days; 0 marks only expired ones. Defaults to 30. + ExpiryWarningDays *int `yaml:"expiryWarningDays"` +} + +type Values struct { + // RevealTimeout hides a revealed value after this long; 0s never. + // Defaults to 60s. + RevealTimeout *time.Duration `yaml:"revealTimeout"` + // HideOnBlur hides a revealed value when the terminal loses focus. + // Defaults to true. + HideOnBlur *bool `yaml:"hideOnBlur"` + ClipboardClear *time.Duration `yaml:"clipboardClear"` +} + +type Storage struct { + // Cache keeps what was loaded (never values) between runs, so lists + // show at once and refresh in the background; defaults to true. + Cache *bool `yaml:"cache"` + // History counts uses of items for the Recent list; defaults to true. + History *bool `yaml:"history"` +} + type Updates struct { // AutoCheck looks for a newer release in the background at startup, // at most once per AutoCheckInterval; defaults to true. U and @@ -92,7 +105,7 @@ type LinkHandler struct { Prompt *bool `yaml:"prompt"` } -type Share struct { +type Sharing struct { // Key is the secret whose value encrypts shared files (its // lazysecrets:// link, Key Vault URL or reference), for repos whose // .lazysecrets.yaml doesn't name one. @@ -110,9 +123,11 @@ type KeyRules struct { } type Providers struct { - Azure Azure `yaml:"azure"` - Databricks Databricks `yaml:"databricks"` - Repos Repos `yaml:"repos"` + // Timeout bounds each backend request; defaults to 30s. + Timeout time.Duration `yaml:"timeout"` + Azure Azure `yaml:"azure"` + Databricks Databricks `yaml:"databricks"` + Repos Repos `yaml:"repos"` } // Repos are git repositories' secret files (SOPS, .env). Inside a repo, @@ -229,19 +244,19 @@ func decodeInto(c *Config, path string) error { } func (c Config) validate(path string) error { - if v := c.SourcesView; v != "" && v != "grouped" && v != "flat" { - return fmt.Errorf("%s: sourcesView: %q is not grouped or flat", path, v) + if v := c.UI.SourcesView; v != "" && v != "grouped" && v != "flat" { + return fmt.Errorf("%s: ui.sourcesView: %q is not grouped or flat", path, v) } for _, cmd := range c.CustomCommands { if err := cmd.Validate(); err != nil { return fmt.Errorf("%s: %w", path, err) } } - if d := c.ExpiryWarningDays; d != nil && *d < 0 { - return fmt.Errorf("%s: expiryWarningDays: %d is negative", path, *d) + if d := c.UI.ExpiryWarningDays; d != nil && *d < 0 { + return fmt.Errorf("%s: ui.expiryWarningDays: %d is negative", path, *d) } - if d := c.LoadDelay; d != nil && (*d < 0 || *d > 5*time.Second) { - return fmt.Errorf("%s: loadDelay: %s is not between 0s and 5s", path, *d) + if d := c.UI.LoadDelay; d != nil && (*d < 0 || *d > 5*time.Second) { + return fmt.Errorf("%s: ui.loadDelay: %s is not between 0s and 5s", path, *d) } if d := c.Updates.AutoCheckInterval; d != nil && *d <= 0 { return fmt.Errorf("%s: updates.autoCheckInterval: %s is not positive (autoCheck: false turns it off)", path, *d) @@ -251,16 +266,16 @@ func (c Config) validate(path string) error { return fmt.Errorf("%s: updates.updateSources[%d]: %q is not an https:// repository address, like %s", path, i, r, update.GitHubRepo) } } - if k := c.Share.Key; k != "" { + if k := c.Sharing.Key; k != "" { if _, err := share.ParseKey(k); err != nil { - return fmt.Errorf("%s: share.key: %w", path, err) + return fmt.Errorf("%s: sharing.key: %w", path, err) } } - if n := c.Share.KeyRules.MinLength; n != 0 && n < 16 { - return fmt.Errorf("%s: share.keyRules.minLength: %d is under 16", path, n) + if n := c.Sharing.KeyRules.MinLength; n != 0 && n < 16 { + return fmt.Errorf("%s: sharing.keyRules.minLength: %d is under 16", path, n) } - if n := c.Share.KeyRules.MinClasses; n < 0 || n > 4 { - return fmt.Errorf("%s: share.keyRules.minClasses: %d is not between 1 and 4", path, n) + if n := c.Sharing.KeyRules.MinClasses; n < 0 || n > 4 { + return fmt.Errorf("%s: sharing.keyRules.minClasses: %d is not between 1 and 4", path, n) } for i, v := range c.Providers.Azure.Vaults { if !strings.HasPrefix(v, "https://") { diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 06736d2..a3011dd 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -19,11 +19,15 @@ func write(t *testing.T, content string) string { func TestLoad(t *testing.T) { c, err := Load(write(t, ` -icons: false -sourcesView: flat -clipboardClear: 45s -timeout: 1m +ui: + icons: false + sourcesView: flat +values: + clipboardClear: 45s +storage: + history: false providers: + timeout: 1m azure: hideSubscriptions: [platform-mgmt] vaults: @@ -35,12 +39,12 @@ providers: if err != nil { t.Fatal(err) } - if c.Icons == nil || *c.Icons || c.ClipboardClear == nil || *c.ClipboardClear != 45*time.Second || - c.Timeout != time.Minute || len(c.Providers.Azure.Vaults) != 1 || c.Providers.Databricks.HideProfiles[0] != "test" { + if c.UI.Icons == nil || *c.UI.Icons || c.Values.ClipboardClear == nil || *c.Values.ClipboardClear != 45*time.Second || + c.Providers.Timeout != time.Minute || len(c.Providers.Azure.Vaults) != 1 || c.Providers.Databricks.HideProfiles[0] != "test" { t.Errorf("config = %+v", c) } - if c.SourcesView != "flat" { - t.Errorf("sourcesView = %q", c.SourcesView) + if c.UI.SourcesView != "flat" || On(c.Storage.History) || !On(c.Storage.Cache) { + t.Errorf("ui = %+v, storage = %+v", c.UI, c.Storage) } if !On(c.Providers.Azure.Enabled) || On(c.Providers.Databricks.Enabled) { t.Error("enabled flags wrong") @@ -57,7 +61,7 @@ func TestLoadMissingAndEmpty(t *testing.T) { "empty": write(t, ""), } { c, err := Load(path) - if err != nil || c.Icons != nil || c.ClipboardClear != nil { + if err != nil || c.UI.Icons != nil || c.Values.ClipboardClear != nil { t.Errorf("%s: %+v, %v", name, c, err) } } @@ -68,15 +72,16 @@ func TestLoadRejectsMistakes(t *testing.T) { "typo": "icon: true\n", "bad vault": "providers:\n azure:\n vaults: [kv-foo]\n", "old layout": "azure:\n vaults: [https://kv.vault.azure.net/]\n", - "duration": "timeout: soon\n", - "view": "sourcesView: tree\n", + "flat keys": "icons: false\n", + "duration": "providers:\n timeout: soon\n", + "view": "ui:\n sourcesView: tree\n", "sources": "updates:\n updateSources: [git.example.com/team/lazysecrets]\n", "http": "updates:\n updateSources: [http://git.example.com/team/lazysecrets]\n", "interval": "updates:\n autoCheckInterval: 0s\n", - "share key": "share:\n key: https://kv.vault.azure.net/\n", - "workspace": "share:\n key: \"{{secrets/team/env-share}}\"\n", - "short keys": "share:\n keyRules:\n minLength: 8\n", - "classes": "share:\n keyRules:\n minClasses: 5\n", + "share key": "sharing:\n key: https://kv.vault.azure.net/\n", + "workspace": "sharing:\n key: \"{{secrets/team/env-share}}\"\n", + "short keys": "sharing:\n keyRules:\n minLength: 8\n", + "classes": "sharing:\n keyRules:\n minClasses: 5\n", } { if _, err := Load(write(t, content)); err == nil { t.Errorf("%s: no error", name) @@ -88,7 +93,8 @@ func TestLoadRejectsMistakes(t *testing.T) { func TestLoadAllLayers(t *testing.T) { user := write(t, ` -icons: false +ui: + icons: false theme: azureColor: [blue] errorColor: [red, bold] @@ -97,6 +103,8 @@ providers: hideSubscriptions: [a] `) theme := write(t, ` +ui: + sourcesView: flat theme: azureColor: ["#82aaff"] activeBorderColor: ["#ff966c", bold] @@ -105,7 +113,8 @@ theme: if err != nil { t.Fatal(err) } - if c.Icons == nil || *c.Icons || len(c.Providers.Azure.HideSubscriptions) != 1 { + // A group set in both files merges: icons from one, sourcesView the other. + if c.UI.Icons == nil || *c.UI.Icons || c.UI.SourcesView != "flat" || len(c.Providers.Azure.HideSubscriptions) != 1 { t.Errorf("user settings lost: %+v", c) } want := map[string][]string{ @@ -128,11 +137,11 @@ func TestUpdates(t *testing.T) { } func TestLoadDelay(t *testing.T) { - c, err := Load(write(t, "loadDelay: 300ms\n")) - if err != nil || c.LoadDelay == nil || *c.LoadDelay != 300*time.Millisecond { - t.Errorf("loadDelay = %v, %v", c.LoadDelay, err) + c, err := Load(write(t, "ui:\n loadDelay: 300ms\n")) + if err != nil || c.UI.LoadDelay == nil || *c.UI.LoadDelay != 300*time.Millisecond { + t.Errorf("loadDelay = %v, %v", c.UI.LoadDelay, err) } - if _, err := Load(write(t, "loadDelay: 1m\n")); err == nil { + if _, err := Load(write(t, "ui:\n loadDelay: 1m\n")); err == nil { t.Error("a minute's delay accepted") } } diff --git a/internal/provider/repo/repo.go b/internal/provider/repo/repo.go index d9261eb..277858d 100644 --- a/internal/provider/repo/repo.go +++ b/internal/provider/repo/repo.go @@ -599,11 +599,11 @@ func normalizeRemote(remote string) (string, string) { type lazysecretsFile struct { Include []string `yaml:"include"` Exclude []string `yaml:"exclude"` - Share struct { + Sharing struct { // Key is the link to the secret that encrypts the repo's // shared env files (package share). Key string `yaml:"key"` - } `yaml:"share"` + } `yaml:"sharing"` } // ShareKey is the share.key of the repo's .lazysecrets.yaml, if any. @@ -619,7 +619,7 @@ func ShareKey(root string) (string, error) { if err := yaml.Unmarshal(b, &f); err != nil { return "", fmt.Errorf("%s/.lazysecrets.yaml: %w", root, err) } - return f.Share.Key, nil + return f.Sharing.Key, nil } // selector says which files to look at: user (the include/exclude of the diff --git a/internal/share/share_test.go b/internal/share/share_test.go index 069c68f..d431201 100644 --- a/internal/share/share_test.go +++ b/internal/share/share_test.go @@ -243,7 +243,7 @@ func TestDefaultKey(t *testing.T) { if err != nil || !ok || k.Name != "env-share" { t.Errorf("general: %+v %v %v", k, ok, err) } - os.WriteFile(filepath.Join(dir, ".lazysecrets.yaml"), []byte("share:\n key: lazysecrets://azure/kv-app.vault.azure.net/secrets/app-share\n"), 0o644) + os.WriteFile(filepath.Join(dir, ".lazysecrets.yaml"), []byte("sharing:\n key: lazysecrets://azure/kv-app.vault.azure.net/secrets/app-share\n"), 0o644) if k, _, _ := DefaultKey(dir, general); k.Name != "app-share" { t.Errorf("the repo's should win: %+v", k) } diff --git a/internal/ui/app/sharefiles_test.go b/internal/ui/app/sharefiles_test.go index 220d4a9..d33d0e3 100644 --- a/internal/ui/app/sharefiles_test.go +++ b/internal/ui/app/sharefiles_test.go @@ -137,7 +137,7 @@ func TestShareWithShareKeyConfirms(t *testing.T) { } root := cloneWith(t, map[string]string{ ".env": "A=1\n", - ".lazysecrets.yaml": "share:\n key: https://" + kv + "/secrets/db-password\n", + ".lazysecrets.yaml": "sharing:\n key: https://" + kv + "/secrets/db-password\n", }) clip := &fakeClipboard{} d := newDriverOpts(t, Options{Clipboard: clip, ShareRules: demoRules}, fake.Azure(0), repo.New(repo.Settings{Dir: root})) diff --git a/internal/ui/app/usage.go b/internal/ui/app/usage.go index 71c226e..78484db 100644 --- a/internal/ui/app/usage.go +++ b/internal/ui/app/usage.go @@ -8,7 +8,7 @@ import ( // Usage: revealing, copying, opening in the browser, sharing and jumping // to an item count as using it. The store keeps when each item was last // used and how often, for the Recent section of the jump list. The config's -// history: false turns this off. +// storage.history: false turns this off. // used counts a use of the remembered item key, keeping its context. func (m *Model) used(key string) {