Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
4093a5a
Add experimental workspace open command
simonfaltum Mar 12, 2026
db5bf27
Fix review findings: URL patterns, fragment handling, workspace ID
simonfaltum Mar 13, 2026
edf1682
Fix BROWSER contract and workspace ID detection
simonfaltum Mar 13, 2026
25e54ee
Use plural resource types and add --url flag
simonfaltum Mar 13, 2026
c514e85
Add experiments resource type
simonfaltum Mar 13, 2026
59d2959
Share workspace URL helpers between experimental and bundle open
simonfaltum Mar 13, 2026
c5ae950
Revert bundle initialize_urls changes to avoid URL output regression
simonfaltum Mar 13, 2026
d2d9e60
Handle multi-argument BROWSER and warn on workspace ID lookup failure
simonfaltum Mar 13, 2026
e14c23c
Address review: remove redundant check, add all resource types, add a…
simonfaltum Mar 13, 2026
d773fff
Fix registered_models URL, quoted BROWSER env, and BROWSER=none message
simonfaltum Mar 13, 2026
14ba838
Merge remote-tracking branch 'origin/main' into simonfaltum/workspace…
simonfaltum Mar 13, 2026
2b76247
Merge branch 'main' into simonfaltum/workspace-open
simonfaltum Mar 14, 2026
11e37be
Fix browser command execution on Windows by using shell
simonfaltum Mar 15, 2026
f9e6129
Fix auth login timeout on Windows by not inheriting stdin in browser …
simonfaltum Mar 15, 2026
be8948d
Revert libs/browser: use existing exec-based browser patterns
simonfaltum Mar 15, 2026
43727e8
Add dedicated unit tests for libs/workspaceurls package
simonfaltum Mar 16, 2026
20af4ed
Merge branch 'main' into simonfaltum/workspace-open
simonfaltum Mar 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bundle/config/resources/alerts.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net/url"

"github.com/databricks/cli/libs/log"
"github.com/databricks/cli/libs/workspaceurls"
"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/marshal"
"github.com/databricks/databricks-sdk-go/service/sql"
Expand Down Expand Up @@ -52,8 +53,7 @@ func (a *Alert) InitializeURL(baseURL url.URL) {
if a.ID == "" {
return
}
baseURL.Path = "sql/alerts-v2/" + a.ID
a.URL = baseURL.String()
a.URL = workspaceurls.ResourceURL(baseURL, workspaceurls.AlertPattern, a.ID)
}

func (a *Alert) GetName() string {
Expand Down
4 changes: 2 additions & 2 deletions bundle/config/resources/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net/url"

"github.com/databricks/cli/libs/log"
"github.com/databricks/cli/libs/workspaceurls"
"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/marshal"
"github.com/databricks/databricks-sdk-go/service/apps"
Expand Down Expand Up @@ -83,8 +84,7 @@ func (a *App) InitializeURL(baseURL url.URL) {
if a.ModifiedStatus == "" || a.ModifiedStatus == ModifiedStatusCreated {
return
}
baseURL.Path = "apps/" + a.GetName()
a.URL = baseURL.String()
a.URL = workspaceurls.ResourceURL(baseURL, workspaceurls.AppPattern, a.GetName())
}

func (a *App) GetName() string {
Expand Down
4 changes: 2 additions & 2 deletions bundle/config/resources/clusters.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net/url"

"github.com/databricks/cli/libs/log"
"github.com/databricks/cli/libs/workspaceurls"
"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/marshal"
"github.com/databricks/databricks-sdk-go/service/compute"
Expand Down Expand Up @@ -47,8 +48,7 @@ func (s *Cluster) InitializeURL(baseURL url.URL) {
if s.ID == "" {
return
}
baseURL.Path = "compute/clusters/" + s.ID
s.URL = baseURL.String()
s.URL = workspaceurls.ResourceURL(baseURL, workspaceurls.ClusterPattern, s.ID)
}

func (s *Cluster) GetName() string {
Expand Down
5 changes: 2 additions & 3 deletions bundle/config/resources/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package resources

import (
"context"
"fmt"
"net/url"

"github.com/databricks/cli/libs/log"
"github.com/databricks/cli/libs/workspaceurls"
"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/marshal"
"github.com/databricks/databricks-sdk-go/service/dashboards"
Expand Down Expand Up @@ -114,8 +114,7 @@ func (r *Dashboard) InitializeURL(baseURL url.URL) {
return
}

baseURL.Path = fmt.Sprintf("dashboardsv3/%s/published", r.ID)
r.URL = baseURL.String()
r.URL = workspaceurls.ResourceURL(baseURL, workspaceurls.DashboardPattern, r.ID)
}

func (r *Dashboard) GetName() string {
Expand Down
4 changes: 2 additions & 2 deletions bundle/config/resources/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"strconv"

"github.com/databricks/cli/libs/log"
"github.com/databricks/cli/libs/workspaceurls"
"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/marshal"
"github.com/databricks/databricks-sdk-go/service/jobs"
Expand Down Expand Up @@ -54,8 +55,7 @@ func (j *Job) InitializeURL(baseURL url.URL) {
if j.ID == "" {
return
}
baseURL.Path = "jobs/" + j.ID
j.URL = baseURL.String()
j.URL = workspaceurls.ResourceURL(baseURL, workspaceurls.JobPattern, j.ID)
}

func (j *Job) GetName() string {
Expand Down
4 changes: 2 additions & 2 deletions bundle/config/resources/mlflow_experiment.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net/url"

"github.com/databricks/cli/libs/log"
"github.com/databricks/cli/libs/workspaceurls"
"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/marshal"
"github.com/databricks/databricks-sdk-go/service/ml"
Expand Down Expand Up @@ -49,8 +50,7 @@ func (s *MlflowExperiment) InitializeURL(baseURL url.URL) {
if s.ID == "" {
return
}
baseURL.Path = "ml/experiments/" + s.ID
s.URL = baseURL.String()
s.URL = workspaceurls.ResourceURL(baseURL, workspaceurls.ExperimentPattern, s.ID)
}

func (s *MlflowExperiment) GetName() string {
Expand Down
4 changes: 2 additions & 2 deletions bundle/config/resources/mlflow_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net/url"

"github.com/databricks/cli/libs/log"
"github.com/databricks/cli/libs/workspaceurls"
"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/marshal"
"github.com/databricks/databricks-sdk-go/service/ml"
Expand Down Expand Up @@ -49,8 +50,7 @@ func (s *MlflowModel) InitializeURL(baseURL url.URL) {
if s.ID == "" {
return
}
baseURL.Path = "ml/models/" + s.ID
s.URL = baseURL.String()
s.URL = workspaceurls.ResourceURL(baseURL, workspaceurls.ModelPattern, s.ID)
}

func (s *MlflowModel) GetName() string {
Expand Down
4 changes: 2 additions & 2 deletions bundle/config/resources/model_serving_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net/url"

"github.com/databricks/cli/libs/log"
"github.com/databricks/cli/libs/workspaceurls"
"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/marshal"
"github.com/databricks/databricks-sdk-go/service/serving"
Expand Down Expand Up @@ -54,8 +55,7 @@ func (s *ModelServingEndpoint) InitializeURL(baseURL url.URL) {
if s.ID == "" {
return
}
baseURL.Path = "ml/endpoints/" + s.ID
s.URL = baseURL.String()
s.URL = workspaceurls.ResourceURL(baseURL, workspaceurls.ModelServingEndpointPattern, s.ID)
}

func (s *ModelServingEndpoint) GetName() string {
Expand Down
4 changes: 2 additions & 2 deletions bundle/config/resources/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net/url"

"github.com/databricks/cli/libs/log"
"github.com/databricks/cli/libs/workspaceurls"
"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/marshal"
"github.com/databricks/databricks-sdk-go/service/pipelines"
Expand Down Expand Up @@ -49,8 +50,7 @@ func (p *Pipeline) InitializeURL(baseURL url.URL) {
if p.ID == "" {
return
}
baseURL.Path = "pipelines/" + p.ID
p.URL = baseURL.String()
p.URL = workspaceurls.ResourceURL(baseURL, workspaceurls.PipelinePattern, p.ID)
}

func (p *Pipeline) GetName() string {
Expand Down
8 changes: 6 additions & 2 deletions bundle/config/resources/registered_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"strings"

"github.com/databricks/cli/libs/log"
"github.com/databricks/cli/libs/workspaceurls"
"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/marshal"
"github.com/databricks/databricks-sdk-go/service/catalog"
Expand Down Expand Up @@ -54,8 +55,11 @@ func (s *RegisteredModel) InitializeURL(baseURL url.URL) {
if s.ID == "" {
return
}
baseURL.Path = "explore/data/models/" + strings.ReplaceAll(s.ID, ".", "/")
s.URL = baseURL.String()
s.URL = workspaceurls.ResourceURL(
baseURL,
workspaceurls.RegisteredModelPattern,
strings.ReplaceAll(s.ID, ".", "/"),
)
}

func (s *RegisteredModel) GetName() string {
Expand Down
4 changes: 2 additions & 2 deletions bundle/config/resources/sql_warehouses.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net/url"

"github.com/databricks/cli/libs/log"
"github.com/databricks/cli/libs/workspaceurls"
"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/marshal"
"github.com/databricks/databricks-sdk-go/service/sql"
Expand Down Expand Up @@ -47,8 +48,7 @@ func (sw *SqlWarehouse) InitializeURL(baseURL url.URL) {
if sw.ID == "" {
return
}
baseURL.Path = "sql/warehouses/" + sw.ID
sw.URL = baseURL.String()
sw.URL = workspaceurls.ResourceURL(baseURL, workspaceurls.WarehousePattern, sw.ID)
}

func (sw *SqlWarehouse) GetName() string {
Expand Down
57 changes: 2 additions & 55 deletions cmd/auth/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,21 @@ import (
"context"
"errors"
"fmt"
"io"
"runtime"
"strings"
"time"

"github.com/databricks/cli/libs/auth"
"github.com/databricks/cli/libs/browser"
"github.com/databricks/cli/libs/cmdio"
"github.com/databricks/cli/libs/databrickscfg"
"github.com/databricks/cli/libs/databrickscfg/cfgpickers"
"github.com/databricks/cli/libs/databrickscfg/profile"
"github.com/databricks/cli/libs/env"
"github.com/databricks/cli/libs/exec"
"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/config"
"github.com/databricks/databricks-sdk-go/config/experimental/auth/authconv"
"github.com/databricks/databricks-sdk-go/credentials/u2m"
browserpkg "github.com/pkg/browser"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -174,7 +172,7 @@ depends on the existing profiles you have set in your configuration file
}
persistentAuthOpts := []u2m.PersistentAuthOption{
u2m.WithOAuthArgument(oauthArgument),
u2m.WithBrowser(getBrowserFunc(cmd)),
u2m.WithBrowser(browser.NewOpener(cmd.Context(), ".")),
}
if len(scopesList) > 0 {
persistentAuthOpts = append(persistentAuthOpts, u2m.WithScopes(scopesList))
Expand Down Expand Up @@ -400,60 +398,9 @@ func loadProfileByName(ctx context.Context, profileName string, profiler profile
return nil, nil
}

// openURLSuppressingStderr opens a URL in the browser while suppressing stderr output.
// This prevents xdg-open error messages from being displayed to the user.
func openURLSuppressingStderr(url string) error {
// Save the original stderr from the browser package
originalStderr := browserpkg.Stderr
defer func() {
browserpkg.Stderr = originalStderr
}()

// Redirect stderr to discard to suppress xdg-open errors
browserpkg.Stderr = io.Discard

// Call the browser open function
return browserpkg.OpenURL(url)
}

// oauthLoginClearKeys returns profile keys that should be explicitly removed
// when performing an OAuth login. Derives auth credential fields dynamically
// from the SDK's ConfigAttributes to stay in sync as new auth methods are added.
func oauthLoginClearKeys() []string {
return databrickscfg.AuthCredentialKeys()
}

// getBrowserFunc returns a function that opens the given URL in the browser.
// It respects the BROWSER environment variable:
// - empty string: uses the default browser
// - "none": prints the URL to stdout without opening a browser
// - custom command: executes the specified command with the URL as argument
func getBrowserFunc(cmd *cobra.Command) func(url string) error {
browser := env.Get(cmd.Context(), "BROWSER")
switch browser {
case "":
return openURLSuppressingStderr
case "none":
return func(url string) error {
cmdio.LogString(cmd.Context(), "Please complete authentication by opening this link in your browser:\n"+url)
return nil
}
default:
return func(url string) error {
// Run the browser command via a shell.
// It can be a script or a binary and scripts cannot be executed directly on Windows.
e, err := exec.NewCommandExecutor(".")
if err != nil {
return err
}

e.WithInheritOutput()
cmd, err := e.StartCommand(cmd.Context(), fmt.Sprintf("%q %q", browser, url))
if err != nil {
return err
}

return cmd.Wait()
}
}
}
3 changes: 2 additions & 1 deletion cmd/auth/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"time"

"github.com/databricks/cli/libs/auth"
"github.com/databricks/cli/libs/browser"
"github.com/databricks/cli/libs/cmdio"
"github.com/databricks/cli/libs/databrickscfg"
"github.com/databricks/cli/libs/databrickscfg/profile"
Expand Down Expand Up @@ -432,7 +433,7 @@ func runInlineLogin(ctx context.Context, profiler profile.Profiler) (string, *pr
}
persistentAuthOpts := []u2m.PersistentAuthOption{
u2m.WithOAuthArgument(oauthArgument),
u2m.WithBrowser(openURLSuppressingStderr),
u2m.WithBrowser(browser.NewOpener(ctx, ".")),
}
if len(scopesList) > 0 {
persistentAuthOpts = append(persistentAuthOpts, u2m.WithScopes(scopesList))
Expand Down
1 change: 1 addition & 0 deletions cmd/experimental/experimental.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ development. They may change or be removed in future versions without notice.`,
}

cmd.AddCommand(aitoolscmd.NewAitoolsCmd())
cmd.AddCommand(newWorkspaceOpenCommand())

return cmd
}
Loading
Loading