Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve experience for multiple builtin templates #1052

Merged
merged 10 commits into from
Dec 27, 2023
48 changes: 39 additions & 9 deletions cmd/bundle/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,35 +27,58 @@ type nativeTemplate struct {
aliases []string
}

const customTemplate = "custom..."

var nativeTemplates = []nativeTemplate{
{
name: "default-python",
description: "The default Python template",
description: "The default Python template for Notebooks / Delta Live Tables / Workflows",
},
{
name: "mlops-stacks",
gitUrl: "https://github.com/databricks/mlops-stacks",
description: "The Databricks MLOps Stacks template (https://github.com/databricks/mlops-stacks)",
description: "The Databricks MLOps Stacks template (github.com/databricks/mlops-stacks)",
aliases: []string{"mlops-stack"},
},
{
name: customTemplate,
description: "Bring your own template",
},
}

func nativeTemplateDescriptions() string {
// Return template descriptions for command-line help
func nativeTemplateHelpDescriptions() string {
var lines []string
for _, template := range nativeTemplates {
lines = append(lines, fmt.Sprintf("- %s: %s", template.name, template.description))
if template.name != customTemplate {
lines = append(lines, fmt.Sprintf("- %s: %s", template.name, template.description))
}
}
return strings.Join(lines, "\n")
}

func nativeTemplateOptions() []string {
names := make([]string, 0, len(nativeTemplates))
// Return template options for an interactive prompt
func nativeTemplateOptions() []cmdio.Tuple {
names := make([]cmdio.Tuple, 0, len(nativeTemplates))
for _, template := range nativeTemplates {
names = append(names, template.name)
tuple := cmdio.Tuple{
Name: template.name,
Id: template.description,
}
names = append(names, tuple)
}
return names
}

func getNativeTemplateByDescription(description string) string {
for _, template := range nativeTemplates {
if template.description == description {
return template.name
}
}
return ""
}

func getUrlForNativeTemplate(name string) string {
for _, template := range nativeTemplates {
if template.name == name {
Expand Down Expand Up @@ -99,7 +122,7 @@ TEMPLATE_PATH optionally specifies which template to use. It can be one of the f
- a local file system path with a template directory
- a Git repository URL, e.g. https://github.com/my/repository

See https://docs.databricks.com/en/dev-tools/bundles/templates.html for more information on templates.`, nativeTemplateDescriptions()),
See https://docs.databricks.com/en/dev-tools/bundles/templates.html for more information on templates.`, nativeTemplateHelpDescriptions()),
}

var configFile string
Expand Down Expand Up @@ -134,10 +157,17 @@ See https://docs.databricks.com/en/dev-tools/bundles/templates.html for more inf
if !cmdio.IsPromptSupported(ctx) {
return errors.New("please specify a template")
}
templatePath, err = cmdio.AskSelect(ctx, "Template to use", nativeTemplateOptions())
description, err := cmdio.SelectOrdered(ctx, nativeTemplateOptions(), "Template to use")
if err != nil {
return err
}
templatePath = getNativeTemplateByDescription(description)
}

if templatePath == customTemplate {
cmdio.LogString(ctx, "Please specify a path or Git repository to use a custom template.")
cmdio.LogString(ctx, "See https://docs.databricks.com/en/dev-tools/bundles/templates.html to learn more about custom templates.")
return nil
}

// Expand templatePath to a git URL if it's an alias for a known native template
Expand Down
14 changes: 11 additions & 3 deletions cmd/bundle/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package bundle
import (
"testing"

"github.com/databricks/cli/libs/cmdio"
"github.com/stretchr/testify/assert"
)

Expand All @@ -27,11 +28,18 @@ func TestBundleInitRepoName(t *testing.T) {
}

func TestNativeTemplateOptions(t *testing.T) {
lennartkats-db marked this conversation as resolved.
Show resolved Hide resolved
assert.Equal(t, []string{"default-python", "mlops-stacks"}, nativeTemplateOptions())
expected := []cmdio.Tuple{
{Name: "default-python", Id: "The default Python template for Notebooks / Delta Live Tables / Workflows"},
{Name: "mlops-stacks", Id: "The Databricks MLOps Stacks template (github.com/databricks/mlops-stacks)"},
{Name: "custom...", Id: "Bring your own template"},
}
assert.Equal(t, expected, nativeTemplateOptions())
}

func TestNativeTemplateDescriptions(t *testing.T) {
assert.Equal(t, "- default-python: The default Python template\n- mlops-stacks: The Databricks MLOps Stacks template (https://github.com/databricks/mlops-stacks)", nativeTemplateDescriptions())
func TestNativeTemplateHelpDescriptions(t *testing.T) {
expected := `- default-python: The default Python template for Notebooks / Delta Live Tables / Workflows
- mlops-stacks: The Databricks MLOps Stacks template (github.com/databricks/mlops-stacks)`
assert.Equal(t, expected, nativeTemplateHelpDescriptions())
}

func TestGetUrlForNativeTemplate(t *testing.T) {
Expand Down
30 changes: 18 additions & 12 deletions libs/cmdio/io.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,19 +155,13 @@ func RenderReader(ctx context.Context, r io.Reader) error {
}
}

type tuple struct{ Name, Id string }
type Tuple struct{ Name, Id string }

func (c *cmdIO) Select(names map[string]string, label string) (id string, err error) {
func (c *cmdIO) Select(items []Tuple, label string) (id string, err error) {
if !c.interactive {
return "", fmt.Errorf("expected to have %s", label)
}
var items []tuple
for k, v := range names {
items = append(items, tuple{k, v})
}
slices.SortFunc(items, func(a, b tuple) int {
return strings.Compare(a.Name, b.Name)
})

idx, _, err := (&promptui.Select{
Label: label,
Items: items,
Expand All @@ -190,13 +184,25 @@ func (c *cmdIO) Select(names map[string]string, label string) (id string, err er
return
}

// Show a selection prompt where the user can pick one of the name/id items.
// The items are sorted alphabetically by name.
func Select[V any](ctx context.Context, names map[string]V, label string) (id string, err error) {
c := fromContext(ctx)
stringNames := map[string]string{}
var items []Tuple
for k, v := range names {
stringNames[k] = fmt.Sprint(v)
items = append(items, Tuple{k, fmt.Sprint(v)})
}
return c.Select(stringNames, label)
slices.SortFunc(items, func(a, b Tuple) int {
return strings.Compare(a.Name, b.Name)
})
return c.Select(items, label)
}

// Show a selection prompt where the user can pick one of the name/id items.
// The items can appear in a custom order and are not alphabetically sorted.
lennartkats-db marked this conversation as resolved.
Show resolved Hide resolved
func SelectOrdered(ctx context.Context, items []Tuple, label string) (id string, err error) {
lennartkats-db marked this conversation as resolved.
Show resolved Hide resolved
c := fromContext(ctx)
return c.Select(items, label)
}

func (c *cmdIO) Secret(label string) (value string, err error) {
Expand Down