From b0f78bef42d139ee4c2aca5bab4ef37981faded5 Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Mon, 27 Apr 2026 12:27:23 +0200 Subject: [PATCH 1/5] Surface plugin stability tier in apps init Round-trip the new optional `stability` field on AppKit plugin manifests (experimental / preview / stable, schema 1.1, see databricks/appkit#264) and surface non-stable tiers as a colored parenthetical suffix in the `databricks apps init` plugin picker. Stability is stored as a plain string so unknown future tiers round-trip and render in gray instead of breaking the loader. --- cmd/apps/init.go | 2 +- libs/apps/manifest/manifest.go | 17 ++++++++++++ libs/apps/manifest/manifest_test.go | 41 ++++++++++++++++++++++++++--- libs/apps/prompt/prompt.go | 26 ++++++++++++++++++ libs/apps/prompt/prompt_test.go | 23 ++++++++++++++++ 5 files changed, 105 insertions(+), 4 deletions(-) diff --git a/cmd/apps/init.go b/cmd/apps/init.go index 6e6ba2ccee8..b7a6f6ca434 100644 --- a/cmd/apps/init.go +++ b/cmd/apps/init.go @@ -357,7 +357,7 @@ func promptForPluginsAndDeps(ctx context.Context, m *manifest.Manifest, preSelec if len(config.Features) == 0 && len(selectablePlugins) > 0 { options := make([]huh.Option[string], 0, len(selectablePlugins)) for _, p := range selectablePlugins { - label := p.DisplayName + " - " + p.Description + label := p.DisplayName + prompt.RenderStabilityTier(p.StabilityLabel()) + " - " + p.Description options = append(options, huh.NewOption(label, p.Name)) } diff --git a/libs/apps/manifest/manifest.go b/libs/apps/manifest/manifest.go index 43a98385157..ab3788306c3 100644 --- a/libs/apps/manifest/manifest.go +++ b/libs/apps/manifest/manifest.go @@ -76,6 +76,23 @@ type Plugin struct { RequiredByTemplate bool `json:"requiredByTemplate"` Resources Resources `json:"resources"` OnSetupMessage string `json:"onSetupMessage"` + + // Stability is one of "experimental", "preview", "stable", or empty. + // Stored as a plain string so unknown future values round-trip unchanged. + // See https://github.com/databricks/appkit/pull/264. + Stability string `json:"stability,omitempty"` +} + +// StabilityLabel returns a user-facing tier label for non-stable plugins. +// Returns "" for stable, unset, or any value that maps to stable. +// Unknown values pass through so we are forward-compatible with new tiers. +func (p Plugin) StabilityLabel() string { + switch p.Stability { + case "", "stable": + return "" + default: + return p.Stability + } } // Manifest represents the appkit.plugins.json file structure. diff --git a/libs/apps/manifest/manifest_test.go b/libs/apps/manifest/manifest_test.go index f3ca3fe129f..18489050de8 100644 --- a/libs/apps/manifest/manifest_test.go +++ b/libs/apps/manifest/manifest_test.go @@ -16,13 +16,14 @@ func TestLoad(t *testing.T) { content := `{ "$schema": "https://databricks.github.io/appkit/schemas/template-plugins.schema.json", - "version": "1.0", + "version": "1.1", "plugins": { "analytics": { "name": "analytics", "displayName": "Analytics Plugin", "description": "SQL query execution", "package": "@databricks/appkit", + "stability": "preview", "resources": { "required": [ { @@ -39,11 +40,23 @@ func TestLoad(t *testing.T) { "optional": [] } }, + "genie": { + "name": "genie", + "displayName": "Genie Plugin", + "description": "Genie space integration", + "package": "@databricks/appkit", + "stability": "experimental", + "resources": { + "required": [], + "optional": [] + } + }, "server": { "name": "server", "displayName": "Server Plugin", "description": "HTTP server", "package": "@databricks/appkit", + "stability": "stable", "requiredByTemplate": true, "resources": { "required": [], @@ -58,10 +71,32 @@ func TestLoad(t *testing.T) { m, err := manifest.Load(dir) require.NoError(t, err) - assert.Equal(t, "1.0", m.Version) - assert.Len(t, m.Plugins, 2) + assert.Equal(t, "1.1", m.Version) + assert.Len(t, m.Plugins, 3) assert.True(t, m.Plugins["server"].RequiredByTemplate) assert.False(t, m.Plugins["analytics"].RequiredByTemplate) + assert.Equal(t, "preview", m.Plugins["analytics"].Stability) + assert.Equal(t, "experimental", m.Plugins["genie"].Stability) + assert.Equal(t, "stable", m.Plugins["server"].Stability) +} + +func TestPlugin_StabilityLabel(t *testing.T) { + tests := []struct { + stability string + want string + }{ + {"", ""}, + {"stable", ""}, + {"preview", "preview"}, + {"experimental", "experimental"}, + {"alpha", "alpha"}, + } + for _, tc := range tests { + t.Run(tc.stability, func(t *testing.T) { + p := manifest.Plugin{Stability: tc.stability} + assert.Equal(t, tc.want, p.StabilityLabel()) + }) + } } func TestLoadNotFound(t *testing.T) { diff --git a/libs/apps/prompt/prompt.go b/libs/apps/prompt/prompt.go index 1b10f15024a..78a5a858c96 100644 --- a/libs/apps/prompt/prompt.go +++ b/libs/apps/prompt/prompt.go @@ -68,6 +68,32 @@ var ( Bold(true) ) +// Stability tier styles, applied to the parenthetical suffix in plugin labels. +var ( + stabilityExperimentalStyle = lipgloss.NewStyle().Foreground(colorOrange) + stabilityPreviewStyle = lipgloss.NewStyle().Foreground(colorYellow) + stabilityUnknownStyle = lipgloss.NewStyle().Foreground(colorGray) +) + +// RenderStabilityTier renders a stability tier as a colored " (tier)" suffix, +// or returns "" for stable/unset. Unknown tiers are rendered in gray so we +// remain forward-compatible with future tier names. +func RenderStabilityTier(tier string) string { + if tier == "" { + return "" + } + var style lipgloss.Style + switch tier { + case "experimental": + style = stabilityExperimentalStyle + case "preview": + style = stabilityPreviewStyle + default: + style = stabilityUnknownStyle + } + return " " + style.Render("("+tier+")") +} + // PrintAnswered prints a completed prompt answer to keep history visible. func PrintAnswered(ctx context.Context, title, value string) { cmdio.LogString(ctx, fmt.Sprintf("%s %s", answeredTitleStyle.Render(title+":"), answeredValueStyle.Render(value))) diff --git a/libs/apps/prompt/prompt_test.go b/libs/apps/prompt/prompt_test.go index 3400eab9bea..5d87e41d556 100644 --- a/libs/apps/prompt/prompt_test.go +++ b/libs/apps/prompt/prompt_test.go @@ -310,3 +310,26 @@ func TestMaxAppNameLength(t *testing.T) { assert.Len(t, invalidName, 27) assert.Error(t, ValidateProjectName(invalidName)) } + +func TestRenderStabilityTier(t *testing.T) { + tests := []struct { + tier string + wantEmpty bool + wantSubstr string + }{ + {"", true, ""}, + {"experimental", false, "(experimental)"}, + {"preview", false, "(preview)"}, + {"alpha", false, "(alpha)"}, + } + for _, tc := range tests { + t.Run(tc.tier, func(t *testing.T) { + got := RenderStabilityTier(tc.tier) + if tc.wantEmpty { + assert.Empty(t, got) + return + } + assert.Contains(t, got, tc.wantSubstr) + }) + } +} From 6e344267296d0cf949eb8c87e20b2cfcda87627f Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Mon, 27 Apr 2026 19:36:30 +0200 Subject: [PATCH 2/5] refactor(apps): collapse plugin stability tiers to beta/stable Mirrors the AppKit PR which dropped `experimental` and renamed `preview` to `beta` (databricks/appkit#264). Forward-compat fallback for unknown tiers is preserved. Signed-off-by: MarioCadenas --- libs/apps/manifest/manifest.go | 2 +- libs/apps/manifest/manifest_test.go | 11 +++++------ libs/apps/prompt/prompt.go | 11 ++++------- libs/apps/prompt/prompt_test.go | 3 +-- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/libs/apps/manifest/manifest.go b/libs/apps/manifest/manifest.go index ab3788306c3..ba0f024de03 100644 --- a/libs/apps/manifest/manifest.go +++ b/libs/apps/manifest/manifest.go @@ -77,7 +77,7 @@ type Plugin struct { Resources Resources `json:"resources"` OnSetupMessage string `json:"onSetupMessage"` - // Stability is one of "experimental", "preview", "stable", or empty. + // Stability is one of "beta", "stable", or empty. // Stored as a plain string so unknown future values round-trip unchanged. // See https://github.com/databricks/appkit/pull/264. Stability string `json:"stability,omitempty"` diff --git a/libs/apps/manifest/manifest_test.go b/libs/apps/manifest/manifest_test.go index 18489050de8..c0c4dad38d3 100644 --- a/libs/apps/manifest/manifest_test.go +++ b/libs/apps/manifest/manifest_test.go @@ -23,7 +23,7 @@ func TestLoad(t *testing.T) { "displayName": "Analytics Plugin", "description": "SQL query execution", "package": "@databricks/appkit", - "stability": "preview", + "stability": "beta", "resources": { "required": [ { @@ -45,7 +45,7 @@ func TestLoad(t *testing.T) { "displayName": "Genie Plugin", "description": "Genie space integration", "package": "@databricks/appkit", - "stability": "experimental", + "stability": "alpha", "resources": { "required": [], "optional": [] @@ -75,8 +75,8 @@ func TestLoad(t *testing.T) { assert.Len(t, m.Plugins, 3) assert.True(t, m.Plugins["server"].RequiredByTemplate) assert.False(t, m.Plugins["analytics"].RequiredByTemplate) - assert.Equal(t, "preview", m.Plugins["analytics"].Stability) - assert.Equal(t, "experimental", m.Plugins["genie"].Stability) + assert.Equal(t, "beta", m.Plugins["analytics"].Stability) + assert.Equal(t, "alpha", m.Plugins["genie"].Stability) assert.Equal(t, "stable", m.Plugins["server"].Stability) } @@ -87,8 +87,7 @@ func TestPlugin_StabilityLabel(t *testing.T) { }{ {"", ""}, {"stable", ""}, - {"preview", "preview"}, - {"experimental", "experimental"}, + {"beta", "beta"}, {"alpha", "alpha"}, } for _, tc := range tests { diff --git a/libs/apps/prompt/prompt.go b/libs/apps/prompt/prompt.go index 78a5a858c96..64832e9e26d 100644 --- a/libs/apps/prompt/prompt.go +++ b/libs/apps/prompt/prompt.go @@ -70,9 +70,8 @@ var ( // Stability tier styles, applied to the parenthetical suffix in plugin labels. var ( - stabilityExperimentalStyle = lipgloss.NewStyle().Foreground(colorOrange) - stabilityPreviewStyle = lipgloss.NewStyle().Foreground(colorYellow) - stabilityUnknownStyle = lipgloss.NewStyle().Foreground(colorGray) + stabilityBetaStyle = lipgloss.NewStyle().Foreground(colorYellow) + stabilityUnknownStyle = lipgloss.NewStyle().Foreground(colorGray) ) // RenderStabilityTier renders a stability tier as a colored " (tier)" suffix, @@ -84,10 +83,8 @@ func RenderStabilityTier(tier string) string { } var style lipgloss.Style switch tier { - case "experimental": - style = stabilityExperimentalStyle - case "preview": - style = stabilityPreviewStyle + case "beta": + style = stabilityBetaStyle default: style = stabilityUnknownStyle } diff --git a/libs/apps/prompt/prompt_test.go b/libs/apps/prompt/prompt_test.go index 5d87e41d556..01091cf72ce 100644 --- a/libs/apps/prompt/prompt_test.go +++ b/libs/apps/prompt/prompt_test.go @@ -318,8 +318,7 @@ func TestRenderStabilityTier(t *testing.T) { wantSubstr string }{ {"", true, ""}, - {"experimental", false, "(experimental)"}, - {"preview", false, "(preview)"}, + {"beta", false, "(beta)"}, {"alpha", false, "(alpha)"}, } for _, tc := range tests { From b87467e89713e824c086a79fd0a1dfa0ef55e64d Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Tue, 28 Apr 2026 12:46:37 +0200 Subject: [PATCH 3/5] feat(apps): expose plugin stability to init template The AppKit template now branches on `{{$p.Stability}}` to route beta plugins through the `/beta` subpath export. Make pluginVar carry the field so the template can read it. See databricks/appkit#264 commit d826a532. Signed-off-by: MarioCadenas --- cmd/apps/init.go | 19 +++++++++++++++---- cmd/apps/init_test.go | 23 +++++++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/cmd/apps/init.go b/cmd/apps/init.go index b7a6f6ca434..71f8c43f137 100644 --- a/cmd/apps/init.go +++ b/cmd/apps/init.go @@ -295,9 +295,16 @@ type dotEnvVars struct { Example string } -// pluginVar represents a selected plugin. Currently empty, but extensible -// with properties as the plugin model evolves. -type pluginVar struct{} +// pluginVar represents a selected plugin in template substitution. +// Fields here are part of the AppKit template contract — the template +// reads them via {{$p.Field}} on map values in templateVars.Plugins. +type pluginVar struct { + // Stability mirrors manifest.Plugin.Stability ("" for stable, "beta" + // for beta, future tiers preserved). The AppKit template branches + // imports on this — see databricks/appkit#264 commit d826a532, which + // routes beta plugins through the `@databricks/appkit/beta` subpath. + Stability string +} // templateVars holds the variables for template substitution. type templateVars struct { @@ -1036,7 +1043,11 @@ func runCreate(ctx context.Context, opts createOptions) error { plugins := make(map[string]*pluginVar, len(selectedPlugins)) for _, name := range selectedPlugins { - plugins[name] = &pluginVar{} + pv := &pluginVar{} + if mp, ok := m.Plugins[name]; ok { + pv.Stability = mp.Stability + } + plugins[name] = pv } // Template variables with generated content diff --git a/cmd/apps/init_test.go b/cmd/apps/init_test.go index d837474e125..380cffc2bb6 100644 --- a/cmd/apps/init_test.go +++ b/cmd/apps/init_test.go @@ -251,6 +251,29 @@ func TestExecuteTemplateInvalidSyntaxReturnsOriginal(t *testing.T) { assert.Equal(t, input, string(result)) } +// TestExecuteTemplatePluginStability locks down the contract that the +// AppKit init template relies on: ranging over .plugins exposes a +// .Stability field per plugin, with stable/unset rendering as the empty +// string. See databricks/appkit#264 commit d826a532 (server.ts branches +// imports between `@databricks/appkit` and `@databricks/appkit/beta`). +func TestExecuteTemplatePluginStability(t *testing.T) { + ctx := t.Context() + vars := templateVars{ + Plugins: map[string]*pluginVar{ + "stable-plugin": {}, + "beta-plugin": {Stability: "beta"}, + }, + } + + input := `{{range $n, $p := .plugins}}{{$n}}={{$p.Stability}};{{end}}` + result, err := executeTemplate(ctx, "server.ts", []byte(input), vars) + require.NoError(t, err) + got := string(result) + + assert.Contains(t, got, "stable-plugin=;") + assert.Contains(t, got, "beta-plugin=beta;") +} + func TestInitCmdBranchAndVersionMutuallyExclusive(t *testing.T) { cmd := newInitCmd() cmd.PreRunE = nil // skip workspace client setup for flag validation test From 6bd7b5cbfb0b06dc78a5d6dffb8ae295138ed3b2 Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Tue, 28 Apr 2026 12:54:39 +0200 Subject: [PATCH 4/5] test(apps): pin text/template features used by AppKit beta-import pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AppKit init template was tightened to emit a single combined beta import line via a string-accumulator pre-pass (databricks/appkit#264 commit 488797fc): {{- $betaImports := "" -}} {{- range $name, $p := .plugins -}} {{- if eq $p.Stability "beta" -}} {{- if eq $betaImports "" -}} {{- $betaImports = $name -}} {{- else -}} {{- $betaImports = printf "%s, %s" $betaImports $name -}} {{- end -}} {{- end -}} {{- end -}} That pattern depends on three text/template features: - Variable reassignment (`$x = ...`) inside `range`, against an outer-scope variable. - The `printf` builtin. - Pointer-field access (`$p.Stability`) on map values. If a future refactor of executeTemplate (or a Go template engine swap) breaks any of these, users would silently get malformed server.ts files at `databricks apps init` time. This regression test exercises the exact pattern with hermetic input — no AppKit checkout required — and asserts the expected import shapes for all-stable, single beta, multi beta, all-beta, and unknown-tier (alpha) cases. Signed-off-by: MarioCadenas --- cmd/apps/init_test.go | 139 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/cmd/apps/init_test.go b/cmd/apps/init_test.go index 380cffc2bb6..3f82522545f 100644 --- a/cmd/apps/init_test.go +++ b/cmd/apps/init_test.go @@ -8,6 +8,7 @@ import ( "os" "os/exec" "path/filepath" + "strings" "testing" "github.com/databricks/cli/libs/apps/manifest" @@ -274,6 +275,144 @@ func TestExecuteTemplatePluginStability(t *testing.T) { assert.Contains(t, got, "beta-plugin=beta;") } +// TestExecuteTemplateBetaImportAccumulator pins the full text/template +// pattern used by the AppKit server.ts template (databricks/appkit#264 +// commit 488797fc): a string-accumulator pre-pass over .plugins that +// reassigns an outer-scope variable inside `range` and concatenates +// names via `printf`, then emits a single guarded import line. +// +// If a future refactor of executeTemplate breaks variable reassignment, +// printf, or pointer-field access on map values, this test fails before +// users see broken init output. +func TestExecuteTemplateBetaImportAccumulator(t *testing.T) { + ctx := t.Context() + + // Mirror of the relevant slice of template/server/server.ts in AppKit. + // Kept as a literal string (not loaded from the AppKit repo) so this + // test is hermetic and survives AppKit branch movement. + input := `{{- $betaImports := "" -}} +{{- range $name, $p := .plugins -}} + {{- if eq $p.Stability "beta" -}} + {{- if eq $betaImports "" -}} + {{- $betaImports = $name -}} + {{- else -}} + {{- $betaImports = printf "%s, %s" $betaImports $name -}} + {{- end -}} + {{- end -}} +{{- end -}} +import { createApp{{range $name, $p := .plugins}}{{if ne $p.Stability "beta"}}, {{$name}}{{end}}{{end}} } from '@databricks/appkit'; +{{- if ne $betaImports "" }} +import { {{$betaImports}} } from '@databricks/appkit/beta'; +{{- end}} +` + + cases := []struct { + name string + plugins map[string]*pluginVar + wantStableImports []string // names that must appear on the stable line + wantBetaImports []string // names that must appear on the beta line, "" means no beta line + wantNoBetaLine bool + }{ + { + name: "all stable: no beta line", + plugins: map[string]*pluginVar{ + "server": {}, + "analytics": {}, + }, + wantStableImports: []string{"server", "analytics"}, + wantNoBetaLine: true, + }, + { + name: "mixed single beta", + plugins: map[string]*pluginVar{ + "server": {}, + "betaOne": {Stability: "beta"}, + }, + wantStableImports: []string{"server"}, + wantBetaImports: []string{"betaOne"}, + }, + { + name: "mixed multiple betas: combined into one import line", + plugins: map[string]*pluginVar{ + "server": {}, + "betaOne": {Stability: "beta"}, + "betaTwo": {Stability: "beta"}, + }, + wantStableImports: []string{"server"}, + wantBetaImports: []string{"betaOne", "betaTwo"}, + }, + { + name: "all beta: createApp alone on stable line", + plugins: map[string]*pluginVar{ + "betaOne": {Stability: "beta"}, + "betaTwo": {Stability: "beta"}, + }, + wantBetaImports: []string{"betaOne", "betaTwo"}, + }, + { + name: "future tier (alpha) routes to stable line for now", + plugins: map[string]*pluginVar{ + "server": {}, + "alphaOne": {Stability: "alpha"}, + }, + wantStableImports: []string{"server", "alphaOne"}, + wantNoBetaLine: true, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + vars := templateVars{Plugins: tc.plugins} + result, err := executeTemplate(ctx, "server.ts", []byte(input), vars) + require.NoError(t, err) + got := string(result) + + lines := strings.Split(got, "\n") + require.NotEmpty(t, lines) + + // Stable line is always first: starts with `import { createApp` + // and ends with `from '@databricks/appkit';`. + stableLine := lines[0] + assert.True(t, strings.HasPrefix(stableLine, "import { createApp"), + "stable line: %q", stableLine) + assert.True(t, strings.HasSuffix(stableLine, "} from '@databricks/appkit';"), + "stable line: %q", stableLine) + for _, name := range tc.wantStableImports { + assert.Contains(t, stableLine, name, + "stable line missing %q: %q", name, stableLine) + } + for _, name := range tc.wantBetaImports { + assert.NotContains(t, stableLine, ", "+name, + "beta plugin %q leaked onto stable line: %q", name, stableLine) + } + + if tc.wantNoBetaLine { + assert.NotContains(t, got, "@databricks/appkit/beta", + "unexpected beta import emitted: %q", got) + return + } + + // Beta line: exactly one `from '@databricks/appkit/beta'` line. + betaLineCount := strings.Count(got, "from '@databricks/appkit/beta'") + assert.Equal(t, 1, betaLineCount, + "expected exactly one beta import line, got %d: %q", betaLineCount, got) + + var betaLine string + for _, l := range lines { + if strings.Contains(l, "@databricks/appkit/beta") { + betaLine = l + break + } + } + require.NotEmpty(t, betaLine, "beta line not found in: %q", got) + for _, name := range tc.wantBetaImports { + assert.Contains(t, betaLine, name, + "beta line missing %q: %q", name, betaLine) + } + }) + } +} + func TestInitCmdBranchAndVersionMutuallyExclusive(t *testing.T) { cmd := newInitCmd() cmd.PreRunE = nil // skip workspace client setup for flag validation test From 434660d3ba0f8ec5d06e96314669469e99ecd239 Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Thu, 30 Apr 2026 14:58:14 +0200 Subject: [PATCH 5/5] refactor(apps): rename stable stability tier to ga Mirrors the AppKit-side rename (databricks/appkit#264). Real synced manifests will now carry `"stability": "ga"` instead of `"stable"`; empty/unset still maps to GA. Also renames internal test fixtures and identifiers from "stable" to "ga"/"GA" so the codebase stays consistent with the new wire value. Signed-off-by: MarioCadenas --- cmd/apps/init.go | 2 +- cmd/apps/init_test.go | 62 ++++++++++++++--------------- libs/apps/manifest/manifest.go | 8 ++-- libs/apps/manifest/manifest_test.go | 6 +-- libs/apps/prompt/prompt.go | 2 +- 5 files changed, 40 insertions(+), 40 deletions(-) diff --git a/cmd/apps/init.go b/cmd/apps/init.go index 71f8c43f137..6f7269e6fd0 100644 --- a/cmd/apps/init.go +++ b/cmd/apps/init.go @@ -299,7 +299,7 @@ type dotEnvVars struct { // Fields here are part of the AppKit template contract — the template // reads them via {{$p.Field}} on map values in templateVars.Plugins. type pluginVar struct { - // Stability mirrors manifest.Plugin.Stability ("" for stable, "beta" + // Stability mirrors manifest.Plugin.Stability ("" for GA, "beta" // for beta, future tiers preserved). The AppKit template branches // imports on this — see databricks/appkit#264 commit d826a532, which // routes beta plugins through the `@databricks/appkit/beta` subpath. diff --git a/cmd/apps/init_test.go b/cmd/apps/init_test.go index 3f82522545f..b8a9a8f443c 100644 --- a/cmd/apps/init_test.go +++ b/cmd/apps/init_test.go @@ -254,15 +254,15 @@ func TestExecuteTemplateInvalidSyntaxReturnsOriginal(t *testing.T) { // TestExecuteTemplatePluginStability locks down the contract that the // AppKit init template relies on: ranging over .plugins exposes a -// .Stability field per plugin, with stable/unset rendering as the empty +// .Stability field per plugin, with GA/unset rendering as the empty // string. See databricks/appkit#264 commit d826a532 (server.ts branches // imports between `@databricks/appkit` and `@databricks/appkit/beta`). func TestExecuteTemplatePluginStability(t *testing.T) { ctx := t.Context() vars := templateVars{ Plugins: map[string]*pluginVar{ - "stable-plugin": {}, - "beta-plugin": {Stability: "beta"}, + "ga-plugin": {}, + "beta-plugin": {Stability: "beta"}, }, } @@ -271,7 +271,7 @@ func TestExecuteTemplatePluginStability(t *testing.T) { require.NoError(t, err) got := string(result) - assert.Contains(t, got, "stable-plugin=;") + assert.Contains(t, got, "ga-plugin=;") assert.Contains(t, got, "beta-plugin=beta;") } @@ -307,20 +307,20 @@ import { {{$betaImports}} } from '@databricks/appkit/beta'; ` cases := []struct { - name string - plugins map[string]*pluginVar - wantStableImports []string // names that must appear on the stable line - wantBetaImports []string // names that must appear on the beta line, "" means no beta line - wantNoBetaLine bool + name string + plugins map[string]*pluginVar + wantGAImports []string // names that must appear on the GA line + wantBetaImports []string // names that must appear on the beta line, "" means no beta line + wantNoBetaLine bool }{ { - name: "all stable: no beta line", + name: "all GA: no beta line", plugins: map[string]*pluginVar{ "server": {}, "analytics": {}, }, - wantStableImports: []string{"server", "analytics"}, - wantNoBetaLine: true, + wantGAImports: []string{"server", "analytics"}, + wantNoBetaLine: true, }, { name: "mixed single beta", @@ -328,8 +328,8 @@ import { {{$betaImports}} } from '@databricks/appkit/beta'; "server": {}, "betaOne": {Stability: "beta"}, }, - wantStableImports: []string{"server"}, - wantBetaImports: []string{"betaOne"}, + wantGAImports: []string{"server"}, + wantBetaImports: []string{"betaOne"}, }, { name: "mixed multiple betas: combined into one import line", @@ -338,11 +338,11 @@ import { {{$betaImports}} } from '@databricks/appkit/beta'; "betaOne": {Stability: "beta"}, "betaTwo": {Stability: "beta"}, }, - wantStableImports: []string{"server"}, - wantBetaImports: []string{"betaOne", "betaTwo"}, + wantGAImports: []string{"server"}, + wantBetaImports: []string{"betaOne", "betaTwo"}, }, { - name: "all beta: createApp alone on stable line", + name: "all beta: createApp alone on GA line", plugins: map[string]*pluginVar{ "betaOne": {Stability: "beta"}, "betaTwo": {Stability: "beta"}, @@ -350,13 +350,13 @@ import { {{$betaImports}} } from '@databricks/appkit/beta'; wantBetaImports: []string{"betaOne", "betaTwo"}, }, { - name: "future tier (alpha) routes to stable line for now", + name: "future tier (alpha) routes to GA line for now", plugins: map[string]*pluginVar{ "server": {}, "alphaOne": {Stability: "alpha"}, }, - wantStableImports: []string{"server", "alphaOne"}, - wantNoBetaLine: true, + wantGAImports: []string{"server", "alphaOne"}, + wantNoBetaLine: true, }, } @@ -370,20 +370,20 @@ import { {{$betaImports}} } from '@databricks/appkit/beta'; lines := strings.Split(got, "\n") require.NotEmpty(t, lines) - // Stable line is always first: starts with `import { createApp` + // GA line is always first: starts with `import { createApp` // and ends with `from '@databricks/appkit';`. - stableLine := lines[0] - assert.True(t, strings.HasPrefix(stableLine, "import { createApp"), - "stable line: %q", stableLine) - assert.True(t, strings.HasSuffix(stableLine, "} from '@databricks/appkit';"), - "stable line: %q", stableLine) - for _, name := range tc.wantStableImports { - assert.Contains(t, stableLine, name, - "stable line missing %q: %q", name, stableLine) + gaLine := lines[0] + assert.True(t, strings.HasPrefix(gaLine, "import { createApp"), + "GA line: %q", gaLine) + assert.True(t, strings.HasSuffix(gaLine, "} from '@databricks/appkit';"), + "GA line: %q", gaLine) + for _, name := range tc.wantGAImports { + assert.Contains(t, gaLine, name, + "GA line missing %q: %q", name, gaLine) } for _, name := range tc.wantBetaImports { - assert.NotContains(t, stableLine, ", "+name, - "beta plugin %q leaked onto stable line: %q", name, stableLine) + assert.NotContains(t, gaLine, ", "+name, + "beta plugin %q leaked onto GA line: %q", name, gaLine) } if tc.wantNoBetaLine { diff --git a/libs/apps/manifest/manifest.go b/libs/apps/manifest/manifest.go index ba0f024de03..c4ecdd7f82f 100644 --- a/libs/apps/manifest/manifest.go +++ b/libs/apps/manifest/manifest.go @@ -77,18 +77,18 @@ type Plugin struct { Resources Resources `json:"resources"` OnSetupMessage string `json:"onSetupMessage"` - // Stability is one of "beta", "stable", or empty. + // Stability is one of "beta", "ga", or empty. // Stored as a plain string so unknown future values round-trip unchanged. // See https://github.com/databricks/appkit/pull/264. Stability string `json:"stability,omitempty"` } -// StabilityLabel returns a user-facing tier label for non-stable plugins. -// Returns "" for stable, unset, or any value that maps to stable. +// StabilityLabel returns a user-facing tier label for non-GA plugins. +// Returns "" for GA, unset, or any value that maps to GA. // Unknown values pass through so we are forward-compatible with new tiers. func (p Plugin) StabilityLabel() string { switch p.Stability { - case "", "stable": + case "", "ga": return "" default: return p.Stability diff --git a/libs/apps/manifest/manifest_test.go b/libs/apps/manifest/manifest_test.go index c0c4dad38d3..db571993b68 100644 --- a/libs/apps/manifest/manifest_test.go +++ b/libs/apps/manifest/manifest_test.go @@ -56,7 +56,7 @@ func TestLoad(t *testing.T) { "displayName": "Server Plugin", "description": "HTTP server", "package": "@databricks/appkit", - "stability": "stable", + "stability": "ga", "requiredByTemplate": true, "resources": { "required": [], @@ -77,7 +77,7 @@ func TestLoad(t *testing.T) { assert.False(t, m.Plugins["analytics"].RequiredByTemplate) assert.Equal(t, "beta", m.Plugins["analytics"].Stability) assert.Equal(t, "alpha", m.Plugins["genie"].Stability) - assert.Equal(t, "stable", m.Plugins["server"].Stability) + assert.Equal(t, "ga", m.Plugins["server"].Stability) } func TestPlugin_StabilityLabel(t *testing.T) { @@ -86,7 +86,7 @@ func TestPlugin_StabilityLabel(t *testing.T) { want string }{ {"", ""}, - {"stable", ""}, + {"ga", ""}, {"beta", "beta"}, {"alpha", "alpha"}, } diff --git a/libs/apps/prompt/prompt.go b/libs/apps/prompt/prompt.go index 64832e9e26d..277aa949e1f 100644 --- a/libs/apps/prompt/prompt.go +++ b/libs/apps/prompt/prompt.go @@ -75,7 +75,7 @@ var ( ) // RenderStabilityTier renders a stability tier as a colored " (tier)" suffix, -// or returns "" for stable/unset. Unknown tiers are rendered in gray so we +// or returns "" for GA/unset. Unknown tiers are rendered in gray so we // remain forward-compatible with future tier names. func RenderStabilityTier(tier string) string { if tier == "" {