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
32 changes: 21 additions & 11 deletions cmd/dlt/dlt.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package dlt

import (
"context"
"log/slog"

"github.com/databricks/cli/libs/cmdio"
"github.com/databricks/cli/libs/flags"
"github.com/databricks/cli/libs/log"
"github.com/databricks/cli/libs/log/handler"
"github.com/spf13/cobra"
)

Expand All @@ -12,21 +15,28 @@ func New() *cobra.Command {
Use: "dlt",
Short: "DLT CLI",
Long: "DLT CLI (stub, to be filled in)",
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
// Initialize cmdio context
cmdIO := cmdio.NewIO(cmd.Context(), flags.OutputText, cmd.InOrStdin(), cmd.OutOrStdout(), cmd.ErrOrStderr(), "", "")
ctx := cmdio.InContext(cmd.Context(), cmdIO)

// Set up logger with WARN level
h := handler.NewFriendlyHandler(cmd.ErrOrStderr(), &handler.Options{
Color: cmdio.IsTTY(cmd.ErrOrStderr()),
Level: log.LevelWarn,
})
logger := slog.New(h)
ctx = log.NewContext(ctx, logger)

cmd.SetContext(ctx)
return nil
},
Run: func(cmd *cobra.Command, args []string) {
_ = cmd.Help()
},
}

// Add 'init' stub command (same description as bundle init)
initCmd := &cobra.Command{
Use: "init",
Short: "Initialize a new DLT project in the current directory",
Long: "Initialize a new DLT project in the current directory. This is a stub for future implementation.",
Run: func(cmd *cobra.Command, args []string) {
cmdio.LogString(context.Background(), "dlt init is not yet implemented. This will initialize a new DLT project in the future.")
},
}
cmd.AddCommand(initCmd)
cmd.AddCommand(initCommand())

return cmd
}
46 changes: 46 additions & 0 deletions cmd/dlt/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package dlt

import (
"github.com/databricks/cli/cmd/root"
"github.com/databricks/cli/libs/template"
"github.com/spf13/cobra"
)

func initCommand() *cobra.Command {
var outputDir string
var configFile string
cmd := &cobra.Command{
Use: "init",
Short: "Initialize a new DLT project",
PreRunE: root.MustWorkspaceClient,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()

r := template.Resolver{
TemplatePathOrUrl: "lakeflow-pipelines",
ConfigFile: configFile,
OutputDir: outputDir,
}

tmpl, err := r.Resolve(ctx)
if err != nil {
return err
}
defer tmpl.Reader.Cleanup(ctx)

err = tmpl.Writer.PromptForInput(ctx, tmpl.Reader)
if err != nil {
return err
}
tmpl.Writer.SetConfig("is_dlt", true)
err = tmpl.Writer.Finalize(ctx)
if err != nil {
return err
}
return nil
},
}
cmd.Flags().StringVar(&outputDir, "output-dir", "", "Directory to write the initialized template to")
cmd.Flags().StringVar(&configFile, "config-file", "", "JSON file containing key value pairs of input parameters required for template initialization")
return cmd
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
"pattern": "^[a-z0-9_]+$",
"pattern_match_failure_message": "Name must consist of lower case letters, numbers, and underscores."
},
"is_dlt": {
"skip_prompt_if": {},
"type": "boolean",
"description": "DLT pipelines using this template",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"description": "DLT pipelines using this template",
"description": "Whether this template is initialized via the DLT CLI",

"default": false
},
"default_catalog": {
"type": "string",
"default": "{{default_catalog}}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ This file only contains template directives; it is skipped for the actual output
{{skip "__preamble"}}

{{$isSQL := eq .language "sql"}}
{{$isDLT := eq .is_dlt true}}

{{if $isSQL}}
{{skip "{{.project_name}}/resources/{{.project_name}}_pipeline/utilities/utils.py"}}
Expand All @@ -14,3 +15,7 @@ This file only contains template directives; it is skipped for the actual output
{{skip "{{.project_name}}/resources/{{.project_name}}_pipeline/transformations/sample_zones_{{.project_name}}.sql"}}
{{skip "{{.project_name}}/resources/{{.project_name}}_pipeline/transformations/sample_trips_{{.project_name}}.sql"}}
{{end}}

{{if $isDLT}}
{{skip "{{.project_name}}/resources/{{.project_name}}_pipeline/{{.project_name}}.job.yml"}}
{{end}}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# This is a Databricks asset bundle definition for {{.project_name}}.
# See https://docs.databricks.com/dev-tools/bundles/index.html for documentation.
bundle:
{{$isDLT := eq .is_dlt true}}{{if $isDLT}}project{{else}}bundle{{end}}:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
{{$isDLT := eq .is_dlt true}}{{if $isDLT}}project{{else}}bundle{{end}}:
{{- $isDLT := eq .is_dlt true}}
{{if $isDLT}}project{{else}}bundle{{end}}:

Please play a bit with the - here to make this a bit easier to read. The - chops a way all whitespace.

name: {{.project_name}}
uuid: {{bundle_uuid}}
{{if not $isDLT}}uuid: {{bundle_uuid}}{{end}}

include:
- resources/*.yml
- resources/*/*.yml
{{if $isDLT}}- ./*.yml{{else}}- resources/*.yml

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dlt template should include both

- resources/*/*.yml{{end}}

# Variable declarations. These variables are assigned in the dev/prod targets below.
variables:
Expand All @@ -25,6 +25,7 @@ targets:
# See also https://docs.databricks.com/dev-tools/bundles/deployment-modes.html.
mode: development
default: true
{{if $isDLT}}deploy_on_run: true{{end}}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would leave a blank line when false; please experiment with {{- :)

workspace:
host: {{workspace_host}}
variables:
Expand All @@ -36,11 +37,11 @@ targets:
mode: production
workspace:
host: {{workspace_host}}
# We explicitly deploy to /Workspace/Users/{{user_name}} to make sure we only have a single copy.
root_path: /Workspace/Users/{{user_name}}/.bundle/${bundle.name}/${bundle.target}
permissions:
{{if not $isDLT}}# We explicitly deploy to /Workspace/Users/{{user_name}} to make sure we only have a single copy.
root_path: /Workspace/Users/{{user_name}}/.bundle/${bundle.name}/${bundle.target}}{{end}}
{{if $isDLT}}owner: user@company.com{{else}}permissions:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep in mind that the template won't yet work if you replace the root_path with the owner as I originally suggested. The CLI would first need to support that.

So you should probably sequence this change to be later.

- {{if is_service_principal}}service_principal{{else}}user{{end}}_name: {{user_name}}
level: CAN_MANAGE
level: CAN_MANAGE{{end}}
variables:
catalog: {{.default_catalog}}
schema: {{template `prod_schema` .}}
Expand Down
36 changes: 28 additions & 8 deletions libs/template/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,20 @@ type Writer interface {
// 2. The output directory where the template will be materialized.
Configure(ctx context.Context, configPath, outputDir string) error

// Finalize walks the template file tree, persists files to disk, and prints the success message.
Finalize(ctx context.Context) error

// Materialize the template to the local file system.
Materialize(ctx context.Context, r Reader) error

// Log telemetry for the template initialization event.
LogTelemetry(ctx context.Context)

// SetConfig sets a value in the template's config.
SetConfig(key string, value any)

// PromptForInput prompts the user for any missing config values.
PromptForInput(ctx context.Context, reader Reader) error
}

type defaultWriter struct {
Expand All @@ -49,6 +58,13 @@ type defaultWriter struct {
renderer *renderer
}

// SetConfig sets a value in the template's config.
func (tmpl *defaultWriter) SetConfig(key string, value any) {
if tmpl.renderer != nil {
tmpl.renderer.config[key] = value
}
}

func constructOutputFiler(ctx context.Context, outputDir string) (filer.Filer, error) {
outputDir, err := filepath.Abs(outputDir)
if err != nil {
Expand Down Expand Up @@ -81,7 +97,7 @@ func (tmpl *defaultWriter) Configure(ctx context.Context, configPath, outputDir
return nil
}

func (tmpl *defaultWriter) promptForInput(ctx context.Context, reader Reader) error {
func (tmpl *defaultWriter) PromptForInput(ctx context.Context, reader Reader) error {
readerFs, err := reader.FS(ctx)
if err != nil {
return err
Expand Down Expand Up @@ -143,15 +159,10 @@ func (tmpl *defaultWriter) printSuccessMessage(ctx context.Context) error {
return nil
}

func (tmpl *defaultWriter) Materialize(ctx context.Context, reader Reader) error {
err := tmpl.promptForInput(ctx, reader)
if err != nil {
return err
}

func (tmpl *defaultWriter) Finalize(ctx context.Context) error {
// Walk the template file tree and compute in-memory representations of the
// output files.
err = tmpl.renderer.walk()
err := tmpl.renderer.walk()
if err != nil {
return err
}
Expand All @@ -165,6 +176,15 @@ func (tmpl *defaultWriter) Materialize(ctx context.Context, reader Reader) error
return tmpl.printSuccessMessage(ctx)
}

func (tmpl *defaultWriter) Materialize(ctx context.Context, reader Reader) error {
err := tmpl.PromptForInput(ctx, reader)
if err != nil {
return err
}

return tmpl.Finalize(ctx)
}

func (tmpl *defaultWriter) LogTelemetry(ctx context.Context) {
telemetry.Log(ctx, protos.DatabricksCliLog{
BundleInitEvent: &protos.BundleInitEvent{
Expand Down