feat: with nothing to show, the right pane says how to get started
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
9b0329e9da
commit
164d933bea
4 files changed
+107
-1
No files matched your search
@@ -60,7 +60,8 @@ lazysecrets # start lazysecrets
|
|||||||
lazysecrets --demo # test with demo data
|
lazysecrets --demo # test with demo data
|
||||||
```
|
```
|
||||||
|
|
||||||
Pick a source on the left, then a vault, then a secret. The keys you'll
|
Pick a source on the left, then a vault, then a secret. With nothing
|
||||||
|
signed in yet, the right pane says how to get started. The keys you'll
|
||||||
use most:
|
use most:
|
||||||
|
|
||||||
| Key | Action |
|
| Key | Action |
|
||||||
|
|||||||
@@ -1236,3 +1236,36 @@ func TestStatusPane(t *testing.T) {
|
|||||||
t.Errorf("back to %v", d.m.focus)
|
t.Errorf("back to %v", d.m.focus)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// empty is an account with nothing in it, like Databricks without profiles.
|
||||||
|
type empty struct{ provider.Provider }
|
||||||
|
|
||||||
|
func (empty) Sources(context.Context) ([]provider.Source, error) { return nil, nil }
|
||||||
|
|
||||||
|
func TestGetStarted(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
d := newDriverOpts(t, Options{}, signedOut{fake.Azure(0)}, empty{fake.Databricks(0)})
|
||||||
|
scr := d.screen()
|
||||||
|
for _, want := range []string{"get started", "Azure az login", "L runs it here",
|
||||||
|
"databricks auth login --host", "lazysecrets --demo"} {
|
||||||
|
if !strings.Contains(scr, want) {
|
||||||
|
t.Errorf("missing %q:\n%s", want, scr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.keys("l", "j")
|
||||||
|
if a := d.m.loginFor(); a == nil || a.Account != "Azure · admin" {
|
||||||
|
t.Errorf("L signs in to %+v", a)
|
||||||
|
}
|
||||||
|
d.keys("y")
|
||||||
|
if got := d.clip().content; got != "az login" {
|
||||||
|
t.Errorf("y copied %q", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once an account has something, an empty one says what it needs.
|
||||||
|
d = newDriverOpts(t, Options{}, fake.Azure(0), empty{fake.Databricks(0)})
|
||||||
|
d.m.sources.SelectWhere(func(r sourceRow) bool { return r.kind == rowGroup && r.provider == fake.Databricks(0).ID() })
|
||||||
|
d.send(nil)
|
||||||
|
if scr := d.screen(); strings.Contains(scr, "get started") || !strings.Contains(scr, "databricks auth login") {
|
||||||
|
t.Errorf("empty Databricks:\n%s", scr)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,6 +53,8 @@ func (m *Model) syncFields() tea.Cmd {
|
|||||||
case paneSources:
|
case paneSources:
|
||||||
r, ok := m.sources.Selected()
|
r, ok := m.sources.Selected()
|
||||||
switch {
|
switch {
|
||||||
|
case m.nothingFound():
|
||||||
|
m.fields.Show("get started", m.getStartedFields())
|
||||||
case !ok:
|
case !ok:
|
||||||
m.fields.Show("", nil)
|
m.fields.Show("", nil)
|
||||||
case m.selectedProblem() != "":
|
case m.selectedProblem() != "":
|
||||||
@@ -106,6 +108,10 @@ func (m *Model) showAccount(r sourceRow) tea.Cmd {
|
|||||||
}
|
}
|
||||||
fields = append(fields, groupFields(st)...)
|
fields = append(fields, groupFields(st)...)
|
||||||
}
|
}
|
||||||
|
if res, done := m.sourceResults[r.provider]; done && res.err == nil && len(res.sources) == 0 {
|
||||||
|
hint := emptyHint(p.Kind())
|
||||||
|
fields = append(fields, panes.Field{Label: "nothing", Value: "found: try", Info: true}, panes.Field{Value: hint.Value, URL: hint.URL})
|
||||||
|
}
|
||||||
m.fields.Show(p.Label(), fields)
|
m.fields.Show(p.Label(), fields)
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"git.kebcloud.no/keb/lazysecrets/internal/provider"
|
||||||
|
"git.kebcloud.no/keb/lazysecrets/internal/ui/panes"
|
||||||
|
"git.kebcloud.no/keb/lazysecrets/internal/update"
|
||||||
|
)
|
||||||
|
|
||||||
|
// When no account has anything to show, as on a first run, the right
|
||||||
|
// pane says how to get started: the sign-in for each kind of account
|
||||||
|
// (y copies it, L runs az login), and the demo.
|
||||||
|
|
||||||
|
// nothingFound reports whether every account has loaded, with nothing.
|
||||||
|
func (m Model) nothingFound() bool {
|
||||||
|
if m.sourcesPending > 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, p := range m.providers {
|
||||||
|
if len(m.sourceResults[p.ID()].sources) > 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// getStartedFields are the rows saying how to get started.
|
||||||
|
func (m Model) getStartedFields() []panes.Field {
|
||||||
|
out := []panes.Field{{Value: "Nothing to show yet: sign in where your secrets are.", Info: true}}
|
||||||
|
seen := map[provider.Kind]bool{}
|
||||||
|
for _, p := range m.providers {
|
||||||
|
if seen[p.Kind()] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[p.Kind()] = true
|
||||||
|
f := emptyHint(p.Kind())
|
||||||
|
var auth *provider.AuthError
|
||||||
|
switch err := m.sourceResults[p.ID()].err; {
|
||||||
|
case errors.As(err, &auth) && len(auth.Login) > 0:
|
||||||
|
f.Provider = p.ID() // L signs in
|
||||||
|
out = append(out, f, panes.Field{Value: withKey(m.keys.Login, "%s runs it here"), Info: true})
|
||||||
|
case err != nil:
|
||||||
|
out = append(out, f, panes.Field{Value: err.Error(), Info: true, Err: true})
|
||||||
|
default:
|
||||||
|
out = append(out, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return append(out,
|
||||||
|
panes.Field{Label: "try", Value: "lazysecrets --demo"},
|
||||||
|
panes.Field{Value: "built-in sample data, to look around", Info: true},
|
||||||
|
panes.Field{Label: "docs", Value: "getting started", URL: update.DefaultRepo + "#get-started"},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// emptyHint is how an account of kind gets something to show.
|
||||||
|
func emptyHint(kind provider.Kind) panes.Field {
|
||||||
|
switch kind {
|
||||||
|
case provider.KindDatabricks:
|
||||||
|
return panes.Field{Label: "Databricks", Value: "databricks auth login --host https://<workspace>"}
|
||||||
|
case provider.KindRepo:
|
||||||
|
return panes.Field{Label: "repos", Value: "SOPS and .env files in your git repos",
|
||||||
|
URL: update.DefaultRepo + "/src/branch/main/docs/repos.md"}
|
||||||
|
}
|
||||||
|
return panes.Field{Label: string(kind), Value: "az login"}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user