2023-08-07 13:14:25 +00:00
|
|
|
package template
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2023-08-25 09:03:42 +00:00
|
|
|
"embed"
|
|
|
|
"io/fs"
|
|
|
|
"os"
|
|
|
|
"path"
|
2023-08-07 13:14:25 +00:00
|
|
|
"path/filepath"
|
2023-08-30 14:01:08 +00:00
|
|
|
|
|
|
|
"github.com/databricks/cli/libs/cmdio"
|
2023-08-07 13:14:25 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const libraryDirName = "library"
|
|
|
|
const templateDirName = "template"
|
|
|
|
const schemaFileName = "databricks_template_schema.json"
|
|
|
|
|
2023-08-25 09:03:42 +00:00
|
|
|
//go:embed all:templates
|
|
|
|
var builtinTemplates embed.FS
|
|
|
|
|
2023-08-07 13:14:25 +00:00
|
|
|
// This function materializes the input templates as a project, using user defined
|
|
|
|
// configurations.
|
|
|
|
// Parameters:
|
|
|
|
//
|
|
|
|
// ctx: context containing a cmdio object. This is used to prompt the user
|
|
|
|
// configFilePath: file path containing user defined config values
|
|
|
|
// templateRoot: root of the template definition
|
2023-08-17 20:32:30 +00:00
|
|
|
// outputDir: root of directory where to initialize the template
|
|
|
|
func Materialize(ctx context.Context, configFilePath, templateRoot, outputDir string) error {
|
2023-08-25 09:03:42 +00:00
|
|
|
// Use a temporary directory in case any builtin templates like default-python are used
|
|
|
|
tempDir, err := os.MkdirTemp("", "templates")
|
|
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
templateRoot, err = prepareBuiltinTemplates(templateRoot, tempDir)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-08-07 13:14:25 +00:00
|
|
|
templatePath := filepath.Join(templateRoot, templateDirName)
|
|
|
|
libraryPath := filepath.Join(templateRoot, libraryDirName)
|
|
|
|
schemaPath := filepath.Join(templateRoot, schemaFileName)
|
2023-08-25 09:03:42 +00:00
|
|
|
helpers := loadHelpers(ctx)
|
2023-08-07 13:14:25 +00:00
|
|
|
|
|
|
|
config, err := newConfig(ctx, schemaPath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-10-25 12:27:25 +00:00
|
|
|
// Print welcome message
|
|
|
|
welcome := config.schema.WelcomeMessage
|
|
|
|
if welcome != "" {
|
|
|
|
cmdio.LogString(ctx, welcome)
|
|
|
|
}
|
|
|
|
|
2023-08-07 13:14:25 +00:00
|
|
|
// Read and assign config values from file
|
|
|
|
if configFilePath != "" {
|
|
|
|
err = config.assignValuesFromFile(configFilePath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-19 07:08:36 +00:00
|
|
|
r, err := newRenderer(ctx, config.values, helpers, templatePath, libraryPath, outputDir)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-08-07 13:14:25 +00:00
|
|
|
// Prompt user for any missing config values. Assign default values if
|
|
|
|
// terminal is not TTY
|
2023-10-19 07:08:36 +00:00
|
|
|
err = config.promptOrAssignDefaultValues(r)
|
2023-08-07 13:14:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = config.validate()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Walk and render the template, since input configuration is complete
|
|
|
|
err = r.walk()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-08-25 09:03:42 +00:00
|
|
|
|
|
|
|
err = r.persistToDisk()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-10-19 07:08:36 +00:00
|
|
|
|
|
|
|
success := config.schema.SuccessMessage
|
|
|
|
if success == "" {
|
|
|
|
cmdio.LogString(ctx, "✨ Successfully initialized template")
|
|
|
|
} else {
|
|
|
|
success, err = r.executeTemplate(success)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
cmdio.LogString(ctx, success)
|
|
|
|
}
|
2023-08-25 09:03:42 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the given templateRoot matches
|
|
|
|
func prepareBuiltinTemplates(templateRoot string, tempDir string) (string, error) {
|
|
|
|
_, err := fs.Stat(builtinTemplates, path.Join("templates", templateRoot))
|
|
|
|
if err != nil {
|
|
|
|
// The given path doesn't appear to be using out built-in templates
|
|
|
|
return templateRoot, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// We have a built-in template with the same name as templateRoot!
|
|
|
|
// Now we need to make a fully copy of the builtin templates to a real file system
|
|
|
|
// since template.Parse() doesn't support embed.FS.
|
|
|
|
err = fs.WalkDir(builtinTemplates, "templates", func(path string, entry fs.DirEntry, err error) error {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
targetPath := filepath.Join(tempDir, path)
|
|
|
|
if entry.IsDir() {
|
|
|
|
return os.Mkdir(targetPath, 0755)
|
|
|
|
} else {
|
|
|
|
content, err := fs.ReadFile(builtinTemplates, path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return os.WriteFile(targetPath, content, 0644)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return filepath.Join(tempDir, "templates", templateRoot), nil
|
2023-08-07 13:14:25 +00:00
|
|
|
}
|