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
2 changes: 1 addition & 1 deletion cmd/auth/check/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func checkCLICredentials(w io.Writer) bool {

// If env vars were set but invalid, report the error. Unlike
// EnsureAuthenticated this deliberately skips the "not falling back to
// the stored profile" line: check evaluates the stored profile itself
// the stored credentials" line: check evaluates the stored credentials itself
// right below and reports that result on its own.
if !errors.Is(err, auth.ErrEnvCredentialsNotSet) {
auth.ReportEnvCredentialsError(w, creds, err)
Expand Down
9 changes: 8 additions & 1 deletion cmd/auth/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/datarobot/cli/cmd/auth/export"
"github.com/datarobot/cli/cmd/auth/login"
"github.com/datarobot/cli/cmd/auth/logout"
"github.com/datarobot/cli/cmd/auth/profile"
"github.com/datarobot/cli/cmd/auth/seturl"
"github.com/datarobot/cli/internal/version"
"github.com/spf13/cobra"
Expand All @@ -36,15 +37,21 @@ Manage your DataRobot credentials and connection settings:
• Log in using OAuth authentication
• Log out and clear stored credentials
• Export your credentials as environment variables
• Inspect named profiles for working with multiple DataRobot installations

🚀 Quick start: dr auth set-url && dr auth login`,
🚀 Quick start: dr auth set-url && dr auth login

💡 Multiple installations? Use --profile <name> (or DATAROBOT_CLI_PROFILE) on
any command, e.g. dr --profile eu auth set-url && dr --profile eu auth login.
See 'dr auth profile list'.`,
}

cmd.AddCommand(
check.Cmd(),
export.Cmd(),
login.Cmd(),
logout.Cmd(),
profile.Cmd(),
seturl.Cmd(),
)

Expand Down
4 changes: 4 additions & 0 deletions cmd/auth/login/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ If the browser cannot be opened, the CLI prints a link to open yourself. Pass
SilenceErrors: true,
SilenceUsage: true,
RunE: RunE,
Annotations: map[string]string{
// login is expected to create a not-yet-existing --profile.
config.ProfileCreateAnnotationKey: "true",
},
}

// Read directly from cobra rather than binding to viper: this is a transient
Expand Down
51 changes: 51 additions & 0 deletions cmd/auth/profile/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2026 DataRobot, Inc. and its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package profile provides read-only inspection of the named profiles
// stored in drconfig.yaml. Profiles are created by pointing --profile (or
// DATAROBOT_CLI_PROFILE) at a name that doesn't exist yet and running
// `dr auth login` or `dr auth set-url`; there is no `create` here.
package profile

import (
"github.com/datarobot/cli/cmd/auth/profile/list"
"github.com/datarobot/cli/cmd/auth/profile/show"
"github.com/spf13/cobra"
)

func Cmd() *cobra.Command {
cmd := &cobra.Command{
Use: "profile",
Aliases: []string{"profiles"},
Short: "📇 Inspect named DataRobot profiles",
Long: `Inspect the named profiles stored in drconfig.yaml.

A profile is a named set of credentials (endpoint + token, optionally
ca-cert) alongside the default one, so you can work against
several DataRobot installations without re-authenticating each time.
Select one with --profile <name> or DATAROBOT_CLI_PROFILE=<name> on any
command.

There is no 'create' or 'delete' subcommand: a profile is created the first
time you run 'dr --profile <name> auth login' (or 'auth set-url') for a name
that doesn't exist yet, and removed by editing drconfig.yaml directly.`,
}

cmd.AddCommand(
list.Cmd(),
show.Cmd(),
)

return cmd
}
128 changes: 128 additions & 0 deletions cmd/auth/profile/list/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright 2026 DataRobot, Inc. and its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package list

import (
"fmt"
"os"

"github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/lipgloss/table"
"github.com/datarobot/cli/internal/config"
"github.com/datarobot/cli/internal/outputformat"
"github.com/datarobot/cli/tui"
"github.com/spf13/cobra"
)

// defaultProfileLabel is how the top-level (unnamed) profile is displayed
// and how it is addressed in JSON output and by `dr auth profile show`.
const defaultProfileLabel = config.DefaultProfileLabel

// profileOutput is the JSON representation of one profile for
// --output-format json.
type profileOutput struct {
Name string `json:"name"`
Endpoint string `json:"endpoint"`
HasToken bool `json:"has_token"`
Active bool `json:"active"`
}

func Cmd() *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: "📋 List named profiles from drconfig.yaml",
Long: "List the default profile and every named profile stored in drconfig.yaml, marking the active one.",
RunE: runList,
}

return cmd
}

func runList(cmd *cobra.Command, _ []string) error {
def, profiles, err := config.LoadProfiles()
if err != nil {
return fmt.Errorf("failed to read profiles: %w", err)
}

active := config.ActiveProfile()

outputs := make([]profileOutput, 0, len(profiles)+1)
outputs = append(outputs, toProfileOutput(defaultProfileLabel, def, active == ""))

for _, p := range profiles {
outputs = append(outputs, toProfileOutput(p.Name, p, p.Name == active))
}

format := outputformat.GetFormat(cmd)
if format == outputformat.OutputFormatJSON {
return outputformat.PrintJSONEnvelope(os.Stdout, "profiles", outputs)
}

printProfilesTable(outputs)

return nil
}

func toProfileOutput(name string, info config.ProfileInfo, active bool) profileOutput {
return profileOutput{
Name: name,
Endpoint: info.Endpoint,
HasToken: info.HasToken,
Active: active,
}
}

func printProfilesTable(outputs []profileOutput) {
fmt.Println(tui.SubTitleStyle.Render("DataRobot Profiles"))

nameStyle := tui.BaseTextStyle.
Foreground(tui.GetAdaptiveColor(tui.DrPurple, tui.DrPurpleDark)).
Padding(0, 1)

dimStyle := tui.DimStyle.Padding(0, 1)

t := table.New().
Border(lipgloss.RoundedBorder()).
BorderStyle(tui.TableBorderStyle).
StyleFunc(func(_, col int) lipgloss.Style {
if col == 0 {
return nameStyle
}

return dimStyle
}).
Headers("NAME", "ENDPOINT", "TOKEN", "ACTIVE")

for _, o := range outputs {
endpoint := o.Endpoint
if endpoint == "" {
endpoint = "-"
}

token := "not set"
if o.HasToken {
token = "set"
}

active := ""
if o.Active {
active = "✓"
}

t.Row(o.Name, endpoint, token, active)
}

_, _ = fmt.Fprintln(os.Stdout, t.Render())
}
Loading
Loading