Skip to content
1 change: 1 addition & 0 deletions .nextchanges/cli/apps-init-skip-examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Add `--skip-examples` to `databricks apps init` to leave out the template's example files (sample SQL queries and the example test). ([#7591](https://github.com/databricks/cli/pull/7591))
9 changes: 9 additions & 0 deletions cmd/apps/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ func newInitCmd() *cobra.Command {
setValues []string
autoApprove bool
skipInstall bool
skipExamples bool
packageManager string
authMode string
)
Expand Down Expand Up @@ -191,6 +192,7 @@ Environment variables:
setValues: setValues,
autoApprove: autoApprove,
skipInstall: skipInstall,
skipExamples: skipExamples,
packageManager: packageManager,
packageManagerChanged: cmd.Flags().Changed("package-manager"),
authMode: authMode,
Expand All @@ -215,6 +217,7 @@ Environment variables:
cmd.Flags().BoolVar(&autoApprove, "auto-approve", false, "Skip confirmation prompts for optional resources. Optional resources are only configured when their values are provided via --set.")
cmd.Flags().StringVar(&authMode, "auth-mode", "", "Default resource auth mode: obo (on behalf of the user) or sp (service principal). With obo, resources that cannot be accessed on behalf of the user use sp. Prompts if not provided in interactive mode, otherwise sp.")
cmd.Flags().BoolVar(&skipInstall, "skip-install", false, "Skip installing project dependencies (e.g. npm install / uv sync). Cannot be combined with --run.")
cmd.Flags().BoolVar(&skipExamples, "skip-examples", false, "Leave out the template's example files (sample SQL queries and the example test) that a real app replaces")
cmd.Flags().StringVar(&packageManager, "package-manager", "", "Node.js package manager (pnpm, npm; default: inferred from template)")

return cmd
Expand Down Expand Up @@ -243,6 +246,7 @@ type createOptions struct {
setValues []string // --set plugin.resourceKey.field=value pairs
autoApprove bool
skipInstall bool
skipExamples bool
packageManager string
packageManagerChanged bool
authMode string // --auth-mode default for all resources
Expand Down Expand Up @@ -527,6 +531,9 @@ type templateVars struct {
DotEnv dotEnvVars
AppEnv string
PackageManager string
// SkipExamples is set by --skip-examples. Templates leave their example
// files out when it is true; a missing key (older CLIs) keeps them.
SkipExamples bool
// Plugins maps plugin name to its metadata
// Missing keys return nil, enabling {{if .plugins.analytics}} conditionals.
Plugins map[string]*pluginVar
Expand Down Expand Up @@ -1807,6 +1814,7 @@ func runCreate(ctx context.Context, opts createOptions) (runErr error) {
},
AppEnv: generator.GenerateAppEnv(selectedPluginList, genConfig),
PackageManager: selectedManager.Name,
SkipExamples: opts.skipExamples,
Plugins: plugins,
}

Expand Down Expand Up @@ -2271,6 +2279,7 @@ func templateData(vars templateVars) map[string]any {
"appDescription": vars.AppDescription,
"workspaceHost": vars.WorkspaceHost,
"packageManager": vars.PackageManager,
"skipExamples": vars.SkipExamples,
"bundle": map[string]any{
"variables": vars.Bundle.Variables,
"resources": vars.Bundle.Resources,
Expand Down
74 changes: 74 additions & 0 deletions cmd/apps/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"flag"
"fmt"
"io"
"io/fs"
"os"
Expand Down Expand Up @@ -273,6 +274,24 @@ func TestPackageManagerTemplateVariable(t *testing.T) {
}
}

func TestSkipExamplesTemplateVariable(t *testing.T) {
ctx := t.Context()
input := "{{if not .skipExamples}}x{{end}}"
for _, tt := range []struct {
skipExamples bool
expected string
}{
{false, "x"},
{true, ""},
} {
vars := testVars()
vars.SkipExamples = tt.skipExamples
result, err := executeTemplate(ctx, "test.sql", []byte(input), vars)
require.NoError(t, err)
assert.Equal(t, tt.expected, string(result), "skipExamples=%v", tt.skipExamples)
}
}

func TestExecuteTemplateInvalidSyntaxReturnsOriginal(t *testing.T) {
ctx := t.Context()
vars := templateVars{ProjectName: "my-app"}
Expand Down Expand Up @@ -1342,6 +1361,61 @@ func TestInitCmd_SkipInstallFlagRegistered(t *testing.T) {
assert.Equal(t, "false", flag.DefValue)
}

func TestInitCmd_SkipExamplesFlagRegistered(t *testing.T) {
cmd := newInitCmd()
flag := cmd.Flags().Lookup("skip-examples")
require.NotNil(t, flag)
assert.Equal(t, "false", flag.DefValue)
}

// TestRunCreateSkipExamples scaffolds a template whose example files are
// gated on {{if not .skipExamples}}. With --skip-examples they render empty,
// so they are not written and config/queries/ is removed once it is empty.
func TestRunCreateSkipExamples(t *testing.T) {
src := t.TempDir()
files := map[string]string{
"appkit.plugins.json": `{"plugins":{}}`,
"README.md": "# {{.projectName}}\n",
"config/queries/hello_world.sql": "{{if not .skipExamples -}}\nSELECT 1\n{{- end}}\n",
"server/server.ts": "export {};\n",
"server/example.test.ts": "{{if not .skipExamples -}}\ntest('x', () => {});\n{{- end}}\n",
}
for name, content := range files {
path := filepath.Join(src, filepath.FromSlash(name))
require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755))
require.NoError(t, os.WriteFile(path, []byte(content), 0o644))
}

for _, skipExamples := range []bool{false, true} {
t.Run(fmt.Sprintf("skipExamples=%v", skipExamples), func(t *testing.T) {
output := t.TempDir()
ctx := cmdctx.SetWorkspaceClient(cmdio.MockDiscard(t.Context()), nil)
err := runCreate(ctx, createOptions{
templatePath: src,
name: "test-app",
nameProvided: true,
outputDir: output,
skipInstall: true,
skipExamples: skipExamples,
})
require.NoError(t, err)

app := filepath.Join(output, "test-app")
assert.FileExists(t, filepath.Join(app, "README.md"))
assert.FileExists(t, filepath.Join(app, "server", "server.ts"))
if skipExamples {
assert.NoDirExists(t, filepath.Join(app, "config"))
assert.NoFileExists(t, filepath.Join(app, "server", "example.test.ts"))
return
}
got, err := os.ReadFile(filepath.Join(app, "config", "queries", "hello_world.sql"))
require.NoError(t, err)
assert.Equal(t, "SELECT 1\n", string(got))
assert.FileExists(t, filepath.Join(app, "server", "example.test.ts"))
})
}
}

func TestPrependInstall(t *testing.T) {
tests := []struct {
name string
Expand Down
Loading