commit 7f97a217f10a39b499eb729275eb4cbed0c5e079
parent 9f151d333f1000d8aa675cec11fa206c53d7706c
Author: Sean Enck <sean@ttypty.com>
Date: Sat, 29 Jul 2023 08:12:30 -0400
documentation is now templated too
Diffstat:
8 files changed, 102 insertions(+), 53 deletions(-)
diff --git a/internal/app/completions.go b/internal/app/completions.go
@@ -35,13 +35,9 @@ type (
)
// GenerateCompletions handles creating shell completion outputs
-func GenerateCompletions(isBash, defaults bool) ([]string, error) {
- name, err := exeName()
- if err != nil {
- return nil, err
- }
+func GenerateCompletions(isBash, defaults bool, exe string) ([]string, error) {
c := Completions{
- Executable: name,
+ Executable: exe,
InsertCommand: InsertCommand,
RemoveCommand: RemoveCommand,
TOTPSubCommands: []string{TOTPMinimalCommand, TOTPOnceCommand, TOTPShowCommand},
@@ -54,8 +50,8 @@ func GenerateCompletions(isBash, defaults bool) ([]string, error) {
HelpAdvancedCommand: HelpAdvancedCommand,
TOTPCommand: TOTPCommand,
MoveCommand: MoveCommand,
- DoList: fmt.Sprintf("%s %s", name, ListCommand),
- DoTOTPList: fmt.Sprintf("%s %s %s", name, TOTPCommand, TOTPListCommand),
+ DoList: fmt.Sprintf("%s %s", exe, ListCommand),
+ DoTOTPList: fmt.Sprintf("%s %s %s", exe, TOTPCommand, TOTPListCommand),
Options: []string{MultiLineCommand, EnvCommand, HelpCommand, ListCommand, ShowCommand, VersionCommand, JSONCommand},
}
isReadOnly := false
diff --git a/internal/app/completions_test.go b/internal/app/completions_test.go
@@ -16,32 +16,32 @@ 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)
+ defaults, _ := app.GenerateCompletions(bash, true, "lb")
+ roNoTOTPClip, _ := app.GenerateCompletions(bash, false, "lb")
if roNoTOTPClip[0] == defaults[0] {
t.Error("should not match defaults")
}
os.Setenv("LOCKBOX_NOTOTP", "")
- roNoClip, _ := app.GenerateCompletions(bash, false)
+ roNoClip, _ := app.GenerateCompletions(bash, false, "lb")
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)
+ noClip, _ := app.GenerateCompletions(bash, false, "lb")
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)
+ ro, _ := app.GenerateCompletions(bash, false, "lb")
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)
+ isDefaultsToo, _ := app.GenerateCompletions(bash, false, "lb")
if isDefaultsToo[0] != defaults[0] {
t.Error("defaults should match env defaults")
}
diff --git a/internal/app/core.go b/internal/app/core.go
@@ -2,6 +2,7 @@
package app
import (
+ "bytes"
"embed"
"fmt"
"io"
@@ -9,6 +10,7 @@ import (
"path/filepath"
"sort"
"strings"
+ "text/template"
"github.com/enckse/lockbox/internal/backend"
"github.com/enckse/lockbox/internal/config"
@@ -89,6 +91,19 @@ type (
args []string
tx *backend.Transaction
}
+ // Documentation is how documentation segments are templated
+ Documentation struct {
+ Executable string
+ MoveCommand string
+ RemoveCommand string
+ ReKeyCommand string
+ ReKey struct {
+ Store string
+ KeyFile string
+ Key string
+ KeyMode string
+ }
+ }
)
// NewDefaultCommand creates a new app command
@@ -161,20 +176,8 @@ func commandText(args, name, desc string) string {
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
-}
-
// Usage return usage information
-func Usage(verbose bool) ([]string, error) {
- name, err := exeName()
- if err != nil {
- return nil, err
- }
+func Usage(verbose bool, exe string) ([]string, error) {
var results []string
results = append(results, command(BashCommand, "", "generate user environment bash completion"))
results = append(results, subCommand(BashCommand, BashDefaultsCommand, "", "generate default bash completion"))
@@ -200,20 +203,42 @@ func Usage(verbose bool) ([]string, error) {
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)}
+ usage := []string{fmt.Sprintf("%s usage:", exe)}
if verbose {
results = append(results, "")
doc, err := readDoc("details")
if err != nil {
return nil, err
}
- results = append(results, strings.Split(strings.TrimSpace(doc), "\n")...)
+ t, err := template.New("d").Parse(doc)
+ if err != nil {
+ return nil, err
+ }
+ document := Documentation{
+ Executable: filepath.Base(exe),
+ MoveCommand: MoveCommand,
+ RemoveCommand: RemoveCommand,
+ ReKeyCommand: ReKeyCommand,
+ }
+ document.ReKey.Store = setDocFlag(config.ReKeyStoreFlag)
+ document.ReKey.Key = setDocFlag(config.ReKeyKeyFlag)
+ document.ReKey.KeyMode = setDocFlag(config.ReKeyKeyModeFlag)
+ document.ReKey.KeyFile = setDocFlag(config.ReKeyKeyModeFlag)
+ var buf bytes.Buffer
+ if err := t.Execute(&buf, document); err != nil {
+ return nil, err
+ }
+ results = append(results, strings.Split(strings.TrimSpace(buf.String()), "\n")...)
results = append(results, "")
results = append(results, config.ListEnvironmentVariables()...)
}
return append(usage, results...), nil
}
+func setDocFlag(f string) string {
+ return fmt.Sprintf("-%s=", f)
+}
+
func readDoc(doc string) (string, error) {
b, err := docs.ReadFile(filepath.Join("doc", doc))
if err != nil {
diff --git a/internal/app/core_test.go b/internal/app/core_test.go
@@ -8,12 +8,12 @@ import (
)
func TestUsage(t *testing.T) {
- u, _ := app.Usage(false)
+ u, _ := app.Usage(false, "lb")
if len(u) != 24 {
t.Errorf("invalid usage, out of date? %d", len(u))
}
- u, _ = app.Usage(true)
- if len(u) != 97 {
+ u, _ = app.Usage(true, "lb")
+ if len(u) != 99 {
t.Errorf("invalid verbose usage, out of date? %d", len(u))
}
for _, usage := range u {
diff --git a/internal/app/doc/details b/internal/app/doc/details
@@ -1,30 +1,30 @@
[database]
-While 'lb' uses a kdbx file (version 4), it is opinionated about how to store
-data within the file. This means 'lb' should be the only way a user normally
+While '{{ $.Executable }}' uses a kdbx file (version 4), it is opinionated about how to store
+data within the file. This means '{{ $.Executable }}' should be the only way a user normally
interacts with or changes the underlying database. However as the file is a
normal kdbx file format (e.g. can be opened in tools supporting the format) it
is possible to use the database in those applications, just take caution when
-changing it outside of 'lb' usage. If a database not normally used by 'lb' is
-to be used by 'lb', try using the various readonly settings to control
+changing it outside of '{{ $.Executable }}' usage. If a database not normally used by '{{ $.Executable }}' is
+to be used by '{{ $.Executable }}', try using the various readonly settings to control
interactions.
[globs]
-The 'rm' and 'mv' command can handle a simplistic glob if it is at the END
+The '{{ $.RemoveCommand }}' and '{{ $.MoveCommand }}' command can handle a simplistic glob if it is at the END
of the path. This allows for bulk-removal of entries at multiple levels.
Confirmation will still be required for removal (matching entries will be
listed).
-For 'mv' the destination must NOT be an entry but the final destination
+For '{{ $.MoveCommand }}' the destination must NOT be an entry but the final destination
location for all matched entries. Overwriting is not allowed by moving
via glob and moving via globs can ONLY be done via leaf level globs.
Examples:
-lb rm path/to/leaf/dir/*
+{{ $.Executable }} {{ $.RemoveCommand }} path/to/leaf/dir/*
-lb rm path/to/*
+{{ $.Executable }} {{ $.RemoveCommand }} path/to/*
-lb mv path/to/* new/path/
+{{ $.Executable }} {{ $.MoveCommand }} path/to/* new/path/
[clipboard]
By default clipboard commands are detected via determing the platform and
@@ -32,17 +32,19 @@ utilizing default commands to interact with (copy to/paste to) the clipboard.
These settings can be overriden via environment variables.
[totp]
-By default 'lb' tries to use some reasonable defaults to setup/manage oauth
+By default '{{ $.Executable }}' tries to use some reasonable defaults to setup/manage oauth
token inputs and displaying of code outputs. Many of these settings can be
changed via environment variables.
[rekey]
-The password store can have the key (and file) changed via the 'rekey'
-subcommand. This command requires a '-store' value and some combination
-of '-key=', '-keymode=', and '-keyfile=' depending on the new database
+The password store can have the key (and file) changed via the '{{ $.ReKeyCommand }}'
+subcommand. This command requires a '{{ $.ReKey.Store }}' value and some combination
+of '{{ $.ReKey.Key }}', '{{ $.ReKey.KeyMode }}', and '{{ $.ReKey.KeyFile }}' depending on the new database
credential preferences. The settings correspond to the 'LOCKBOX_'
-settings normally used when running `lb`.
+settings normally used when running `{{ $.Executable }}`.
+
+Note that is an advanced feature and should be used with caution/backups/etc.
[environment variables]
-The following environment variables can alter how 'lb' works.
+The following environment variables can alter how '{{ $.Executable }}' works.
diff --git a/internal/app/info.go b/internal/app/info.go
@@ -5,6 +5,8 @@ import (
"errors"
"fmt"
"io"
+ "os"
+ "path/filepath"
"strings"
"github.com/enckse/lockbox/internal/config"
@@ -23,6 +25,14 @@ func Info(w io.Writer, command string, args []string) (bool, error) {
return false, nil
}
+func exeName() (string, error) {
+ exe, err := os.Executable()
+ if err != nil {
+ return "", err
+ }
+ return filepath.Base(exe), nil
+}
+
func info(command string, args []string) ([]string, error) {
switch command {
case HelpCommand:
@@ -37,7 +47,11 @@ func info(command string, args []string) ([]string, error) {
return nil, errors.New("invalid help option")
}
}
- results, err := Usage(isAdvanced)
+ exe, err := exeName()
+ if err != nil {
+ return nil, err
+ }
+ results, err := Usage(isAdvanced, exe)
if err != nil {
return nil, err
}
@@ -56,7 +70,11 @@ func info(command string, args []string) ([]string, error) {
if err != nil {
return nil, err
}
- return GenerateCompletions(command == BashCommand, defaults)
+ exe, err := exeName()
+ if err != nil {
+ return nil, err
+ }
+ return GenerateCompletions(command == BashCommand, defaults, exe)
}
return nil, nil
}
diff --git a/internal/config/core.go b/internal/config/core.go
@@ -31,6 +31,14 @@ const (
// WindowsLinuxPlatform for WSL subsystems
WindowsLinuxPlatform = "wsl"
unknownPlatform = ""
+ // ReKeyStoreFlag is the flag used for rekey to set the store
+ ReKeyStoreFlag = "store"
+ // ReKeyKeyFileFlag is the flag used for rekey to set the keyfile
+ ReKeyKeyFileFlag = "keyfile"
+ // ReKeyKeyFlag is the flag used for rekey to set the key
+ ReKeyKeyFlag = "key"
+ // ReKeyKeyModeFlag is the flag used for rekey to set the key mode
+ ReKeyKeyModeFlag = "keymode"
)
var detectEnvironmentPaths = []string{filepath.Join(".config", envFile), filepath.Join(".config", "lockbox", envFile)}
diff --git a/internal/config/vars.go b/internal/config/vars.go
@@ -87,10 +87,10 @@ var (
// GetReKey will get the rekey environment settings
func GetReKey(args []string) ([]string, error) {
set := flag.NewFlagSet("rekey", flag.ExitOnError)
- store := set.String("store", "", "new store")
- key := set.String("key", "", "new key")
- keyFile := set.String("keyfile", "", "new keyfile")
- keyMode := set.String("keymode", "", "new keymode")
+ store := set.String(ReKeyStoreFlag, "", "new store")
+ key := set.String(ReKeyKeyFlag, "", "new key")
+ keyFile := set.String(ReKeyKeyFileFlag, "", "new keyfile")
+ keyMode := set.String(ReKeyKeyModeFlag, "", "new keymode")
if err := set.Parse(args); err != nil {
return nil, err
}