diff --git a/.nextchanges/cli/repos-git-cli-folders.md b/.nextchanges/cli/repos-git-cli-folders.md new file mode 100644 index 00000000000..815f70dcaaf --- /dev/null +++ b/.nextchanges/cli/repos-git-cli-folders.md @@ -0,0 +1 @@ +Fixed `databricks repos get/update/delete` failing with `object at path "..." is not a repo` for Git-CLI-enabled folders, which the workspace API reports as directories rather than repos. Git CLI is a preview feature; as an alternative mitigation it can be turned off in the workspace admin previews settings. diff --git a/acceptance/workspace/repos/get_errors/out.requests.txt b/acceptance/workspace/repos/get_errors/out.requests.txt index 2bfe07b1317..ed3cb2661e7 100644 --- a/acceptance/workspace/repos/get_errors/out.requests.txt +++ b/acceptance/workspace/repos/get_errors/out.requests.txt @@ -9,17 +9,3 @@ "path": "/Repos/me@databricks.com/doesnotexist" } } -{ - "method": "POST", - "path": "/api/2.0/workspace/mkdirs", - "body": { - "path": "/not-a-repo" - } -} -{ - "method": "GET", - "path": "/api/2.0/workspace/get-status", - "q": { - "path": "/not-a-repo" - } -} diff --git a/acceptance/workspace/repos/get_errors/output.txt b/acceptance/workspace/repos/get_errors/output.txt index 443f30491f3..9055734262b 100644 --- a/acceptance/workspace/repos/get_errors/output.txt +++ b/acceptance/workspace/repos/get_errors/output.txt @@ -1,6 +1,3 @@ >>> [CLI] repos get /Repos/me@databricks.com/doesnotexist -o json Error: failed to look up repo by path: Path (/Repos/me@databricks.com/doesnotexist) doesn't exist. - ->>> [CLI] repos get /not-a-repo -o json -Error: object at path "/not-a-repo" is not a repo diff --git a/acceptance/workspace/repos/get_errors/script b/acceptance/workspace/repos/get_errors/script index b813fe7b4d4..6050aa1cfc8 100644 --- a/acceptance/workspace/repos/get_errors/script +++ b/acceptance/workspace/repos/get_errors/script @@ -1,4 +1 @@ musterr trace $CLI repos get /Repos/me@databricks.com/doesnotexist -o json - -$CLI workspace mkdirs /not-a-repo -musterr trace $CLI repos get /not-a-repo -o json diff --git a/acceptance/workspace/repos/git_cli_folder/out.requests.txt b/acceptance/workspace/repos/git_cli_folder/out.requests.txt new file mode 100644 index 00000000000..50626ea4e4b --- /dev/null +++ b/acceptance/workspace/repos/git_cli_folder/out.requests.txt @@ -0,0 +1,49 @@ +{ + "method": "GET", + "path": "/.well-known/databricks-config" +} +{ + "method": "POST", + "path": "/api/2.0/repos", + "body": { + "path": "/Workspace/Users/me@databricks.com/test-repo", + "provider": "gitHub", + "url": "https://github.com/databricks/databricks-empty-ide-project.git" + } +} +{ + "method": "GET", + "path": "/api/2.0/workspace/get-status", + "q": { + "path": "/Workspace/Users/me@databricks.com/test-repo" + } +} +{ + "method": "GET", + "path": "/api/2.0/repos/[NUMID]" +} +{ + "method": "GET", + "path": "/api/2.0/workspace/get-status", + "q": { + "path": "/Workspace/Users/me@databricks.com/test-repo" + } +} +{ + "method": "PATCH", + "path": "/api/2.0/repos/[NUMID]", + "body": { + "branch": "update-by-path" + } +} +{ + "method": "GET", + "path": "/api/2.0/workspace/get-status", + "q": { + "path": "/Workspace/Users/me@databricks.com/test-repo" + } +} +{ + "method": "DELETE", + "path": "/api/2.0/repos/[NUMID]" +} diff --git a/acceptance/workspace/repos/git_cli_folder/out.test.toml b/acceptance/workspace/repos/git_cli_folder/out.test.toml new file mode 100644 index 00000000000..f784a183258 --- /dev/null +++ b/acceptance/workspace/repos/git_cli_folder/out.test.toml @@ -0,0 +1,3 @@ +Local = true +Cloud = false +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct"] diff --git a/acceptance/workspace/repos/git_cli_folder/output.txt b/acceptance/workspace/repos/git_cli_folder/output.txt new file mode 100644 index 00000000000..e22b603c39a --- /dev/null +++ b/acceptance/workspace/repos/git_cli_folder/output.txt @@ -0,0 +1,19 @@ + +>>> [CLI] repos create https://github.com/databricks/databricks-empty-ide-project.git gitHub --path /Workspace/Users/me@databricks.com/test-repo +[NUMID] + +=== Get by path resolves a Git CLI folder +>>> [CLI] repos get /Workspace/Users/me@databricks.com/test-repo -o json +{ + "branch": "main", + "id": [NUMID], + "path": "/Workspace/Users/me@databricks.com/test-repo", + "provider": "gitHub", + "url": "https://github.com/databricks/databricks-empty-ide-project.git" +} + +=== Update by path resolves a Git CLI folder +>>> [CLI] repos update /Workspace/Users/me@databricks.com/test-repo --branch update-by-path + +=== Delete by path resolves a Git CLI folder +>>> [CLI] repos delete /Workspace/Users/me@databricks.com/test-repo diff --git a/acceptance/workspace/repos/git_cli_folder/script b/acceptance/workspace/repos/git_cli_folder/script new file mode 100644 index 00000000000..6e617d6f798 --- /dev/null +++ b/acceptance/workspace/repos/git_cli_folder/script @@ -0,0 +1,16 @@ +url=https://github.com/databricks/databricks-empty-ide-project.git +provider=gitHub +# A Git-CLI-enabled folder lives outside /Repos and get-status reports it as a +# DIRECTORY, not a REPO. Path-based commands must still resolve it to a repo ID. +path=/Workspace/Users/me@databricks.com/test-repo + +trace $CLI repos create $url $provider --path $path | jq .id -r + +title "Get by path resolves a Git CLI folder" +trace $CLI repos get $path -o json + +title "Update by path resolves a Git CLI folder" +trace $CLI repos update $path --branch update-by-path + +title "Delete by path resolves a Git CLI folder" +trace $CLI repos delete $path diff --git a/cmd/workspace/repos/overrides.go b/cmd/workspace/repos/overrides.go index 72f58bb5010..ca10dbd0fb3 100644 --- a/cmd/workspace/repos/overrides.go +++ b/cmd/workspace/repos/overrides.go @@ -166,14 +166,13 @@ func repoArgumentToRepoID(ctx context.Context, w *databricks.WorkspaceClient, ar return id, nil } - // If the argument cannot be parsed as a repo ID, try to look it up by name. + // Look up the path via get-status. We don't gate on the object type: Git-CLI + // folders report DIRECTORY rather than REPO, and the repos API is the + // authority on whether the ID resolves to a repo. oi, err := w.Workspace.GetStatusByPath(ctx, arg) if err != nil { return 0, fmt.Errorf("failed to look up repo by path: %w", err) } - if oi.ObjectType != workspace.ObjectTypeRepo { - return 0, fmt.Errorf("object at path %q is not a repo", arg) - } return oi.ObjectId, nil } diff --git a/libs/testserver/fake_workspace.go b/libs/testserver/fake_workspace.go index 8d6e8ee0dd3..5c7f9cfee40 100644 --- a/libs/testserver/fake_workspace.go +++ b/libs/testserver/fake_workspace.go @@ -418,7 +418,14 @@ func (s *FakeWorkspace) WorkspaceGetStatus(requestPath string) Response { } else if entry, ok := s.files[cleaned]; ok { info = entry.Info } else if repoId, ok := s.repoIdByPath[cleaned]; ok { - info = workspace.ObjectInfo{ObjectType: "REPO", Path: cleaned, ObjectId: repoId} + // Control-plane repos (under /Repos) report the REPO object type, while + // Git-CLI-enabled folders elsewhere are materialized as plain DIRECTORY + // nodes. Both resolve to a valid repo ID via the repos API. + objectType := workspace.ObjectTypeRepo + if !strings.HasPrefix(cleaned, "/Repos/") { + objectType = workspace.ObjectTypeDirectory + } + info = workspace.ObjectInfo{ObjectType: objectType, Path: cleaned, ObjectId: repoId} } else { // Match the real Workspace API wording, which echoes the requested path. return Response{