Skip to content

Conversation

@MarioCadenas
Copy link
Contributor

@MarioCadenas MarioCadenas commented Jan 11, 2026

Changes

Overview

This PR adds new AppKit development commands to databricks apps, making them first-class citizens alongside the auto-generated Apps API commands. The implementation follows the same pattern established by the pipelines commands.

New Command Structure

databricks apps now has three command groups:

Group Commands Description
Available Commands deploy, dev-remote, init, logs, run-local, validate Custom development commands
Management Commands create, delete, get, list, start, stop, etc. Auto-generated API commands
Permission Commands get-permissions, set-permissions, etc. Permission management

Key Features

  1. databricks apps init - Initialize new AppKit projects from templates with interactive prompts, but also allowing full prompt override via flags.
  2. databricks apps dev-remote - Run local Vite dev server with WebSocket bridge to remote app (this command already exists, but now has some improvements like deriving the project from the folder and reconnecting).
  3. databricks apps validate - Run validation of the app running type checking, linting and building.
  4. databricks apps deploy - Dual-mode deployment:
    • Bundle mode (when databricks.yml exists): Validates → Deploys bundle → Runs app
    • API mode: Standard API deployment with APP_NAME argument

Directory Structure

cmd/apps/                    # Custom commands
├── apps.go                  # Commands() + ManagementGroupID
├── init.go                  # Initialize projects
├── dev.go                   # Dev-remote command
├── deploy_bundle.go         # Bundle-aware deploy
├── logs.go                  # App logs
├── run_local.go               # Run locally
└── validate.go             # Validate the project compiles

cmd/workspace/apps/          # Auto-generated + overrides
├── apps.go                  # SDK commands
├── overrides.go             # Imports cmd/apps, groups commands
└── errors.go                # Error handling

libs/apps/                   # Shared libraries
├── features/                # Feature definitions
├── prompt/                  # Interactive prompts
├── validation/              # Project validation
└── vite/                    # Vite bridge

Why

Tests

@MarioCadenas MarioCadenas force-pushed the appkit-cli-commands branch 3 times, most recently from 04f4c88 to 6e23dec Compare January 14, 2026 09:01
@eng-dev-ecosystem-bot
Copy link
Collaborator

eng-dev-ecosystem-bot commented Jan 14, 2026

Commit: 6e80bed

Run: 21229485522

Env 🟨​KNOWN 🔄​flaky 💚​RECOVERED 🙈​SKIP ✅​pass 🙈​skip Time
🟨​ aws linux 3 5 6 411 693 21:38
🟨​ aws windows 3 5 6 413 691 19:16
🟨​ aws-ucws linux 3 8 5 583 567 29:56
🟨​ aws-ucws windows 3 8 5 585 565 27:07
💚​ azure linux 2 8 411 692 24:07
💚​ azure windows 2 8 413 690 19:26
🔄​ azure-ucws linux 2 4 7 578 566 29:41
🔄​ azure-ucws windows 3 5 7 578 564 33:46
🔄​ gcp linux 2 2 8 398 698 20:48
💚​ gcp windows 2 8 402 696 19:29
21 interesting tests: 8 RECOVERED, 5 SKIP, 5 flaky, 3 KNOWN
Test Name aws linux aws windows aws-ucws linux aws-ucws windows azure linux azure windows azure-ucws linux azure-ucws windows gcp linux gcp windows
🟨​ TestAccept 🟨​K 🟨​K 🟨​K 🟨​K 💚​R 💚​R 🔄​f 💚​R 💚​R 💚​R
🙈​ TestAccept/bundle/deployment/bind/alert 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/generate/alert 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/alerts/basic 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/alerts/with_file 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/permissions 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🟨​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions 🟨​K 🟨​K 🟨​K 🟨​K 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🟨​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions/DATABRICKS_BUNDLE_ENGINE=direct 🟨​K 🟨​K 🟨​K 🟨​K
💚​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions/DATABRICKS_BUNDLE_ENGINE=terraform 💚​R 💚​R 💚​R 💚​R
💚​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions 💚​R 💚​R 💚​R 💚​R 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
💚​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions/DATABRICKS_BUNDLE_ENGINE=direct 💚​R 💚​R 💚​R 💚​R
💚​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions/DATABRICKS_BUNDLE_ENGINE=terraform 💚​R 💚​R 💚​R 💚​R
🔄​ TestAccept/bundle/resources/quality_monitors/change_assets_dir/DATABRICKS_BUNDLE_ENGINE=direct ✅​p ✅​p ✅​p 🔄​f
🔄​ TestAccept/bundle/resources/quality_monitors/change_assets_dir/DATABRICKS_BUNDLE_ENGINE=terraform ✅​p ✅​p ✅​p 🔄​f
🔄​ TestAccept/bundle/resources/quality_monitors/change_table_name 🙈​s 🙈​s ✅​p ✅​p 🙈​s 🙈​s 🔄​f 🔄​f 🙈​s 🙈​s
💚​ TestAccept/bundle/resources/synced_database_tables/basic 🙈​S 🙈​S 💚​R 💚​R 🙈​S 🙈​S 💚​R 💚​R 🙈​S 🙈​S
💚​ TestAccept/bundle/resources/synced_database_tables/basic/DATABRICKS_BUNDLE_ENGINE=direct 💚​R 💚​R 💚​R 💚​R
💚​ TestAccept/bundle/resources/synced_database_tables/basic/DATABRICKS_BUNDLE_ENGINE=terraform 💚​R 💚​R 💚​R 💚​R
💚​ TestAccept/ssh/connection 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
🔄​ TestFilerWorkspaceNotebook ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p 🔄​f ✅​p
🔄​ TestFilerWorkspaceNotebook/rJupyterNb.ipynb ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p 🔄​f ✅​p
Top 50 slowest tests (at least 2 minutes):
duration env testname
8:37 azure-ucws windows TestAccept/bundle/resources/synced_database_tables/basic/DATABRICKS_BUNDLE_ENGINE=terraform
7:39 aws-ucws linux TestAccept/bundle/resources/synced_database_tables/basic/DATABRICKS_BUNDLE_ENGINE=terraform
6:38 aws-ucws windows TestAccept/bundle/resources/synced_database_tables/basic/DATABRICKS_BUNDLE_ENGINE=direct
6:37 aws-ucws windows TestAccept/bundle/resources/synced_database_tables/basic/DATABRICKS_BUNDLE_ENGINE=terraform
6:24 aws-ucws linux TestAccept/ssh/connection
6:05 azure linux TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
5:51 aws-ucws linux TestAccept/bundle/resources/synced_database_tables/basic/DATABRICKS_BUNDLE_ENGINE=direct
5:44 gcp linux TestAccept/ssh/connection
5:33 aws-ucws linux TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
5:32 aws-ucws linux TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
5:32 aws windows TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
5:31 aws linux TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
5:29 aws linux TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
5:29 aws-ucws windows TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
5:26 aws windows TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
5:22 gcp windows TestAccept/ssh/connection
5:19 aws linux TestAccept/ssh/connection
5:17 gcp linux TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
5:14 gcp windows TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
5:11 aws-ucws windows TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
5:10 gcp linux TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
5:10 gcp windows TestSecretsPutSecretStringValue
5:04 aws-ucws windows TestAccept/ssh/connection
5:03 azure-ucws linux TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
4:51 gcp windows TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
4:45 azure-ucws linux TestAccept/bundle/resources/synced_database_tables/basic/DATABRICKS_BUNDLE_ENGINE=terraform
4:39 aws windows TestSecretsPutSecretStringValue
4:35 azure windows TestAccept/ssh/connection
4:34 azure linux TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
4:33 aws windows TestAccept/ssh/connection
4:31 azure-ucws windows TestAccept/ssh/connection
4:24 azure-ucws windows TestAccept/bundle/resources/synced_database_tables/basic/DATABRICKS_BUNDLE_ENGINE=direct
4:21 azure-ucws linux TestAccept/bundle/resources/synced_database_tables/basic/DATABRICKS_BUNDLE_ENGINE=direct
4:16 azure-ucws windows TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
4:15 azure-ucws windows TestSecretsPutSecretStringValue
4:14 azure windows TestSecretsPutSecretStringValue
4:13 azure linux TestAccept/ssh/connection
4:09 azure-ucws windows TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
4:08 azure windows TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
4:05 azure-ucws linux TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
3:57 azure windows TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
3:47 gcp linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:14 gcp linux TestSecretsPutSecretStringValue
3:06 gcp linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:06 aws linux TestSecretsPutSecretStringValue
3:05 gcp windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:47 aws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:43 gcp windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:41 aws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:39 aws-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform

@@ -0,0 +1,419 @@
package app
Copy link
Contributor

Choose a reason for hiding this comment

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

check with @pietern. He wants to unify CLI UX.

Copy link
Contributor

@pietern pietern left a comment

Choose a reason for hiding this comment

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

Blocking merging. Need to discuss the deps first.

@keugenek keugenek self-requested a review January 16, 2026 12:02
Copy link
Contributor

@arsenyinfo arsenyinfo left a comment

Choose a reason for hiding this comment

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

Those changes duplicate existing entities like template, validation etc and are not in sync with agentic prompts atm. We can't merge it unless there is a unification plan.

@keugenek
Copy link
Contributor

@MarioCadenas
Copy link
Contributor Author

Those changes duplicate existing entities like template, validation etc and are not in sync with agentic prompts atm. We can't merge it unless there is a unification plan.

the idea is to unify yes, but this comes with the approach of having this as humans first, through the apps scope, but of course, many of this should be used by the ai tools

@MarioCadenas
Copy link
Contributor Author

This PR largely duplicates cli aitools already implemented http://github.com/databricks/cli/tree/main/experimental/aitools

Also https://github.com/databricks/appkit/pull/57/changes template - another duplication https://github.com/databricks/cli/tree/main/experimental/aitools/templates/appkit

It has some similar things yes, I already mentioned this, we were doing similar things and we needed to converge into one.
The template is not really a duplication, is a much simpler one that allows configuration and goes into the scope that it should be. With ideally removing the other one

Let's talk about it in the sync

@fjakobs
Copy link
Contributor

fjakobs commented Jan 16, 2026

@arsenyinfo @keugenek this indeed duplicates functionality from aitools tools. The way I see it we are graduating these commands from the internal AI only namespace to fully supported first class commands, which will be used by humans and agents.

We keep the existing tools under aitools tools until the prompts are updated and we no longer need them.

@MarioCadenas MarioCadenas force-pushed the appkit-cli-commands branch 2 times, most recently from f277573 to ef5a6c6 Compare January 19, 2026 09:07
@fjakobs
Copy link
Contributor

fjakobs commented Jan 19, 2026

It would be nice to make dev-local an alias to run-local so we have naming consistency between dev-local and run-local

return fmt.Errorf("failed to run app: %w. Run `databricks apps logs %s` to view logs", err, appName)
}

cmdio.LogString(ctx, "✔ Deployment complete!")
Copy link
Contributor

Choose a reason for hiding this comment

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

@andrewnester We'll need to update this after adding a "started" state to apps.

} else {
log.Debugf(ctx, "No validator found for project type, skipping validation")
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

What kind of validation happens here that doesn't happen in the regular "bundle deploy"?

We need to be careful not to make this a hard prereq for deployment.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, this is actually to make things better, it runs validation of the app by running linting, typechecking, and building, so it verifies that everything works and the app won't fail to deploy once its running the building process inside the apps process.

Its basically to avoid the annoying behaviour of having to go into databricks or check the logs command to see why the app failed to deploy.

Right now we just have 1 validator for node apps, at some point we will probably have one for python apps too.

But if it doesn't find a validator, then it just keeps going (to avoid introducing breaking changes)

// substituteVars replaces template variables in a string.
func substituteVars(s string, vars templateVars) string {
s = strings.ReplaceAll(s, "{{.project_name}}", vars.ProjectName)
s = strings.ReplaceAll(s, "{{.sql_warehouse_id}}", vars.SQLWarehouseID)
Copy link
Contributor

Choose a reason for hiding this comment

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

All this low-level string mangling stuff seems incredibly unfortunate. Is the aitools template not already usable here, minus the AGENTS.md file? cc @fjakobs

go.mod Outdated
github.com/Masterminds/semver/v3 v3.4.0 // MIT
github.com/briandowns/spinner v1.23.1 // Apache 2.0
github.com/charmbracelet/huh v0.8.0
github.com/charmbracelet/lipgloss v1.1.0
Copy link
Contributor

Choose a reason for hiding this comment

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

Please also include the license like the other deps.

}

return tempDir, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

You can use git.Clone from libs/git.

profile := ""
if w := cmdctx.WorkspaceClient(ctx); w != nil && w.Config != nil {
workspaceHost = w.Config.Host
profile = w.Config.Profile
Copy link
Contributor

Choose a reason for hiding this comment

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

Profile may be empty.

// runPostCreateDeploy runs the deploy command in the current directory.
func runPostCreateDeploy(ctx context.Context) error {
// Use os.Args[0] to get the path to the current executable
executable := os.Args[0]
Copy link
Contributor

Choose a reason for hiding this comment

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

This is incompatible with os.Chdir, unless you expect the CLI to be in $PATH.

For example, if you invoke as .databricks/databricks (snapshot build), and chdir, it won't work.

"github.com/stretchr/testify/assert"
)

func TestParseGitHubURL(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Candidate to move to libs/git.

chore: fixup

chore: fixup

chore: fixup

chore: fixup

chore: fixup

chore: fixup

chore: fixup

chore: fixup

chore: fixup

chore: fixup

chore: fixup

chore: fixup

chore: fixup

chore: fixup

chore: fixup

chore: fixup

chore: fixup

chore: fixup

chore: fixup

chore: fixup

chore: fixup

chore: fixup

chore: validate command

chore: fixup

chore: remove import command

chore: fixup
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants