From 57e29681526d12aeff439b85e6ce9862f267e341 Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Mon, 8 Jun 2026 06:34:15 +0300 Subject: [PATCH 1/3] add local completion installer --- op.conf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/op.conf b/op.conf index 934f35e..95d7918 100644 --- a/op.conf +++ b/op.conf @@ -44,3 +44,7 @@ tidy: go mod tidy verify: go mod verify #? verify dependencies match checksums + +## Completions + +comp-install: config completion bash > ~/.local/share/bash-completion/completions/config From 5347dcef10d5bc07347b218708c87020b7dc7d1a Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Mon, 8 Jun 2026 10:24:29 +0300 Subject: [PATCH 2/3] - Replace `CONFIG_FILE` positional arg with `--file PATH` --- README.md | 16 ++++---- cmd/cli.go | 72 +++++++++++++++++------------------- cmd/cli_edit_test.go | 6 +-- cmd/cli_get_list_test.go | 61 +++++++++++++++++++----------- cmd/cli_help_test.go | 40 +++++++++++--------- cmd/cli_set_test.go | 44 +++++++++++----------- cmd/cli_unset_delete_test.go | 46 +++++++++++------------ cmd/help/array-add.txt | 8 ++-- cmd/help/array-delete.txt | 8 ++-- cmd/help/array-set.txt | 8 ++-- cmd/help/delete.txt | 10 ++--- cmd/help/dump.txt | 16 ++++---- cmd/help/edit.txt | 8 ++-- cmd/help/get.txt | 12 +++--- cmd/help/list.txt | 14 +++---- cmd/help/set.txt | 20 +++++----- cmd/help/unset.txt | 12 +++--- format/document.go | 9 ----- format/document_test.go | 19 ---------- 19 files changed, 210 insertions(+), 219 deletions(-) diff --git a/README.md b/README.md index fce6eb1..4077762 100644 --- a/README.md +++ b/README.md @@ -55,11 +55,11 @@ Supported config files: Run `config help formats` for format-specific behavior. ```bash -config get path/to/config.toml server.port -config set path/to/config.toml server.port 3000 -config array add path/to/config.toml sandbox_workspace_write.writable_roots '$HOME/.cache' -config unset path/to/config.toml server.password -config list path/to/config.toml server +config get -f path/to/config.toml server.port +config set -f path/to/config.toml server.port 3000 +config array add -f path/to/config.toml sandbox_workspace_write.writable_roots '$HOME/.cache' +config unset -f path/to/config.toml server.password +config list -f path/to/config.toml server ``` For repeated edits, set `CONFIG_FILE` once: @@ -74,20 +74,20 @@ config edit Use `--diff` or `--diff --color` (`-dc`) to preview an edit: ```bash -config set config.yaml server.port 3000 -dc +config set -f config.yaml server.port 3000 -dc ``` Use `--string` when a value should remain text even if it looks like a typed literal: ```bash -config set config.toml version 1.0 --string +config set -f config.toml version 1.0 --string ``` Use `--in` and `--on` to update records: ```bash -config set config.toml port 3000 --in servers --on name:api +config set -f config.toml port 3000 --in servers --on name:api ``` ## Feature Specs diff --git a/cmd/cli.go b/cmd/cli.go index cf63c93..443143a 100644 --- a/cmd/cli.go +++ b/cmd/cli.go @@ -146,14 +146,13 @@ func NewRootCommand(version string, stdout, stderr io.Writer) *cobra.Command { func newSetCommand(stdout, stderr io.Writer) *cobra.Command { var opts setOptions cmd := &cobra.Command{ - Use: "set [CONFIG_FILE] KEY VALUE [options]", + Use: "set KEY VALUE [options]", Short: "Create or update config values", Args: func(cmd *cobra.Command, args []string) error { - configFile, rest, err := parseCommandArgs(args, 2, 2, "usage: config set [CONFIG_FILE] KEY VALUE [options]") + rest, err := parseCommandArgs(args, 2, 2, "usage: config set KEY VALUE [options]") if err != nil { return err } - opts.configFile = configFile opts.key = rest[0] opts.value = rest[1] return nil @@ -166,6 +165,7 @@ func newSetCommand(stdout, stderr io.Writer) *cobra.Command { }, } cmd.SetHelpFunc(helpPrinter("set")) + addFileFlag(cmd, &opts.configFile) cmd.Flags().StringVar(&opts.in, "in", "", "Edit a record in COLLECTION") cmd.Flags().StringVar(&opts.on, "on", "", "Select or create a record by FIELD:VALUE") cmd.Flags().BoolVarP(&opts.string, "string", "s", false, "Store VALUE as a string") @@ -179,14 +179,13 @@ func newSetCommand(stdout, stderr io.Writer) *cobra.Command { func newGetCommand(stdout io.Writer) *cobra.Command { var opts getOptions cmd := &cobra.Command{ - Use: "get [CONFIG_FILE] KEY", + Use: "get KEY", Short: "Show a config value", Args: func(cmd *cobra.Command, args []string) error { - configFile, rest, err := parseCommandArgs(args, 1, 1, "usage: config get [CONFIG_FILE] KEY") + rest, err := parseCommandArgs(args, 1, 1, "usage: config get KEY") if err != nil { return err } - opts.configFile = configFile opts.key = rest[0] return nil }, @@ -195,6 +194,7 @@ func newGetCommand(stdout io.Writer) *cobra.Command { }, } cmd.SetHelpFunc(helpPrinter("get")) + addFileFlag(cmd, &opts.configFile) cmd.Flags().StringVar(&opts.in, "in", "", "Read a field from a record in COLLECTION") cmd.Flags().StringArrayVar(&opts.on, "on", nil, "Select a record by FIELD:VALUE") return cmd @@ -203,14 +203,13 @@ func newGetCommand(stdout io.Writer) *cobra.Command { func newUnsetCommand(stdout, stderr io.Writer) *cobra.Command { var opts unsetOptions cmd := &cobra.Command{ - Use: "unset [CONFIG_FILE] KEY [options]", + Use: "unset KEY [options]", Short: "Delete a config value", Args: func(cmd *cobra.Command, args []string) error { - configFile, rest, err := parseCommandArgs(args, 1, 1, "usage: config unset [CONFIG_FILE] KEY [options]") + rest, err := parseCommandArgs(args, 1, 1, "usage: config unset KEY [options]") if err != nil { return err } - opts.configFile = configFile opts.key = rest[0] return nil }, @@ -223,6 +222,7 @@ func newUnsetCommand(stdout, stderr io.Writer) *cobra.Command { }, } cmd.SetHelpFunc(helpPrinter("unset")) + addFileFlag(cmd, &opts.configFile) cmd.Flags().StringVar(&opts.in, "in", "", "Remove a field from a record in COLLECTION") cmd.Flags().StringArrayVar(&opts.on, "on", nil, "Select a record by FIELD:VALUE") cmd.Flags().StringVar(&opts.ifValue, "if", "", "Only unset when the current value matches VALUE") @@ -237,15 +237,14 @@ func newUnsetCommand(stdout, stderr io.Writer) *cobra.Command { func newDeleteCommand(stdout, stderr io.Writer) *cobra.Command { var opts deleteOptions cmd := &cobra.Command{ - Use: "delete [CONFIG_FILE] KEY [options]", + Use: "delete KEY [options]", Aliases: []string{"del"}, Short: "Delete a config container", Args: func(cmd *cobra.Command, args []string) error { - configFile, rest, err := parseCommandArgs(args, 1, 1, "usage: config delete [CONFIG_FILE] KEY [options]") + rest, err := parseCommandArgs(args, 1, 1, "usage: config delete KEY [options]") if err != nil { return err } - opts.configFile = configFile opts.key = rest[0] return nil }, @@ -257,6 +256,7 @@ func newDeleteCommand(stdout, stderr io.Writer) *cobra.Command { }, } cmd.SetHelpFunc(helpPrinter("delete")) + addFileFlag(cmd, &opts.configFile) cmd.Flags().StringArrayVar(&opts.on, "on", nil, "Select a record by FIELD:VALUE") cmd.Flags().BoolVar(&opts.ifEmpty, "if-empty", false, "Only delete when the container has no values") cmd.Flags().BoolVarP(&opts.dry, "dry", "n", false, "Print the updated config without modifying the file") @@ -269,15 +269,14 @@ func newDeleteCommand(stdout, stderr io.Writer) *cobra.Command { func newListCommand(stdout io.Writer) *cobra.Command { var opts listOptions cmd := &cobra.Command{ - Use: "list [CONFIG_FILE] [KEY]", + Use: "list [KEY]", Aliases: []string{"ls"}, Short: "Show config values", Args: func(cmd *cobra.Command, args []string) error { - configFile, rest, err := parseCommandArgs(args, 0, 1, "usage: config list [CONFIG_FILE] [KEY]") + rest, err := parseCommandArgs(args, 0, 1, "usage: config list [KEY]") if err != nil { return err } - opts.configFile = configFile if len(rest) == 1 { opts.key = rest[0] } @@ -288,6 +287,7 @@ func newListCommand(stdout io.Writer) *cobra.Command { }, } cmd.SetHelpFunc(helpPrinter("list")) + addFileFlag(cmd, &opts.configFile) cmd.Flags().BoolVarP(&opts.color, "color", "c", false, "Colorize keys and separators") return cmd } @@ -295,14 +295,13 @@ func newListCommand(stdout io.Writer) *cobra.Command { func newDumpCommand(stdout io.Writer) *cobra.Command { var opts dumpOptions cmd := &cobra.Command{ - Use: "dump [CONFIG_FILE] [KEY]", + Use: "dump [KEY]", Short: "Dump config data", Args: func(cmd *cobra.Command, args []string) error { - configFile, rest, err := parseCommandArgs(args, 0, 1, "usage: config dump [CONFIG_FILE] [KEY]") + rest, err := parseCommandArgs(args, 0, 1, "usage: config dump [KEY]") if err != nil { return err } - opts.configFile = configFile if len(rest) == 1 { opts.key = rest[0] } @@ -313,6 +312,7 @@ func newDumpCommand(stdout io.Writer) *cobra.Command { }, } cmd.SetHelpFunc(helpPrinter("dump")) + addFileFlag(cmd, &opts.configFile) cmd.Flags().BoolVar(&opts.json, "json", false, "Dump as JSON") return cmd } @@ -320,14 +320,13 @@ func newDumpCommand(stdout io.Writer) *cobra.Command { func newEditCommand(stdout, stderr io.Writer) *cobra.Command { var opts editOptions cmd := &cobra.Command{ - Use: "edit [CONFIG_FILE]", + Use: "edit [options]", Short: "Open the config file in an editor", Args: func(cmd *cobra.Command, args []string) error { - configFile, _, err := parseCommandArgs(args, 0, 0, "usage: config edit [CONFIG_FILE]") + _, err := parseCommandArgs(args, 0, 0, "usage: config edit [options]") if err != nil { return err } - opts.configFile = configFile return nil }, RunE: func(cmd *cobra.Command, args []string) error { @@ -335,6 +334,7 @@ func newEditCommand(stdout, stderr io.Writer) *cobra.Command { }, } cmd.SetHelpFunc(helpPrinter("edit")) + addFileFlag(cmd, &opts.configFile) return cmd } @@ -385,15 +385,14 @@ func newArrayCommand(stdout, stderr io.Writer) *cobra.Command { func newArrayEditCommand(name, short string, stdout, stderr io.Writer, edit func(format.Document, string, arrayOptions) (string, error), aliases ...string) *cobra.Command { var opts arrayOptions cmd := &cobra.Command{ - Use: name + " [CONFIG_FILE] KEY VALUE... [options]", + Use: name + " KEY VALUE... [options]", Aliases: aliases, Short: short, Args: func(cmd *cobra.Command, args []string) error { - configFile, rest, err := parseCommandArgs(args, 2, -1, "usage: config array "+name+" [CONFIG_FILE] KEY VALUE... [options]") + rest, err := parseCommandArgs(args, 2, -1, "usage: config array "+name+" KEY VALUE... [options]") if err != nil { return err } - opts.configFile = configFile opts.key = rest[0] opts.values = rest[1:] return nil @@ -406,6 +405,7 @@ func newArrayEditCommand(name, short string, stdout, stderr io.Writer, edit func }, } cmd.SetHelpFunc(helpPrinter("array-" + name)) + addFileFlag(cmd, &opts.configFile) cmd.Flags().BoolVarP(&opts.dry, "dry", "n", false, "Print the updated config without modifying the file") cmd.Flags().BoolVarP(&opts.diff, "diff", "d", false, "Print a unified diff without modifying the file") cmd.Flags().BoolVarP(&opts.color, "color", "c", false, "Colorize diff output") @@ -499,22 +499,15 @@ func commandHelpTopic(name string) (string, bool) { } } -func parseCommandArgs(args []string, minRest, maxRest int, usage string) (string, []string, error) { - configFile := "" - rest := args - if len(args) > 0 && format.TargetPath(args[0]) { - configFile = args[0] - rest = args[1:] - } else if env := os.Getenv("CONFIG_FILE"); env != "" { - configFile = env - } else { - return "", nil, errors.New("config file not specified") - } +func addFileFlag(cmd *cobra.Command, target *string) { + cmd.Flags().StringVarP(target, "file", "f", "", "Path to the config file") +} - if len(rest) < minRest || (maxRest >= 0 && len(rest) > maxRest) { - return "", nil, usageError{usage} +func parseCommandArgs(args []string, minRest, maxRest int, usage string) ([]string, error) { + if len(args) < minRest || (maxRest >= 0 && len(args) > maxRest) { + return nil, usageError{usage} } - return configFile, rest, nil + return args, nil } func helpPrinter(name string) func(*cobra.Command, []string) { @@ -840,6 +833,9 @@ func resolveConfigFile(explicit string) (string, error) { if explicit != "" { return explicit, nil } + if env := os.Getenv("CONFIG_FILE"); env != "" { + return env, nil + } return "", errors.New("config file not specified") } diff --git a/cmd/cli_edit_test.go b/cmd/cli_edit_test.go index 9e80b3a..cc75fb5 100644 --- a/cmd/cli_edit_test.go +++ b/cmd/cli_edit_test.go @@ -24,7 +24,7 @@ func TestEditOpensExplicitConfigFileInEditor(t *testing.T) { t.Setenv("EDITOR", writeEditorScript(t)) t.Setenv("CONFIG_EDIT_LOG", logPath) - err := Execute([]string{"edit", path}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"edit", "-f", path}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -81,12 +81,12 @@ func TestEditRejectsExtraArguments(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "name = \"demo\"\n") - err := Execute([]string{"edit", path, "extra"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"edit", "-f", path, "extra"}, "1.2.3", &stdout, &stderr) if err == nil { t.Fatal("expected error") } - if err.Error() != "usage: config edit [CONFIG_FILE]" { + if err.Error() != "usage: config edit [options]" { t.Fatalf("unexpected error: %v", err) } } diff --git a/cmd/cli_get_list_test.go b/cmd/cli_get_list_test.go index cc6b2e0..833fef4 100644 --- a/cmd/cli_get_list_test.go +++ b/cmd/cli_get_list_test.go @@ -12,7 +12,7 @@ func TestGetPrintsTOMLValue(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "[database]\nport = 5432\n") - err := Execute([]string{"get", path, "database.port"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"get", "-f", path, "database.port"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -26,7 +26,7 @@ func TestGetPrintsSelectedTOMLRecordValue(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "[[servers]]\nname = \"api\"\nport = 3000\n\n[[servers]]\nname = \"worker\"\nport = 3001\n") - err := Execute([]string{"get", path, "port", "--in", "servers", "--on", "name:worker"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"get", "-f", path, "port", "--in", "servers", "--on", "name:worker"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -40,7 +40,7 @@ func TestGetSelectedRecordRequiresInWithOn(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "[[servers]]\nname = \"api\"\nport = 3000\n") - err := Execute([]string{"get", path, "port", "--on", "name:api"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"get", "-f", path, "port", "--on", "name:api"}, "1.2.3", &stdout, &stderr) if err == nil { t.Fatal("expected error") @@ -54,7 +54,7 @@ func TestGetSelectedRecordRequiresOnWithIn(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "[[servers]]\nname = \"api\"\nport = 3000\n") - err := Execute([]string{"get", path, "port", "--in", "servers"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"get", "-f", path, "port", "--in", "servers"}, "1.2.3", &stdout, &stderr) if err == nil { t.Fatal("expected error") @@ -97,14 +97,14 @@ func TestGetFailsWhenConfigFileIsNotSpecified(t *testing.T) { } func TestGetUnsupportedExplicitConfigFileReportsUnsupportedFormat(t *testing.T) { + clearConfigFileEnv(t) var stdout, stderr bytes.Buffer path := filepath.Join(t.TempDir(), "config.conf") if err := os.WriteFile(path, []byte("port = 3000\n"), 0644); err != nil { t.Fatal(err) } - t.Setenv("CONFIG_FILE", path) - err := Execute([]string{"get", "database.port"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"get", "-f", path, "database.port"}, "1.2.3", &stdout, &stderr) if err == nil { t.Fatal("expected error") @@ -121,7 +121,7 @@ func TestGetPrintsJSONValue(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempJSON(t, `{"database":{"port":5432}}`) - err := Execute([]string{"get", path, "database.port"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"get", "-f", path, "database.port"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -138,7 +138,7 @@ func TestGetPrintsINIValue(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempINI(t, "title = demo app\n[server]\nport = 3000\n") - err := Execute([]string{"get", path, "server.port"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"get", "-f", path, "server.port"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -151,13 +151,13 @@ func TestGetPrintsINIValue(t *testing.T) { } } -func TestExplicitTargetPathOverridesConfigFileEnv(t *testing.T) { +func TestFileFlagOverridesConfigFileEnv(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "[database]\nport = 5432\n") t.Setenv("CONFIG_FILE", path) yamlPath := writeTempYAML(t, "database:\n port: 5432\n") - err := Execute([]string{"get", yamlPath, "database.port"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"get", "-f", yamlPath, "database.port"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -170,11 +170,28 @@ func TestExplicitTargetPathOverridesConfigFileEnv(t *testing.T) { } } +func TestPositionalTargetPathIsNotAccepted(t *testing.T) { + var stdout, stderr bytes.Buffer + path := writeTempTOML(t, "[database]\nport = 5432\n") + + err := Execute([]string{"get", path, "database.port"}, "1.2.3", &stdout, &stderr) + + if err == nil { + t.Fatal("expected error") + } + if stdout.Len() != 0 { + t.Fatalf("stdout = %q", stdout.String()) + } + if !strings.Contains(err.Error(), "usage: config get KEY") { + t.Fatalf("unexpected error: %v", err) + } +} + func TestGetMissingRequiredArgFailsAfterConfigFileIsResolved(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "[database]\nport = 5432\n") - err := Execute([]string{"get", path}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"get", "-f", path}, "1.2.3", &stdout, &stderr) if err == nil { t.Fatal("expected error") @@ -182,7 +199,7 @@ func TestGetMissingRequiredArgFailsAfterConfigFileIsResolved(t *testing.T) { if stdout.Len() != 0 { t.Fatalf("stdout = %q", stdout.String()) } - if !strings.Contains(err.Error(), "usage: config get [CONFIG_FILE] KEY") { + if !strings.Contains(err.Error(), "usage: config get KEY") { t.Fatalf("unexpected error: %v", err) } } @@ -191,7 +208,7 @@ func TestListPrintsTOMLValues(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "title = \"demo app\"\n\n[server]\nport = 3000\nenabled = true\n") - err := Execute([]string{"list", path}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"list", "-f", path}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -206,7 +223,7 @@ func TestListAliasPrintsTOMLValues(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "[server]\nport = 3000\n") - err := Execute([]string{"ls", path}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"ls", "-f", path}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -221,7 +238,7 @@ func TestListPrintsTOMLValuesWithColor(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "[server]\nport = 3000\n") - err := Execute([]string{"list", path, "--color"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"list", "-f", path, "--color"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -236,7 +253,7 @@ func TestListPrintsTOMLValuesUnderTable(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "title = \"demo app\"\n\n[server]\nport = 3000\nenabled = true\n") - err := Execute([]string{"list", path, "server"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"list", "-f", path, "server"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -251,7 +268,7 @@ func TestListPrintsINIValuesUnderSection(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempINI(t, "title = demo app\n[server]\nport = 3000\nenabled = true\n") - err := Execute([]string{"list", path, "server"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"list", "-f", path, "server"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -296,7 +313,7 @@ func TestDumpPrintsTOMLAsYAML(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "title = \"demo app\"\n\n[server]\nport = 3000\nenabled = true\n") - err := Execute([]string{"dump", path}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"dump", "-f", path}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -311,7 +328,7 @@ func TestDumpPrintsTOMLSubtree(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "title = \"demo app\"\n\n[server]\nports = [3000, 3001]\nenabled = true\n") - err := Execute([]string{"dump", path, "server"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"dump", "-f", path, "server"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -326,7 +343,7 @@ func TestDumpPrintsTOMLSubtreeAsJSON(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "title = \"demo app\"\n\n[server]\nports = [3000, 3001]\nenabled = true\n") - err := Execute([]string{"dump", path, "server", "--json"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"dump", "-f", path, "server", "--json"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -341,7 +358,7 @@ func TestDumpPrintsYAMLSubtree(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempYAML(t, "server:\n hosts:\n - api.example.com\n - worker.example.com\n enabled: true\n") - err := Execute([]string{"dump", path, "server.hosts"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"dump", "-f", path, "server.hosts"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -356,7 +373,7 @@ func TestDumpPrintsYAMLSubtreeAsJSON(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempYAML(t, "server:\n hosts:\n - api.example.com\n - worker.example.com\n enabled: true\n") - err := Execute([]string{"dump", path, "server", "--json"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"dump", "-f", path, "server", "--json"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) diff --git a/cmd/cli_help_test.go b/cmd/cli_help_test.go index 273c1ed..8b49df1 100644 --- a/cmd/cli_help_test.go +++ b/cmd/cli_help_test.go @@ -54,7 +54,7 @@ func TestHelpCommandShowsCommandHelp(t *testing.T) { t.Fatalf("Execute returned error: %v", err) } assertContains(t, stdout.String(), "Create or update config values") - assertContains(t, stdout.String(), "config set [CONFIG_FILE] KEY VALUE [options]") + assertContains(t, stdout.String(), "config set KEY VALUE [options]") } func TestHelpCommandShowsNestedCommandHelp(t *testing.T) { @@ -66,7 +66,7 @@ func TestHelpCommandShowsNestedCommandHelp(t *testing.T) { t.Fatalf("Execute returned error: %v", err) } assertContains(t, stdout.String(), "Add values to a scalar array") - assertContains(t, stdout.String(), "config array add [CONFIG_FILE] KEY VALUE... [options]") + assertContains(t, stdout.String(), "config array add KEY VALUE... [options]") assertContains(t, stdout.String(), "Creates the array when KEY is not set.") } @@ -125,8 +125,8 @@ func TestSetHelp(t *testing.T) { if stderr.Len() != 0 { t.Fatalf("stderr = %q", stderr.String()) } - assertContains(t, stdout.String(), "Usage:\n config set [CONFIG_FILE] KEY VALUE [options]") - assertContains(t, stdout.String(), "CONFIG_FILE\n Path to the config file") + assertContains(t, stdout.String(), "Usage:\n config set KEY VALUE [options]") + assertContains(t, stdout.String(), "--file, -f PATH\n Path to the config file") assertContains(t, stdout.String(), "--in COLLECTION\n Edit a record in COLLECTION") assertContains(t, stdout.String(), "--on FIELD:VALUE\n Select or create a record by FIELD:VALUE") assertContains(t, stdout.String(), "--string, -s\n Store VALUE as a string") @@ -147,13 +147,13 @@ func TestDeleteHelp(t *testing.T) { if stderr.Len() != 0 { t.Fatalf("stderr = %q", stderr.String()) } - assertContains(t, stdout.String(), "Usage:\n config delete [CONFIG_FILE] KEY [options]") + assertContains(t, stdout.String(), "Usage:\n config delete KEY [options]") assertContains(t, stdout.String(), "Aliases:\n del") assertContains(t, stdout.String(), "config del servers.1") - if strings.Contains(stdout.String(), "config del [CONFIG_FILE] KEY [options]") { + if strings.Contains(stdout.String(), "config del KEY [options]") { t.Fatalf("delete usage should not include aliases:\n%s", stdout.String()) } - assertContains(t, stdout.String(), "CONFIG_FILE\n Path to the config file") + assertContains(t, stdout.String(), "--file, -f PATH\n Path to the config file") assertContains(t, stdout.String(), "KEY\n Dot notation string describing the intended config container") assertContains(t, stdout.String(), "--on FIELD:VALUE\n Select a record by FIELD:VALUE. May be repeated.") assertContains(t, stdout.String(), "--if-empty\n Only delete when the container has no values") @@ -173,7 +173,8 @@ func TestUnsetHelp(t *testing.T) { if stderr.Len() != 0 { t.Fatalf("stderr = %q", stderr.String()) } - assertContains(t, stdout.String(), "Usage:\n config unset [CONFIG_FILE] KEY [options]") + assertContains(t, stdout.String(), "Usage:\n config unset KEY [options]") + assertContains(t, stdout.String(), "--file, -f PATH\n Path to the config file") assertContains(t, stdout.String(), "--in COLLECTION\n Remove a field from a record in COLLECTION") assertContains(t, stdout.String(), "--on FIELD:VALUE\n Select a record by FIELD:VALUE. May be repeated.") assertContains(t, stdout.String(), "--if VALUE\n Only unset when the current value matches VALUE") @@ -199,7 +200,7 @@ func TestArrayHelp(t *testing.T) { if strings.Contains(stdout.String(), "Examples:") { t.Fatalf("array group help should not include examples:\n%s", stdout.String()) } - if strings.Contains(stdout.String(), "config array add [CONFIG_FILE] KEY VALUE") { + if strings.Contains(stdout.String(), "config array add KEY VALUE") { t.Fatalf("array group help should not include subcommand details:\n%s", stdout.String()) } } @@ -215,8 +216,9 @@ func TestArraySubcommandHelp(t *testing.T) { if stderr.Len() != 0 { t.Fatalf("stderr = %q", stderr.String()) } - assertContains(t, stdout.String(), "Usage:\n config array add [CONFIG_FILE] KEY VALUE... [options]\n config array add --help | -h") + assertContains(t, stdout.String(), "Usage:\n config array add KEY VALUE... [options]\n config array add --help | -h") assertContains(t, stdout.String(), "Creates the array when KEY is not set.") + assertContains(t, stdout.String(), "--file, -f PATH\n Path to the config file") assertContains(t, stdout.String(), "--dry, -n\n Print the updated config without modifying the file") } @@ -231,11 +233,11 @@ func TestArrayDeleteSubcommandHelp(t *testing.T) { if stderr.Len() != 0 { t.Fatalf("stderr = %q", stderr.String()) } - assertContains(t, stdout.String(), "Usage:\n config array delete [CONFIG_FILE] KEY VALUE... [options]") + assertContains(t, stdout.String(), "Usage:\n config array delete KEY VALUE... [options]") assertContains(t, stdout.String(), "Aliases:\n del") assertContains(t, stdout.String(), "Deletes KEY when no values remain.") assertContains(t, stdout.String(), "config array del roots /tmp /var/tmp") - if strings.Contains(stdout.String(), "config array del [CONFIG_FILE] KEY VALUE... [options]") { + if strings.Contains(stdout.String(), "config array del KEY VALUE... [options]") { t.Fatalf("array delete usage should not include aliases:\n%s", stdout.String()) } } @@ -251,9 +253,10 @@ func TestGetHelp(t *testing.T) { if stderr.Len() != 0 { t.Fatalf("stderr = %q", stderr.String()) } - assertContains(t, stdout.String(), "Usage:\n config get [CONFIG_FILE] KEY [options]") + assertContains(t, stdout.String(), "Usage:\n config get KEY [options]") assertContains(t, stdout.String(), "Scalar values and arrays of scalar values are returned.") assertContains(t, stdout.String(), "Values are printed in a format-neutral display form") + assertContains(t, stdout.String(), "--file, -f PATH\n Path to the config file") assertContains(t, stdout.String(), "--in COLLECTION\n Read a field from a record in COLLECTION") assertContains(t, stdout.String(), "--on FIELD:VALUE\n Select a record by FIELD:VALUE. May be repeated.") } @@ -270,8 +273,9 @@ func TestDumpHelp(t *testing.T) { t.Fatalf("stderr = %q", stderr.String()) } assertContains(t, stdout.String(), "Dump config data") - assertContains(t, stdout.String(), "Usage:\n config dump [CONFIG_FILE] [KEY] [options]") + assertContains(t, stdout.String(), "Usage:\n config dump [KEY] [options]") assertContains(t, stdout.String(), "KEY\n Optional key or table path to dump") + assertContains(t, stdout.String(), "--file, -f PATH\n Path to the config file") assertContains(t, stdout.String(), "--json\n Dump as JSON instead of YAML") } @@ -287,11 +291,12 @@ func TestListHelp(t *testing.T) { t.Fatalf("stderr = %q", stderr.String()) } assertContains(t, stdout.String(), "Show config values") - assertContains(t, stdout.String(), "Usage:\n config list [CONFIG_FILE] [KEY]") + assertContains(t, stdout.String(), "Usage:\n config list [KEY]") assertContains(t, stdout.String(), "Aliases:\n ls") + assertContains(t, stdout.String(), "--file, -f PATH\n Path to the config file") assertContains(t, stdout.String(), "--color, -c\n Colorize keys and separators") assertContains(t, stdout.String(), "config ls --color") - if strings.Contains(stdout.String(), "config ls [CONFIG_FILE] [KEY]") { + if strings.Contains(stdout.String(), "config ls [KEY]") { t.Fatalf("list usage should not include aliases:\n%s", stdout.String()) } } @@ -308,7 +313,8 @@ func TestEditHelp(t *testing.T) { t.Fatalf("stderr = %q", stderr.String()) } assertContains(t, stdout.String(), "Open the config file in an editor") - assertContains(t, stdout.String(), "Usage:\n config edit [CONFIG_FILE]") + assertContains(t, stdout.String(), "Usage:\n config edit [options]") + assertContains(t, stdout.String(), "--file, -f PATH\n Path to the config file") assertContains(t, stdout.String(), "EDITOR\n Editor command to run. Defaults to vi.") } diff --git a/cmd/cli_set_test.go b/cmd/cli_set_test.go index e581e9b..23ae3b0 100644 --- a/cmd/cli_set_test.go +++ b/cmd/cli_set_test.go @@ -12,7 +12,7 @@ func TestSetEchoesParsedArgsWithFlagAfterPositionals(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "server.port = 2000\n") - err := Execute([]string{"set", path, "server.port", "3000", "--dry"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"set", "-f", path, "server.port", "3000", "--dry"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -32,7 +32,7 @@ func TestSetEchoesParsedArgsWithFlagBeforePositionals(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "server.port = 2000\n") - err := Execute([]string{"set", "--dry", path, "server.port", "3000"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"set", "--dry", "-f", path, "server.port", "3000"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -46,7 +46,7 @@ func TestSetEchoesParsedArgsWithShortFlag(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "server.port = 2000\n") - err := Execute([]string{"set", "-n", path, "server.port", "3000"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"set", "-n", "-f", path, "server.port", "3000"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -60,7 +60,7 @@ func TestSetEchoesParsedArgsWithDiffFlag(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "server.port = 2000\n") - err := Execute([]string{"set", "-d", path, "server.port", "3000"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"set", "-d", "-f", path, "server.port", "3000"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -77,7 +77,7 @@ func TestSetColorizesDiff(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "server.port = 2000\n") - err := Execute([]string{"set", "--diff", "--color", path, "server.port", "3000"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"set", "--diff", "--color", "-f", path, "server.port", "3000"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -90,7 +90,7 @@ func TestSetCompactsDiffAndColorShortFlags(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "server.port = 2000\n") - err := Execute([]string{"set", "-dc", path, "server.port", "3000"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"set", "-dc", "-f", path, "server.port", "3000"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -169,7 +169,7 @@ func TestSetWritesUpdatedTOML(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "server.port = 2000\n") - err := Execute([]string{"set", path, "server.port", "3000"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"set", "-f", path, "server.port", "3000"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -186,7 +186,7 @@ func TestSetWritesUpdatedJSON(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempJSON(t, `{"title":"demo app"}`) - err := Execute([]string{"set", path, "server.port", "3000"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"set", "-f", path, "server.port", "3000"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -204,7 +204,7 @@ func TestArrayAddWritesUpdatedJSON(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempJSON(t, `{"roots":["$HOME/.cache"]}`) - err := Execute([]string{"array", "add", path, "roots", "/tmp", "$HOME/.cache"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"array", "add", "-f", path, "roots", "/tmp", "$HOME/.cache"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -222,7 +222,7 @@ func TestArrayDeleteAliasWritesUpdatedJSON(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempJSON(t, `{"roots":["/var/tmp","/tmp"]}`) - err := Execute([]string{"array", "del", path, "roots", "/tmp"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"array", "del", "-f", path, "roots", "/tmp"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -240,12 +240,12 @@ func TestSetRejectsMultipleValues(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "[server]\nports = [1000]\n") - err := Execute([]string{"set", path, "server.ports", "3000", "3001"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"set", "-f", path, "server.ports", "3000", "3001"}, "1.2.3", &stdout, &stderr) if err == nil { t.Fatal("expected error") } - if !strings.Contains(err.Error(), "usage: config set [CONFIG_FILE] KEY VALUE [options]") { + if !strings.Contains(err.Error(), "usage: config set KEY VALUE [options]") { t.Fatalf("unexpected error: %v", err) } } @@ -254,7 +254,7 @@ func TestSetWritesTOMLLiteralValue(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "release.date = \"old\"\n") - err := Execute([]string{"set", path, "release.date", "2027-03-24"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"set", "-f", path, "release.date", "2027-03-24"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -273,7 +273,7 @@ func TestSetIdenticalValueDoesNotWriteFile(t *testing.T) { } defer os.Chmod(path, 0644) - err := Execute([]string{"set", path, "server.port", "3000"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"set", "-f", path, "server.port", "3000"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -290,7 +290,7 @@ func TestSetIdenticalValueDiffPrintsNothing(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "server.port = 3000\n") - err := Execute([]string{"set", "--diff", path, "server.port", "3000"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"set", "--diff", "-f", path, "server.port", "3000"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -307,7 +307,7 @@ func TestSetIdenticalValueDryPrintsUnchangedConfig(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "server.port = 3000\n") - err := Execute([]string{"set", "--dry", path, "server.port", "3000"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"set", "--dry", "-f", path, "server.port", "3000"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -324,7 +324,7 @@ func TestSetStringFlagForcesStringValue(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "version = 1.0\n") - err := Execute([]string{"set", "--string", path, "version", "1.0"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"set", "--string", "-f", path, "version", "1.0"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -339,7 +339,7 @@ func TestSetReadsValueFromStdin(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "message = \"short\"\n") - err := ExecuteWithIO([]string{"set", path, "message", "-"}, "1.2.3", strings.NewReader("hello\nworld"), &stdout, &stderr) + err := ExecuteWithIO([]string{"set", "-f", path, "message", "-"}, "1.2.3", strings.NewReader("hello\nworld"), &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -357,7 +357,7 @@ func TestSetDryAndDiffAreMutuallyExclusive(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "server.port = 2000\n") - err := Execute([]string{"set", "--dry", "--diff", path, "server.port", "3000"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"set", "--dry", "--diff", "-f", path, "server.port", "3000"}, "1.2.3", &stdout, &stderr) if err == nil { t.Fatal("expected error") @@ -387,7 +387,7 @@ func TestSetInOnWritesArrayRecord(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "[[servers]]\nname = \"api\"\nport = 3000\n") - err := Execute([]string{"set", path, "port", "8080", "--in", "servers", "--on", "name:api"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"set", "-f", path, "port", "8080", "--in", "servers", "--on", "name:api"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -402,7 +402,7 @@ func TestSetInRequiresOnFlag(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "[[servers]]\nname = \"api\"\nport = 3000\n") - err := Execute([]string{"set", path, "port", "8080", "--in", "servers"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"set", "-f", path, "port", "8080", "--in", "servers"}, "1.2.3", &stdout, &stderr) if err == nil { t.Fatal("expected error") @@ -416,7 +416,7 @@ func TestSetColorRequiresDiff(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "server.port = 2000\n") - err := Execute([]string{"set", "--color", path, "server.port", "3000"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"set", "--color", "-f", path, "server.port", "3000"}, "1.2.3", &stdout, &stderr) if err == nil { t.Fatal("expected error") diff --git a/cmd/cli_unset_delete_test.go b/cmd/cli_unset_delete_test.go index 7e091be..4aadf51 100644 --- a/cmd/cli_unset_delete_test.go +++ b/cmd/cli_unset_delete_test.go @@ -11,7 +11,7 @@ func TestUnsetWritesUpdatedTOML(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "[database]\nhost = \"localhost\"\nport = 5432\n") - err := Execute([]string{"unset", path, "database.port"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"unset", "-f", path, "database.port"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -28,7 +28,7 @@ func TestUnsetWritesUpdatedJSON(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempJSON(t, `{"database":{"host":"localhost","port":5432}}`) - err := Execute([]string{"unset", path, "database.port"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"unset", "-f", path, "database.port"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -75,7 +75,7 @@ func TestUnsetDryPrintsUpdatedTOML(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "[database]\nhost = \"localhost\"\nport = 5432\n") - err := Execute([]string{"unset", "--dry", path, "database.port"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"unset", "--dry", "-f", path, "database.port"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -92,7 +92,7 @@ func TestUnsetDiffPrintsUnifiedDiff(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "[database]\nhost = \"localhost\"\nport = 5432\n") - err := Execute([]string{"unset", "--diff", path, "database.port"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"unset", "--diff", "-f", path, "database.port"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -108,16 +108,16 @@ func TestUnsetIfValueOnlyUnsetsMatchingScalar(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "submit = \"tab\"\nqueue = \"alt-w\"\n") - err := Execute([]string{"unset", path, "submit", "--if", "tab"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"unset", "-f", path, "submit", "--if", "tab"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) } - err = Execute([]string{"unset", path, "queue", "--if", "alt-q"}, "1.2.3", &stdout, &stderr) + err = Execute([]string{"unset", "-f", path, "queue", "--if", "alt-q"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) } - err = Execute([]string{"unset", path, "missing", "--if", "tab"}, "1.2.3", &stdout, &stderr) + err = Execute([]string{"unset", "-f", path, "missing", "--if", "tab"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) } @@ -133,12 +133,12 @@ func TestUnsetIfExistsIgnoresMissingKey(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "submit = \"tab\"\nqueue = \"alt-q\"\n") - err := Execute([]string{"unset", path, "submit", "--if-exists"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"unset", "-f", path, "submit", "--if-exists"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) } - err = Execute([]string{"unset", path, "missing", "--if-exists"}, "1.2.3", &stdout, &stderr) + err = Execute([]string{"unset", "-f", path, "missing", "--if-exists"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) } @@ -154,7 +154,7 @@ func TestUnsetSelectedTOMLRecordValue(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "[[servers]]\nname = \"api\"\nport = 3000\n\n[[servers]]\nname = \"worker\"\nport = 3001\n") - err := Execute([]string{"unset", "--dry", path, "port", "--in", "servers", "--on", "name:worker"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"unset", "--dry", "-f", path, "port", "--in", "servers", "--on", "name:worker"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -169,7 +169,7 @@ func TestUnsetSelectedRecordRequiresInWithOn(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "[[servers]]\nname = \"api\"\nport = 3000\n") - err := Execute([]string{"unset", path, "port", "--on", "name:api"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"unset", "-f", path, "port", "--on", "name:api"}, "1.2.3", &stdout, &stderr) if err == nil { t.Fatal("expected error") @@ -183,7 +183,7 @@ func TestUnsetSelectedRecordRequiresOnWithIn(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "[[servers]]\nname = \"api\"\nport = 3000\n") - err := Execute([]string{"unset", path, "port", "--in", "servers"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"unset", "-f", path, "port", "--in", "servers"}, "1.2.3", &stdout, &stderr) if err == nil { t.Fatal("expected error") @@ -211,7 +211,7 @@ func TestDeleteWritesUpdatedJSON(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempJSON(t, `{"title":"demo","style":{"color":"blue"},"server":{"port":3000}}`) - err := Execute([]string{"delete", path, "style"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"delete", "-f", path, "style"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -229,7 +229,7 @@ func TestDeleteAliasWritesUpdatedJSON(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempJSON(t, `{"title":"demo","style":{"color":"blue"},"server":{"port":3000}}`) - err := Execute([]string{"del", path, "style"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"del", "-f", path, "style"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -247,7 +247,7 @@ func TestDeleteDryPrintsUpdatedTOML(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "title = \"demo\"\n\n[style]\ncolor = \"blue\"\nfont = \"arial\"\n") - err := Execute([]string{"delete", "--dry", path, "style"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"delete", "--dry", "-f", path, "style"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -264,7 +264,7 @@ func TestDeleteDiffPrintsUnifiedDiff(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "title = \"demo\"\n\n[style]\ncolor = \"blue\"\nfont = \"arial\"\n") - err := Execute([]string{"delete", "--diff", path, "style"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"delete", "--diff", "-f", path, "style"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -281,7 +281,7 @@ func TestDeleteIfEmptyNoOpsWhenContainerHasValues(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "[style]\ncolor = \"blue\"\n") - err := Execute([]string{"delete", path, "style", "--if-empty"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"delete", "-f", path, "style", "--if-empty"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -298,7 +298,7 @@ func TestDeleteRefusesScalarValue(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "[style]\ncolor = \"blue\"\nfont = \"arial\"\n") - err := Execute([]string{"delete", path, "style.color"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"delete", "-f", path, "style.color"}, "1.2.3", &stdout, &stderr) if err == nil { t.Fatal("expected error") @@ -313,7 +313,7 @@ func TestDeleteAcceptsRepeatedSelectors(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "[[servers]]\nname = \"api\"\nport = 3000\n\n[[servers]]\nname = \"api\"\nport = 3001\n") - err := Execute([]string{"delete", "--dry", path, "servers", "--on", "name:api", "--on", "port:3000"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"delete", "--dry", "-f", path, "servers", "--on", "name:api", "--on", "port:3000"}, "1.2.3", &stdout, &stderr) if err != nil { t.Fatalf("Execute returned error: %v", err) @@ -328,7 +328,7 @@ func TestDeleteColorRequiresDiff(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "[database]\nport = 5432\n") - err := Execute([]string{"delete", "--color", path, "database"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"delete", "--color", "-f", path, "database"}, "1.2.3", &stdout, &stderr) if err == nil { t.Fatal("expected error") @@ -344,7 +344,7 @@ func TestPrintErrorPrefixesOperationalErrors(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "[database]\nport = 5432\n") - err := Execute([]string{"unset", "-dc", path, "database"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"unset", "-dc", "-f", path, "database"}, "1.2.3", &stdout, &stderr) if err == nil { t.Fatal("expected error") @@ -394,7 +394,7 @@ func TestSetMissingRequiredArgFailsAfterConfigFileIsResolved(t *testing.T) { var stdout, stderr bytes.Buffer path := writeTempTOML(t, "server.port = 2000\n") - err := Execute([]string{"set", path, "server.port"}, "1.2.3", &stdout, &stderr) + err := Execute([]string{"set", "-f", path, "server.port"}, "1.2.3", &stdout, &stderr) if err == nil { t.Fatal("expected error") @@ -402,7 +402,7 @@ func TestSetMissingRequiredArgFailsAfterConfigFileIsResolved(t *testing.T) { if stdout.Len() != 0 { t.Fatalf("stdout = %q", stdout.String()) } - if !strings.Contains(err.Error(), "usage: config set [CONFIG_FILE] KEY VALUE [options]") { + if !strings.Contains(err.Error(), "usage: config set KEY VALUE [options]") { t.Fatalf("unexpected error: %v", err) } } diff --git a/cmd/help/array-add.txt b/cmd/help/array-add.txt index fd6b5fd..9002880 100644 --- a/cmd/help/array-add.txt +++ b/cmd/help/array-add.txt @@ -1,7 +1,7 @@ Add values to a scalar array Usage: - config array add [CONFIG_FILE] KEY VALUE... [options] + config array add KEY VALUE... [options] config array add --help | -h Behavior: @@ -9,9 +9,6 @@ Behavior: Ignores values that are already present. Parameters: - CONFIG_FILE - Path to the config file - KEY Dot notation string describing the intended array key @@ -19,6 +16,9 @@ Parameters: Scalar values to add to the array Options: + --file, -f PATH + Path to the config file + --dry, -n Print the updated config without modifying the file diff --git a/cmd/help/array-delete.txt b/cmd/help/array-delete.txt index ea1c719..0e9b6c9 100644 --- a/cmd/help/array-delete.txt +++ b/cmd/help/array-delete.txt @@ -1,7 +1,7 @@ Remove values from a scalar array Usage: - config array delete [CONFIG_FILE] KEY VALUE... [options] + config array delete KEY VALUE... [options] config array delete --help | -h Aliases: @@ -13,9 +13,6 @@ Behavior: Deletes KEY when no values remain. Parameters: - CONFIG_FILE - Path to the config file - KEY Dot notation string describing the intended array key @@ -23,6 +20,9 @@ Parameters: Scalar values to remove from the array Options: + --file, -f PATH + Path to the config file + --dry, -n Print the updated config without modifying the file diff --git a/cmd/help/array-set.txt b/cmd/help/array-set.txt index 5194f2c..a1ae2e7 100644 --- a/cmd/help/array-set.txt +++ b/cmd/help/array-set.txt @@ -1,13 +1,10 @@ Replace a scalar array Usage: - config array set [CONFIG_FILE] KEY VALUE... [options] + config array set KEY VALUE... [options] config array set --help | -h Parameters: - CONFIG_FILE - Path to the config file - KEY Dot notation string describing the intended array key @@ -15,6 +12,9 @@ Parameters: Scalar values to store in the array Options: + --file, -f PATH + Path to the config file + --dry, -n Print the updated config without modifying the file diff --git a/cmd/help/delete.txt b/cmd/help/delete.txt index b6e65a0..3b131b9 100644 --- a/cmd/help/delete.txt +++ b/cmd/help/delete.txt @@ -1,20 +1,20 @@ Delete a config container Usage: - config delete [CONFIG_FILE] KEY [options] + config delete KEY [options] config delete --help | -h Aliases: del Parameters: - CONFIG_FILE - Path to the config file - KEY Dot notation string describing the intended config container Options: + --file, -f PATH + Path to the config file + --on FIELD:VALUE Select a record by FIELD:VALUE. May be repeated. @@ -31,7 +31,7 @@ Options: Colorize diff output Examples: - config delete path/to/config.toml style + config delete -f path/to/config.toml style config delete servers.1 config del servers.1 config delete servers --on name:web diff --git a/cmd/help/dump.txt b/cmd/help/dump.txt index d4d8eda..adf8f28 100644 --- a/cmd/help/dump.txt +++ b/cmd/help/dump.txt @@ -1,23 +1,23 @@ Dump config data Usage: - config dump [CONFIG_FILE] [KEY] [options] + config dump [KEY] [options] config dump --help | -h Parameters: - CONFIG_FILE - Path to the config file - KEY Optional key or table path to dump Options: + --file, -f PATH + Path to the config file + --json Dump as JSON instead of YAML Examples: - config dump path/to/config.toml - config dump path/to/config.toml database - config dump path/to/config.toml database --json - config dump path/to/config.yaml server.hosts + config dump -f path/to/config.toml + config dump -f path/to/config.toml database + config dump -f path/to/config.toml database --json + config dump -f path/to/config.yaml server.hosts config dump diff --git a/cmd/help/edit.txt b/cmd/help/edit.txt index 04560d1..bc3296e 100644 --- a/cmd/help/edit.txt +++ b/cmd/help/edit.txt @@ -1,11 +1,11 @@ Open the config file in an editor Usage: - config edit [CONFIG_FILE] + config edit [options] config edit --help | -h -Parameters: - CONFIG_FILE +Options: + --file, -f PATH Path to the config file Environment: @@ -13,5 +13,5 @@ Environment: Editor command to run. Defaults to vi. Examples: - config edit path/to/config.toml + config edit -f path/to/config.toml config edit diff --git a/cmd/help/get.txt b/cmd/help/get.txt index 0453184..2546474 100644 --- a/cmd/help/get.txt +++ b/cmd/help/get.txt @@ -1,13 +1,10 @@ Show a config value Usage: - config get [CONFIG_FILE] KEY [options] + config get KEY [options] config get --help | -h Parameters: - CONFIG_FILE - Path to the config file - KEY Dot notation string describing the intended config key @@ -19,6 +16,9 @@ Notes: source literal. Options: + --file, -f PATH + Path to the config file + --in COLLECTION Read a field from a record in COLLECTION. Must be used with --on. @@ -26,6 +26,6 @@ Options: Select a record by FIELD:VALUE. May be repeated. Examples: - config get path/to/config.toml server.port + config get -f path/to/config.toml server.port config get server.port - config get path/to/config.toml port --in servers --on name:api + config get -f path/to/config.toml port --in servers --on name:api diff --git a/cmd/help/list.txt b/cmd/help/list.txt index f2406f1..a8e9c5c 100644 --- a/cmd/help/list.txt +++ b/cmd/help/list.txt @@ -1,27 +1,27 @@ Show config values Usage: - config list [CONFIG_FILE] [KEY] + config list [KEY] config list --help | -h Aliases: ls Parameters: - CONFIG_FILE - Path to the config file - KEY Optional key or table path to list Options: + --file, -f PATH + Path to the config file + --color, -c Colorize keys and separators Examples: - config list path/to/config.toml - config list path/to/config.toml database - config list path/to/config.toml database.port + config list -f path/to/config.toml + config list -f path/to/config.toml database + config list -f path/to/config.toml database.port config list --color config ls --color config list diff --git a/cmd/help/set.txt b/cmd/help/set.txt index 403435d..c8f6682 100644 --- a/cmd/help/set.txt +++ b/cmd/help/set.txt @@ -1,13 +1,10 @@ Create or update config values Usage: - config set [CONFIG_FILE] KEY VALUE [options] + config set KEY VALUE [options] config set --help | -h Parameters: - CONFIG_FILE - Path to the config file - KEY Dot notation string describing the intended config key @@ -17,6 +14,9 @@ Parameters: values are stored as strings. Use `-` to read from stdin. Options: + --file, -f PATH + Path to the config file + --in COLLECTION Edit a record in COLLECTION @@ -37,23 +37,23 @@ Options: Examples: # Simple update - config set path/to/config.toml server.port 3000 + config set -f path/to/config.toml server.port 3000 # Indexed array-record update - config set path/to/config.toml servers.0.port 3000 + config set -f path/to/config.toml servers.0.port 3000 # Unique array-record update or create - config set path/to/config.toml port 3000 --in servers --on name:web + config set -f path/to/config.toml port 3000 --in servers --on name:web # Typed literal value - config set path/to/config.toml release.date 2027-03-24 + config set -f path/to/config.toml release.date 2027-03-24 # String escape hatch - config set path/to/config.toml version 1.0 --string + config set -f path/to/config.toml version 1.0 --string # Priming a config file using environment variable export CONFIG_FILE=~/.codex/config.toml config set server.port 3000 # Reading a multiline string from stdin - printf 'hello\nworld' | config set path/to/config.toml title.intro - --string + printf 'hello\nworld' | config set -f path/to/config.toml title.intro - --string diff --git a/cmd/help/unset.txt b/cmd/help/unset.txt index 69dd9a1..c85985e 100644 --- a/cmd/help/unset.txt +++ b/cmd/help/unset.txt @@ -1,17 +1,17 @@ Delete a config value Usage: - config unset [CONFIG_FILE] KEY [options] + config unset KEY [options] config unset --help | -h Parameters: - CONFIG_FILE - Path to the config file - KEY Dot notation string describing the intended config key Options: + --file, -f PATH + Path to the config file + --in COLLECTION Remove a field from a record in COLLECTION. Must be used with --on. @@ -34,8 +34,8 @@ Options: Colorize diff output Examples: - config unset path/to/config.toml server.port + config unset -f path/to/config.toml server.port config unset server.port config unset server.port --if 3000 config unset server.port --if-exists - config unset path/to/config.toml port --in servers --on name:api + config unset -f path/to/config.toml port --in servers --on name:api diff --git a/format/document.go b/format/document.go index 6eadcfd..f11f914 100644 --- a/format/document.go +++ b/format/document.go @@ -46,15 +46,6 @@ func Resolve(path string) (Document, string, error) { } } -func TargetPath(path string) bool { - switch extension(path) { - case ".toml", ".yaml", ".yml", ".json", ".ini": - return true - default: - return false - } -} - func extension(path string) string { ext := strings.ToLower(filepath.Ext(path)) return ext diff --git a/format/document_test.go b/format/document_test.go index 1674614..a037d51 100644 --- a/format/document_test.go +++ b/format/document_test.go @@ -94,22 +94,3 @@ func TestResolveUnsupportedFormat(t *testing.T) { t.Fatal("expected error") } } - -func TestTargetPath(t *testing.T) { - tests := map[string]bool{ - "config.toml": true, - "CONFIG.TOML": true, - "config.yaml": true, - "config.yml": true, - "config.json": true, - "config.ini": true, - "config.conf": false, - "server.ports": false, - } - - for path, want := range tests { - if got := TargetPath(path); got != want { - t.Fatalf("TargetPath(%q) = %v, want %v", path, got, want) - } - } -} From 111a73594c8f8b6faa06a371bb146bddd202acb2 Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Mon, 8 Jun 2026 11:11:39 +0300 Subject: [PATCH 3/3] - Improve all help texts --- cmd/cli.go | 16 ++++++++++++---- cmd/cli_help_test.go | 21 +++++++++++++++++---- cmd/help/array-add.txt | 7 ++++++- cmd/help/array-delete.txt | 9 ++++++++- cmd/help/array-set.txt | 7 ++++++- cmd/help/completion.txt | 5 ----- cmd/help/delete.txt | 14 ++++++++++++-- cmd/help/dump.txt | 19 +++++++++++++------ cmd/help/edit.txt | 8 ++++++-- cmd/help/get.txt | 12 +++++++++--- cmd/help/help.txt | 25 ------------------------- cmd/help/list.txt | 17 ++++++++++------- cmd/help/set.txt | 22 +++++++++++----------- cmd/help/unset.txt | 16 +++++++++++++--- 14 files changed, 123 insertions(+), 75 deletions(-) delete mode 100644 cmd/help/help.txt diff --git a/cmd/cli.go b/cmd/cli.go index 443143a..4d0a3e8 100644 --- a/cmd/cli.go +++ b/cmd/cli.go @@ -419,7 +419,7 @@ func newHelpCommand(stdout io.Writer) *cobra.Command { Short: "Show command help or topic help", Args: func(cmd *cobra.Command, args []string) error { if len(args) > 2 { - return usageError{"usage: config help [COMMAND|TOPIC]"} + return usageError{"usage: " + helpUsage} } return nil }, @@ -447,19 +447,27 @@ func newHelpCommand(stdout io.Writer) *cobra.Command { return fmt.Errorf("unknown help topic %q", name) }, } - cmd.SetHelpFunc(helpPrinter("help")) + cmd.SetHelpFunc(helpIndexPrinter(stdout)) return cmd } +func helpIndexPrinter(stdout io.Writer) func(*cobra.Command, []string) { + return func(*cobra.Command, []string) { + fmt.Fprintln(stdout, helpIndex()) + } +} + +const helpUsage = "config help [COMMAND|TOPIC]" + func helpIndex() string { lines := []string{ "Usage:", - " config help [TOPIC]", + " " + helpUsage, "", "Commands:", } - for _, command := range []string{"set", "get", "unset", "delete", "array", "list", "dump", "edit", "completion"} { + for _, command := range []string{"set", "get", "unset", "delete", "array", "array set", "array add", "array delete", "list", "dump", "edit", "completion"} { lines = append(lines, " "+command) } diff --git a/cmd/cli_help_test.go b/cmd/cli_help_test.go index 8b49df1..b6a692f 100644 --- a/cmd/cli_help_test.go +++ b/cmd/cli_help_test.go @@ -37,14 +37,27 @@ func TestHelpCommandShowsTopicIndex(t *testing.T) { if err != nil { t.Fatalf("Execute returned error: %v", err) } - assertContains(t, stdout.String(), "Usage:\n config help [TOPIC]") - assertContains(t, stdout.String(), "Commands:\n set\n get\n unset\n delete\n array\n list\n dump\n edit\n completion") + assertContains(t, stdout.String(), "Usage:\n config help [COMMAND|TOPIC]") + assertContains(t, stdout.String(), "Commands:\n set\n get\n unset\n delete\n array\n array set\n array add\n array delete\n list\n dump\n edit\n completion") assertContains(t, stdout.String(), "Other topics:\n environment\n formats") if strings.Contains(stdout.String(), "Shortcut:") { t.Fatalf("help index should not include shortcut text:\n%s", stdout.String()) } } +func TestHelpCommandHelpShowsTopicIndex(t *testing.T) { + var stdout, stderr bytes.Buffer + + err := Execute([]string{"help", "--help"}, "1.2.3", &stdout, &stderr) + + if err != nil { + t.Fatalf("Execute returned error: %v", err) + } + assertContains(t, stdout.String(), "Usage:\n config help [COMMAND|TOPIC]") + assertContains(t, stdout.String(), "Commands:\n set\n get\n unset\n delete\n array\n array set\n array add\n array delete\n list\n dump\n edit\n completion") + assertContains(t, stdout.String(), "Other topics:\n environment\n formats") +} + func TestHelpCommandShowsCommandHelp(t *testing.T) { var stdout, stderr bytes.Buffer @@ -133,7 +146,7 @@ func TestSetHelp(t *testing.T) { assertContains(t, stdout.String(), "--dry, -n\n Print the updated config without modifying the file") assertContains(t, stdout.String(), "--diff, -d\n Print a unified diff without modifying the file") assertContains(t, stdout.String(), "--color, -c\n Colorize diff output") - assertContains(t, stdout.String(), "export CONFIG_FILE=~/.codex/config.toml") + assertContains(t, stdout.String(), "export CONFIG_FILE=config.toml") } func TestDeleteHelp(t *testing.T) { @@ -295,7 +308,7 @@ func TestListHelp(t *testing.T) { assertContains(t, stdout.String(), "Aliases:\n ls") assertContains(t, stdout.String(), "--file, -f PATH\n Path to the config file") assertContains(t, stdout.String(), "--color, -c\n Colorize keys and separators") - assertContains(t, stdout.String(), "config ls --color") + assertContains(t, stdout.String(), "config list database.port --color") if strings.Contains(stdout.String(), "config ls [KEY]") { t.Fatalf("list usage should not include aliases:\n%s", stdout.String()) } diff --git a/cmd/help/array-add.txt b/cmd/help/array-add.txt index 9002880..4d86545 100644 --- a/cmd/help/array-add.txt +++ b/cmd/help/array-add.txt @@ -17,7 +17,7 @@ Parameters: Options: --file, -f PATH - Path to the config file + Path to the config file (default: $CONFIG_FILE) --dry, -n Print the updated config without modifying the file @@ -29,4 +29,9 @@ Options: Colorize diff output Examples: + # Add values + config array add -f config.toml roots /var/tmp "$HOME/.cache" + + # Use a default file for repeated commands + export CONFIG_FILE=config.toml config array add roots /var/tmp "$HOME/.cache" diff --git a/cmd/help/array-delete.txt b/cmd/help/array-delete.txt index 0e9b6c9..b8e1636 100644 --- a/cmd/help/array-delete.txt +++ b/cmd/help/array-delete.txt @@ -21,7 +21,7 @@ Parameters: Options: --file, -f PATH - Path to the config file + Path to the config file (default: $CONFIG_FILE) --dry, -n Print the updated config without modifying the file @@ -33,5 +33,12 @@ Options: Colorize diff output Examples: + # Remove a value + config array delete -f config.toml roots /tmp + + # Use a default file for repeated commands + export CONFIG_FILE=config.toml config array delete roots /tmp + + # Use the short command alias config array del roots /tmp /var/tmp diff --git a/cmd/help/array-set.txt b/cmd/help/array-set.txt index a1ae2e7..fce4b0f 100644 --- a/cmd/help/array-set.txt +++ b/cmd/help/array-set.txt @@ -13,7 +13,7 @@ Parameters: Options: --file, -f PATH - Path to the config file + Path to the config file (default: $CONFIG_FILE) --dry, -n Print the updated config without modifying the file @@ -25,4 +25,9 @@ Options: Colorize diff output Examples: + # Replace all values + config array set -f config.toml roots "$HOME/.cache" /tmp + + # Use a default file for repeated commands + export CONFIG_FILE=config.toml config array set roots "$HOME/.cache" /tmp diff --git a/cmd/help/completion.txt b/cmd/help/completion.txt index 6128ccc..eebd632 100644 --- a/cmd/help/completion.txt +++ b/cmd/help/completion.txt @@ -7,8 +7,3 @@ Usage: Parameters: SHELL Shell to generate completions for: bash, zsh, fish - -Examples: - config completion bash - config completion zsh - config completion fish diff --git a/cmd/help/delete.txt b/cmd/help/delete.txt index 3b131b9..0740193 100644 --- a/cmd/help/delete.txt +++ b/cmd/help/delete.txt @@ -13,7 +13,7 @@ Parameters: Options: --file, -f PATH - Path to the config file + Path to the config file (default: $CONFIG_FILE) --on FIELD:VALUE Select a record by FIELD:VALUE. May be repeated. @@ -31,8 +31,18 @@ Options: Colorize diff output Examples: - config delete -f path/to/config.toml style + # Delete a container + config delete -f config.toml style + + # Use a default file for repeated commands + export CONFIG_FILE=config.toml config delete servers.1 + + # Use the short command alias config del servers.1 + + # Delete a matching array record config delete servers --on name:web + + # Delete only when the container is empty config delete tui.keymap.composer --if-empty diff --git a/cmd/help/dump.txt b/cmd/help/dump.txt index adf8f28..a303908 100644 --- a/cmd/help/dump.txt +++ b/cmd/help/dump.txt @@ -10,14 +10,21 @@ Parameters: Options: --file, -f PATH - Path to the config file + Path to the config file (default: $CONFIG_FILE) --json Dump as JSON instead of YAML Examples: - config dump -f path/to/config.toml - config dump -f path/to/config.toml database - config dump -f path/to/config.toml database --json - config dump -f path/to/config.yaml server.hosts - config dump + # Dump all data + config dump -f config.toml + + # Use a default file for repeated commands + export CONFIG_FILE=config.toml + config dump database + + # Dump as JSON + config dump database --json + + # Dump data under a key + config dump server.hosts diff --git a/cmd/help/edit.txt b/cmd/help/edit.txt index bc3296e..eca309e 100644 --- a/cmd/help/edit.txt +++ b/cmd/help/edit.txt @@ -6,12 +6,16 @@ Usage: Options: --file, -f PATH - Path to the config file + Path to the config file (default: $CONFIG_FILE) Environment: EDITOR Editor command to run. Defaults to vi. Examples: - config edit -f path/to/config.toml + # Open a file + config edit -f config.toml + + # Use a default file for repeated commands + export CONFIG_FILE=config.toml config edit diff --git a/cmd/help/get.txt b/cmd/help/get.txt index 2546474..f3ff6cd 100644 --- a/cmd/help/get.txt +++ b/cmd/help/get.txt @@ -17,7 +17,7 @@ Notes: Options: --file, -f PATH - Path to the config file + Path to the config file (default: $CONFIG_FILE) --in COLLECTION Read a field from a record in COLLECTION. Must be used with --on. @@ -26,6 +26,12 @@ Options: Select a record by FIELD:VALUE. May be repeated. Examples: - config get -f path/to/config.toml server.port + # Read a value + config get -f config.toml server.port + + # Use a default file for repeated commands + export CONFIG_FILE=config.toml config get server.port - config get -f path/to/config.toml port --in servers --on name:api + + # Read from a matching array record + config get port --in servers --on name:api diff --git a/cmd/help/help.txt b/cmd/help/help.txt deleted file mode 100644 index 9fc215e..0000000 --- a/cmd/help/help.txt +++ /dev/null @@ -1,25 +0,0 @@ -Show command help or topic help - -Usage: - config help [COMMAND|TOPIC] - config help --help | -h - -Commands: - set - get - unset - delete - array - list - dump - edit - completion - -Topics: - environment - formats - -Examples: - config help set - config help array add - config help environment diff --git a/cmd/help/list.txt b/cmd/help/list.txt index a8e9c5c..8f65e09 100644 --- a/cmd/help/list.txt +++ b/cmd/help/list.txt @@ -13,15 +13,18 @@ Parameters: Options: --file, -f PATH - Path to the config file + Path to the config file (default: $CONFIG_FILE) --color, -c Colorize keys and separators Examples: - config list -f path/to/config.toml - config list -f path/to/config.toml database - config list -f path/to/config.toml database.port - config list --color - config ls --color - config list + # List all values + config list -f config.toml + + # Use a default file for repeated commands + export CONFIG_FILE=config.toml + config list database + + # List values under a key + config list database.port --color diff --git a/cmd/help/set.txt b/cmd/help/set.txt index c8f6682..b9acccc 100644 --- a/cmd/help/set.txt +++ b/cmd/help/set.txt @@ -15,7 +15,7 @@ Parameters: Options: --file, -f PATH - Path to the config file + Path to the config file (default: $CONFIG_FILE) --in COLLECTION Edit a record in COLLECTION @@ -37,23 +37,23 @@ Options: Examples: # Simple update - config set -f path/to/config.toml server.port 3000 + config set -f config.toml server.port 3000 + + # Use a default file for repeated commands + export CONFIG_FILE=config.toml + config set server.port 3000 # Indexed array-record update - config set -f path/to/config.toml servers.0.port 3000 + config set servers.0.port 3000 # Unique array-record update or create - config set -f path/to/config.toml port 3000 --in servers --on name:web + config set port 3000 --in servers --on name:web # Typed literal value - config set -f path/to/config.toml release.date 2027-03-24 + config set release.date 2027-03-24 # String escape hatch - config set -f path/to/config.toml version 1.0 --string - - # Priming a config file using environment variable - export CONFIG_FILE=~/.codex/config.toml - config set server.port 3000 + config set version 1.0 --string # Reading a multiline string from stdin - printf 'hello\nworld' | config set -f path/to/config.toml title.intro - --string + printf 'hello\nworld' | config set title.intro - --string diff --git a/cmd/help/unset.txt b/cmd/help/unset.txt index c85985e..e8a3754 100644 --- a/cmd/help/unset.txt +++ b/cmd/help/unset.txt @@ -10,7 +10,7 @@ Parameters: Options: --file, -f PATH - Path to the config file + Path to the config file (default: $CONFIG_FILE) --in COLLECTION Remove a field from a record in COLLECTION. Must be used with --on. @@ -34,8 +34,18 @@ Options: Colorize diff output Examples: - config unset -f path/to/config.toml server.port + # Simple delete + config unset -f config.toml server.port + + # Use a default file for repeated commands + export CONFIG_FILE=config.toml config unset server.port + + # Delete only when the current value matches config unset server.port --if 3000 + + # Delete if the key exists config unset server.port --if-exists - config unset -f path/to/config.toml port --in servers --on name:api + + # Delete from a matching array record + config unset port --in servers --on name:api