Skip to content
31 changes: 23 additions & 8 deletions cmd/atlas/atlas.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package main

import (
"context"
"fmt"
"log"
"os"
Expand All @@ -25,13 +26,13 @@ import (
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/cli/root"
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/config"
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/telemetry"
"github.com/spf13/afero"
"github.com/spf13/cobra"
)

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func execute(rootCmd *cobra.Command) {
ctx := telemetry.NewContext()
func execute(ctx context.Context, rootCmd *cobra.Command) {
// append here to avoid a recursive link on generated docs
rootCmd.Long += `

Expand All @@ -51,12 +52,17 @@ To learn more, see our documentation: https://www.mongodb.com/docs/atlas/cli/sta
}

// loadConfig reads in config file and ENV variables if set.
func loadConfig() error {
if err := config.LoadAtlasCLIConfig(); err != nil {
return fmt.Errorf("error loading config: %w. Please run `atlas config init` to reconfigure your profile", err)
func loadConfig() (*config.Profile, error) {
configStore, initErr := config.NewViperStore(afero.NewOsFs())

if initErr != nil {
return nil, fmt.Errorf("error loading config: %w. Please run `atlas auth login` to reconfigure your profile", initErr)
}

return nil
profile := config.NewProfile(config.DefaultProfile, configStore)

config.SetProfile(profile)
return profile, nil
}

func trackInitError(e error, rootCmd *cobra.Command) {
Expand Down Expand Up @@ -85,9 +91,18 @@ func main() {
core.DisableColor = true
}

// Load config
profile, loadProfileErr := loadConfig()

rootCmd := root.Builder()
initTrack(rootCmd)
trackInitError(loadConfig(), rootCmd)
trackInitError(loadProfileErr, rootCmd)

// Initialize context, attach
// - telemetry
// - profile
ctx := telemetry.NewContext()
config.WithProfile(ctx, profile)

execute(rootCmd)
execute(ctx, rootCmd)
}
10 changes: 9 additions & 1 deletion internal/cli/config/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,15 @@ func (*editOpts) Run() error {
} else if e := os.Getenv("EDITOR"); e != "" {
editor = e
}
cmd := exec.Command(editor, config.Filename()) //nolint:gosec // it's ok to let users do this

// Get the viper config filename
configDir, err := config.CLIConfigHome()
if err != nil {
return err
}
filename := config.ViperConfigStoreFilename(configDir)

cmd := exec.Command(editor, filename)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
Expand Down
44 changes: 44 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2025 MongoDB Inc
//
// 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 config

import (
"bytes"
"os"
"path"
)

// CLIConfigHome retrieves configHome path.
func CLIConfigHome() (string, error) {
home, err := os.UserConfigDir()
if err != nil {
return "", err
}

return path.Join(home, "atlascli"), nil
}

func Path(f string) (string, error) {
var p bytes.Buffer

h, err := CLIConfigHome()
if err != nil {
return "", err
}

p.WriteString(h)
p.WriteString(f)
return p.String(), nil
}
84 changes: 84 additions & 0 deletions internal/config/identifiers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright 2025 MongoDB Inc
//
// 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 config

import (
"fmt"
"os"
"runtime"
"strings"

"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/version"
)

var (
CLIUserType = newCLIUserTypeFromEnvs()
HostName = getConfigHostnameFromEnvs()
UserAgent = fmt.Sprintf("%s/%s (%s;%s;%s)", AtlasCLI, version.Version, runtime.GOOS, runtime.GOARCH, HostName)
)

// newCLIUserTypeFromEnvs patches the user type information based on set env vars.
func newCLIUserTypeFromEnvs() string {
if value, ok := os.LookupEnv(CLIUserTypeEnv); ok {
return value
}

return DefaultUser
}

// getConfigHostnameFromEnvs patches the agent hostname based on set env vars.
func getConfigHostnameFromEnvs() string {
var builder strings.Builder

envVars := []struct {
envName string
hostName string
}{
{AtlasActionHostNameEnv, AtlasActionHostName},
{GitHubActionsHostNameEnv, GitHubActionsHostName},
{ContainerizedHostNameEnv, DockerContainerHostName},
}

for _, envVar := range envVars {
if envIsTrue(envVar.envName) {
appendToHostName(&builder, envVar.hostName)
} else {
appendToHostName(&builder, "-")
}
}
configHostName := builder.String()

if isDefaultHostName(configHostName) {
return NativeHostName
}
return configHostName
}

func appendToHostName(builder *strings.Builder, configVal string) {
if builder.Len() > 0 {
builder.WriteString("|")
}
builder.WriteString(configVal)
}

// isDefaultHostName checks if the hostname is the default placeholder.
func isDefaultHostName(hostname string) bool {
// Using strings.Count for a more dynamic approach.
return strings.Count(hostname, "-") == strings.Count(hostname, "|")+1
}

func envIsTrue(env string) bool {
return IsTrue(os.Getenv(env))
}
Loading
Loading