Skip to content
27 changes: 24 additions & 3 deletions packages/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ import (

var Telemetry *telemetry.Telemetry

// domainFromWorkspaceConfig holds the apiUrl value read from .infisical.json during init(),
// so that PersistentPreRun can print an informational message about it.
var domainFromWorkspaceConfig string

// workspaceConfigApiUrlMalformed is set during init() when .infisical.json contains a
// non-empty apiUrl that does not begin with http:// or https://.
var workspaceConfigApiUrlMalformed bool

var RootCmd = &cobra.Command{
Use: "infisical",
Short: "Infisical CLI is used to inject environment variables into any process",
Expand Down Expand Up @@ -94,7 +102,7 @@ func init() {
cobra.OnInitialize(initLog)
RootCmd.PersistentFlags().StringP("log-level", "l", "", "log level (trace, debug, info, warn, error, fatal)")
RootCmd.PersistentFlags().Bool("telemetry", true, "Infisical collects non-sensitive telemetry data to enhance features and improve user experience. Participation is voluntary")
RootCmd.PersistentFlags().StringVar(&config.INFISICAL_URL, "domain", fmt.Sprintf("%s/api", util.INFISICAL_DEFAULT_US_URL), "Point the CLI to your Infisical instance (e.g., https://eu.infisical.com for EU Cloud, or https://your-instance.com for self-hosted). Can also set via INFISICAL_API_URL environment variable. Required for non-US Cloud users.")
RootCmd.PersistentFlags().StringVar(&config.INFISICAL_URL, "domain", fmt.Sprintf("%s/api", util.INFISICAL_DEFAULT_US_URL), "Point the CLI to your Infisical instance (e.g., https://eu.infisical.com for EU Cloud, or https://your-instance.com for self-hosted). Can also set via INFISICAL_API_URL environment variable or the 'apiUrl' field in .infisical.json. Required for non-US Cloud users.")
RootCmd.PersistentFlags().Bool("silent", false, "Disable output of tip/info messages. Useful when running in scripts or CI/CD pipelines.")
RootCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
silent, err := cmd.Flags().GetBool("silent")
Expand All @@ -104,6 +112,12 @@ func init() {

config.INFISICAL_URL = util.AppendAPIEndpoint(config.INFISICAL_URL)

if workspaceConfigApiUrlMalformed {
util.PrintWarningWithWriter("The 'apiUrl' field in .infisical.json is not a valid URL (must start with http:// or https://). It will be ignored.", cmd.ErrOrStderr())
} else if domainFromWorkspaceConfig != "" && !silent {
fmt.Fprintf(cmd.ErrOrStderr(), "[INFO] Using domain '%s' from .infisical.json\n", domainFromWorkspaceConfig)
}

// util.DisplayAptInstallationChangeBannerWithWriter(silent, cmd.ErrOrStderr())
if !util.IsRunningInDocker() && !silent && !isStructuredOutputRequested(cmd) {
util.CheckForUpdateWithWriter(cmd.ErrOrStderr())
Expand All @@ -121,11 +135,18 @@ func init() {

}

// if config.INFISICAL_URL is set to the default value, check if INFISICAL_URL is set in the environment
// this is used to allow overrides of the default value
// Override the default domain if the --domain flag was not explicitly set.
// Priority order (highest to lowest): --domain flag > INFISICAL_API_URL env var > .infisical.json apiUrl field > default
if !RootCmd.Flag("domain").Changed {
if envInfisicalBackendUrl, ok := os.LookupEnv("INFISICAL_API_URL"); ok {
config.INFISICAL_URL = util.AppendAPIEndpoint(envInfisicalBackendUrl)
} else if workspaceConfig, err := util.GetWorkSpaceFromFile(); err == nil && workspaceConfig.ApiUrl != "" {
if strings.HasPrefix(workspaceConfig.ApiUrl, "http://") || strings.HasPrefix(workspaceConfig.ApiUrl, "https://") {
config.INFISICAL_URL = util.AppendAPIEndpoint(workspaceConfig.ApiUrl)
domainFromWorkspaceConfig = workspaceConfig.ApiUrl
} else {
workspaceConfigApiUrlMalformed = true
}
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/models/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ type WorkspaceConfigFile struct {
WorkspaceId string `json:"workspaceId"`
DefaultEnvironment string `json:"defaultEnvironment"`
GitBranchToEnvironmentMapping map[string]string `json:"gitBranchToEnvironmentMapping"`
ApiUrl string `json:"apiUrl,omitempty"`
}

type SymmetricEncryptionResult struct {
Expand Down
38 changes: 38 additions & 0 deletions packages/util/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package util

import (
"testing"
)

func TestGetWorkspaceConfigByPath_WithApiUrl(t *testing.T) {
cfg, err := GetWorkspaceConfigByPath("testdata/infisical-with-api-url.json")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if cfg.WorkspaceId != "test-workspace-id" {
t.Errorf("expected workspaceId 'test-workspace-id', got '%s'", cfg.WorkspaceId)
}
if cfg.ApiUrl != "https://custom.infisical.com/api" {
t.Errorf("expected apiUrl 'https://custom.infisical.com/api', got '%s'", cfg.ApiUrl)
}
}

func TestGetWorkspaceConfigByPath_WithoutApiUrl(t *testing.T) {
cfg, err := GetWorkspaceConfigByPath("testdata/infisical-default-env.json")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if cfg.ApiUrl != "" {
t.Errorf("expected empty apiUrl, got '%s'", cfg.ApiUrl)
}
}

func TestGetWorkspaceConfigByPath_WithMalformedApiUrl(t *testing.T) {
cfg, err := GetWorkspaceConfigByPath("testdata/infisical-with-malformed-api-url.json")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if cfg.ApiUrl != "not-a-valid-url" {
t.Errorf("expected apiUrl 'not-a-valid-url', got '%s'", cfg.ApiUrl)
}
}
6 changes: 6 additions & 0 deletions packages/util/testdata/infisical-with-api-url.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"workspaceId": "test-workspace-id",
"defaultEnvironment": "dev",
"gitBranchToEnvironmentMapping": null,
"apiUrl": "https://custom.infisical.com/api"
}
6 changes: 6 additions & 0 deletions packages/util/testdata/infisical-with-malformed-api-url.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"workspaceId": "test-workspace-id",
"defaultEnvironment": "dev",
"gitBranchToEnvironmentMapping": null,
"apiUrl": "not-a-valid-url"
}