Skip to content

Commit a42aff3

Browse files
Add completion install, uninstall, and status subcommands (#4581)
## Why Users currently have to manually configure shell tab completion by piping `databricks completion <shell>` output into the right RC file. This is error-prone and shell-specific. Adding `install`/`uninstall`/`status` subcommands automates the setup. ## Changes Adds three new subcommands to the existing `completion` command: ``` databricks completion databricks completion bash # Generate bash completion script databricks completion zsh # Generate zsh completion script databricks completion fish # Generate fish completion script databricks completion powershell # Generate powershell completion script databricks completion install # NEW: Install completions into shell RC file databricks completion uninstall # NEW: Remove completions from shell RC file databricks completion status # NEW: Show current completion status ``` **`completion install [--shell <shell>] [--auto-approve]`** — Auto-detects shell from `$SHELL`, appends an eval shim wrapped in `BEGIN`/`END` markers to the appropriate RC file. Fish uses a file drop to `~/.config/fish/completions/` instead. Shows the detected shell and target file and asks for confirmation before writing; `--auto-approve` skips the prompt (for scripts/agents). If completions are already present (our marker, Homebrew, or an external fish file), reports that and exits without prompting. **`completion uninstall [--shell <shell>] [--auto-approve]`** — Removes the marker block from the RC file (or deletes the fish completions file if it contains our marker). Strict parsing: returns an error if markers are corrupted rather than risking RC file damage. External completions (Homebrew, package manager fish files) are detected and reported but not removed. **`completion status [--shell <shell>]`** — Reports whether completions are installed, detecting our marker block, Homebrew-based zsh installs, and fish file-based installs. Also serves as a dry-run to preview the detected shell and target file before running install. **Shell support**: bash, zsh, fish, powershell (pwsh 7+), powershell5 (Windows PowerShell 5.1). Shell detection checks `$SHELL` first (catches Git Bash/MSYS on Windows), falls back to PATH lookup on Windows. If `$SHELL` contains an unrecognized value on Windows (e.g. `powershell.exe`), detection falls through to PATH-based lookup. **Architecture**: Core logic lives in `libs/completion/` with no cobra dependency, so it can be called from other commands (e.g. a guided setup flow). `cmd/completion/` contains thin cobra wrappers. The existing shell generation subcommands (bash/zsh/fish/powershell) are reimplemented with runtime writer resolution to avoid a Cobra `OutOrStdout()` capture timing issue with the test harness. ## Tests - Unit tests in `libs/completion/` covering shell detection, install/uninstall/status logic, idempotency, marker corruption detection, edge cases (empty files, missing dirs, permission preservation, Windows permission semantics, fish foreign-file ownership, `$SHELL=powershell.exe` fallback) - Acceptance test in `acceptance/cmd/completion/` covering the full install → status → idempotent reinstall → uninstall → status round-trip, plus smoke tests for all four shell script generators and `--no-descriptions` --------- Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 15df9cd commit a42aff3

File tree

18 files changed

+1649
-0
lines changed

18 files changed

+1649
-0
lines changed

NEXT_CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
## Release v0.290.0
44

5+
### CLI
6+
* Add `completion install`, `uninstall`, and `status` subcommands ([#4581](https://github.com/databricks/cli/pull/4581))
7+
8+
### Internal
9+
510
### Dependency updates
611
* Upgrade TF provider to 1.109.0 ([#4561](https://github.com/databricks/cli/pull/4561))
712
* Upgrade Go SDK to v0.110.0 ([#4552](https://github.com/databricks/cli/pull/4552))

acceptance/cmd/completion/out.test.toml

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
2+
>>> [CLI] completion install --shell zsh --auto-approve
3+
Databricks CLI completions installed for zsh.
4+
Restart your shell or run 'source home/.zshrc' to activate.
5+
6+
Warning: zsh completions require the completion system to be initialized.
7+
Add the following to your home/.zshrc:
8+
autoload -U compinit && compinit
9+
10+
>>> [CLI] completion status --shell zsh
11+
Shell: zsh
12+
File: home/.zshrc
13+
Status: installed
14+
15+
Warning: zsh completions require the completion system to be initialized.
16+
Add the following to your home/.zshrc:
17+
autoload -U compinit && compinit
18+
19+
>>> [CLI] completion install --shell zsh --auto-approve
20+
Databricks CLI completions are already installed for zsh in home/.zshrc.
21+
22+
Warning: zsh completions require the completion system to be initialized.
23+
Add the following to your home/.zshrc:
24+
autoload -U compinit && compinit
25+
26+
>>> [CLI] completion uninstall --shell zsh --auto-approve
27+
Databricks CLI completions removed for zsh from home/.zshrc.
28+
29+
>>> [CLI] completion status --shell zsh
30+
Shell: zsh
31+
File: home/.zshrc
32+
Status: not installed
33+
# bash completion V2 for databricks -*- shell-script -*-
34+
#compdef databricks
35+
# fish completion for databricks -*- shell-script -*-
36+
# powershell completion for databricks -*- shell-script -*-
37+
# bash completion V2 for databricks -*- shell-script -*-

acceptance/cmd/completion/script

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
sethome "./home"
2+
3+
# Track the home path for stable output across platforms.
4+
add_repl.py "$HOME" HOME
5+
6+
# Prevent Homebrew detection from affecting status output.
7+
export HOMEBREW_PREFIX=/nonexistent
8+
9+
# Test install (use zsh to avoid OS-dependent bash RC file path)
10+
trace $CLI completion install --shell zsh --auto-approve
11+
12+
# Test status shows installed
13+
trace $CLI completion status --shell zsh
14+
15+
# Test idempotent install (--auto-approve is harmless when already installed)
16+
trace $CLI completion install --shell zsh --auto-approve
17+
18+
# Test uninstall
19+
trace $CLI completion uninstall --shell zsh --auto-approve
20+
21+
# Test status after uninstall
22+
trace $CLI completion status --shell zsh
23+
24+
# Test shell subcommands produce output
25+
$CLI completion bash 2>&1 | head -1
26+
$CLI completion zsh 2>&1 | head -1
27+
$CLI completion fish 2>&1 | head -1
28+
$CLI completion powershell 2>&1 | head -1
29+
30+
# Test bash --no-descriptions
31+
$CLI completion bash --no-descriptions 2>&1 | head -1
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Ignore = [
2+
"home",
3+
]
4+
5+
Local = true
6+
Cloud = false
7+
8+
# Completion tests don't involve bundles, skip the engine matrix.
9+
[EnvMatrix]
10+
DATABRICKS_BUNDLE_ENGINE = ["terraform"]

cmd/cmd.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/databricks/cli/cmd/auth"
1313
"github.com/databricks/cli/cmd/bundle"
1414
"github.com/databricks/cli/cmd/cache"
15+
"github.com/databricks/cli/cmd/completion"
1516
"github.com/databricks/cli/cmd/configure"
1617
"github.com/databricks/cli/cmd/experimental"
1718
"github.com/databricks/cli/cmd/fs"
@@ -94,6 +95,7 @@ func New(ctx context.Context) *cobra.Command {
9495
// Add other subcommands.
9596
cli.AddCommand(api.New())
9697
cli.AddCommand(auth.New())
98+
cli.AddCommand(completion.New())
9799
cli.AddCommand(bundle.New())
98100
cli.AddCommand(cache.New())
99101
cli.AddCommand(experimental.New())

cmd/completion/completion.go

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
package completion
2+
3+
import (
4+
"context"
5+
"os"
6+
"path/filepath"
7+
"strings"
8+
9+
"github.com/databricks/cli/cmd/root"
10+
"github.com/databricks/cli/libs/cmdio"
11+
libcompletion "github.com/databricks/cli/libs/completion"
12+
"github.com/spf13/cobra"
13+
)
14+
15+
// New returns the "completion" command with shell subcommands and
16+
// install/uninstall/status subcommands. Providing a command named
17+
// "completion" prevents Cobra's InitDefaultCompletionCmd from generating
18+
// its default, so we replicate the shell subcommands here with runtime
19+
// writer resolution to avoid the frozen-writer bug.
20+
func New() *cobra.Command {
21+
cmd := &cobra.Command{
22+
Use: "completion",
23+
Short: "Generate the autocompletion script for the specified shell",
24+
Long: `Generate the autocompletion script for databricks for the specified shell.
25+
See each sub-command's help for details on how to use the generated script.
26+
`,
27+
Args: cobra.NoArgs,
28+
ValidArgsFunction: cobra.NoFileCompletions,
29+
RunE: root.ReportUnknownSubcommand,
30+
}
31+
32+
cmd.AddCommand(
33+
newBashCmd(),
34+
newZshCmd(),
35+
newFishCmd(),
36+
newPowershellCmd(),
37+
newInstallCmd(),
38+
newUninstallCmd(),
39+
newStatusCmd(),
40+
)
41+
42+
return cmd
43+
}
44+
45+
func newBashCmd() *cobra.Command {
46+
var noDesc bool
47+
cmd := &cobra.Command{
48+
Use: "bash",
49+
Short: "Generate the autocompletion script for bash",
50+
Long: `Generate the autocompletion script for the bash shell.
51+
52+
This script depends on the 'bash-completion' package.
53+
If it is not installed already, you can install it via your OS's package manager.
54+
55+
To load completions in your current shell session:
56+
57+
source <(databricks completion bash)
58+
59+
To load completions for every new session, execute once:
60+
61+
#### Linux:
62+
63+
databricks completion bash > /etc/bash_completion.d/databricks
64+
65+
#### macOS:
66+
67+
databricks completion bash > $(brew --prefix)/etc/bash_completion.d/databricks
68+
69+
You will need to start a new shell for this setup to take effect.
70+
`,
71+
Args: cobra.NoArgs,
72+
DisableFlagsInUseLine: true,
73+
ValidArgsFunction: cobra.NoFileCompletions,
74+
RunE: func(cmd *cobra.Command, args []string) error {
75+
return cmd.Root().GenBashCompletionV2(cmd.OutOrStdout(), !noDesc)
76+
},
77+
}
78+
cmd.Flags().BoolVar(&noDesc, "no-descriptions", false, "disable completion descriptions")
79+
return cmd
80+
}
81+
82+
func newZshCmd() *cobra.Command {
83+
var noDesc bool
84+
cmd := &cobra.Command{
85+
Use: "zsh",
86+
Short: "Generate the autocompletion script for zsh",
87+
Long: `Generate the autocompletion script for the zsh shell.
88+
89+
If shell completion is not already enabled in your environment you will need
90+
to enable it. You can execute the following once:
91+
92+
echo "autoload -U compinit; compinit" >> ~/.zshrc
93+
94+
To load completions in your current shell session:
95+
96+
source <(databricks completion zsh)
97+
98+
To load completions for every new session, execute once:
99+
100+
#### Linux:
101+
102+
databricks completion zsh > "${fpath[1]}/_databricks"
103+
104+
#### macOS:
105+
106+
databricks completion zsh > $(brew --prefix)/share/zsh/site-functions/_databricks
107+
108+
You will need to start a new shell for this setup to take effect.
109+
`,
110+
Args: cobra.NoArgs,
111+
ValidArgsFunction: cobra.NoFileCompletions,
112+
RunE: func(cmd *cobra.Command, args []string) error {
113+
if noDesc {
114+
return cmd.Root().GenZshCompletionNoDesc(cmd.OutOrStdout())
115+
}
116+
return cmd.Root().GenZshCompletion(cmd.OutOrStdout())
117+
},
118+
}
119+
cmd.Flags().BoolVar(&noDesc, "no-descriptions", false, "disable completion descriptions")
120+
return cmd
121+
}
122+
123+
func newFishCmd() *cobra.Command {
124+
var noDesc bool
125+
cmd := &cobra.Command{
126+
Use: "fish",
127+
Short: "Generate the autocompletion script for fish",
128+
Long: `Generate the autocompletion script for the fish shell.
129+
130+
To load completions in your current shell session:
131+
132+
databricks completion fish | source
133+
134+
To load completions for every new session, execute once:
135+
136+
databricks completion fish > ~/.config/fish/completions/databricks.fish
137+
138+
You will need to start a new shell for this setup to take effect.
139+
`,
140+
Args: cobra.NoArgs,
141+
ValidArgsFunction: cobra.NoFileCompletions,
142+
RunE: func(cmd *cobra.Command, args []string) error {
143+
return cmd.Root().GenFishCompletion(cmd.OutOrStdout(), !noDesc)
144+
},
145+
}
146+
cmd.Flags().BoolVar(&noDesc, "no-descriptions", false, "disable completion descriptions")
147+
return cmd
148+
}
149+
150+
func newPowershellCmd() *cobra.Command {
151+
var noDesc bool
152+
cmd := &cobra.Command{
153+
Use: "powershell",
154+
Short: "Generate the autocompletion script for powershell",
155+
Long: `Generate the autocompletion script for powershell.
156+
157+
To load completions in your current shell session:
158+
159+
databricks completion powershell | Out-String | Invoke-Expression
160+
161+
To load completions for every new session, add the output of the above command
162+
to your powershell profile.
163+
`,
164+
Args: cobra.NoArgs,
165+
ValidArgsFunction: cobra.NoFileCompletions,
166+
RunE: func(cmd *cobra.Command, args []string) error {
167+
if noDesc {
168+
return cmd.Root().GenPowerShellCompletion(cmd.OutOrStdout())
169+
}
170+
return cmd.Root().GenPowerShellCompletionWithDesc(cmd.OutOrStdout())
171+
},
172+
}
173+
cmd.Flags().BoolVar(&noDesc, "no-descriptions", false, "disable completion descriptions")
174+
return cmd
175+
}
176+
177+
// warnIfCompinitMissing prints a warning when zsh completions are present but
178+
// the user's .zshrc does not call compinit. Without compinit, neither our eval
179+
// shim nor Homebrew's _databricks file will be loaded.
180+
func warnIfCompinitMissing(ctx context.Context, shell libcompletion.Shell, home string) {
181+
if shell != libcompletion.Zsh {
182+
return
183+
}
184+
rcPath := libcompletion.TargetFilePath(shell, home)
185+
content, err := os.ReadFile(rcPath)
186+
if err != nil {
187+
return
188+
}
189+
if strings.Contains(string(content), "compinit") {
190+
return
191+
}
192+
cmdio.LogString(ctx, "")
193+
cmdio.LogString(ctx, "Warning: zsh completions require the completion system to be initialized.")
194+
cmdio.LogString(ctx, "Add the following to your "+filepath.ToSlash(rcPath)+":")
195+
cmdio.LogString(ctx, " autoload -U compinit && compinit")
196+
}
197+
198+
// addShellFlag registers the --shell flag and its completion function on cmd.
199+
func addShellFlag(cmd *cobra.Command, target *string) {
200+
cmd.Flags().StringVar(target, "shell", "", "Shell type: bash, zsh, fish, powershell, powershell5")
201+
cmd.RegisterFlagCompletionFunc("shell", func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
202+
return []cobra.Completion{"bash", "zsh", "fish", "powershell", "powershell5"}, cobra.ShellCompDirectiveNoFileComp
203+
})
204+
}

0 commit comments

Comments
 (0)