Skip to content
Merged
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
1 change: 0 additions & 1 deletion .replicated.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
profile: "prod"
appId: ""
appSlug: ""
promoteToChannelIds: []
Expand Down
3 changes: 1 addition & 2 deletions cli/cmd/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@ This is useful when working with different Replicated accounts (production, deve
or different API endpoints.

Credentials are stored in ~/.replicated/config.yaml with file permissions set to 600 (owner read/write only).
You can reference profiles in your .replicated.yaml files using the 'profile' field.

Authentication priority:
1. REPLICATED_API_TOKEN environment variable (highest priority)
2. Profile specified in .replicated.yaml
2. --profile flag (per-command override)
3. Default profile from ~/.replicated/config.yaml
4. Legacy single token (backward compatibility)

Expand Down
4 changes: 2 additions & 2 deletions cli/cmd/profile_set_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ func (r *runners) InitProfileSetDefaultCommand(parent *cobra.Command) *cobra.Com
cmd := &cobra.Command{
Use: "set-default [profile-name]",
Short: "Set the default authentication profile",
Long: `Set the default authentication profile that will be used when no profile is specified
in .replicated.yaml and no environment variables are set.`,
Long: `Set the default authentication profile that will be used when no --profile flag is specified
and no environment variables are set.`,
Example: `# Set production as the default profile
replicated profile set-default prod`,
Args: cobra.ExactArgs(1),
Expand Down
51 changes: 51 additions & 0 deletions cli/cmd/profile_use.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package cmd

import (
"fmt"

"github.com/pkg/errors"
"github.com/replicatedhq/replicated/pkg/credentials"
"github.com/spf13/cobra"
)

func (r *runners) InitProfileUseCommand(parent *cobra.Command) *cobra.Command {
cmd := &cobra.Command{
Use: "use [profile-name]",
Short: "Set the default authentication profile",
Long: `Set the default authentication profile that will be used when no --profile flag is specified
and no environment variables are set.`,
Example: `# Use production as the default profile
replicated profile use prod`,
Args: cobra.ExactArgs(1),
SilenceUsage: true,
RunE: r.profileUse,
}
parent.AddCommand(cmd)

return cmd
}

func (r *runners) profileUse(_ *cobra.Command, args []string) error {
profileName := args[0]

if profileName == "" {
return errors.New("profile name cannot be empty")
}

// Check if profile exists
_, err := credentials.GetProfile(profileName)
if err == credentials.ErrProfileNotFound {
return errors.Errorf("profile '%s' not found", profileName)
}
if err != nil {
return errors.Wrap(err, "failed to get profile")
}

// Set as default
if err := credentials.SetDefaultProfile(profileName); err != nil {
return errors.Wrap(err, "failed to set default profile")
}

fmt.Printf("Now using profile '%s' as default\n", profileName)
return nil
}
59 changes: 51 additions & 8 deletions cli/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
"github.com/replicatedhq/replicated/pkg/credentials"
"github.com/replicatedhq/replicated/pkg/kotsclient"
"github.com/replicatedhq/replicated/pkg/platformclient"
"github.com/replicatedhq/replicated/pkg/tools"
"github.com/replicatedhq/replicated/pkg/types"
"github.com/replicatedhq/replicated/pkg/version"
"github.com/spf13/cobra"
Expand All @@ -32,6 +31,7 @@ const (
var (
appSlugOrID string
apiToken string
profileNameFlag string
platformOrigin = "https://api.replicated.com/vendor"
kurlDotSHOrigin = "https://kurl.sh"
cache *replicatedcache.Cache
Expand Down Expand Up @@ -66,6 +66,7 @@ func GetRootCmd() *cobra.Command {
}
rootCmd.PersistentFlags().StringVar(&appSlugOrID, "app", "", "The app slug or app id to use in all calls")
rootCmd.PersistentFlags().StringVar(&apiToken, "token", "", "The API token to use to access your app in the Vendor API")
rootCmd.PersistentFlags().StringVar(&profileNameFlag, "profile", "", "The authentication profile to use for this command")
rootCmd.PersistentFlags().BoolVar(&debugFlag, "debug", false, "Enable debug output")

return rootCmd
Expand Down Expand Up @@ -299,6 +300,7 @@ func Execute(rootCmd *cobra.Command, stdin io.Reader, stdout io.Writer, stderr i
runCmds.InitProfileLsCommand(profileCmd)
runCmds.InitProfileRmCommand(profileCmd)
runCmds.InitProfileSetDefaultCommand(profileCmd)
runCmds.InitProfileUseCommand(profileCmd)

apiCmd := runCmds.InitAPICommand(runCmds.rootCmd)
runCmds.InitAPIGet(apiCmd)
Expand All @@ -314,12 +316,24 @@ func Execute(rootCmd *cobra.Command, stdin io.Reader, stdout io.Writer, stderr i

preRunSetupAPIs := func(cmd *cobra.Command, args []string) error {
if apiToken == "" {
// Try to load profile from .replicated.yaml
// Try to load profile from --profile flag, then default profile
var profileName string
configParser := tools.NewConfigParser()
config, err := configParser.FindAndParseConfig("")
if err == nil && config.Profile != "" {
profileName = config.Profile
var profileSource string
if profileNameFlag != "" {
// Command-line flag takes precedence
profileName = profileNameFlag
profileSource = "--profile flag"
} else {
// Fall back to default profile from ~/.replicated/config.yaml
defaultProfileName, err := credentials.GetDefaultProfile()
if err == nil && defaultProfileName != "" {
profileName = defaultProfileName
profileSource = "default profile"
}
}

if debugFlag && profileName != "" {
fmt.Fprintf(os.Stderr, "[DEBUG] Using profile '%s' (from %s)\n", profileName, profileSource)
}

// Get credentials with profile support
Expand All @@ -337,19 +351,48 @@ func Execute(rootCmd *cobra.Command, stdin io.Reader, stdout io.Writer, stderr i

apiToken = creds.APIToken

if debugFlag {
maskedToken := apiToken
if len(maskedToken) > 8 {
maskedToken = maskedToken[:4] + "..." + maskedToken[len(maskedToken)-4:]
}
fmt.Fprintf(os.Stderr, "[DEBUG] API Token: %s\n", maskedToken)
}

// If using a profile, check if it has custom origins
if creds.IsProfile && profileName != "" {
apiOrigin, registryOrigin, err := credentials.GetProfileOrigins(profileName)
if err == nil {
if apiOrigin != "" {
platformOrigin = apiOrigin
// Strip trailing slashes to avoid double-slash in URL construction
platformOrigin = strings.TrimRight(apiOrigin, "/")
if debugFlag {
if apiOrigin != platformOrigin {
fmt.Fprintf(os.Stderr, "[DEBUG] Normalized API origin (removed trailing slash): %s -> %s\n", apiOrigin, platformOrigin)
} else {
fmt.Fprintf(os.Stderr, "[DEBUG] Using custom API origin from profile: %s\n", platformOrigin)
}
}
}
if registryOrigin != "" {
// Store registry origin for later use (if needed by commands)
os.Setenv("REPLICATED_REGISTRY_ORIGIN", registryOrigin)
// Also strip trailing slashes from registry origin
normalizedRegistryOrigin := strings.TrimRight(registryOrigin, "/")
os.Setenv("REPLICATED_REGISTRY_ORIGIN", normalizedRegistryOrigin)
if debugFlag {
if registryOrigin != normalizedRegistryOrigin {
fmt.Fprintf(os.Stderr, "[DEBUG] Normalized registry origin (removed trailing slash): %s -> %s\n", registryOrigin, normalizedRegistryOrigin)
} else {
fmt.Fprintf(os.Stderr, "[DEBUG] Using custom registry origin from profile: %s\n", normalizedRegistryOrigin)
}
}
}
}
}

if debugFlag {
fmt.Fprintf(os.Stderr, "[DEBUG] Platform API origin: %s\n", platformOrigin)
}
}

// allow override
Expand Down
1 change: 0 additions & 1 deletion pkg/tools/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ type Config struct {
Preflights []PreflightConfig `yaml:"preflights,omitempty"`
ReleaseLabel string `yaml:"releaseLabel,omitempty"`
Manifests []string `yaml:"manifests,omitempty"`
Profile string `yaml:"profile,omitempty"`
ReplLint *ReplLintConfig `yaml:"repl-lint,omitempty"`
}

Expand Down