Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion cmd/apps/bundle_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"time"

Expand All @@ -27,12 +29,49 @@ func makeArgsOptionalWithBundle(cmd *cobra.Command, usage string) {
return fmt.Errorf("accepts at most 1 arg(s), received %d", len(args))
}
if !hasBundleConfig() && len(args) != 1 {
return fmt.Errorf("accepts 1 arg(s), received %d", len(args))
return missingAppNameError()
}
return nil
}
}

// missingAppNameError returns an error message that explains what the positional
// argument should be, and attempts to infer a suggestion from the local environment.
func missingAppNameError() error {
hint := inferAppNameHint()
msg := `missing required argument: APP_NAME

Usage: databricks apps <command> APP_NAME

APP_NAME is the name of the Databricks app to operate on.
Alternatively, run this command from a project directory containing
databricks.yml to auto-detect the app name.`

if hint != "" {
msg += "\n\nDid you mean?\n databricks apps deploy " + hint
}

return errors.New(msg)
}

// inferAppNameHint tries to suggest an app name from the local environment.
// Only returns a hint if the current directory looks like a Databricks app
// (contains app.yml or app.yaml), using the directory name as the suggestion.
func inferAppNameHint() string {
wd, err := os.Getwd()
if err != nil {
return ""
}

for _, filename := range []string{"app.yml", "app.yaml"} {
if _, err := os.Stat(filepath.Join(wd, filename)); err == nil {
return filepath.Base(wd)
}
}

return ""
}

// getAppNameFromArgs returns the app name from args or detects it from the bundle.
// Returns (appName, fromBundle, error).
func getAppNameFromArgs(cmd *cobra.Command, args []string) (string, bool, error) {
Expand Down
68 changes: 68 additions & 0 deletions cmd/apps/bundle_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package apps
import (
"context"
"errors"
"os"
"path/filepath"
"testing"

"github.com/databricks/databricks-sdk-go/service/apps"
Expand Down Expand Up @@ -106,6 +108,72 @@ func TestFormatAppStatusMessage(t *testing.T) {
})
}

func TestInferAppNameHint(t *testing.T) {
t.Run("returns empty when no app config exists", func(t *testing.T) {
t.Chdir(t.TempDir())

assert.Equal(t, "", inferAppNameHint())
})

t.Run("returns dir name when app.yml exists", func(t *testing.T) {
dir := t.TempDir()
t.Chdir(dir)
err := os.WriteFile(filepath.Join(dir, "app.yml"), []byte("command: [\"python\"]"), 0o644)
assert.NoError(t, err)

assert.Equal(t, filepath.Base(dir), inferAppNameHint())
})

t.Run("returns dir name when app.yaml exists", func(t *testing.T) {
dir := t.TempDir()
t.Chdir(dir)
err := os.WriteFile(filepath.Join(dir, "app.yaml"), []byte("command: [\"python\"]"), 0o644)
assert.NoError(t, err)

assert.Equal(t, filepath.Base(dir), inferAppNameHint())
})

t.Run("returns empty when cwd has been deleted", func(t *testing.T) {
dir := t.TempDir()
t.Chdir(dir)
os.Remove(dir)

assert.Equal(t, "", inferAppNameHint())
})
}

func TestMissingAppNameError(t *testing.T) {
t.Run("includes APP_NAME and usage info", func(t *testing.T) {
t.Chdir(t.TempDir())

err := missingAppNameError()
assert.Contains(t, err.Error(), "APP_NAME")
assert.Contains(t, err.Error(), "databricks.yml")
assert.NotContains(t, err.Error(), "Did you mean")
})

t.Run("includes hint when app.yml exists", func(t *testing.T) {
dir := t.TempDir()
t.Chdir(dir)
writeErr := os.WriteFile(filepath.Join(dir, "app.yml"), []byte("command: [\"python\"]"), 0o644)
assert.NoError(t, writeErr)

err := missingAppNameError()
assert.Contains(t, err.Error(), "Did you mean")
assert.Contains(t, err.Error(), filepath.Base(dir))
})

t.Run("gracefully handles deleted cwd", func(t *testing.T) {
dir := t.TempDir()
t.Chdir(dir)
os.Remove(dir)

err := missingAppNameError()
assert.Contains(t, err.Error(), "APP_NAME")
assert.NotContains(t, err.Error(), "Did you mean")
})
}

func TestMakeArgsOptionalWithBundle(t *testing.T) {
t.Run("updates command usage", func(t *testing.T) {
cmd := &cobra.Command{}
Expand Down
Loading