Skip to content

Commit fe1b364

Browse files
Added a warning for non exportable types in export-dir (#4101)
Fixes: #4081 ## Changes Skip non-exportable workspace objects (like `MLFLOW_EXPERIMENT`) during `workspace export-dir` instead of failing the entire export. Warnings are collected and displayed at the end. ## Why Currently, `export-dir` fails when a directory contains an MLflow experiment because experiments cannot be exported via the workspace export API. This makes it impossible to export any folder containing an experiment. This change skips non-exportable object types (`LIBRARY`, `DASHBOARD`, `REPO`, `MLFLOW_EXPERIMENT`) and shows warnings at the end: ``` Exporting files from /my/folder Warnings: - /my/folder/my-experiment (skipped; cannot export MLFLOW_EXPERIMENT) Export complete ``` **Note:** Ideally, `ObjectTypeMlflowExperiment` should be added to `databricks-sdk-go` as a constant. Currently using a string literal as a workaround. ## Tests - Added acceptance test `cmd/workspace/export-dir-skip-experiments` - Updated `cmd/workspace/export-dir-file-size-limit` output format - Verified manually against a real workspace with an MLflow experiment
1 parent 825a6a5 commit fe1b364

File tree

7 files changed

+72
-20
lines changed

7 files changed

+72
-20
lines changed

NEXT_CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Notable Changes
66

77
### CLI
8+
* Skip non-exportable objects (e.g., `MLFLOW_EXPERIMENT`) during `workspace export-dir` instead of failing ([#4081](https://github.com/databricks/cli/issues/4081))
89

910
### Bundles
1011

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11

22
>>> [CLI] workspace export-dir /test-dir [TEST_TMP_DIR]/export
33
Exporting files from /test-dir
4-
Warning: /test-dir/file.py (skipped; file too large)
5-
6-
The following files were skipped because they exceed the maximum size limit:
7-
- /test-dir/file.py (skipped; file too large)
8-
4+
/test-dir/file.py (skipped; file too large)
95
Export complete

acceptance/cmd/workspace/export-dir-skip-experiments/out.test.toml

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
>>> [CLI] workspace export-dir /test-dir [TEST_TMP_DIR]/export
3+
Exporting files from /test-dir
4+
/test-dir/experiment (skipped; cannot export MLFLOW_EXPERIMENT)
5+
Export complete
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
mkdir -p "$TEST_TMP_DIR/export"
2+
trace $CLI workspace export-dir /test-dir "$TEST_TMP_DIR/export"
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
Local = true
2+
Cloud = false
3+
4+
[Env]
5+
MSYS_NO_PATHCONV = "1"
6+
7+
[[Server]]
8+
Pattern = "GET /api/2.0/workspace/list"
9+
Response.Body = '''
10+
{
11+
"objects": [
12+
{
13+
"path": "/test-dir/experiment",
14+
"object_type": "MLFLOW_EXPERIMENT",
15+
"object_id": 125
16+
}
17+
]
18+
}
19+
'''
20+
21+
[[Server]]
22+
Pattern = "GET /api/2.0/workspace/get-status"
23+
Response.Body = '''
24+
{
25+
"path": "/test-dir",
26+
"object_type": "DIRECTORY",
27+
"object_id": 123
28+
}
29+
'''

cmd/workspace/workspace/export_dir.go

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package workspace
33
import (
44
"context"
55
"errors"
6+
"fmt"
67
"io"
78
"io/fs"
89
"net/http"
@@ -24,7 +25,6 @@ type exportDirOptions struct {
2425
sourceDir string
2526
targetDir string
2627
overwrite bool
27-
warnings []string
2828
}
2929

3030
// isFileSizeError checks if the error is due to file size limits.
@@ -51,6 +51,26 @@ func isFileSizeError(err error) bool {
5151
return false
5252
}
5353

54+
// Object types that cannot be exported via the workspace export API.
55+
// These will be skipped with a warning during export-dir.
56+
var nonExportableTypes = []workspace.ObjectType{
57+
workspace.ObjectTypeLibrary,
58+
workspace.ObjectTypeDashboard,
59+
workspace.ObjectTypeRepo,
60+
// MLFLOW_EXPERIMENT is not defined as a constant in the SDK
61+
workspace.ObjectType("MLFLOW_EXPERIMENT"),
62+
}
63+
64+
// isNonExportable checks if an object type cannot be exported.
65+
func isNonExportable(objectType workspace.ObjectType) bool {
66+
for _, t := range nonExportableTypes {
67+
if objectType == t {
68+
return true
69+
}
70+
}
71+
return false
72+
}
73+
5474
// The callback function exports the file specified at relPath. This function is
5575
// meant to be used in conjunction with fs.WalkDir
5676
func (opts *exportDirOptions) callback(ctx context.Context, workspaceFiler filer.Filer) func(string, fs.DirEntry, error) error {
@@ -77,6 +97,13 @@ func (opts *exportDirOptions) callback(ctx context.Context, workspaceFiler filer
7797
return err
7898
}
7999
objectInfo := info.Sys().(workspace.ObjectInfo)
100+
101+
// Skip non-exportable objects (e.g., MLFLOW_EXPERIMENT, LIBRARY)
102+
if isNonExportable(objectInfo.ObjectType) {
103+
cmdio.LogString(ctx, fmt.Sprintf("%s (skipped; cannot export %s)", sourcePath, objectInfo.ObjectType))
104+
return nil
105+
}
106+
80107
targetPath += notebook.GetExtensionByLanguage(&objectInfo)
81108

82109
// Skip file if a file already exists in path.
@@ -92,9 +119,7 @@ func (opts *exportDirOptions) callback(ctx context.Context, workspaceFiler filer
92119
if err != nil {
93120
// Check if this is a file size limit error
94121
if isFileSizeError(err) {
95-
warning := sourcePath + " (skipped; file too large)"
96-
cmdio.LogString(ctx, "Warning: "+warning)
97-
opts.warnings = append(opts.warnings, warning)
122+
cmdio.LogString(ctx, sourcePath+" (skipped; file too large)")
98123
return nil
99124
}
100125
return err
@@ -140,7 +165,6 @@ func newExportDir() *cobra.Command {
140165
w := cmdctx.WorkspaceClient(ctx)
141166
opts.sourceDir = args[0]
142167
opts.targetDir = args[1]
143-
opts.warnings = []string{}
144168

145169
// Initialize a filer and a file system on the source directory
146170
workspaceFiler, err := filer.NewWorkspaceFilesClient(w, opts.sourceDir)
@@ -159,16 +183,6 @@ func newExportDir() *cobra.Command {
159183
return err
160184
}
161185

162-
// Print all warnings at the end if any were collected
163-
if len(opts.warnings) > 0 {
164-
cmdio.LogString(ctx, "")
165-
cmdio.LogString(ctx, "The following files were skipped because they exceed the maximum size limit:")
166-
for _, warning := range opts.warnings {
167-
cmdio.LogString(ctx, " - "+warning)
168-
}
169-
cmdio.LogString(ctx, "")
170-
}
171-
172186
return cmdio.RenderWithTemplate(ctx, newExportCompletedEvent(opts.targetDir), "", "Export complete\n")
173187
}
174188

0 commit comments

Comments
 (0)