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.
This commit is contained in:
14 files changed
+142
-115
No files matched your search
+60
-45
@@ -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://") {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user