From 6816c242320e3b1c9071291affd0182f213101ee Mon Sep 17 00:00:00 2001 From: Ilya Kuznetsov Date: Wed, 11 Feb 2026 14:58:05 +0100 Subject: [PATCH 1/9] Strip field values --- bundle/configsync/diff.go | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/bundle/configsync/diff.go b/bundle/configsync/diff.go index 1e27da9150..4b008a9ffb 100644 --- a/bundle/configsync/diff.go +++ b/bundle/configsync/diff.go @@ -8,6 +8,7 @@ import ( "io/fs" "os" "path/filepath" + "strings" "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config/engine" @@ -85,7 +86,7 @@ func filterEntityDefaults(basePath string, value any) any { return result } -func convertChangeDesc(path string, cd *deployplan.ChangeDesc) (*ConfigChangeDesc, error) { +func convertChangeDesc(path string, cd *deployplan.ChangeDesc, syncRootPath string) (*ConfigChangeDesc, error) { hasConfigValue := cd.Old != nil || cd.New != nil normalizedValue, err := normalizeValue(cd.Remote) if err != nil { @@ -115,12 +116,43 @@ func convertChangeDesc(path string, cd *deployplan.ChangeDesc) (*ConfigChangeDes normalizedValue = filterEntityDefaults(path, normalizedValue) } + if op == OperationAdd && syncRootPath != "" { + normalizedValue = translateWorkspacePaths(normalizedValue, syncRootPath) + } + return &ConfigChangeDesc{ Operation: op, Value: normalizedValue, }, nil } +// translateWorkspacePaths recursively converts absolute workspace paths to relative +// paths when they fall within the bundle's sync root. Paths outside the sync +// root are left unchanged. +func translateWorkspacePaths(value any, syncRootPath string) any { + switch v := value.(type) { + case string: + if after, ok := strings.CutPrefix(v, syncRootPath+"/"); ok { + return "./" + after + } + return v + case map[string]any: + result := make(map[string]any, len(v)) + for key, val := range v { + result[key] = translateWorkspacePaths(val, syncRootPath) + } + return result + case []any: + result := make([]any, len(v)) + for i, val := range v { + result[i] = translateWorkspacePaths(val, syncRootPath) + } + return result + default: + return value + } +} + // DetectChanges compares current remote state with the last deployed state // and returns a map of resource changes. func DetectChanges(ctx context.Context, b *bundle.Bundle, engine engine.EngineType) (Changes, error) { @@ -155,7 +187,7 @@ func DetectChanges(ctx context.Context, b *bundle.Bundle, engine engine.EngineTy continue } - change, err := convertChangeDesc(path, changeDesc) + change, err := convertChangeDesc(path, changeDesc, b.SyncRootPath) if err != nil { return nil, fmt.Errorf("failed to compute config change for path %s: %w", path, err) } From afdaf139725bcf019ddafe22f4733d519d45a446 Mon Sep 17 00:00:00 2001 From: Ilya Kuznetsov Date: Wed, 11 Feb 2026 15:00:05 +0100 Subject: [PATCH 2/9] Cleanup --- bundle/configsync/diff.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/bundle/configsync/diff.go b/bundle/configsync/diff.go index 4b008a9ffb..c02d2e7127 100644 --- a/bundle/configsync/diff.go +++ b/bundle/configsync/diff.go @@ -137,17 +137,15 @@ func translateWorkspacePaths(value any, syncRootPath string) any { } return v case map[string]any: - result := make(map[string]any, len(v)) for key, val := range v { - result[key] = translateWorkspacePaths(val, syncRootPath) + v[key] = translateWorkspacePaths(val, syncRootPath) } - return result + return v case []any: - result := make([]any, len(v)) for i, val := range v { - result[i] = translateWorkspacePaths(val, syncRootPath) + v[i] = translateWorkspacePaths(val, syncRootPath) } - return result + return v default: return value } From 2dc645f15786ca325bae46c71d9cdb24b65cd631 Mon Sep 17 00:00:00 2001 From: Ilya Kuznetsov Date: Wed, 11 Feb 2026 17:40:00 +0100 Subject: [PATCH 3/9] Add test --- .../job_multiple_tasks/output.txt | 11 +++++++++++ .../config-remote-sync/job_multiple_tasks/script | 15 ++++++++++++++- .../job_multiple_tasks/synced_notebook.py | 1 + .../job_multiple_tasks/test.toml | 2 +- 4 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 acceptance/bundle/config-remote-sync/job_multiple_tasks/synced_notebook.py diff --git a/acceptance/bundle/config-remote-sync/job_multiple_tasks/output.txt b/acceptance/bundle/config-remote-sync/job_multiple_tasks/output.txt index 1cb47b6625..f6f7da674b 100644 --- a/acceptance/bundle/config-remote-sync/job_multiple_tasks/output.txt +++ b/acceptance/bundle/config-remote-sync/job_multiple_tasks/output.txt @@ -78,6 +78,7 @@ Resource: resources.jobs.rename_task_job tasks[task_key='b_task_renamed']: add tasks[task_key='c_task'].depends_on[0].task_key: replace tasks[task_key='d_task'].depends_on[0].task_key: replace + tasks[task_key='synced_task']: add @@ -114,3 +115,13 @@ Resource: resources.jobs.rename_task_job + - task_key: b_task_renamed notebook_task: notebook_path: /Users/{{workspace_user_name}}/c_task +@@ -72,2 +72,9 @@ + node_type_id: [NODE_TYPE_ID] + num_workers: 1 ++ - new_cluster: ++ node_type_id: [NODE_TYPE_ID] ++ num_workers: 1 ++ spark_version: 13.3.x-snapshot-scala2.12 ++ notebook_task: ++ notebook_path: ./synced_notebook ++ task_key: synced_task diff --git a/acceptance/bundle/config-remote-sync/job_multiple_tasks/script b/acceptance/bundle/config-remote-sync/job_multiple_tasks/script index 097516a7a2..2fb6aeef4f 100755 --- a/acceptance/bundle/config-remote-sync/job_multiple_tasks/script +++ b/acceptance/bundle/config-remote-sync/job_multiple_tasks/script @@ -49,7 +49,7 @@ $CLI bundle deploy title "Rename b_task to b_task_renamed (4 tasks, 2 with depends_on, 1 without)" rename_job_id="$(read_id.py rename_task_job)" -edit_resource.py jobs $rename_job_id <<'EOF' +edit_resource.py jobs $rename_job_id < Date: Wed, 11 Feb 2026 18:40:21 +0100 Subject: [PATCH 4/9] Fix destroy in tests --- .../job_multiple_tasks/output.txt | 20 +++++++++---------- .../job_multiple_tasks/script | 4 ++-- .../job_multiple_tasks/synced_notebook.py | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/acceptance/bundle/config-remote-sync/job_multiple_tasks/output.txt b/acceptance/bundle/config-remote-sync/job_multiple_tasks/output.txt index 653ad4918c..4402135de9 100644 --- a/acceptance/bundle/config-remote-sync/job_multiple_tasks/output.txt +++ b/acceptance/bundle/config-remote-sync/job_multiple_tasks/output.txt @@ -115,6 +115,16 @@ Resource: resources.jobs.rename_task_job + - task_key: b_task_renamed notebook_task: notebook_path: /Users/{{workspace_user_name}}/c_task +@@ -72,2 +72,9 @@ + node_type_id: [NODE_TYPE_ID] + num_workers: 1 ++ - new_cluster: ++ node_type_id: [NODE_TYPE_ID] ++ num_workers: 1 ++ spark_version: 13.3.x-snapshot-scala2.12 ++ spark_python_task: ++ python_file: ./synced_notebook.py ++ task_key: synced_task >>> [CLI] bundle destroy --auto-approve The following resources will be deleted: @@ -125,13 +135,3 @@ All files and directories at the following location will be deleted: /Workspace/ Deleting files... Destroy complete! -@@ -72,2 +72,9 @@ - node_type_id: [NODE_TYPE_ID] - num_workers: 1 -+ - new_cluster: -+ node_type_id: [NODE_TYPE_ID] -+ num_workers: 1 -+ spark_version: 13.3.x-snapshot-scala2.12 -+ notebook_task: -+ notebook_path: ./synced_notebook -+ task_key: synced_task diff --git a/acceptance/bundle/config-remote-sync/job_multiple_tasks/script b/acceptance/bundle/config-remote-sync/job_multiple_tasks/script index e73330694b..3beede38c8 100755 --- a/acceptance/bundle/config-remote-sync/job_multiple_tasks/script +++ b/acceptance/bundle/config-remote-sync/job_multiple_tasks/script @@ -66,8 +66,8 @@ for task in r["tasks"]: # Add synced_task with path inside sync root r["tasks"].append({ "task_key": "synced_task", - "notebook_task": { - "notebook_path": "${PWD}/synced_notebook" + "spark_python_task": { + "python_file": "${PWD}/synced_notebook.py" }, "new_cluster": { "spark_version": "${DEFAULT_SPARK_VERSION}", diff --git a/acceptance/bundle/config-remote-sync/job_multiple_tasks/synced_notebook.py b/acceptance/bundle/config-remote-sync/job_multiple_tasks/synced_notebook.py index 1645e04b1d..2077613e57 100644 --- a/acceptance/bundle/config-remote-sync/job_multiple_tasks/synced_notebook.py +++ b/acceptance/bundle/config-remote-sync/job_multiple_tasks/synced_notebook.py @@ -1 +1 @@ -# Databricks notebook source +# Synced python file From bc1d743e06a80a9eea30dbcfc2846a88d0b5f624 Mon Sep 17 00:00:00 2001 From: Ilya Kuznetsov Date: Thu, 12 Feb 2026 13:22:20 +0100 Subject: [PATCH 5/9] Fix missing extension --- .../job_multiple_tasks/output.txt | 4 +- .../job_multiple_tasks/script | 4 +- .../job_multiple_tasks/synced_notebook.py | 2 +- bundle/config/mutator/translate_paths.go | 14 ++----- bundle/configsync/diff.go | 41 ++++++++++++++----- libs/notebook/ext.go | 9 ++++ 6 files changed, 48 insertions(+), 26 deletions(-) diff --git a/acceptance/bundle/config-remote-sync/job_multiple_tasks/output.txt b/acceptance/bundle/config-remote-sync/job_multiple_tasks/output.txt index 4402135de9..697cf22fc8 100644 --- a/acceptance/bundle/config-remote-sync/job_multiple_tasks/output.txt +++ b/acceptance/bundle/config-remote-sync/job_multiple_tasks/output.txt @@ -122,8 +122,8 @@ Resource: resources.jobs.rename_task_job + node_type_id: [NODE_TYPE_ID] + num_workers: 1 + spark_version: 13.3.x-snapshot-scala2.12 -+ spark_python_task: -+ python_file: ./synced_notebook.py ++ notebook_task: ++ notebook_path: ./synced_notebook.py + task_key: synced_task >>> [CLI] bundle destroy --auto-approve diff --git a/acceptance/bundle/config-remote-sync/job_multiple_tasks/script b/acceptance/bundle/config-remote-sync/job_multiple_tasks/script index 3beede38c8..e73330694b 100755 --- a/acceptance/bundle/config-remote-sync/job_multiple_tasks/script +++ b/acceptance/bundle/config-remote-sync/job_multiple_tasks/script @@ -66,8 +66,8 @@ for task in r["tasks"]: # Add synced_task with path inside sync root r["tasks"].append({ "task_key": "synced_task", - "spark_python_task": { - "python_file": "${PWD}/synced_notebook.py" + "notebook_task": { + "notebook_path": "${PWD}/synced_notebook" }, "new_cluster": { "spark_version": "${DEFAULT_SPARK_VERSION}", diff --git a/acceptance/bundle/config-remote-sync/job_multiple_tasks/synced_notebook.py b/acceptance/bundle/config-remote-sync/job_multiple_tasks/synced_notebook.py index 2077613e57..1645e04b1d 100644 --- a/acceptance/bundle/config-remote-sync/job_multiple_tasks/synced_notebook.py +++ b/acceptance/bundle/config-remote-sync/job_multiple_tasks/synced_notebook.py @@ -1 +1 @@ -# Synced python file +# Databricks notebook source diff --git a/bundle/config/mutator/translate_paths.go b/bundle/config/mutator/translate_paths.go index 86f0601875..213bf37fad 100644 --- a/bundle/config/mutator/translate_paths.go +++ b/bundle/config/mutator/translate_paths.go @@ -184,29 +184,21 @@ func (t *translateContext) translateNotebookPath(ctx context.Context, literal, l return "", fmt.Errorf("notebook %s not found", literal) } - extensions := []string{ - notebook.ExtensionPython, - notebook.ExtensionR, - notebook.ExtensionScala, - notebook.ExtensionSql, - notebook.ExtensionJupyter, - } - // Check whether a file with a notebook extension already exists. This // way we can provide a more targeted error message. - for _, ext := range extensions { + for _, ext := range notebook.Extensions { literalWithExt := literal + ext localRelPathWithExt := localRelPath + ext if _, err := fs.Stat(t.b.SyncRoot, localRelPathWithExt); err == nil { return "", fmt.Errorf(`notebook %q not found. Did you mean %q? Local notebook references are expected to contain one of the following -file extensions: [%s]`, literal, literalWithExt, strings.Join(extensions, ", ")) +file extensions: [%s]`, literal, literalWithExt, strings.Join(notebook.Extensions, ", ")) } } // Return a generic error message if no matching possible file is found. return "", fmt.Errorf(`notebook %q not found. Local notebook references are expected -to contain one of the following file extensions: [%s]`, literal, strings.Join(extensions, ", ")) +to contain one of the following file extensions: [%s]`, literal, strings.Join(notebook.Extensions, ", ")) } if err != nil { return "", fmt.Errorf("unable to determine if %s is a notebook: %w", localFullPath, err) diff --git a/bundle/configsync/diff.go b/bundle/configsync/diff.go index c02d2e7127..d71301cac9 100644 --- a/bundle/configsync/diff.go +++ b/bundle/configsync/diff.go @@ -16,6 +16,7 @@ import ( "github.com/databricks/cli/bundle/deployplan" "github.com/databricks/cli/bundle/direct" "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/notebook" "github.com/databricks/cli/libs/dyn/convert" "github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/structs/structpath" @@ -86,7 +87,7 @@ func filterEntityDefaults(basePath string, value any) any { return result } -func convertChangeDesc(path string, cd *deployplan.ChangeDesc, syncRootPath string) (*ConfigChangeDesc, error) { +func convertChangeDesc(path string, cd *deployplan.ChangeDesc, syncRootPath string, syncRoot fs.FS) (*ConfigChangeDesc, error) { hasConfigValue := cd.Old != nil || cd.New != nil normalizedValue, err := normalizeValue(cd.Remote) if err != nil { @@ -117,7 +118,7 @@ func convertChangeDesc(path string, cd *deployplan.ChangeDesc, syncRootPath stri } if op == OperationAdd && syncRootPath != "" { - normalizedValue = translateWorkspacePaths(normalizedValue, syncRootPath) + normalizedValue = translateWorkspacePaths(normalizedValue, syncRootPath, syncRoot) } return &ConfigChangeDesc{ @@ -128,22 +129,24 @@ func convertChangeDesc(path string, cd *deployplan.ChangeDesc, syncRootPath stri // translateWorkspacePaths recursively converts absolute workspace paths to relative // paths when they fall within the bundle's sync root. Paths outside the sync -// root are left unchanged. -func translateWorkspacePaths(value any, syncRootPath string) any { +// root are left unchanged. For notebook paths where the extension was stripped +// by translate_paths, it restores the extension by checking the local filesystem. +func translateWorkspacePaths(value any, syncRootPath string, syncRoot fs.FS) any { switch v := value.(type) { case string: - if after, ok := strings.CutPrefix(v, syncRootPath+"/"); ok { - return "./" + after + after, ok := strings.CutPrefix(v, syncRootPath+"/") + if !ok { + return v } - return v + return "./" + resolveNotebookExtension(syncRoot, after) case map[string]any: for key, val := range v { - v[key] = translateWorkspacePaths(val, syncRootPath) + v[key] = translateWorkspacePaths(val, syncRootPath, syncRoot) } return v case []any: for i, val := range v { - v[i] = translateWorkspacePaths(val, syncRootPath) + v[i] = translateWorkspacePaths(val, syncRootPath, syncRoot) } return v default: @@ -151,6 +154,24 @@ func translateWorkspacePaths(value any, syncRootPath string) any { } } +// resolveNotebookExtension checks if a relative path refers to a notebook whose +// extension was stripped by translate_paths. If the file doesn't exist as-is but +// exists with a notebook extension, the extension is appended. +func resolveNotebookExtension(syncRoot fs.FS, relPath string) string { + if syncRoot == nil { + return relPath + } + if _, err := fs.Stat(syncRoot, relPath); err == nil { + return relPath + } + for _, ext := range notebook.Extensions { + if _, err := fs.Stat(syncRoot, relPath+ext); err == nil { + return relPath + ext + } + } + return relPath +} + // DetectChanges compares current remote state with the last deployed state // and returns a map of resource changes. func DetectChanges(ctx context.Context, b *bundle.Bundle, engine engine.EngineType) (Changes, error) { @@ -185,7 +206,7 @@ func DetectChanges(ctx context.Context, b *bundle.Bundle, engine engine.EngineTy continue } - change, err := convertChangeDesc(path, changeDesc, b.SyncRootPath) + change, err := convertChangeDesc(path, changeDesc, b.SyncRootPath, b.SyncRoot) if err != nil { return nil, fmt.Errorf("failed to compute config change for path %s: %w", path, err) } diff --git a/libs/notebook/ext.go b/libs/notebook/ext.go index c34ad2cc9e..bbffe23c2d 100644 --- a/libs/notebook/ext.go +++ b/libs/notebook/ext.go @@ -11,6 +11,15 @@ const ( ExtensionJupyter string = ".ipynb" ) +// Extensions lists all notebook file extensions. +var Extensions = []string{ + ExtensionPython, + ExtensionR, + ExtensionScala, + ExtensionSql, + ExtensionJupyter, +} + var ExtensionToLanguage = map[string]workspace.Language{ ExtensionPython: workspace.LanguagePython, ExtensionR: workspace.LanguageR, From c4aab482aa8fefa76400e3fa7de9061093b57693 Mon Sep 17 00:00:00 2001 From: Ilya Kuznetsov Date: Thu, 12 Feb 2026 13:58:02 +0100 Subject: [PATCH 6/9] Fix relative paths and extensions --- .../multiple_files/output.txt | 11 ++- .../config-remote-sync/multiple_files/script | 15 +++++ .../multiple_files/synced_notebook.py | 1 + .../multiple_files/test.toml | 2 +- bundle/configsync/diff.go | 55 +-------------- bundle/configsync/resolve.go | 67 +++++++++++++++++++ 6 files changed, 96 insertions(+), 55 deletions(-) create mode 100644 acceptance/bundle/config-remote-sync/multiple_files/synced_notebook.py diff --git a/acceptance/bundle/config-remote-sync/multiple_files/output.txt b/acceptance/bundle/config-remote-sync/multiple_files/output.txt index 959d8e9066..d3bda6eadf 100644 --- a/acceptance/bundle/config-remote-sync/multiple_files/output.txt +++ b/acceptance/bundle/config-remote-sync/multiple_files/output.txt @@ -4,6 +4,7 @@ Updating deployment state... Deployment complete! === Modify job_one: max_concurrent_runs, rename c_task +=== Add synced_task to job_one (notebook in sync root, resource in subdirectory) === Modify job_two: max_concurrent_runs, rename first task, add extra_task === Add extra_task to local config (not in saved state, triggers entity-level replace) === Detect and save changes @@ -14,6 +15,7 @@ Resource: resources.jobs.job_one tasks[task_key='a_task'].depends_on[0].task_key: replace tasks[task_key='c_task']: remove tasks[task_key='c_task_renamed']: add + tasks[task_key='synced_task']: add Resource: resources.jobs.job_two max_concurrent_runs: replace @@ -50,11 +52,18 @@ Resource: resources.jobs.job_two + task_key: c_task_renamed - task_key: a_task notebook_task: -@@ -21,3 +21,3 @@ +@@ -21,3 +21,10 @@ num_workers: 1 depends_on: - - task_key: c_task + - task_key: c_task_renamed ++ - new_cluster: ++ node_type_id: [NODE_TYPE_ID] ++ num_workers: 1 ++ spark_version: 13.3.x-snapshot-scala2.12 ++ notebook_task: ++ notebook_path: ../synced_notebook.py ++ task_key: synced_task === Changes in job2.yml diff --git a/acceptance/bundle/config-remote-sync/multiple_files/script b/acceptance/bundle/config-remote-sync/multiple_files/script index d66bb7bbbf..29c33f8f43 100755 --- a/acceptance/bundle/config-remote-sync/multiple_files/script +++ b/acceptance/bundle/config-remote-sync/multiple_files/script @@ -27,6 +27,21 @@ for task in r["tasks"]: dep["task_key"] = "c_task_renamed" EOF +title "Add synced_task to job_one (notebook in sync root, resource in subdirectory)" +edit_resource.py jobs $job_one_id < Date: Thu, 12 Feb 2026 14:24:14 +0100 Subject: [PATCH 7/9] Use different extension for variety --- .../multiple_files/output.txt | 2 +- .../multiple_files/sample_exploration.ipynb | 63 +++++++++++++++++++ .../config-remote-sync/multiple_files/script | 2 +- .../multiple_files/synced_notebook.py | 1 - 4 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 acceptance/bundle/config-remote-sync/multiple_files/sample_exploration.ipynb delete mode 100644 acceptance/bundle/config-remote-sync/multiple_files/synced_notebook.py diff --git a/acceptance/bundle/config-remote-sync/multiple_files/output.txt b/acceptance/bundle/config-remote-sync/multiple_files/output.txt index d3bda6eadf..e616ca008c 100644 --- a/acceptance/bundle/config-remote-sync/multiple_files/output.txt +++ b/acceptance/bundle/config-remote-sync/multiple_files/output.txt @@ -62,7 +62,7 @@ Resource: resources.jobs.job_two + num_workers: 1 + spark_version: 13.3.x-snapshot-scala2.12 + notebook_task: -+ notebook_path: ../synced_notebook.py ++ notebook_path: ../sample_exploration.ipynb + task_key: synced_task === Changes in job2.yml diff --git a/acceptance/bundle/config-remote-sync/multiple_files/sample_exploration.ipynb b/acceptance/bundle/config-remote-sync/multiple_files/sample_exploration.ipynb new file mode 100644 index 0000000000..b83ee2a9ab --- /dev/null +++ b/acceptance/bundle/config-remote-sync/multiple_files/sample_exploration.ipynb @@ -0,0 +1,63 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": {}, + "inputWidgets": {}, + "nuid": "[UUID]", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "### Example Exploratory Notebook\n", + "\n", + "Use this notebook to explore the data generated by the pipeline in your preferred programming language.\n", + "\n", + "**Note**: This notebook is not executed as part of the pipeline." + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": {}, + "inputWidgets": {}, + "nuid": "[UUID]", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "# !!! Before performing any data analysis, make sure to run the pipeline to materialize the sample datasets. The tables referenced in this notebook depend on that step.\n", + "\n", + "display(spark.sql(\"SELECT * FROM hive_metastore.[USERNAME].sample_trips_lakeflow_project\"))" + ] + } + ], + "metadata": { + "application/vnd.databricks.v1+notebook": { + "computePreferences": null, + "dashboards": [], + "environmentMetadata": null, + "inputWidgetPreferences": null, + "language": "python", + "notebookMetadata": { + "pythonIndentUnit": 2 + }, + "notebookName": "sample_exploration", + "widgets": {} + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/acceptance/bundle/config-remote-sync/multiple_files/script b/acceptance/bundle/config-remote-sync/multiple_files/script index 29c33f8f43..4316a3f1cc 100755 --- a/acceptance/bundle/config-remote-sync/multiple_files/script +++ b/acceptance/bundle/config-remote-sync/multiple_files/script @@ -32,7 +32,7 @@ edit_resource.py jobs $job_one_id < Date: Thu, 12 Feb 2026 14:44:24 +0100 Subject: [PATCH 8/9] Add test for directories --- acceptance/bundle/config-remote-sync/multiple_files/test.toml | 2 +- .../bundle/config-remote-sync/pipeline_fields/output.txt | 4 +++- .../config-remote-sync/pipeline_fields/pipeline_root/.gitkeep | 0 acceptance/bundle/config-remote-sync/pipeline_fields/script | 1 + .../bundle/config-remote-sync/pipeline_fields/test.toml | 2 +- 5 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 acceptance/bundle/config-remote-sync/pipeline_fields/pipeline_root/.gitkeep diff --git a/acceptance/bundle/config-remote-sync/multiple_files/test.toml b/acceptance/bundle/config-remote-sync/multiple_files/test.toml index dcf3e5d83e..b8b496456d 100644 --- a/acceptance/bundle/config-remote-sync/multiple_files/test.toml +++ b/acceptance/bundle/config-remote-sync/multiple_files/test.toml @@ -1,7 +1,7 @@ Cloud = true RecordRequests = false -Ignore = [".databricks", "dummy.whl", "databricks.yml", "resources/job1.yml", "resources/job2.yml", "synced_notebook.py"] +Ignore = [".databricks", "dummy.whl", "databricks.yml", "resources/job1.yml", "resources/job2.yml", "sample_exploration.ipynb"] [Env] DATABRICKS_BUNDLE_ENABLE_EXPERIMENTAL_YAML_SYNC = "true" diff --git a/acceptance/bundle/config-remote-sync/pipeline_fields/output.txt b/acceptance/bundle/config-remote-sync/pipeline_fields/output.txt index d536597186..09d5f382e7 100644 --- a/acceptance/bundle/config-remote-sync/pipeline_fields/output.txt +++ b/acceptance/bundle/config-remote-sync/pipeline_fields/output.txt @@ -12,6 +12,7 @@ Resource: resources.pipelines.my_pipeline environment.dependencies: replace notifications[0].alerts: replace notifications[0].email_recipients: replace + root_path: add schema: replace tags['foo']: add @@ -28,7 +29,7 @@ Resource: resources.pipelines.my_pipeline - resources: pipelines: -@@ -7,19 +6,24 @@ +@@ -7,19 +6,25 @@ name: test-pipeline-[UNIQUE_NAME] catalog: main - schema: default @@ -53,6 +54,7 @@ Resource: resources.pipelines.my_pipeline - + tags: + foo: bar ++ root_path: ./pipeline_root targets: default: diff --git a/acceptance/bundle/config-remote-sync/pipeline_fields/pipeline_root/.gitkeep b/acceptance/bundle/config-remote-sync/pipeline_fields/pipeline_root/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/acceptance/bundle/config-remote-sync/pipeline_fields/script b/acceptance/bundle/config-remote-sync/pipeline_fields/script index 69e2c87b7c..a83b940966 100755 --- a/acceptance/bundle/config-remote-sync/pipeline_fields/script +++ b/acceptance/bundle/config-remote-sync/pipeline_fields/script @@ -17,6 +17,7 @@ r["configuration"]["key2"] = "value2" r["notifications"][0]["email_recipients"].append("admin@example.com") r["notifications"][0]["alerts"].append("on-update-failure") r["schema"] = "prod" +r["root_path"] = "${PWD}/pipeline_root" if "tags" not in r: r["tags"] = {} r["tags"]["foo"] = "bar" diff --git a/acceptance/bundle/config-remote-sync/pipeline_fields/test.toml b/acceptance/bundle/config-remote-sync/pipeline_fields/test.toml index 3174477f7d..f307000e06 100644 --- a/acceptance/bundle/config-remote-sync/pipeline_fields/test.toml +++ b/acceptance/bundle/config-remote-sync/pipeline_fields/test.toml @@ -2,7 +2,7 @@ Cloud = true RequiresUnityCatalog = true RecordRequests = false -Ignore = [".databricks", "databricks.yml", "databricks.yml.backup"] +Ignore = [".databricks", "databricks.yml", "databricks.yml.backup", "pipeline_root"] [Env] DATABRICKS_BUNDLE_ENABLE_EXPERIMENTAL_YAML_SYNC = "true" From 4440e4a0153ca63634ef77142578b2c07eaa4507 Mon Sep 17 00:00:00 2001 From: Ilya Kuznetsov Date: Thu, 12 Feb 2026 15:50:18 +0100 Subject: [PATCH 9/9] Replace support --- .../config-remote-sync/job_multiple_tasks/output.txt | 11 +++++++++-- .../config-remote-sync/job_multiple_tasks/script | 7 ++++++- bundle/configsync/resolve.go | 2 +- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/acceptance/bundle/config-remote-sync/job_multiple_tasks/output.txt b/acceptance/bundle/config-remote-sync/job_multiple_tasks/output.txt index 697cf22fc8..e1f51e2833 100644 --- a/acceptance/bundle/config-remote-sync/job_multiple_tasks/output.txt +++ b/acceptance/bundle/config-remote-sync/job_multiple_tasks/output.txt @@ -69,11 +69,12 @@ Deploying resources... Updating deployment state... Deployment complete! -=== Rename b_task to b_task_renamed (4 tasks, 2 with depends_on, 1 without) +=== Rename b_task, replace a_task notebook_path, add synced_task === Detect task key rename Detected changes in 1 resource(s): Resource: resources.jobs.rename_task_job + tasks[task_key='a_task'].notebook_task.notebook_path: replace tasks[task_key='b_task']: remove tasks[task_key='b_task_renamed']: add tasks[task_key='c_task'].depends_on[0].task_key: replace @@ -115,7 +116,13 @@ Resource: resources.jobs.rename_task_job + - task_key: b_task_renamed notebook_task: notebook_path: /Users/{{workspace_user_name}}/c_task -@@ -72,2 +72,9 @@ +@@ -67,7 +67,14 @@ + - task_key: a_task + notebook_task: +- notebook_path: /Users/{{workspace_user_name}}/a_task ++ notebook_path: ./synced_notebook.py + new_cluster: + spark_version: 13.3.x-snapshot-scala2.12 node_type_id: [NODE_TYPE_ID] num_workers: 1 + - new_cluster: diff --git a/acceptance/bundle/config-remote-sync/job_multiple_tasks/script b/acceptance/bundle/config-remote-sync/job_multiple_tasks/script index e73330694b..10103acb18 100755 --- a/acceptance/bundle/config-remote-sync/job_multiple_tasks/script +++ b/acceptance/bundle/config-remote-sync/job_multiple_tasks/script @@ -52,7 +52,7 @@ mv databricks.yml.resolved databricks.yml # Deploy the updated configuration to sync state $CLI bundle deploy -title "Rename b_task to b_task_renamed (4 tasks, 2 with depends_on, 1 without)" +title "Rename b_task, replace a_task notebook_path, add synced_task" rename_job_id="$(read_id.py rename_task_job)" edit_resource.py jobs $rename_job_id <