Skip to content
Draft
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
776 changes: 436 additions & 340 deletions cmd/apps/init.go
Copy link
Member

@pkosiec pkosiec Feb 11, 2026

Choose a reason for hiding this comment

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

To be honest I'm not a fan of defining the flags statically, if they are about providing dynamic arguments for a given template. And probably it is also limiting passing second secret (like in the comment before)

What do you think about doing what Helm (https://helm.sh/docs/helm/helm_install/) does: --set foo=bar? In that way we could provide multiple secrets, SQL warehouses, etc.
One single flag --set but it accepts key and value.

Even better, we could do --set analytics.warehouseId=foo - to avoid naming conflicts between plugins. Like:

--set analytics.lakebaseEndpoint=... --set myOwnPlugin.lakebaseEndpoint=...

Large diffs are not rendered by default.

144 changes: 136 additions & 8 deletions cmd/apps/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ func TestSubstituteVars(t *testing.T) {
AppDescription: "My awesome app",
Profile: "default",
WorkspaceHost: "https://dbc-123.cloud.databricks.com",
PluginImport: "analytics",
PluginUsage: "analytics()",
PluginImports: "analytics",
PluginUsages: "analytics()",
}

tests := []struct {
Expand Down Expand Up @@ -107,12 +107,12 @@ func TestSubstituteVars(t *testing.T) {
},
{
name: "plugin import substitution",
input: "import { {{.plugin_import}} } from 'appkit'",
input: "import { {{.plugin_imports}} } from 'appkit'",
expected: "import { analytics } from 'appkit'",
},
{
name: "plugin usage substitution",
input: "plugins: [{{.plugin_usage}}]",
input: "plugins: [{{.plugin_usages}}]",
expected: "plugins: [analytics()]",
},
{
Expand Down Expand Up @@ -143,8 +143,8 @@ func TestSubstituteVarsNoPlugins(t *testing.T) {
AppDescription: "My app",
Profile: "",
WorkspaceHost: "",
PluginImport: "", // No plugins
PluginUsage: "",
PluginImports: "", // No plugins
PluginUsages: "",
}

tests := []struct {
Expand All @@ -154,12 +154,12 @@ func TestSubstituteVarsNoPlugins(t *testing.T) {
}{
{
name: "removes plugin import with comma",
input: "import { core, {{.plugin_import}} } from 'appkit'",
input: "import { core, {{.plugin_imports}} } from 'appkit'",
expected: "import { core } from 'appkit'",
},
{
name: "removes plugin usage line",
input: "plugins: [\n {{.plugin_usage}},\n]",
input: "plugins: [\n {{.plugin_usages}},\n]",
expected: "plugins: [\n]",
},
}
Expand Down Expand Up @@ -283,3 +283,131 @@ func TestParseDeployAndRunFlags(t *testing.T) {
})
}
}

func TestValidateMultiFieldFlags(t *testing.T) {
tests := []struct {
name string
opts createOptions
wantErr string
}{
{
name: "both database fields provided",
opts: createOptions{databaseInstance: "inst", databaseName: "db"},
},
{
name: "neither database field provided",
opts: createOptions{},
},
{
name: "only database instance name",
opts: createOptions{databaseInstance: "inst"},
wantErr: "--database-instance and --database-name must be provided together",
},
{
name: "only database database name",
opts: createOptions{databaseName: "db"},
wantErr: "--database-instance and --database-name must be provided together",
},
{
name: "both secret fields provided",
opts: createOptions{secretScope: "scope", secretKey: "key"},
},
{
name: "neither secret field provided",
opts: createOptions{},
},
{
name: "only secret scope",
opts: createOptions{secretScope: "scope"},
wantErr: "--secret-scope and --secret-key must be provided together",
},
{
name: "only secret key",
opts: createOptions{secretKey: "key"},
wantErr: "--secret-scope and --secret-key must be provided together",
},
{
name: "all multi-field flags provided",
opts: createOptions{
databaseInstance: "inst",
databaseName: "db",
secretScope: "scope",
secretKey: "key",
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.opts.validateMultiFieldFlags()
if tt.wantErr != "" {
require.Error(t, err)
assert.Equal(t, tt.wantErr, err.Error())
} else {
assert.NoError(t, err)
}
})
}
}

func TestPopulateResourceValues(t *testing.T) {
opts := createOptions{
warehouseID: "wh-123",
jobID: "job-456",
modelEndpointID: "ep-789",
volumeID: "cat.schema.vol",
vectorSearchIndexID: "idx-1",
functionID: "cat.schema.fn",
connectionID: "conn-1",
genieSpaceID: "gs-1",
experimentID: "exp-1",
databaseInstance: "inst",
databaseName: "mydb",
secretScope: "scope",
secretKey: "key",
}

rv := make(map[string]string)
opts.populateResourceValues(rv)

assert.Equal(t, "wh-123", rv["sql-warehouse.id"])
assert.Equal(t, "job-456", rv["job.id"])
assert.Equal(t, "ep-789", rv["model-endpoint.id"])
assert.Equal(t, "cat.schema.vol", rv["volume.id"])
assert.Equal(t, "idx-1", rv["vector-search-index.id"])
assert.Equal(t, "cat.schema.fn", rv["function.id"])
assert.Equal(t, "conn-1", rv["connection.id"])
assert.Equal(t, "gs-1", rv["genie-space.space_id"])
assert.Equal(t, "exp-1", rv["experiment.id"])
assert.Equal(t, "inst", rv["database.instance_name"])
assert.Equal(t, "mydb", rv["database.database_name"])
assert.Equal(t, "scope", rv["secret.scope"])
assert.Equal(t, "key", rv["secret.key"])
}

func TestPopulateResourceValuesSkipsEmpty(t *testing.T) {
opts := createOptions{
warehouseID: "wh-123",
}

rv := make(map[string]string)
opts.populateResourceValues(rv)

assert.Equal(t, "wh-123", rv["sql-warehouse.id"])
assert.Len(t, rv, 1)
}

func TestAppendUnique(t *testing.T) {
result := appendUnique([]string{"a", "b"}, "b", "c", "a", "d")
assert.Equal(t, []string{"a", "b", "c", "d"}, result)
}

func TestAppendUniqueEmptyBase(t *testing.T) {
result := appendUnique(nil, "x", "y", "x")
assert.Equal(t, []string{"x", "y"}, result)
}

func TestAppendUniqueNoValues(t *testing.T) {
result := appendUnique([]string{"a", "b"})
assert.Equal(t, []string{"a", "b"}, result)
}
Loading