Skip to content

Commit 6c3d347

Browse files
fix(apps): render verb-specific usage and hint in missingAppNameError
The shared zero-arg validator used by deploy/start/stop/delete printed a hardcoded "databricks apps deploy <hint>" suggestion and a generic "<command>" placeholder in the usage line, so users running `apps stop` without an APP_NAME were nudged toward `apps deploy` instead. - Thread the cobra command into missingAppNameError and render both the Usage line and the "Did you mean?" hint from cmd.CommandPath(). - Extend bundle_helpers_test.go with table-driven coverage that exercises deploy/start/stop/delete and asserts the hint matches the verb. - inferAppNameHint now requires app.yml/app.yaml to be a regular file, so a directory of that name no longer produces a bogus hint. - Regenerate the no-bundle-no-args acceptance fixture for the new message. Co-authored-by: Isaac
1 parent 3587a78 commit 6c3d347

3 files changed

Lines changed: 56 additions & 11 deletions

File tree

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1-
Error: accepts 1 arg(s), received 0
1+
Error: missing required argument: APP_NAME
2+
3+
Usage: databricks apps deploy APP_NAME
4+
5+
APP_NAME is the name of the Databricks app to operate on.
6+
Alternatively, run this command from a project directory containing
7+
databricks.yml to auto-detect the app name.
28

39
Exit code: 1

cmd/apps/bundle_helpers.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,26 +29,34 @@ func makeArgsOptionalWithBundle(cmd *cobra.Command, usage string) {
2929
return fmt.Errorf("accepts at most 1 arg(s), received %d", len(args))
3030
}
3131
if !hasBundleConfig() && len(args) != 1 {
32-
return missingAppNameError()
32+
return missingAppNameError(cmd)
3333
}
3434
return nil
3535
}
3636
}
3737

3838
// missingAppNameError returns an error message that explains what the positional
3939
// argument should be, and attempts to infer a suggestion from the local environment.
40-
func missingAppNameError() error {
40+
// The full subcommand path (e.g. "databricks apps start") is rendered from cmd so
41+
// the usage line and "Did you mean?" hint match the verb the user actually ran.
42+
func missingAppNameError(cmd *cobra.Command) error {
4143
hint := inferAppNameHint()
42-
msg := `missing required argument: APP_NAME
44+
commandPath := "databricks apps <command>"
45+
if cmd != nil {
46+
if p := cmd.CommandPath(); p != "" {
47+
commandPath = p
48+
}
49+
}
50+
msg := fmt.Sprintf(`missing required argument: APP_NAME
4351
44-
Usage: databricks apps <command> APP_NAME
52+
Usage: %s APP_NAME
4553
4654
APP_NAME is the name of the Databricks app to operate on.
4755
Alternatively, run this command from a project directory containing
48-
databricks.yml to auto-detect the app name.`
56+
databricks.yml to auto-detect the app name.`, commandPath)
4957

5058
if hint != "" {
51-
msg += "\n\nDid you mean?\n databricks apps deploy " + hint
59+
msg += fmt.Sprintf("\n\nDid you mean?\n %s %s", commandPath, hint)
5260
}
5361

5462
return errors.New(msg)
@@ -64,7 +72,8 @@ func inferAppNameHint() string {
6472
}
6573

6674
for _, filename := range []string{"app.yml", "app.yaml"} {
67-
if _, err := os.Stat(filepath.Join(wd, filename)); err == nil {
75+
info, err := os.Stat(filepath.Join(wd, filename))
76+
if err == nil && info.Mode().IsRegular() {
6877
return filepath.Base(wd)
6978
}
7079
}

cmd/apps/bundle_helpers_test.go

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ func TestMissingAppNameError(t *testing.T) {
145145
t.Run("includes APP_NAME and usage info", func(t *testing.T) {
146146
t.Chdir(t.TempDir())
147147

148-
err := missingAppNameError()
148+
err := missingAppNameError(nil)
149149
assert.Contains(t, err.Error(), "APP_NAME")
150150
assert.Contains(t, err.Error(), "databricks.yml")
151151
assert.NotContains(t, err.Error(), "Did you mean")
@@ -157,7 +157,7 @@ func TestMissingAppNameError(t *testing.T) {
157157
writeErr := os.WriteFile(filepath.Join(dir, "app.yml"), []byte("command: [\"python\"]"), 0o644)
158158
assert.NoError(t, writeErr)
159159

160-
err := missingAppNameError()
160+
err := missingAppNameError(nil)
161161
assert.Contains(t, err.Error(), "Did you mean")
162162
assert.Contains(t, err.Error(), filepath.Base(dir))
163163
})
@@ -167,10 +167,40 @@ func TestMissingAppNameError(t *testing.T) {
167167
t.Chdir(dir)
168168
os.Remove(dir)
169169

170-
err := missingAppNameError()
170+
err := missingAppNameError(nil)
171171
assert.Contains(t, err.Error(), "APP_NAME")
172172
assert.NotContains(t, err.Error(), "Did you mean")
173173
})
174+
175+
t.Run("renders usage and hint from cmd path per verb", func(t *testing.T) {
176+
dir := t.TempDir()
177+
t.Chdir(dir)
178+
writeErr := os.WriteFile(filepath.Join(dir, "app.yml"), []byte("command: [\"python\"]"), 0o644)
179+
assert.NoError(t, writeErr)
180+
181+
for _, verb := range []string{"deploy", "start", "stop", "delete"} {
182+
t.Run(verb, func(t *testing.T) {
183+
root := &cobra.Command{Use: "databricks"}
184+
apps := &cobra.Command{Use: "apps"}
185+
sub := &cobra.Command{Use: verb}
186+
root.AddCommand(apps)
187+
apps.AddCommand(sub)
188+
189+
err := missingAppNameError(sub)
190+
assert.Contains(t, err.Error(), "Usage: databricks apps "+verb+" APP_NAME")
191+
assert.Contains(t, err.Error(), "databricks apps "+verb+" "+filepath.Base(dir))
192+
})
193+
}
194+
})
195+
196+
t.Run("ignores non-regular app.yml entries", func(t *testing.T) {
197+
dir := t.TempDir()
198+
t.Chdir(dir)
199+
assert.NoError(t, os.Mkdir(filepath.Join(dir, "app.yml"), 0o755))
200+
201+
err := missingAppNameError(nil)
202+
assert.NotContains(t, err.Error(), "Did you mean")
203+
})
174204
}
175205

176206
func TestMakeArgsOptionalWithBundle(t *testing.T) {

0 commit comments

Comments
 (0)