From 720aaa5d0dc1c03555571f8fb713db07ef7be028 Mon Sep 17 00:00:00 2001 From: Andrew Nester Date: Wed, 18 Feb 2026 11:52:14 +0100 Subject: [PATCH 1/2] Added support for git_source and git_repository for Apps --- acceptance/bundle/apps/git_source/app/app.py | 1 + acceptance/bundle/apps/git_source/app/app.yml | 1 + .../apps/git_source/databricks.yml.tmpl | 18 +++ .../bundle/apps/git_source/out.test.toml | 6 + acceptance/bundle/apps/git_source/output.txt | 106 ++++++++++++++++++ acceptance/bundle/apps/git_source/script | 48 ++++++++ acceptance/bundle/apps/git_source/test.toml | 50 +++++++++ bundle/config/resources/apps.go | 5 + bundle/internal/schema/annotations.yml | 6 + bundle/run/app.go | 5 + bundle/schema/jsonschema.json | 4 + 11 files changed, 250 insertions(+) create mode 100644 acceptance/bundle/apps/git_source/app/app.py create mode 100644 acceptance/bundle/apps/git_source/app/app.yml create mode 100644 acceptance/bundle/apps/git_source/databricks.yml.tmpl create mode 100644 acceptance/bundle/apps/git_source/out.test.toml create mode 100644 acceptance/bundle/apps/git_source/output.txt create mode 100644 acceptance/bundle/apps/git_source/script create mode 100644 acceptance/bundle/apps/git_source/test.toml diff --git a/acceptance/bundle/apps/git_source/app/app.py b/acceptance/bundle/apps/git_source/app/app.py new file mode 100644 index 0000000000..271596cec0 --- /dev/null +++ b/acceptance/bundle/apps/git_source/app/app.py @@ -0,0 +1 @@ +print("Simple test app") diff --git a/acceptance/bundle/apps/git_source/app/app.yml b/acceptance/bundle/apps/git_source/app/app.yml new file mode 100644 index 0000000000..45b242d406 --- /dev/null +++ b/acceptance/bundle/apps/git_source/app/app.yml @@ -0,0 +1 @@ +command: ["python", "app.py"] diff --git a/acceptance/bundle/apps/git_source/databricks.yml.tmpl b/acceptance/bundle/apps/git_source/databricks.yml.tmpl new file mode 100644 index 0000000000..d5901c0dab --- /dev/null +++ b/acceptance/bundle/apps/git_source/databricks.yml.tmpl @@ -0,0 +1,18 @@ +bundle: + name: app-git-source-$UNIQUE_NAME + +workspace: + root_path: "~/.bundle/app-git-source-$UNIQUE_NAME" + +resources: + apps: + my_app: + name: app-$UNIQUE_NAME + description: "App with git source" + git_repository: + url: https://github.com/databricks/cli + provider: gitHub + git_source: + branch: main + source_code_path: internal/testdata/simple-app + source_code_path: ./app diff --git a/acceptance/bundle/apps/git_source/out.test.toml b/acceptance/bundle/apps/git_source/out.test.toml new file mode 100644 index 0000000000..6feb8784c8 --- /dev/null +++ b/acceptance/bundle/apps/git_source/out.test.toml @@ -0,0 +1,6 @@ +Local = true +Cloud = false +RequiresWarehouse = true + +[EnvMatrix] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct"] diff --git a/acceptance/bundle/apps/git_source/output.txt b/acceptance/bundle/apps/git_source/output.txt new file mode 100644 index 0000000000..eba0028348 --- /dev/null +++ b/acceptance/bundle/apps/git_source/output.txt @@ -0,0 +1,106 @@ + +=== Validate bundle configuration +>>> [CLI] bundle validate +Name: app-git-source-[UNIQUE_NAME] +Target: default +Workspace: + User: [USERNAME] + Path: /Workspace/Users/[USERNAME]/.bundle/app-git-source-[UNIQUE_NAME] + +Validation OK! + +=== Plan bundle deployment +>>> [CLI] bundle plan +create apps.my_app + +Plan: 1 to add, 0 to change, 0 to delete, 0 unchanged + +=== Deploy bundle +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/app-git-source-[UNIQUE_NAME]/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +=== Get app details and verify git_source configuration +>>> [CLI] bundle summary --output json + +>>> [CLI] apps get [APP_NAME] --output json +{ + "name": "[APP_NAME]", + "description": "App with git source", + "git_repository": { + "provider": "gitHub", + "url": "https://github.com/databricks/cli" + }, + "git_source": null +} + +=== Verify no drift after deployment +>>> [CLI] bundle plan +Plan: 0 to add, 0 to change, 0 to delete, 1 unchanged + +=== Run the app to verify it works +>>> cat out.app-run +✓ Getting the status of the app [APP_NAME] +✓ App is in RUNNING state +✓ App compute is in ACTIVE state +✓ Deployment succeeded +You can access the app at [APP_NAME]-123.cloud.databricksapps.com + +=== Update git_source branch and redeploy +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/app-git-source-[UNIQUE_NAME]/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +=== Verify config update was applied +>>> [CLI] apps get [APP_NAME] --output json +{ + "name": "[APP_NAME]", + "git_source": null +} + +=== Destroy bundle (via trap) +=== Verify app is deleted +>>> [CLI] apps get [APP_NAME] +{ + "app_status": { + "message":"Application is running.", + "state":"RUNNING" + }, + "compute_status": { + "message":"App compute is active.", + "state":"ACTIVE" + }, + "description":"App with git source", + "git_repository": { + "provider":"gitHub", + "url":"https://github.com/databricks/cli" + }, + "id":"1000", + "name":"[APP_NAME]", + "url":"[APP_NAME]-123.cloud.databricksapps.com" +} + +>>> [CLI] bundle summary +Name: app-git-source-[UNIQUE_NAME] +Target: default +Workspace: + User: [USERNAME] + Path: /Workspace/Users/[USERNAME]/.bundle/app-git-source-[UNIQUE_NAME] +Resources: + Apps: + my_app: + Name: [APP_NAME] + URL: (not deployed) + +>>> [CLI] bundle destroy --auto-approve +The following resources will be deleted: + delete resources.apps.my_app + +All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/app-git-source-[UNIQUE_NAME] + +Deleting files... +Destroy complete! diff --git a/acceptance/bundle/apps/git_source/script b/acceptance/bundle/apps/git_source/script new file mode 100644 index 0000000000..865d879672 --- /dev/null +++ b/acceptance/bundle/apps/git_source/script @@ -0,0 +1,48 @@ +#!/bin/bash + +# Generate databricks.yml from template +envsubst < databricks.yml.tmpl > databricks.yml + +# Set up cleanup trap to ensure resources are destroyed even on failure +cleanup() { + trace $CLI bundle destroy --auto-approve +} +trap cleanup EXIT + +title "Validate bundle configuration" +trace $CLI bundle validate + +title "Plan bundle deployment" +trace $CLI bundle plan + +title "Deploy bundle" +trace $CLI bundle deploy + +title "Get app details and verify git_source configuration" +app_name=$(trace $CLI bundle summary --output json | jq -r '.resources.apps.my_app.name') +echo "$app_name:APP_NAME" >> ACC_REPLS + +trace $CLI apps get $app_name --output json | jq '{name, description, git_repository, git_source}' + +title "Verify no drift after deployment" +trace $CLI bundle plan + +title "Run the app to verify it works" +$CLI bundle run my_app &> out.app-run || true +trace cat out.app-run | head -20 + +title "Update git_source branch and redeploy" +# Change branch from main to a different value (still main, but via sed to test config change) +sed -i.bak 's/branch: main/branch: main # updated/' databricks.yml +trace $CLI bundle deploy + +title "Verify config update was applied" +trace $CLI apps get $app_name --output json | jq '{name, git_source}' + +title "Destroy bundle (via trap)" +# Destroy will be called by trap on EXIT + +title "Verify app is deleted" +errcode trace $CLI apps get $app_name + +trace $CLI bundle summary diff --git a/acceptance/bundle/apps/git_source/test.toml b/acceptance/bundle/apps/git_source/test.toml new file mode 100644 index 0000000000..ee68c7a08d --- /dev/null +++ b/acceptance/bundle/apps/git_source/test.toml @@ -0,0 +1,50 @@ +# Test that git_repository and git_source fields are properly configured for apps +Local = true +# Temporary disable cloud tests because it fails with "Git repository cannot be defined in this workspace. Please try again later." +Cloud = false +RecordRequests = false +RequiresWarehouse = true + +Ignore = [".databricks", "databricks.yml", "databricks.yml.bak", "out.app-run"] + +# Apps can take longer to deploy +TimeoutCloud = "5m" + +# Mock responses for app deployment +[[Server]] +Pattern = "POST /api/2.0/apps/{app_name}/deployments" +Response.Body = ''' +{ + "deployment_id": "test-deployment-123", + "status": { + "state": "SUCCEEDED", + "message": "Deployment succeeded" + }, + "git_source": { + "branch": "main", + "source_code_path": "internal/testdata/simple-app", + "resolved_commit": "abc123def456" + }, + "source_code_path": "/Workspace/Users/tester@databricks.com/.bundle/files/app", + "mode": "SNAPSHOT" +} +''' + +[[Server]] +Pattern = "GET /api/2.0/apps/{app_name}/deployments/{deployment_id}" +Response.Body = ''' +{ + "deployment_id": "test-deployment-123", + "status": { + "state": "SUCCEEDED", + "message": "Deployment succeeded" + }, + "git_source": { + "branch": "main", + "source_code_path": "internal/testdata/simple-app", + "resolved_commit": "abc123def456" + }, + "source_code_path": "/Workspace/Users/tester@databricks.com/.bundle/files/app", + "mode": "SNAPSHOT" +} +''' diff --git a/bundle/config/resources/apps.go b/bundle/config/resources/apps.go index 7dca924342..18c48f10d3 100644 --- a/bundle/config/resources/apps.go +++ b/bundle/config/resources/apps.go @@ -35,6 +35,7 @@ type AppEnvVar struct { type App struct { BaseResource apps.App // nolint App struct also defines Id and URL field with the same json tag "id" and "url" + // Note: apps.App already includes GitRepository field from the SDK // SourceCodePath is a required field used by DABs to point to Databricks app source code // on local disk and to the corresponding workspace path during app deployment. @@ -45,6 +46,10 @@ type App struct { // This allows users to define app configuration directly in the bundle YAML instead of maintaining a separate app.yaml file. Config *AppConfig `json:"config,omitempty"` + // GitSource specifies the git reference (branch, tag, or commit) to use during deployment. + // This is used in conjunction with GitRepository (from apps.App) and is passed to the Deploy API. + GitSource *apps.GitSource `json:"git_source,omitempty"` + Permissions []AppPermission `json:"permissions,omitempty"` } diff --git a/bundle/internal/schema/annotations.yml b/bundle/internal/schema/annotations.yml index 6464e26cac..76f4a4e86d 100644 --- a/bundle/internal/schema/annotations.yml +++ b/bundle/internal/schema/annotations.yml @@ -519,6 +519,12 @@ github.com/databricks/cli/bundle/config/resources.AlertPermission: "user_name": "description": |- PLACEHOLDER +github.com/databricks/cli/bundle/config/resources.App: + "git_source": + "description": |- + Git source configuration for app deployments. Specifies which git reference (branch, tag, or commit) + to use when deploying the app. Used in conjunction with git_repository to deploy code directly from git. + The source_code_path within git_source specifies the relative path to the app code within the repository. github.com/databricks/cli/bundle/config/resources.AppConfig: "command": "description": |- diff --git a/bundle/run/app.go b/bundle/run/app.go index 3c3b0d3bf6..1dd4484093 100644 --- a/bundle/run/app.go +++ b/bundle/run/app.go @@ -166,6 +166,11 @@ func (a *appRunner) buildAppDeployment() apps.AppDeployment { SourceCodePath: a.app.SourceCodePath, } + // Add git source if provided + if a.app.GitSource != nil { + deployment.GitSource = a.app.GitSource + } + // Add inline config if provided if a.app.Config != nil { if len(a.app.Config.Command) > 0 { diff --git a/bundle/schema/jsonschema.json b/bundle/schema/jsonschema.json index 0a25ac5a95..ad3d88051e 100644 --- a/bundle/schema/jsonschema.json +++ b/bundle/schema/jsonschema.json @@ -191,6 +191,10 @@ "x-databricks-preview": "PRIVATE", "doNotSuggest": true }, + "git_source": { + "description": "Git source configuration for app deployments. Specifies which git reference (branch, tag, or commit)\nto use when deploying the app. Used in conjunction with git_repository to deploy code directly from git.\nThe source_code_path within git_source specifies the relative path to the app code within the repository.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/apps.GitSource" + }, "lifecycle": { "description": "Lifecycle is a struct that contains the lifecycle settings for a resource. It controls the behavior of the resource when it is deployed or destroyed.", "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Lifecycle" From 28bf14b1e87ea8eee563746e87176c03bed4a74a Mon Sep 17 00:00:00 2001 From: Andrew Nester Date: Wed, 18 Feb 2026 11:56:22 +0100 Subject: [PATCH 2/2] generate --- bundle/internal/validation/generated/required_fields.go | 1 + 1 file changed, 1 insertion(+) diff --git a/bundle/internal/validation/generated/required_fields.go b/bundle/internal/validation/generated/required_fields.go index 21bc5655a7..cb859539ec 100644 --- a/bundle/internal/validation/generated/required_fields.go +++ b/bundle/internal/validation/generated/required_fields.go @@ -22,6 +22,7 @@ var RequiredFields = map[string][]string{ "resources.apps.*.active_deployment.git_source.git_repository": {"provider", "url"}, "resources.apps.*.config.env[*]": {"name"}, "resources.apps.*.git_repository": {"provider", "url"}, + "resources.apps.*.git_source.git_repository": {"provider", "url"}, "resources.apps.*.pending_deployment.git_source.git_repository": {"provider", "url"}, "resources.apps.*.permissions[*]": {"level"}, "resources.apps.*.resources[*]": {"name"},