From 7b2a42862911fd4d025c80d5f7e3e0040e35b1bc Mon Sep 17 00:00:00 2001 From: Jan Rose Date: Fri, 10 Jul 2026 11:35:09 +0200 Subject: [PATCH 1/4] Re-apply #5799: Download spark_python_task workspace files in bundle generate job This re-applies commit b2e7e6faf (PR #5799), which was reverted in #5837 (commit 353cb5a16). `bundle generate job` again downloads workspace files referenced by `spark_python_task` and rewrites them to a relative path, like it already does for notebooks. Git-sourced files and cloud URIs are left untouched. The behaviour is gated behind a feature flag in a follow-up commit. --- NEXT_CHANGELOG.md | 3 + .../spark_python_task_job/databricks.yml | 2 + .../spark_python_task_job/out.job.yml | 11 ++++ .../spark_python_task_job/out.test.toml | 3 + .../generate/spark_python_task_job/outjob.py | 1 + .../generate/spark_python_task_job/output.txt | 2 + .../generate/spark_python_task_job/script | 1 + .../generate/spark_python_task_job/test.toml | 41 ++++++++++++ bundle/generate/downloader.go | 22 ++++++- bundle/generate/downloader_test.go | 66 +++++++++++++++++++ 10 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 acceptance/bundle/generate/spark_python_task_job/databricks.yml create mode 100644 acceptance/bundle/generate/spark_python_task_job/out.job.yml create mode 100644 acceptance/bundle/generate/spark_python_task_job/out.test.toml create mode 100644 acceptance/bundle/generate/spark_python_task_job/outjob.py create mode 100644 acceptance/bundle/generate/spark_python_task_job/output.txt create mode 100644 acceptance/bundle/generate/spark_python_task_job/script create mode 100644 acceptance/bundle/generate/spark_python_task_job/test.toml diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index 0f71834ff56..db66f69f03b 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -8,6 +8,9 @@ ### Bundles +* `bundle generate job` now downloads workspace files referenced by `spark_python_task`, rewriting them to a relative path like it already does for notebooks. Git-sourced files and cloud URIs are left untouched ([#5799](https://github.com/databricks/cli/pull/5799)). +* Fix permissions added to a job or pipeline by a Python (PyDABs) mutator failing to deploy with "must have exactly one owner"; the deploying identity is now set as owner, matching resources whose permissions are declared in YAML ([#5821](https://github.com/databricks/cli/pull/5821)). + ### Dependency updates ### API Changes diff --git a/acceptance/bundle/generate/spark_python_task_job/databricks.yml b/acceptance/bundle/generate/spark_python_task_job/databricks.yml new file mode 100644 index 00000000000..7419eb67c8a --- /dev/null +++ b/acceptance/bundle/generate/spark_python_task_job/databricks.yml @@ -0,0 +1,2 @@ +bundle: + name: spark_python_task_job diff --git a/acceptance/bundle/generate/spark_python_task_job/out.job.yml b/acceptance/bundle/generate/spark_python_task_job/out.job.yml new file mode 100644 index 00000000000..091ed6b6726 --- /dev/null +++ b/acceptance/bundle/generate/spark_python_task_job/out.job.yml @@ -0,0 +1,11 @@ +resources: + jobs: + out: + name: sparkpythonjob + tasks: + - task_key: workspace_file_task + spark_python_task: + python_file: outjob.py + - task_key: cloud_uri_task + spark_python_task: + python_file: dbfs:/FileStore/job.py diff --git a/acceptance/bundle/generate/spark_python_task_job/out.test.toml b/acceptance/bundle/generate/spark_python_task_job/out.test.toml new file mode 100644 index 00000000000..f784a183258 --- /dev/null +++ b/acceptance/bundle/generate/spark_python_task_job/out.test.toml @@ -0,0 +1,3 @@ +Local = true +Cloud = false +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct"] diff --git a/acceptance/bundle/generate/spark_python_task_job/outjob.py b/acceptance/bundle/generate/spark_python_task_job/outjob.py new file mode 100644 index 00000000000..7df869a15e7 --- /dev/null +++ b/acceptance/bundle/generate/spark_python_task_job/outjob.py @@ -0,0 +1 @@ +print("Hello, World!") diff --git a/acceptance/bundle/generate/spark_python_task_job/output.txt b/acceptance/bundle/generate/spark_python_task_job/output.txt new file mode 100644 index 00000000000..e160a45625f --- /dev/null +++ b/acceptance/bundle/generate/spark_python_task_job/output.txt @@ -0,0 +1,2 @@ +File successfully saved to outjob.py +Job configuration successfully saved to out.job.yml diff --git a/acceptance/bundle/generate/spark_python_task_job/script b/acceptance/bundle/generate/spark_python_task_job/script new file mode 100644 index 00000000000..16cfc1589ce --- /dev/null +++ b/acceptance/bundle/generate/spark_python_task_job/script @@ -0,0 +1 @@ +$CLI bundle generate job --existing-job-id 1234 --config-dir . --key out --force --source-dir . diff --git a/acceptance/bundle/generate/spark_python_task_job/test.toml b/acceptance/bundle/generate/spark_python_task_job/test.toml new file mode 100644 index 00000000000..6ee12cc669b --- /dev/null +++ b/acceptance/bundle/generate/spark_python_task_job/test.toml @@ -0,0 +1,41 @@ +[[Server]] +Pattern = "GET /api/2.2/jobs/get" +Response.Body = ''' +{ + "job_id": 11223344, + "settings": { + "name": "sparkpythonjob", + "tasks": [ + { + "task_key": "workspace_file_task", + "spark_python_task": { + "python_file": "/Workspace/Users/tester@databricks.com/outjob.py" + } + }, + { + "task_key": "cloud_uri_task", + "spark_python_task": { + "python_file": "dbfs:/FileStore/job.py" + } + } + ] + } +} +''' + +[[Server]] +Pattern = "GET /api/2.0/workspace/get-status" +Response.Body = ''' +{ + "path": "/Workspace/Users/tester@databricks.com/outjob.py", + "object_type": "FILE", + "language": "PYTHON", + "repos_export_format": "SOURCE" +} +''' + +[[Server]] +Pattern = "GET /api/2.0/workspace/export" +Response.Body = ''' +print("Hello, World!") +''' diff --git a/bundle/generate/downloader.go b/bundle/generate/downloader.go index 87aa4ef5751..2a03cda0420 100644 --- a/bundle/generate/downloader.go +++ b/bundle/generate/downloader.go @@ -37,11 +37,24 @@ type Downloader struct { } func (n *Downloader) MarkTaskForDownload(ctx context.Context, task *jobs.Task) error { - if task.NotebookTask == nil { - return nil + if task.NotebookTask != nil { + return n.markNotebookForDownload(ctx, &task.NotebookTask.NotebookPath) } - return n.markNotebookForDownload(ctx, &task.NotebookTask.NotebookPath) + if task.SparkPythonTask != nil && isWorkspaceFileTask(task.SparkPythonTask.Source, task.SparkPythonTask.PythonFile) { + return n.markFileForDownload(ctx, &task.SparkPythonTask.PythonFile) + } + + return nil +} + +// isWorkspaceFileTask reports whether a task file lives in the Databricks +// workspace and should be downloaded. Files sourced from Git (source: GIT) are +// left to the Git repository, and python_file also accepts cloud URIs +// (dbfs:/, s3:/, adls:/, gcs:/) which are not workspace paths; per the Jobs API, +// workspace files are absolute and begin with "/". +func isWorkspaceFileTask(source jobs.Source, filePath string) bool { + return source != jobs.SourceGit && strings.HasPrefix(filePath, "/") } func (n *Downloader) MarkPipelineLibraryForDownload(ctx context.Context, lib *pipelines.PipelineLibrary) error { @@ -218,6 +231,9 @@ func (n *Downloader) MarkTasksForDownload(ctx context.Context, tasks []jobs.Task if task.NotebookTask != nil { paths = append(paths, task.NotebookTask.NotebookPath) } + if task.SparkPythonTask != nil && isWorkspaceFileTask(task.SparkPythonTask.Source, task.SparkPythonTask.PythonFile) { + paths = append(paths, task.SparkPythonTask.PythonFile) + } } if len(paths) > 0 { n.basePath = commonDirPrefix(paths) diff --git a/bundle/generate/downloader_test.go b/bundle/generate/downloader_test.go index ed8c9cbf045..2bb887cb8e2 100644 --- a/bundle/generate/downloader_test.go +++ b/bundle/generate/downloader_test.go @@ -295,6 +295,72 @@ func TestDownloader_MarkTasksForDownload_SingleNotebook(t *testing.T) { assert.Len(t, downloader.files, 1) } +func TestDownloader_MarkTasksForDownload_SparkPythonFile(t *testing.T) { + ctx := t.Context() + m := mocks.NewMockWorkspaceClient(t) + + dir := "base/dir" + sourceDir := filepath.Join(dir, "source") + configDir := filepath.Join(dir, "config") + downloader := NewDownloader(m.WorkspaceClient, sourceDir, configDir) + + pythonFile := "/Users/user/project/etl/job.py" + m.GetMockWorkspaceAPI().EXPECT().GetStatusByPath(ctx, pythonFile).Return(&workspace.ObjectInfo{ + Path: pythonFile, + }, nil) + + tasks := []jobs.Task{ + { + TaskKey: "spark_python_task", + SparkPythonTask: &jobs.SparkPythonTask{ + PythonFile: pythonFile, + }, + }, + } + + err := downloader.MarkTasksForDownload(ctx, tasks) + require.NoError(t, err) + + assert.Equal(t, "../source/job.py", tasks[0].SparkPythonTask.PythonFile) + require.Len(t, downloader.files, 1) + f := downloader.files[filepath.Join(sourceDir, "job.py")] + assert.Equal(t, pythonFile, f.path) + assert.Equal(t, workspace.ExportFormatSource, f.format) +} + +func TestDownloader_MarkTasksForDownload_SparkPythonFileSkipped(t *testing.T) { + ctx := t.Context() + // Cloud URIs and Git-sourced files are not workspace paths, so no + // get-status/download requests should be made for them. + w := newTestWorkspaceClient(t, func(w http.ResponseWriter, r *http.Request) { + t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path) + }) + + downloader := NewDownloader(w, "source", "config") + + tasks := []jobs.Task{ + { + TaskKey: "cloud_uri_task", + SparkPythonTask: &jobs.SparkPythonTask{ + PythonFile: "dbfs:/FileStore/job.py", + }, + }, + { + TaskKey: "git_task", + SparkPythonTask: &jobs.SparkPythonTask{ + PythonFile: "etl/job.py", + Source: jobs.SourceGit, + }, + }, + } + + err := downloader.MarkTasksForDownload(ctx, tasks) + require.NoError(t, err) + assert.Empty(t, downloader.files) + assert.Equal(t, "dbfs:/FileStore/job.py", tasks[0].SparkPythonTask.PythonFile) + assert.Equal(t, "etl/job.py", tasks[1].SparkPythonTask.PythonFile) +} + func TestDownloader_MarkTasksForDownload_NoNotebooks(t *testing.T) { ctx := t.Context() w := newTestWorkspaceClient(t, func(w http.ResponseWriter, r *http.Request) { From 714e8170783436cb5ac2975893153a4b5432e4e5 Mon Sep 17 00:00:00 2001 From: Jan Rose Date: Fri, 10 Jul 2026 11:42:43 +0200 Subject: [PATCH 2/4] Gate spark_python_task download behind --download-spark-python-files flag Downloading workspace files referenced by a spark_python_task is now opt-in via a new --download-spark-python-files flag on bundle generate job (default off). A Python file often imports sibling files that the downloader does not capture, so downloading only the entry point can produce a job that fails at runtime with missing imports; this was the reason #5799 was originally reverted in #5837. The behaviour is threaded through the Downloader as a functional option (WithSparkPythonFiles), so the pipeline, app, and import callers are unaffected. --- NEXT_CHANGELOG.md | 2 +- .../generate/spark_python_task_job/script | 2 +- .../help/bundle-generate-job/output.txt | 11 +++---- bundle/generate/downloader.go | 29 +++++++++++++++--- bundle/generate/downloader_test.go | 30 +++++++++++++++++-- cmd/bundle/generate/job.go | 8 ++++- 6 files changed, 68 insertions(+), 14 deletions(-) diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index db66f69f03b..e117b4d3048 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -8,7 +8,7 @@ ### Bundles -* `bundle generate job` now downloads workspace files referenced by `spark_python_task`, rewriting them to a relative path like it already does for notebooks. Git-sourced files and cloud URIs are left untouched ([#5799](https://github.com/databricks/cli/pull/5799)). +* `bundle generate job` can now download workspace files referenced by `spark_python_task`, rewriting them to a relative path like it already does for notebooks. This is opt-in via the `--download-spark-python-files` flag, because a Python file often imports sibling files that are not captured; Git-sourced files and cloud URIs are left untouched ([#5799](https://github.com/databricks/cli/pull/5799)). * Fix permissions added to a job or pipeline by a Python (PyDABs) mutator failing to deploy with "must have exactly one owner"; the deploying identity is now set as owner, matching resources whose permissions are declared in YAML ([#5821](https://github.com/databricks/cli/pull/5821)). ### Dependency updates diff --git a/acceptance/bundle/generate/spark_python_task_job/script b/acceptance/bundle/generate/spark_python_task_job/script index 16cfc1589ce..a1131f4c5dd 100644 --- a/acceptance/bundle/generate/spark_python_task_job/script +++ b/acceptance/bundle/generate/spark_python_task_job/script @@ -1 +1 @@ -$CLI bundle generate job --existing-job-id 1234 --config-dir . --key out --force --source-dir . +$CLI bundle generate job --existing-job-id 1234 --config-dir . --key out --force --source-dir . --download-spark-python-files diff --git a/acceptance/bundle/help/bundle-generate-job/output.txt b/acceptance/bundle/help/bundle-generate-job/output.txt index f239691ab6a..8334bd81472 100644 --- a/acceptance/bundle/help/bundle-generate-job/output.txt +++ b/acceptance/bundle/help/bundle-generate-job/output.txt @@ -28,11 +28,12 @@ Usage: databricks bundle generate job [flags] Flags: - -d, --config-dir string Dir path where the output config will be stored (default "resources") - --existing-job-id int Job ID of the job to generate config for - -f, --force Force overwrite existing files in the output directory - -h, --help help for job - -s, --source-dir string Dir path where the downloaded files will be stored (default "src") + -d, --config-dir string Dir path where the output config will be stored (default "resources") + --download-spark-python-files download workspace files referenced by spark_python_task and rewrite them to a relative path + --existing-job-id int Job ID of the job to generate config for + -f, --force Force overwrite existing files in the output directory + -h, --help help for job + -s, --source-dir string Dir path where the downloaded files will be stored (default "src") Global Flags: --debug enable debug logging diff --git a/bundle/generate/downloader.go b/bundle/generate/downloader.go index 2a03cda0420..334175496aa 100644 --- a/bundle/generate/downloader.go +++ b/bundle/generate/downloader.go @@ -34,6 +34,23 @@ type Downloader struct { sourceDir string configDir string basePath string + + // downloadSparkPythonFiles controls whether workspace files referenced by a + // spark_python_task are downloaded. It is opt-in because a Python file often + // imports sibling files that are not captured, so downloading only the entry + // point can produce a job that fails at runtime with missing imports. + downloadSparkPythonFiles bool +} + +// DownloaderOption configures a Downloader. +type DownloaderOption func(*Downloader) + +// WithSparkPythonFiles enables downloading workspace files referenced by a +// spark_python_task. +func WithSparkPythonFiles() DownloaderOption { + return func(n *Downloader) { + n.downloadSparkPythonFiles = true + } } func (n *Downloader) MarkTaskForDownload(ctx context.Context, task *jobs.Task) error { @@ -41,7 +58,7 @@ func (n *Downloader) MarkTaskForDownload(ctx context.Context, task *jobs.Task) e return n.markNotebookForDownload(ctx, &task.NotebookTask.NotebookPath) } - if task.SparkPythonTask != nil && isWorkspaceFileTask(task.SparkPythonTask.Source, task.SparkPythonTask.PythonFile) { + if n.downloadSparkPythonFiles && task.SparkPythonTask != nil && isWorkspaceFileTask(task.SparkPythonTask.Source, task.SparkPythonTask.PythonFile) { return n.markFileForDownload(ctx, &task.SparkPythonTask.PythonFile) } @@ -231,7 +248,7 @@ func (n *Downloader) MarkTasksForDownload(ctx context.Context, tasks []jobs.Task if task.NotebookTask != nil { paths = append(paths, task.NotebookTask.NotebookPath) } - if task.SparkPythonTask != nil && isWorkspaceFileTask(task.SparkPythonTask.Source, task.SparkPythonTask.PythonFile) { + if n.downloadSparkPythonFiles && task.SparkPythonTask != nil && isWorkspaceFileTask(task.SparkPythonTask.Source, task.SparkPythonTask.PythonFile) { paths = append(paths, task.SparkPythonTask.PythonFile) } } @@ -366,11 +383,15 @@ func writeAndClose(dst io.WriteCloser, src io.Reader) error { return err } -func NewDownloader(w *databricks.WorkspaceClient, sourceDir, configDir string) *Downloader { - return &Downloader{ +func NewDownloader(w *databricks.WorkspaceClient, sourceDir, configDir string, opts ...DownloaderOption) *Downloader { + n := &Downloader{ files: make(map[string]exportFile), w: w, sourceDir: sourceDir, configDir: configDir, } + for _, opt := range opts { + opt(n) + } + return n } diff --git a/bundle/generate/downloader_test.go b/bundle/generate/downloader_test.go index 2bb887cb8e2..d3d6bca8aca 100644 --- a/bundle/generate/downloader_test.go +++ b/bundle/generate/downloader_test.go @@ -302,7 +302,7 @@ func TestDownloader_MarkTasksForDownload_SparkPythonFile(t *testing.T) { dir := "base/dir" sourceDir := filepath.Join(dir, "source") configDir := filepath.Join(dir, "config") - downloader := NewDownloader(m.WorkspaceClient, sourceDir, configDir) + downloader := NewDownloader(m.WorkspaceClient, sourceDir, configDir, WithSparkPythonFiles()) pythonFile := "/Users/user/project/etl/job.py" m.GetMockWorkspaceAPI().EXPECT().GetStatusByPath(ctx, pythonFile).Return(&workspace.ObjectInfo{ @@ -336,7 +336,7 @@ func TestDownloader_MarkTasksForDownload_SparkPythonFileSkipped(t *testing.T) { t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path) }) - downloader := NewDownloader(w, "source", "config") + downloader := NewDownloader(w, "source", "config", WithSparkPythonFiles()) tasks := []jobs.Task{ { @@ -361,6 +361,32 @@ func TestDownloader_MarkTasksForDownload_SparkPythonFileSkipped(t *testing.T) { assert.Equal(t, "etl/job.py", tasks[1].SparkPythonTask.PythonFile) } +func TestDownloader_MarkTasksForDownload_SparkPythonFileDisabledByDefault(t *testing.T) { + ctx := t.Context() + // Without WithSparkPythonFiles, workspace files referenced by a + // spark_python_task are left untouched, so no requests are made. + w := newTestWorkspaceClient(t, func(w http.ResponseWriter, r *http.Request) { + t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path) + }) + + downloader := NewDownloader(w, "source", "config") + + pythonFile := "/Users/user/project/etl/job.py" + tasks := []jobs.Task{ + { + TaskKey: "spark_python_task", + SparkPythonTask: &jobs.SparkPythonTask{ + PythonFile: pythonFile, + }, + }, + } + + err := downloader.MarkTasksForDownload(ctx, tasks) + require.NoError(t, err) + assert.Empty(t, downloader.files) + assert.Equal(t, pythonFile, tasks[0].SparkPythonTask.PythonFile) +} + func TestDownloader_MarkTasksForDownload_NoNotebooks(t *testing.T) { ctx := t.Context() w := newTestWorkspaceClient(t, func(w http.ResponseWriter, r *http.Request) { diff --git a/cmd/bundle/generate/job.go b/cmd/bundle/generate/job.go index c3aba49c5f2..191e1ed917c 100644 --- a/cmd/bundle/generate/job.go +++ b/cmd/bundle/generate/job.go @@ -27,6 +27,7 @@ func NewGenerateJobCommand() *cobra.Command { var jobId int64 var force bool var bind bool + var downloadSparkPythonFiles bool cmd := &cobra.Command{ Use: "job", @@ -64,6 +65,7 @@ After generation, you can deploy this job to other targets using: cmd.Flags().BoolVarP(&force, "force", "f", false, `Force overwrite existing files in the output directory`) cmd.Flags().BoolVarP(&bind, "bind", "b", false, `automatically bind the generated resource to the existing resource`) cmd.Flags().MarkHidden("bind") + cmd.Flags().BoolVar(&downloadSparkPythonFiles, "download-spark-python-files", false, `download workspace files referenced by spark_python_task and rewrite them to a relative path`) cmd.RunE = func(cmd *cobra.Command, args []string) error { ctx := logdiag.InitContext(cmd.Context()) @@ -80,7 +82,11 @@ After generation, you can deploy this job to other targets using: return err } - downloader := generate.NewDownloader(w, sourceDir, configDir) + var opts []generate.DownloaderOption + if downloadSparkPythonFiles { + opts = append(opts, generate.WithSparkPythonFiles()) + } + downloader := generate.NewDownloader(w, sourceDir, configDir, opts...) // Don't download files if the job is using Git source // When Git source is used, the job will be using the files from the Git repository From 1f4d5685107ad547865353ee45c13ffcc2e41a7c Mon Sep 17 00:00:00 2001 From: Jan Rose Date: Sat, 11 Jul 2026 22:27:09 +0200 Subject: [PATCH 3/4] Update help text --- acceptance/bundle/help/bundle-generate-job/output.txt | 3 ++- cmd/bundle/generate/job.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/acceptance/bundle/help/bundle-generate-job/output.txt b/acceptance/bundle/help/bundle-generate-job/output.txt index 8334bd81472..092babc9af9 100644 --- a/acceptance/bundle/help/bundle-generate-job/output.txt +++ b/acceptance/bundle/help/bundle-generate-job/output.txt @@ -18,7 +18,8 @@ Examples: What gets generated: - Job configuration YAML file in the resources directory -- Any associated notebook or Python files in the source directory +- Any associated notebook files in the source directory +- Any associated python spark files in the source directory, if --download-spark-python-files is provided After generation, you can deploy this job to other targets using: databricks bundle deploy --target staging diff --git a/cmd/bundle/generate/job.go b/cmd/bundle/generate/job.go index 191e1ed917c..705d35bcff7 100644 --- a/cmd/bundle/generate/job.go +++ b/cmd/bundle/generate/job.go @@ -50,7 +50,8 @@ Examples: What gets generated: - Job configuration YAML file in the resources directory -- Any associated notebook or Python files in the source directory +- Any associated notebook files in the source directory +- Any associated python spark files in the source directory, if --download-spark-python-files is provided After generation, you can deploy this job to other targets using: databricks bundle deploy --target staging From 28c420100b5f0de0edd7f9e5a2c9ccc94c5f38bb Mon Sep 17 00:00:00 2001 From: Jan Rose Date: Sat, 11 Jul 2026 22:30:09 +0200 Subject: [PATCH 4/4] Use .nextchanges for changelog entry --- .nextchanges/bundles/bundle-generate-job-download-script.md | 1 + NEXT_CHANGELOG.md | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) create mode 100644 .nextchanges/bundles/bundle-generate-job-download-script.md diff --git a/.nextchanges/bundles/bundle-generate-job-download-script.md b/.nextchanges/bundles/bundle-generate-job-download-script.md new file mode 100644 index 00000000000..14c29808030 --- /dev/null +++ b/.nextchanges/bundles/bundle-generate-job-download-script.md @@ -0,0 +1 @@ +* `bundle generate job` can now download workspace files referenced by `spark_python_task`, rewriting them to a relative path like it already does for notebooks. This is opt-in via the `--download-spark-python-files` flag ([#5799](https://github.com/databricks/cli/pull/5799)). diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index d2933493783..ea05bf2b811 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -10,9 +10,6 @@ ### Bundles -* `bundle generate job` can now download workspace files referenced by `spark_python_task`, rewriting them to a relative path like it already does for notebooks. This is opt-in via the `--download-spark-python-files` flag, because a Python file often imports sibling files that are not captured; Git-sourced files and cloud URIs are left untouched ([#5799](https://github.com/databricks/cli/pull/5799)). -* Fix permissions added to a job or pipeline by a Python (PyDABs) mutator failing to deploy with "must have exactly one owner"; the deploying identity is now set as owner, matching resources whose permissions are declared in YAML ([#5821](https://github.com/databricks/cli/pull/5821)). - ### Dependency updates ### API Changes