commit 38c784898cf1af0597482f9e1ff4aa3f288f7746
parent c3ac881cb98bfaf603b5c890383f4be2620c0005
Author: Sean Enck <sean@ttypty.com>
Date: Tue, 25 Jul 2023 19:49:10 -0400
restructure to move cli -> app
Diffstat:
12 files changed, 353 insertions(+), 357 deletions(-)
diff --git a/Makefile b/Makefile
@@ -6,7 +6,7 @@ all: $(TARGET)
build: $(TARGET)
-$(TARGET): cmd/main.go internal/**/*.go go.* internal/cli/completions*
+$(TARGET): cmd/main.go internal/**/*.go go.* internal/app/doc/*
ifeq ($(VERSION),)
$(error version not set)
endif
diff --git a/cmd/main.go b/cmd/main.go
@@ -10,7 +10,6 @@ import (
"time"
"github.com/enckse/lockbox/internal/app"
- "github.com/enckse/lockbox/internal/cli"
"github.com/enckse/lockbox/internal/inputs"
"github.com/enckse/lockbox/internal/platform"
"github.com/enckse/lockbox/internal/system"
@@ -34,10 +33,10 @@ func handleEarly(command string, args []string) (bool, error) {
return true, nil
}
switch command {
- case cli.VersionCommand:
+ case app.VersionCommand:
fmt.Printf("version: %s\n", version)
return true, nil
- case cli.ClearCommand:
+ case app.ClearCommand:
return true, clearClipboard()
}
return false, nil
@@ -62,31 +61,31 @@ func run() error {
return err
}
switch command {
- case cli.ReKeyCommand:
+ case app.ReKeyCommand:
keyer, err := app.NewDefaultKeyer()
if err != nil {
return err
}
return app.ReKey(p, keyer)
- case cli.ListCommand:
+ case app.ListCommand:
return app.List(p)
- case cli.MoveCommand:
+ case app.MoveCommand:
return app.Move(p)
- case cli.InsertCommand, cli.MultiLineCommand:
+ case app.InsertCommand, app.MultiLineCommand:
mode := app.SingleLineInsert
- if command == cli.MultiLineCommand {
+ if command == app.MultiLineCommand {
mode = app.MultiLineInsert
}
return app.Insert(p, mode)
- case cli.RemoveCommand:
+ case app.RemoveCommand:
return app.Remove(p)
- case cli.JSONCommand:
+ case app.JSONCommand:
return app.JSON(p)
- case cli.ShowCommand, cli.ClipCommand:
- return app.ShowClip(p, command == cli.ShowCommand)
- case cli.ConvCommand:
+ case app.ShowCommand, app.ClipCommand:
+ return app.ShowClip(p, command == app.ShowCommand)
+ case app.ConvCommand:
return app.Conv(p)
- case cli.TOTPCommand:
+ case app.TOTPCommand:
args, err := totp.NewArguments(sub, inputs.TOTPToken())
if err != nil {
return err
diff --git a/internal/app/core.go b/internal/app/core.go
@@ -2,14 +2,83 @@
package app
import (
+ "bytes"
+ "embed"
"fmt"
"io"
"os"
+ "path/filepath"
+ "sort"
+ "strings"
+ "text/template"
"github.com/enckse/lockbox/internal/backend"
+ "github.com/enckse/lockbox/internal/inputs"
"github.com/enckse/lockbox/internal/system"
)
+const (
+ // TOTPCommand is the parent of totp and by defaults generates a rotating token
+ TOTPCommand = "totp"
+ // ConvCommand handles text conversion of the data store
+ ConvCommand = "conv"
+ // ClearCommand is a callback to manage clipboard clearing
+ ClearCommand = "clear"
+ // ClipCommand will copy values to the clipboard
+ ClipCommand = "clip"
+ // FindCommand is for simplistic searching of entries
+ FindCommand = "find"
+ // InsertCommand adds a value
+ InsertCommand = "insert"
+ // ListCommand lists all entries
+ ListCommand = "ls"
+ // MoveCommand will move source to destination
+ MoveCommand = "mv"
+ // ShowCommand will show the value in an entry
+ ShowCommand = "show"
+ // VersionCommand displays version information
+ VersionCommand = "version"
+ // HelpCommand shows usage
+ HelpCommand = "help"
+ // HelpAdvancedCommand shows advanced help
+ HelpAdvancedCommand = "verbose"
+ // RemoveCommand removes an entry
+ RemoveCommand = "rm"
+ // EnvCommand shows environment information used by lockbox
+ EnvCommand = "env"
+ // TOTPClipCommand is the argument for copying totp codes to clipboard
+ TOTPClipCommand = ClipCommand
+ // TOTPMinimalCommand is the argument for getting the short version of a code
+ TOTPMinimalCommand = "minimal"
+ // TOTPListCommand will list the totp-enabled entries
+ TOTPListCommand = ListCommand
+ // TOTPOnceCommand will perform like a normal totp request but not refresh
+ TOTPOnceCommand = "once"
+ // EnvDefaultsCommand will display the default env variables, not those set
+ EnvDefaultsCommand = "defaults"
+ // BashCommand is the command to generate bash completions
+ BashCommand = "bash"
+ // BashDefaultsCommand will generate environment agnostic completions
+ BashDefaultsCommand = "defaults"
+ // ReKeyCommand will rekey the underlying database
+ ReKeyCommand = "rekey"
+ // MultiLineCommand handles multi-line inserts (when not piped)
+ MultiLineCommand = "multiline"
+ // TOTPShowCommand is for showing the TOTP token
+ TOTPShowCommand = ShowCommand
+ // TOTPInsertCommand is for inserting totp tokens
+ TOTPInsertCommand = InsertCommand
+ // JSONCommand handles JSON outputs
+ JSONCommand = "json"
+ // ZshCommand is the command to generate zsh completions
+ ZshCommand = "zsh"
+ // ZshDefaultsCommand will generate environment agnostic completions
+ ZshDefaultsCommand = "defaults"
+)
+
+//go:embed doc/*
+var docs embed.FS
+
type (
// CommandOptions define how commands operate as an application
CommandOptions interface {
@@ -24,6 +93,28 @@ type (
args []string
tx *backend.Transaction
}
+ // Completions handles the inputs to completions for templating
+ Completions struct {
+ Options []string
+ CanClip bool
+ CanTOTP bool
+ ReadOnly bool
+ InsertCommand string
+ TOTPSubCommands []string
+ TOTPListCommand string
+ RemoveCommand string
+ ClipCommand string
+ ShowCommand string
+ MultiLineCommand string
+ MoveCommand string
+ TOTPCommand string
+ DoTOTPList string
+ DoList string
+ Executable string
+ JSONCommand string
+ HelpCommand string
+ HelpAdvancedCommand string
+ }
)
// NewDefaultCommand creates a new app command
@@ -79,3 +170,163 @@ func (a *DefaultCommand) IsPipe() bool {
func (a *DefaultCommand) Input(pipe, multi bool) ([]byte, error) {
return system.GetUserInputPassword(pipe, multi)
}
+
+func subCommand(parent, name, args, desc string) string {
+ return commandText(args, fmt.Sprintf("%s %s", parent, name), desc)
+}
+
+func command(name, args, desc string) string {
+ return commandText(args, name, desc)
+}
+
+func commandText(args, name, desc string) string {
+ arguments := ""
+ if len(args) > 0 {
+ arguments = fmt.Sprintf("[%s]", args)
+ }
+ return fmt.Sprintf(" %-15s %-10s %s", name, arguments, desc)
+}
+
+func exeName() (string, error) {
+ n, err := os.Executable()
+ if err != nil {
+ return "", err
+ }
+ return filepath.Base(n), nil
+}
+
+// GenerateCompletions handles creating shell completion outputs
+func GenerateCompletions(isBash, defaults bool) ([]string, error) {
+ name, err := exeName()
+ if err != nil {
+ return nil, err
+ }
+ c := Completions{
+ Executable: name,
+ InsertCommand: InsertCommand,
+ RemoveCommand: RemoveCommand,
+ TOTPSubCommands: []string{TOTPMinimalCommand, TOTPOnceCommand, TOTPShowCommand},
+ TOTPListCommand: TOTPListCommand,
+ ClipCommand: ClipCommand,
+ ShowCommand: ShowCommand,
+ MultiLineCommand: MultiLineCommand,
+ JSONCommand: JSONCommand,
+ HelpCommand: HelpCommand,
+ HelpAdvancedCommand: HelpAdvancedCommand,
+ TOTPCommand: TOTPCommand,
+ MoveCommand: MoveCommand,
+ DoList: fmt.Sprintf("%s %s", name, ListCommand),
+ DoTOTPList: fmt.Sprintf("%s %s %s", name, TOTPCommand, TOTPListCommand),
+ Options: []string{MultiLineCommand, EnvCommand, HelpCommand, ListCommand, ShowCommand, VersionCommand, JSONCommand},
+ }
+ isReadOnly := false
+ isClip := true
+ isTOTP := true
+ if !defaults {
+ ro, err := inputs.IsReadOnly()
+ if err != nil {
+ return nil, err
+ }
+ isReadOnly = ro
+ noClip, err := inputs.IsNoClipEnabled()
+ if err != nil {
+ return nil, err
+ }
+ if noClip {
+ isClip = false
+ }
+ noTOTP, err := inputs.IsNoTOTP()
+ if err != nil {
+ return nil, err
+ }
+ if noTOTP {
+ isTOTP = false
+ }
+ }
+ c.CanClip = isClip
+ c.ReadOnly = isReadOnly
+ c.CanTOTP = isTOTP
+ if c.CanClip {
+ c.Options = append(c.Options, ClipCommand)
+ c.TOTPSubCommands = append(c.TOTPSubCommands, TOTPClipCommand)
+ }
+ if !c.ReadOnly {
+ c.Options = append(c.Options, MoveCommand, RemoveCommand, InsertCommand)
+ c.TOTPSubCommands = append(c.TOTPSubCommands, TOTPInsertCommand)
+ }
+ if c.CanTOTP {
+ c.Options = append(c.Options, TOTPCommand)
+ }
+ using, err := readDoc("zsh")
+ if err != nil {
+ return nil, err
+ }
+ if isBash {
+ using, err = readDoc("bash")
+ if err != nil {
+ return nil, err
+ }
+ }
+ t, err := template.New("t").Parse(using)
+ if err != nil {
+ return nil, err
+ }
+ var buf bytes.Buffer
+ if err := t.Execute(&buf, c); err != nil {
+ return nil, err
+ }
+ return []string{buf.String()}, nil
+}
+
+// Usage return usage information
+func Usage(verbose bool) ([]string, error) {
+ name, err := exeName()
+ if err != nil {
+ return nil, err
+ }
+ var results []string
+ results = append(results, command(BashCommand, "", "generate user environment bash completion"))
+ results = append(results, subCommand(BashCommand, BashDefaultsCommand, "", "generate default bash completion"))
+ results = append(results, command(ClipCommand, "entry", "copy the entry's value into the clipboard"))
+ results = append(results, command(EnvCommand, "", "display environment variable information"))
+ results = append(results, command(HelpCommand, "", "show this usage information"))
+ results = append(results, subCommand(HelpCommand, HelpAdvancedCommand, "", "display verbose help information"))
+ results = append(results, command(InsertCommand, "entry", "insert a new entry into the store"))
+ results = append(results, command(JSONCommand, "filter", "display detailed information"))
+ results = append(results, command(ListCommand, "", "list entries"))
+ results = append(results, command(MoveCommand, "src dst", "move an entry from source to destination"))
+ results = append(results, command(MultiLineCommand, "entry", "insert a multiline entry into the store"))
+ results = append(results, command(RemoveCommand, "entry", "remove an entry from the store"))
+ results = append(results, command(ShowCommand, "entry", "show the entry's value"))
+ results = append(results, command(TOTPCommand, "entry", "display an updating totp generated code"))
+ results = append(results, subCommand(TOTPCommand, TOTPClipCommand, "entry", "copy totp code to clipboard"))
+ results = append(results, subCommand(TOTPCommand, TOTPInsertCommand, "entry", "insert a new totp entry into the store"))
+ results = append(results, subCommand(TOTPCommand, TOTPListCommand, "", "list entries with totp settings"))
+ results = append(results, subCommand(TOTPCommand, TOTPOnceCommand, "entry", "display the first generated code"))
+ results = append(results, subCommand(TOTPCommand, TOTPMinimalCommand, "entry", "display the first generated code (no details)"))
+ results = append(results, subCommand(TOTPCommand, TOTPShowCommand, "entry", "show the totp entry"))
+ results = append(results, command(VersionCommand, "", "display version information"))
+ results = append(results, command(ZshCommand, "", "generate user environment zsh completion"))
+ results = append(results, subCommand(ZshCommand, ZshDefaultsCommand, "", "generate default zsh completion"))
+ sort.Strings(results)
+ usage := []string{fmt.Sprintf("%s usage:", name)}
+ if verbose {
+ results = append(results, "")
+ doc, err := readDoc("details")
+ if err != nil {
+ return nil, err
+ }
+ results = append(results, strings.Split(strings.TrimSpace(doc), "\n")...)
+ results = append(results, "")
+ results = append(results, inputs.ListEnvironmentVariables(false)...)
+ }
+ return append(usage, results...), nil
+}
+
+func readDoc(doc string) (string, error) {
+ b, err := docs.ReadFile(filepath.Join("doc", doc))
+ if err != nil {
+ return "", err
+ }
+ return string(b), err
+}
diff --git a/internal/app/core_test.go b/internal/app/core_test.go
@@ -0,0 +1,72 @@
+package app_test
+
+import (
+ "os"
+ "strings"
+ "testing"
+
+ "github.com/enckse/lockbox/internal/app"
+)
+
+func TestUsage(t *testing.T) {
+ u, _ := app.Usage(false)
+ if len(u) != 24 {
+ t.Errorf("invalid usage, out of date? %d", len(u))
+ }
+ u, _ = app.Usage(true)
+ if len(u) != 95 {
+ t.Errorf("invalid verbose usage, out of date? %d", len(u))
+ }
+ for _, usage := range u {
+ for _, l := range strings.Split(usage, "\n") {
+ if len(l) > 79 {
+ t.Errorf("usage line > 79 (%d), line: %s", len(l), l)
+ }
+ }
+ }
+}
+
+func TestGenerateCompletions(t *testing.T) {
+ testCompletions(t, true)
+ testCompletions(t, false)
+}
+
+func testCompletions(t *testing.T, bash bool) {
+ os.Setenv("LOCKBOX_NOTOTP", "yes")
+ os.Setenv("LOCKBOX_READONLY", "yes")
+ os.Setenv("LOCKBOX_NOCLIP", "yes")
+ defaults, _ := app.GenerateCompletions(bash, true)
+ roNoTOTPClip, _ := app.GenerateCompletions(bash, false)
+ if roNoTOTPClip[0] == defaults[0] {
+ t.Error("should not match defaults")
+ }
+ os.Setenv("LOCKBOX_NOTOTP", "")
+ roNoClip, _ := app.GenerateCompletions(bash, false)
+ if roNoClip[0] == defaults[0] || roNoClip[0] == roNoTOTPClip[0] {
+ t.Error("should not equal defaults nor no totp/clip")
+ }
+ os.Setenv("LOCKBOX_READONLY", "")
+ os.Setenv("LOCKBOX_NOCLIP", "yes")
+ noClip, _ := app.GenerateCompletions(bash, false)
+ if roNoClip[0] == noClip[0] || noClip[0] == defaults[0] || noClip[0] == roNoTOTPClip[0] {
+ t.Error("readonly/noclip != noclip (nor defaults, nor ro/no totp/clip)")
+ }
+ os.Setenv("LOCKBOX_READONLY", "yes")
+ os.Setenv("LOCKBOX_NOCLIP", "")
+ ro, _ := app.GenerateCompletions(bash, false)
+ if roNoClip[0] == ro[0] || noClip[0] == ro[0] || ro[0] == defaults[0] || ro[0] == roNoTOTPClip[0] {
+ t.Error("readonly/noclip != ro (nor ro == noclip, nor ro == defaults)")
+ }
+ os.Setenv("LOCKBOX_READONLY", "")
+ os.Setenv("LOCKBOX_NOCLIP", "")
+ os.Setenv("LOCKBOX_NOTOTP", "")
+ isDefaultsToo, _ := app.GenerateCompletions(bash, false)
+ if isDefaultsToo[0] != defaults[0] {
+ t.Error("defaults should match env defaults")
+ }
+ for _, confirm := range [][]string{defaults, roNoClip, noClip, ro, isDefaultsToo} {
+ if len(confirm) != 1 {
+ t.Error("completions returned an invalid array")
+ }
+ }
+}
diff --git a/internal/cli/completions.bash b/internal/app/doc/bash
diff --git a/internal/cli/doc.txt b/internal/app/doc/details
diff --git a/internal/cli/completions.zsh b/internal/app/doc/zsh
diff --git a/internal/app/info.go b/internal/app/info.go
@@ -7,7 +7,6 @@ import (
"io"
"strings"
- "github.com/enckse/lockbox/internal/cli"
"github.com/enckse/lockbox/internal/inputs"
)
@@ -26,28 +25,28 @@ func Info(w io.Writer, command string, args []string) (bool, error) {
func info(command string, args []string) ([]string, error) {
switch command {
- case cli.HelpCommand:
+ case HelpCommand:
if len(args) > 1 {
return nil, errors.New("invalid help command")
}
isAdvanced := false
if len(args) == 1 {
- if args[0] == cli.HelpAdvancedCommand {
+ if args[0] == HelpAdvancedCommand {
isAdvanced = true
} else {
return nil, errors.New("invalid help option")
}
}
- results, err := cli.Usage(isAdvanced)
+ results, err := Usage(isAdvanced)
if err != nil {
return nil, err
}
return results, nil
- case cli.EnvCommand, cli.BashCommand, cli.ZshCommand:
- defaultFlag := cli.BashDefaultsCommand
- isEnv := command == cli.EnvCommand
+ case EnvCommand, BashCommand, ZshCommand:
+ defaultFlag := BashDefaultsCommand
+ isEnv := command == EnvCommand
if isEnv {
- defaultFlag = cli.EnvDefaultsCommand
+ defaultFlag = EnvDefaultsCommand
}
defaults, err := getInfoDefault(args, defaultFlag)
if err != nil {
@@ -56,7 +55,7 @@ func info(command string, args []string) ([]string, error) {
if isEnv {
return inputs.ListEnvironmentVariables(!defaults), nil
}
- return cli.GenerateCompletions(command == cli.BashCommand, defaults)
+ return GenerateCompletions(command == BashCommand, defaults)
}
return nil, nil
}
diff --git a/internal/app/rekey.go b/internal/app/rekey.go
@@ -9,7 +9,6 @@ import (
"strings"
"github.com/enckse/lockbox/internal/backend"
- "github.com/enckse/lockbox/internal/cli"
"github.com/enckse/lockbox/internal/inputs"
)
@@ -42,7 +41,7 @@ func NewDefaultKeyer() (DefaultKeyer, error) {
// JSON will get the JSON backing entries
func (r DefaultKeyer) JSON() (map[string]backend.JSON, error) {
- out, err := exec.Command(r.exe, cli.JSONCommand).Output()
+ out, err := exec.Command(r.exe, JSONCommand).Output()
if err != nil {
return nil, err
}
@@ -55,7 +54,7 @@ func (r DefaultKeyer) JSON() (map[string]backend.JSON, error) {
// Insert will insert the rekeying entry
func (r DefaultKeyer) Insert(entry ReKeyEntry) error {
- cmd := exec.Command(r.exe, cli.InsertCommand, entry.Path)
+ cmd := exec.Command(r.exe, InsertCommand, entry.Path)
cmd.Env = append(os.Environ(), entry.Env...)
in, err := cmd.StdinPipe()
if nil != err {
diff --git a/internal/cli/core.go b/internal/cli/core.go
@@ -1,251 +0,0 @@
-// Package cli handles CLI helpers/commands
-package cli
-
-import (
- "bytes"
- _ "embed"
- "fmt"
- "os"
- "path/filepath"
- "sort"
- "strings"
- "text/template"
-
- "github.com/enckse/lockbox/internal/inputs"
-)
-
-const (
- // TOTPCommand is the parent of totp and by defaults generates a rotating token
- TOTPCommand = "totp"
- // ConvCommand handles text conversion of the data store
- ConvCommand = "conv"
- // ClearCommand is a callback to manage clipboard clearing
- ClearCommand = "clear"
- // ClipCommand will copy values to the clipboard
- ClipCommand = "clip"
- // FindCommand is for simplistic searching of entries
- FindCommand = "find"
- // InsertCommand adds a value
- InsertCommand = "insert"
- // ListCommand lists all entries
- ListCommand = "ls"
- // MoveCommand will move source to destination
- MoveCommand = "mv"
- // ShowCommand will show the value in an entry
- ShowCommand = "show"
- // VersionCommand displays version information
- VersionCommand = "version"
- // HelpCommand shows usage
- HelpCommand = "help"
- // HelpAdvancedCommand shows advanced help
- HelpAdvancedCommand = "verbose"
- // RemoveCommand removes an entry
- RemoveCommand = "rm"
- // EnvCommand shows environment information used by lockbox
- EnvCommand = "env"
- // TOTPClipCommand is the argument for copying totp codes to clipboard
- TOTPClipCommand = ClipCommand
- // TOTPMinimalCommand is the argument for getting the short version of a code
- TOTPMinimalCommand = "minimal"
- // TOTPListCommand will list the totp-enabled entries
- TOTPListCommand = ListCommand
- // TOTPOnceCommand will perform like a normal totp request but not refresh
- TOTPOnceCommand = "once"
- // EnvDefaultsCommand will display the default env variables, not those set
- EnvDefaultsCommand = "defaults"
- // BashCommand is the command to generate bash completions
- BashCommand = "bash"
- // BashDefaultsCommand will generate environment agnostic completions
- BashDefaultsCommand = "defaults"
- // ReKeyCommand will rekey the underlying database
- ReKeyCommand = "rekey"
- // MultiLineCommand handles multi-line inserts (when not piped)
- MultiLineCommand = "multiline"
- // TOTPShowCommand is for showing the TOTP token
- TOTPShowCommand = ShowCommand
- // TOTPInsertCommand is for inserting totp tokens
- TOTPInsertCommand = InsertCommand
- // JSONCommand handles JSON outputs
- JSONCommand = "json"
- // ZshCommand is the command to generate zsh completions
- ZshCommand = "zsh"
- // ZshDefaultsCommand will generate environment agnostic completions
- ZshDefaultsCommand = "defaults"
-)
-
-var (
- //go:embed "completions.bash"
- bashCompletions string
- //go:embed "completions.zsh"
- zshCompletions string
-
- //go:embed "doc.txt"
- docSection string
-)
-
-type (
- // Completions handles the inputs to completions for templating
- Completions struct {
- Options []string
- CanClip bool
- CanTOTP bool
- ReadOnly bool
- InsertCommand string
- TOTPSubCommands []string
- TOTPListCommand string
- RemoveCommand string
- ClipCommand string
- ShowCommand string
- MultiLineCommand string
- MoveCommand string
- TOTPCommand string
- DoTOTPList string
- DoList string
- Executable string
- JSONCommand string
- HelpCommand string
- HelpAdvancedCommand string
- }
-)
-
-func subCommand(parent, name, args, desc string) string {
- return commandText(args, fmt.Sprintf("%s %s", parent, name), desc)
-}
-
-func command(name, args, desc string) string {
- return commandText(args, name, desc)
-}
-
-func commandText(args, name, desc string) string {
- arguments := ""
- if len(args) > 0 {
- arguments = fmt.Sprintf("[%s]", args)
- }
- return fmt.Sprintf(" %-15s %-10s %s", name, arguments, desc)
-}
-
-func exeName() (string, error) {
- n, err := os.Executable()
- if err != nil {
- return "", err
- }
- return filepath.Base(n), nil
-}
-
-// GenerateCompletions handles creating shell completion outputs
-func GenerateCompletions(isBash, defaults bool) ([]string, error) {
- name, err := exeName()
- if err != nil {
- return nil, err
- }
- c := Completions{
- Executable: name,
- InsertCommand: InsertCommand,
- RemoveCommand: RemoveCommand,
- TOTPSubCommands: []string{TOTPMinimalCommand, TOTPOnceCommand, TOTPShowCommand},
- TOTPListCommand: TOTPListCommand,
- ClipCommand: ClipCommand,
- ShowCommand: ShowCommand,
- MultiLineCommand: MultiLineCommand,
- JSONCommand: JSONCommand,
- HelpCommand: HelpCommand,
- HelpAdvancedCommand: HelpAdvancedCommand,
- TOTPCommand: TOTPCommand,
- MoveCommand: MoveCommand,
- DoList: fmt.Sprintf("%s %s", name, ListCommand),
- DoTOTPList: fmt.Sprintf("%s %s %s", name, TOTPCommand, TOTPListCommand),
- Options: []string{MultiLineCommand, EnvCommand, HelpCommand, ListCommand, ShowCommand, VersionCommand, JSONCommand},
- }
- isReadOnly := false
- isClip := true
- isTOTP := true
- if !defaults {
- ro, err := inputs.IsReadOnly()
- if err != nil {
- return nil, err
- }
- isReadOnly = ro
- noClip, err := inputs.IsNoClipEnabled()
- if err != nil {
- return nil, err
- }
- if noClip {
- isClip = false
- }
- noTOTP, err := inputs.IsNoTOTP()
- if err != nil {
- return nil, err
- }
- if noTOTP {
- isTOTP = false
- }
- }
- c.CanClip = isClip
- c.ReadOnly = isReadOnly
- c.CanTOTP = isTOTP
- if c.CanClip {
- c.Options = append(c.Options, ClipCommand)
- c.TOTPSubCommands = append(c.TOTPSubCommands, TOTPClipCommand)
- }
- if !c.ReadOnly {
- c.Options = append(c.Options, MoveCommand, RemoveCommand, InsertCommand)
- c.TOTPSubCommands = append(c.TOTPSubCommands, TOTPInsertCommand)
- }
- if c.CanTOTP {
- c.Options = append(c.Options, TOTPCommand)
- }
- using := zshCompletions
- if isBash {
- using = bashCompletions
- }
- t, err := template.New("t").Parse(using)
- if err != nil {
- return nil, err
- }
- var buf bytes.Buffer
- if err := t.Execute(&buf, c); err != nil {
- return nil, err
- }
- return []string{buf.String()}, nil
-}
-
-// Usage return usage information
-func Usage(verbose bool) ([]string, error) {
- name, err := exeName()
- if err != nil {
- return nil, err
- }
- var results []string
- results = append(results, command(BashCommand, "", "generate user environment bash completion"))
- results = append(results, subCommand(BashCommand, BashDefaultsCommand, "", "generate default bash completion"))
- results = append(results, command(ClipCommand, "entry", "copy the entry's value into the clipboard"))
- results = append(results, command(EnvCommand, "", "display environment variable information"))
- results = append(results, command(HelpCommand, "", "show this usage information"))
- results = append(results, subCommand(HelpCommand, HelpAdvancedCommand, "", "display verbose help information"))
- results = append(results, command(InsertCommand, "entry", "insert a new entry into the store"))
- results = append(results, command(JSONCommand, "filter", "display detailed information"))
- results = append(results, command(ListCommand, "", "list entries"))
- results = append(results, command(MoveCommand, "src dst", "move an entry from source to destination"))
- results = append(results, command(MultiLineCommand, "entry", "insert a multiline entry into the store"))
- results = append(results, command(RemoveCommand, "entry", "remove an entry from the store"))
- results = append(results, command(ShowCommand, "entry", "show the entry's value"))
- results = append(results, command(TOTPCommand, "entry", "display an updating totp generated code"))
- results = append(results, subCommand(TOTPCommand, TOTPClipCommand, "entry", "copy totp code to clipboard"))
- results = append(results, subCommand(TOTPCommand, TOTPInsertCommand, "entry", "insert a new totp entry into the store"))
- results = append(results, subCommand(TOTPCommand, TOTPListCommand, "", "list entries with totp settings"))
- results = append(results, subCommand(TOTPCommand, TOTPOnceCommand, "entry", "display the first generated code"))
- results = append(results, subCommand(TOTPCommand, TOTPMinimalCommand, "entry", "display the first generated code (no details)"))
- results = append(results, subCommand(TOTPCommand, TOTPShowCommand, "entry", "show the totp entry"))
- results = append(results, command(VersionCommand, "", "display version information"))
- results = append(results, command(ZshCommand, "", "generate user environment zsh completion"))
- results = append(results, subCommand(ZshCommand, ZshDefaultsCommand, "", "generate default zsh completion"))
- sort.Strings(results)
- usage := []string{fmt.Sprintf("%s usage:", name)}
- if verbose {
- results = append(results, "")
- results = append(results, strings.Split(strings.TrimSpace(docSection), "\n")...)
- results = append(results, "")
- results = append(results, inputs.ListEnvironmentVariables(false)...)
- }
- return append(usage, results...), nil
-}
diff --git a/internal/cli/core_test.go b/internal/cli/core_test.go
@@ -1,72 +0,0 @@
-package cli_test
-
-import (
- "os"
- "strings"
- "testing"
-
- "github.com/enckse/lockbox/internal/cli"
-)
-
-func TestUsage(t *testing.T) {
- u, _ := cli.Usage(false)
- if len(u) != 24 {
- t.Errorf("invalid usage, out of date? %d", len(u))
- }
- u, _ = cli.Usage(true)
- if len(u) != 95 {
- t.Errorf("invalid verbose usage, out of date? %d", len(u))
- }
- for _, usage := range u {
- for _, l := range strings.Split(usage, "\n") {
- if len(l) > 79 {
- t.Errorf("usage line > 79 (%d), line: %s", len(l), l)
- }
- }
- }
-}
-
-func TestGenerateCompletions(t *testing.T) {
- testCompletions(t, true)
- testCompletions(t, false)
-}
-
-func testCompletions(t *testing.T, bash bool) {
- os.Setenv("LOCKBOX_NOTOTP", "yes")
- os.Setenv("LOCKBOX_READONLY", "yes")
- os.Setenv("LOCKBOX_NOCLIP", "yes")
- defaults, _ := cli.GenerateCompletions(bash, true)
- roNoTOTPClip, _ := cli.GenerateCompletions(bash, false)
- if roNoTOTPClip[0] == defaults[0] {
- t.Error("should not match defaults")
- }
- os.Setenv("LOCKBOX_NOTOTP", "")
- roNoClip, _ := cli.GenerateCompletions(bash, false)
- if roNoClip[0] == defaults[0] || roNoClip[0] == roNoTOTPClip[0] {
- t.Error("should not equal defaults nor no totp/clip")
- }
- os.Setenv("LOCKBOX_READONLY", "")
- os.Setenv("LOCKBOX_NOCLIP", "yes")
- noClip, _ := cli.GenerateCompletions(bash, false)
- if roNoClip[0] == noClip[0] || noClip[0] == defaults[0] || noClip[0] == roNoTOTPClip[0] {
- t.Error("readonly/noclip != noclip (nor defaults, nor ro/no totp/clip)")
- }
- os.Setenv("LOCKBOX_READONLY", "yes")
- os.Setenv("LOCKBOX_NOCLIP", "")
- ro, _ := cli.GenerateCompletions(bash, false)
- if roNoClip[0] == ro[0] || noClip[0] == ro[0] || ro[0] == defaults[0] || ro[0] == roNoTOTPClip[0] {
- t.Error("readonly/noclip != ro (nor ro == noclip, nor ro == defaults)")
- }
- os.Setenv("LOCKBOX_READONLY", "")
- os.Setenv("LOCKBOX_NOCLIP", "")
- os.Setenv("LOCKBOX_NOTOTP", "")
- isDefaultsToo, _ := cli.GenerateCompletions(bash, false)
- if isDefaultsToo[0] != defaults[0] {
- t.Error("defaults should match env defaults")
- }
- for _, confirm := range [][]string{defaults, roNoClip, noClip, ro, isDefaultsToo} {
- if len(confirm) != 1 {
- t.Error("completions returned an invalid array")
- }
- }
-}
diff --git a/internal/totp/core.go b/internal/totp/core.go
@@ -12,7 +12,6 @@ import (
"github.com/enckse/lockbox/internal/app"
"github.com/enckse/lockbox/internal/backend"
- "github.com/enckse/lockbox/internal/cli"
"github.com/enckse/lockbox/internal/inputs"
"github.com/enckse/lockbox/internal/platform"
"github.com/enckse/lockbox/internal/system"
@@ -260,21 +259,21 @@ func NewArguments(args []string, tokenType string) (*Arguments, error) {
sub := args[0]
needs := true
switch sub {
- case cli.TOTPListCommand:
+ case app.TOTPListCommand:
needs = false
if len(args) != 1 {
return nil, errors.New("list takes no arguments")
}
opts.Mode = ListMode
- case cli.TOTPInsertCommand:
+ case app.TOTPInsertCommand:
opts.Mode = InsertMode
- case cli.TOTPShowCommand:
+ case app.TOTPShowCommand:
opts.Mode = ShowMode
- case cli.TOTPClipCommand:
+ case app.TOTPClipCommand:
opts.Mode = ClipMode
- case cli.TOTPMinimalCommand:
+ case app.TOTPMinimalCommand:
opts.Mode = MinimalMode
- case cli.TOTPOnceCommand:
+ case app.TOTPOnceCommand:
opts.Mode = OnceMode
default:
return nil, ErrUnknownTOTPMode