Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions acceptance/experimental/open/out.test.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions acceptance/experimental/open/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@

=== print URL for a job
>>> [CLI] experimental open --url jobs 123
[DATABRICKS_URL]/jobs/123?o=[NUMID]

=== print URL for a notebook
>>> [CLI] experimental open --url notebooks 12345
[DATABRICKS_URL]/?o=[NUMID]#notebook/12345

=== unknown resource type
>>> [CLI] experimental open --url unknown 123
Error: unknown resource type "unknown", must be one of: alerts, apps, clusters, dashboards, experiments, jobs, model_serving_endpoints, models, notebooks, pipelines, queries, registered_models, warehouses

Exit code: 1

=== test auto-completion handler
>>> [CLI] __complete experimental open ,
alerts
apps
clusters
dashboards
experiments
jobs
model_serving_endpoints
models
notebooks
pipelines
queries
registered_models
warehouses
:4
Completion ended with directive: ShellCompDirectiveNoFileComp
11 changes: 11 additions & 0 deletions acceptance/experimental/open/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
title "print URL for a job"
trace $CLI experimental open --url jobs 123

title "print URL for a notebook"
trace $CLI experimental open --url notebooks 12345

title "unknown resource type"
errcode trace $CLI experimental open --url unknown 123

title "test auto-completion handler"
trace $CLI __complete experimental open ,
5 changes: 5 additions & 0 deletions acceptance/experimental/open/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
GOOS.windows = false
GOOS.linux = false

# No bundle engine needed for this command.
[EnvMatrix]
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
59 changes: 4 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,9 @@ 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(), ".",
browser.WithDisabledMessage("Please complete authentication by opening this link in your browser:\n"),
)),
}
if len(scopesList) > 0 {
persistentAuthOpts = append(persistentAuthOpts, u2m.WithScopes(scopesList))
Expand Down Expand Up @@ -400,60 +400,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()
}
}
}
5 changes: 4 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,9 @@ func runInlineLogin(ctx context.Context, profiler profile.Profiler) (string, *pr
}
persistentAuthOpts := []u2m.PersistentAuthOption{
u2m.WithOAuthArgument(oauthArgument),
u2m.WithBrowser(openURLSuppressingStderr),
u2m.WithBrowser(browser.NewOpener(ctx, ".",
browser.WithDisabledMessage("Please complete authentication by opening this link in your browser:\n"),
)),
}
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