11package io
22
33import (
4- "bytes"
54 "context"
5+ "encoding/json"
66 "errors"
77 "fmt"
88 "io/fs"
9+ "os"
10+ "os/exec"
911 "path/filepath"
1012 "regexp"
1113 "strings"
12- "text/template"
1314
1415 "github.com/databricks/cli/experimental/apps-mcp/lib/middlewares"
15- "github.com/databricks/cli/experimental/apps-mcp/lib/templates"
1616 "github.com/databricks/cli/libs/filer"
1717 "github.com/databricks/cli/libs/log"
1818)
1919
20+ const (
21+ defaultTemplateRepo = "https://github.com/databricks/cli"
22+ defaultTemplateDir = "experimental/apps-mcp/templates/appkit"
23+ defaultTemplateTag = "main"
24+ )
25+
2026// ScaffoldArgs contains arguments for scaffolding operation
2127type ScaffoldArgs struct {
2228 WorkDir string `json:"work_dir"`
@@ -26,10 +32,9 @@ type ScaffoldArgs struct {
2632
2733// ScaffoldResult contains the result of a scaffold operation
2834type ScaffoldResult struct {
29- FilesCopied int `json:"files_copied"`
30- WorkDir string `json:"work_dir"`
31- TemplateName string `json:"template_name"`
32- TemplateDescription string `json:"template_description"`
35+ WorkDir string `json:"work_dir"`
36+ TemplateName string `json:"template_name"`
37+ AppName string `json:"app_name"`
3338}
3439
3540// Scaffold copies template files to the work directory
@@ -93,84 +98,60 @@ func (p *Provider) Scaffold(ctx context.Context, args *ScaffoldArgs) (*ScaffoldR
9398 return nil , fmt .Errorf ("failed to create directory: %w" , err )
9499 }
95100
96- // Get template
97- tmpl := p .getTemplate ()
98- files , err := tmpl .Files ()
99- if err != nil {
100- return nil , fmt .Errorf ("failed to read template: %w" , err )
101- }
102-
103101 // Get template data
104102 warehouseID , err := middlewares .GetWarehouseID (ctx )
105103 if err != nil {
106104 return nil , fmt .Errorf ("failed to get warehouse ID: %w" , err )
107105 }
108106 host := middlewares .MustGetDatabricksClient (ctx ).Config .Host
109107
110- templateData := map [string ]string {
111- "WarehouseID" : warehouseID ,
112- "WorkspaceURL" : host ,
113- "AppName" : normalizedAppName ,
114- "AppDescription" : args .AppDescription ,
108+ // create temp config file with parameters
109+ configMap := map [string ]string {
110+ "project_name" : normalizedAppName ,
111+ "sql_warehouse_id" : warehouseID ,
112+ "app_description" : args .AppDescription ,
113+ "workspace_host" : host ,
115114 }
116115
117- // Copy files
118- filesCopied := 0
119- for path , content := range files {
120- // Check if there's a corresponding .tmpl file for this path
121- tmplPath := path + ".tmpl"
122- if _ , hasTmpl := files [tmplPath ]; hasTmpl {
123- // Skip this file, the .tmpl version will be processed instead
124- continue
125- }
126-
127- // Determine final path and content
128- var finalPath string
129- var finalContent string
130- if strings .HasSuffix (path , ".tmpl" ) {
131- // This is a template file, process it
132- finalPath = strings .TrimSuffix (path , ".tmpl" )
133-
134- // Parse and execute the template
135- t , err := template .New (path ).Parse (content )
136- if err != nil {
137- return nil , fmt .Errorf ("failed to parse template %s: %w" , path , err )
138- }
116+ configBytes , err := json .Marshal (configMap )
117+ if err != nil {
118+ return nil , fmt .Errorf ("marshal config: %w" , err )
119+ }
139120
140- var buf bytes.Buffer
141- if err := t .Execute (& buf , templateData ); err != nil {
142- return nil , fmt .Errorf ("failed to execute template %s: %w" , path , err )
143- }
144- finalContent = buf .String ()
145- } else {
146- // Regular file, use as-is
147- finalPath = path
148- finalContent = content
149- }
121+ tmpFile , err := os .CreateTemp ("" , "mcp-template-config-*.json" )
122+ if err != nil {
123+ return nil , fmt .Errorf ("create temp config file: %w" , err )
124+ }
125+ defer os .Remove (tmpFile .Name ())
150126
151- // filer.Write handles creating parent directories if requested
152- if err := f .Write (ctx , finalPath , bytes .NewReader ([]byte (finalContent )), filer .CreateParentDirectories ); err != nil {
153- return nil , fmt .Errorf ("failed to write %s: %w" , finalPath , err )
154- }
127+ if _ , err := tmpFile .Write (configBytes ); err != nil {
128+ return nil , fmt .Errorf ("write config file: %w" , err )
129+ }
130+ if err := tmpFile .Close (); err != nil {
131+ return nil , fmt .Errorf ("close config file: %w" , err )
132+ }
155133
156- filesCopied ++
134+ // Invoke databricks CLI to initialize the bundle
135+ cmd := exec .CommandContext (ctx , os .Args [0 ], "bundle" , "init" ,
136+ defaultTemplateRepo ,
137+ "--template-dir" , defaultTemplateDir ,
138+ "--tag" , defaultTemplateTag ,
139+ "--config-file" , tmpFile .Name (),
140+ "--output-dir" , workDir ,
141+ )
142+ cmd .Env = append (os .Environ (), "DATABRICKS_HOST=" + host , "DATABRICKS_BUNDLE_ENGINE=direct-exp" )
143+
144+ output , err := cmd .CombinedOutput ()
145+ if err != nil {
146+ return nil , fmt .Errorf ("databricks bundle init failed: %w\n Output: %s" , err , string (output ))
157147 }
158148
159- log .Infof (ctx , "scaffolded project (template=%s, work_dir=%s, files=%d )" ,
160- tmpl . Name () , workDir , filesCopied )
149+ log .Infof (ctx , "scaffolded project (template=%s, work_dir=%s)" ,
150+ defaultTemplateRepo + "/" + defaultTemplateDir , workDir )
161151
162152 return & ScaffoldResult {
163- FilesCopied : filesCopied ,
164- WorkDir : workDir ,
165- TemplateName : tmpl .Name (),
166- TemplateDescription : tmpl .Description (),
153+ WorkDir : workDir ,
154+ TemplateName : defaultTemplateRepo + "/" + defaultTemplateDir ,
155+ AppName : args .AppName ,
167156 }, nil
168157}
169-
170- func (p * Provider ) getTemplate () templates.Template {
171- // TODO: Support custom templates by checking p.config.Template.Path
172- // and loading from filesystem. Not yet implemented.
173-
174- // Default to AppKit template
175- return p .defaultTemplate
176- }
0 commit comments