Skip to content
Draft
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
81 changes: 81 additions & 0 deletions cliext/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package cliext

import (
"fmt"
"os"
"path/filepath"

"go.temporal.io/sdk/contrib/envconfig"
)

// LoadConfigOptions contains options for loading configuration.
type LoadConfigOptions struct {
// ConfigFilePath is the path to the configuration file.
// If empty, TEMPORAL_CONFIG_FILE env var is checked, then the default path is used.
ConfigFilePath string

// EnvLookup is used for environment variable lookups.
// If nil, os.LookupEnv is used.
EnvLookup EnvLookup
}

// LoadConfigResult contains the result of loading configuration.
type LoadConfigResult struct {
// Config is the loaded configuration.
Config *envconfig.ClientConfig

// ConfigFilePath is the resolved path to the configuration file that was loaded.
// This may differ from the input if TEMPORAL_CONFIG_FILE env var was used.
ConfigFilePath string
}

// LoadConfig loads the client configuration from the specified file or default location.
// If ConfigFilePath is empty, the TEMPORAL_CONFIG_FILE environment variable is checked.
func LoadConfig(options LoadConfigOptions) (LoadConfigResult, error) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using structs for input and output seems easier to maintain and extend going forward.

envLookup := options.EnvLookup
if envLookup == nil {
envLookup = EnvLookupOS
}
configFilePath := options.ConfigFilePath
if configFilePath == "" {
configFilePath, _ = envLookup.LookupEnv("TEMPORAL_CONFIG_FILE")
}
clientConfig, err := envconfig.LoadClientConfig(envconfig.LoadClientConfigOptions{
ConfigFilePath: configFilePath,
EnvLookup: envLookup,
})
if err != nil {
return LoadConfigResult{}, err
}
return LoadConfigResult{
Config: &clientConfig,
ConfigFilePath: configFilePath,
}, nil
}

// WriteConfig writes the configuration to the specified file or default location.
// If configFilePath is empty, the default path will be used.
func WriteConfig(config *envconfig.ClientConfig, configFilePath string) error {
// Get file
if configFilePath == "" {
var err error
if configFilePath, err = envconfig.DefaultConfigFilePath(); err != nil {
return err
}
}

// Convert to TOML
b, err := config.ToTOML(envconfig.ClientConfigToTOMLOptions{})
if err != nil {
return fmt.Errorf("failed building TOML: %w", err)
}

// Write to file, making dirs as needed
if err := os.MkdirAll(filepath.Dir(configFilePath), 0700); err != nil {
return fmt.Errorf("failed making config file parent dirs: %w", err)
}
if err := os.WriteFile(configFilePath, b, 0600); err != nil {
return fmt.Errorf("failed writing config file: %w", err)
}
return nil
}
21 changes: 21 additions & 0 deletions cliext/env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package cliext

import "os"

// EnvLookupOS is the default EnvLookup implementation.
var EnvLookupOS EnvLookup = envLookupOS{}

type EnvLookup interface {
LookupEnv(key string) (string, bool)
Environ() []string
}

type envLookupOS struct{}

func (envLookupOS) LookupEnv(key string) (string, bool) {
return os.LookupEnv(key)
}

func (envLookupOS) Environ() []string {
return os.Environ()
}
35 changes: 35 additions & 0 deletions cliext/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module github.com/temporalio/cli/cliext

go 1.25.0

require go.temporal.io/sdk/contrib/envconfig v0.1.0

require (
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
github.com/nexus-rpc/sdk-go v0.3.0 // indirect
github.com/pborman/uuid v1.2.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/robfig/cron v1.2.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/stretchr/testify v1.10.0 // indirect
go.temporal.io/api v1.44.1 // indirect
go.temporal.io/sdk v1.32.1 // indirect
golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/text v0.17.0 // indirect
golang.org/x/time v0.3.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed // indirect
google.golang.org/grpc v1.66.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
183 changes: 183 additions & 0 deletions cliext/go.sum

Large diffs are not rendered by default.

79 changes: 79 additions & 0 deletions cliext/profile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package cliext

import (
"fmt"

"go.temporal.io/sdk/contrib/envconfig"
)

// LoadProfileOptions contains options for loading a profile.
type LoadProfileOptions struct {
// ConfigFilePath is the path to the configuration file.
// If empty, TEMPORAL_CONFIG_FILE env var is checked, then the default path is used.
ConfigFilePath string

// ProfileName is the name of the profile to load.
// If empty, TEMPORAL_PROFILE env var is checked, then the default profile is used.
ProfileName string

// CreateIfMissing creates an empty profile if it doesn't exist.
CreateIfMissing bool

// EnvLookup is used for environment variable lookups.
// If nil, os.LookupEnv is used.
EnvLookup EnvLookup
}

// LoadProfileResult contains the result of loading a profile.
type LoadProfileResult struct {
// Config is the loaded configuration.
Config *envconfig.ClientConfig

// ConfigFilePath is the resolved path to the configuration file.
ConfigFilePath string

// Profile is the loaded profile.
Profile *envconfig.ClientConfigProfile

// ProfileName is the resolved profile name.
ProfileName string
}

// LoadProfile loads a specific profile from the configuration.
func LoadProfile(opts LoadProfileOptions) (LoadProfileResult, error) {
envLookup := opts.EnvLookup
if envLookup == nil {
envLookup = EnvLookupOS
}

configResult, err := LoadConfig(LoadConfigOptions{
ConfigFilePath: opts.ConfigFilePath,
EnvLookup: envLookup,
})
if err != nil {
return LoadProfileResult{}, err
}

profileName := opts.ProfileName
if profileName == "" {
profileName, _ = envLookup.LookupEnv("TEMPORAL_PROFILE")
}
if profileName == "" {
profileName = envconfig.DefaultConfigFileProfile
}

profile := configResult.Config.Profiles[profileName]
if profile == nil {
if !opts.CreateIfMissing {
return LoadProfileResult{}, fmt.Errorf("profile %q not found", profileName)
}
profile = &envconfig.ClientConfigProfile{}
configResult.Config.Profiles[profileName] = profile
}
return LoadProfileResult{
Config: configResult.Config,
Profile: profile,
ConfigFilePath: configResult.ConfigFilePath,
ProfileName: profileName,
}, nil
}
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ require (
github.com/spf13/cobra v1.9.1
github.com/spf13/pflag v1.0.6
github.com/stretchr/testify v1.10.0
github.com/temporalio/cli/cliext v0.0.0
github.com/temporalio/ui-server/v2 v2.42.1
go.temporal.io/api v1.53.0
go.temporal.io/sdk v1.37.0
Expand Down Expand Up @@ -176,3 +177,5 @@ require (
modernc.org/strutil v1.2.1 // indirect
modernc.org/token v1.1.0 // indirect
)

replace github.com/temporalio/cli/cliext => ./cliext
Loading
Loading