diff --git a/cmd/init/init.go b/cmd/init/init.go index 7839728f..a2f2f2e8 100644 --- a/cmd/init/init.go +++ b/cmd/init/init.go @@ -35,7 +35,7 @@ var initCmd = &cobra.Command{ // read user config to initalize the template with var config map[string]interface{} - b, err := os.ReadFile(ConfigFileName) + b, err := os.ReadFile(filepath.Join(targetDir, ConfigFileName)) if err != nil { return err } @@ -58,10 +58,13 @@ var initCmd = &cobra.Command{ } // materialize the template - return template.WalkFileTree(config, filepath.Join(args[0], TemplateDirname), ".") + return template.WalkFileTree(config, filepath.Join(args[0], TemplateDirname), targetDir) }, } +var targetDir string + func init() { + initCmd.Flags().StringVar(&targetDir, "target-dir", ".", "path to directory template will be initialized in") root.RootCmd.AddCommand(initCmd) } diff --git a/internal/init_test.go b/internal/init_test.go new file mode 100644 index 00000000..ba4737e7 --- /dev/null +++ b/internal/init_test.go @@ -0,0 +1,79 @@ +package internal + +import ( + "fmt" + "os" + "path/filepath" + "testing" + + _ "github.com/databricks/bricks/cmd/init" + "github.com/databricks/bricks/cmd/root" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func assertFileContains(t *testing.T, path string, substr string) { + b, err := os.ReadFile(path) + require.NoError(t, err) + assert.Contains(t, string(b), substr) +} + +func TestTemplateInitialization(t *testing.T) { + // create target directory with the input config + tmp := t.TempDir() + f, err := os.Create(filepath.Join(tmp, "config.json")) + require.NoError(t, err) + _, err = f.WriteString(` + { + "project_name": "development_project", + "cloud_type": "AWS", + "ci_type": "github", + "is_production": false + } + `) + require.NoError(t, err) + + // materialize the template + cmd := root.RootCmd + cmd.SetArgs([]string{"init", filepath.FromSlash("testdata/init/templateDefinition"), "--target-dir", tmp}) + err = cmd.Execute() + require.NoError(t, err) + + // assert on materialized template + assert.FileExists(t, filepath.Join(tmp, "development_project", "aws_file")) + assert.FileExists(t, filepath.Join(tmp, "development_project", ".github")) + assert.NoFileExists(t, filepath.Join(tmp, "development_project", "azure_file")) + assertFileContains(t, filepath.Join(tmp, "development_project", "aws_file"), "This file should only be generated for AWS") + assertFileContains(t, filepath.Join(tmp, "development_project", ".github"), "This is a development project") +} + +func TestTemplateInitialization2(t *testing.T) { + // create target directory with the input config + tmp := t.TempDir() + f, err := os.Create(filepath.Join(tmp, "config.json")) + require.NoError(t, err) + _, err = f.WriteString(` + { + "project_name": "production_project", + "cloud_type": "Azure", + "ci_type": "azure_devops", + "is_production": true + } + `) + require.NoError(t, err) + + // materialize the template + cmd := root.RootCmd + childCommands := cmd.Commands() + fmt.Println(childCommands) + cmd.SetArgs([]string{"init", filepath.FromSlash("testdata/init/templateDefinition"), "--target-dir", tmp}) + err = cmd.Execute() + require.NoError(t, err) + + // assert on materialized template + assert.FileExists(t, filepath.Join(tmp, "production_project", "azure_file")) + assert.FileExists(t, filepath.Join(tmp, "production_project", ".azure_devops")) + assert.NoFileExists(t, filepath.Join(tmp, "production_project", "aws_file")) + assertFileContains(t, filepath.Join(tmp, "production_project", "azure_file"), "This file should only be generated for Azure") + assertFileContains(t, filepath.Join(tmp, "production_project", ".azure_devops"), "This is a production project") +} diff --git a/internal/testdata/init/templateDefinition/schema.json b/internal/testdata/init/templateDefinition/schema.json new file mode 100644 index 00000000..5f927011 --- /dev/null +++ b/internal/testdata/init/templateDefinition/schema.json @@ -0,0 +1,17 @@ +{ + "project_name": { + "description": "Name of the project", + "type": "string" + }, + "cloud_type": { + "description": "type of the cloud for the project", + "type": "string" + }, + "is_production": { + "type": "boolean" + }, + "ci_type": { + "type": "string", + "description": "type of the CI runner, eg: github, azure devops" + } +} diff --git a/internal/testdata/init/templateDefinition/template/{{.project_name}}/.{{.ci_type}} b/internal/testdata/init/templateDefinition/template/{{.project_name}}/.{{.ci_type}} new file mode 100644 index 00000000..b5c2d444 --- /dev/null +++ b/internal/testdata/init/templateDefinition/template/{{.project_name}}/.{{.ci_type}} @@ -0,0 +1,5 @@ +{{if .is_production}} +This is a production project +{{else}} +This is a development project +{{end}} diff --git a/internal/testdata/init/templateDefinition/template/{{.project_name}}/aws_file b/internal/testdata/init/templateDefinition/template/{{.project_name}}/aws_file new file mode 100644 index 00000000..a5f33d20 --- /dev/null +++ b/internal/testdata/init/templateDefinition/template/{{.project_name}}/aws_file @@ -0,0 +1,4 @@ +This file should only be generated for AWS +{{if ne .cloud_type "AWS"}} +{{skipThisFile}} +{{end}} diff --git a/internal/testdata/init/templateDefinition/template/{{.project_name}}/azure_file b/internal/testdata/init/templateDefinition/template/{{.project_name}}/azure_file new file mode 100644 index 00000000..e1be17cb --- /dev/null +++ b/internal/testdata/init/templateDefinition/template/{{.project_name}}/azure_file @@ -0,0 +1,4 @@ +This file should only be generated for Azure +{{if ne .cloud_type "Azure"}} +{{skipThisFile}} +{{end}} diff --git a/libs/template/execute.go b/libs/template/execute.go index 133dd0c7..d5fb88fb 100644 --- a/libs/template/execute.go +++ b/libs/template/execute.go @@ -41,8 +41,10 @@ func generateDirectory(config map[string]any, parentDir, nameTempate string) (st func generateFile(config map[string]any, parentDir, nameTempate, contentTemplate string) error { // compute file content fileContent, err := executeTemplate(config, contentTemplate) - // TODO: maybe we need string matching here to make this work - if err != nil && err == ErrSkipThisFile { + // We do a substring match here because on errors the template library prepends + // some additional information about the callsite from which the ErrSkipThisFile + // error was returned + if err != nil && strings.Contains(err.Error(), ErrSkipThisFile.Error()) { return nil } if err != nil {