diff --git a/cli/packages/cmd/export.go b/cli/packages/cmd/export.go
index dbabb24e2..d0db485b3 100644
--- a/cli/packages/cmd/export.go
+++ b/cli/packages/cmd/export.go
@@ -74,7 +74,7 @@ var exportCmd = &cobra.Command{
util.HandleError(err, "Unable to parse flag")
}
- secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, WorkspaceId: projectId, SecretsPath: secretsPath})
+ secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, WorkspaceId: projectId, SecretsPath: secretsPath}, "")
if err != nil {
util.HandleError(err, "Unable to fetch secrets")
}
@@ -87,7 +87,7 @@ var exportCmd = &cobra.Command{
var output string
if shouldExpandSecrets {
- substitutions := util.ExpandSecrets(secrets, infisicalToken)
+ substitutions := util.ExpandSecrets(secrets, infisicalToken, "")
output, err = formatEnvs(substitutions, format)
if err != nil {
util.HandleError(err)
diff --git a/cli/packages/cmd/run.go b/cli/packages/cmd/run.go
index 8aa5306d9..2bb043c26 100644
--- a/cli/packages/cmd/run.go
+++ b/cli/packages/cmd/run.go
@@ -67,6 +67,11 @@ var runCmd = &cobra.Command{
util.HandleError(err, "Unable to parse flag")
}
+ projectConfigDir, err := cmd.Flags().GetString("project-config-dir")
+ if err != nil {
+ util.HandleError(err, "Unable to parse flag")
+ }
+
secretOverriding, err := cmd.Flags().GetBool("secret-overriding")
if err != nil {
util.HandleError(err, "Unable to parse flag")
@@ -92,7 +97,7 @@ var runCmd = &cobra.Command{
util.HandleError(err, "Unable to parse flag")
}
- secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath, IncludeImport: includeImports})
+ secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath, IncludeImport: includeImports}, projectConfigDir)
if err != nil {
util.HandleError(err, "Could not fetch secrets", "If you are using a service token to fetch secrets, please ensure it is valid")
@@ -105,7 +110,7 @@ var runCmd = &cobra.Command{
}
if shouldExpandSecrets {
- secrets = util.ExpandSecrets(secrets, infisicalToken)
+ secrets = util.ExpandSecrets(secrets, infisicalToken, projectConfigDir)
}
secretsByKey := getSecretsByKeys(secrets)
@@ -198,6 +203,7 @@ func init() {
runCmd.Flags().StringP("command", "c", "", "chained commands to execute (e.g. \"npm install && npm run dev; echo ...\")")
runCmd.Flags().StringP("tags", "t", "", "filter secrets by tag slugs ")
runCmd.Flags().String("path", "/", "get secrets within a folder path")
+ runCmd.Flags().String("project-config-dir", "", "explicitly set the directory where the .infisical.json resides")
}
// Will execute a single command and pass in the given secrets into the process
diff --git a/cli/packages/cmd/secrets.go b/cli/packages/cmd/secrets.go
index 3aa808cf4..778ac574e 100644
--- a/cli/packages/cmd/secrets.go
+++ b/cli/packages/cmd/secrets.go
@@ -68,7 +68,7 @@ var secretsCmd = &cobra.Command{
util.HandleError(err, "Unable to parse flag")
}
- secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath, IncludeImport: includeImports})
+ secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath, IncludeImport: includeImports}, "")
if err != nil {
util.HandleError(err)
}
@@ -80,7 +80,7 @@ var secretsCmd = &cobra.Command{
}
if shouldExpandSecrets {
- secrets = util.ExpandSecrets(secrets, infisicalToken)
+ secrets = util.ExpandSecrets(secrets, infisicalToken, "")
}
visualize.PrintAllSecretDetails(secrets)
@@ -169,7 +169,7 @@ var secretsSetCmd = &cobra.Command{
plainTextEncryptionKey := crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey)
// pull current secrets
- secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, SecretsPath: secretsPath})
+ secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, SecretsPath: secretsPath}, "")
if err != nil {
util.HandleError(err, "unable to retrieve secrets")
}
@@ -406,7 +406,7 @@ func getSecretsByNames(cmd *cobra.Command, args []string) {
util.HandleError(err, "Unable to parse path flag")
}
- secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath})
+ secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath}, "")
if err != nil {
util.HandleError(err, "To fetch all secrets")
}
@@ -455,7 +455,7 @@ func generateExampleEnv(cmd *cobra.Command, args []string) {
util.HandleError(err, "Unable to parse flag")
}
- secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath})
+ secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath}, "")
if err != nil {
util.HandleError(err, "To fetch all secrets")
}
diff --git a/cli/packages/util/config.go b/cli/packages/util/config.go
index 842544d77..221609852 100644
--- a/cli/packages/util/config.go
+++ b/cli/packages/util/config.go
@@ -113,6 +113,28 @@ func GetWorkSpaceFromFile() (models.WorkspaceConfigFile, error) {
return workspaceConfigFile, nil
}
+func GetWorkSpaceFromFilePath(configFileDir string) (models.WorkspaceConfigFile, error) {
+ configFilePath := filepath.Join(configFileDir, ".infisical.json")
+
+ _, configFileStatusError := os.Stat(configFilePath)
+ if os.IsNotExist(configFileStatusError) {
+ return models.WorkspaceConfigFile{}, fmt.Errorf("file %s does not exist", configFilePath)
+ }
+
+ configFileAsBytes, err := os.ReadFile(configFilePath)
+ if err != nil {
+ return models.WorkspaceConfigFile{}, err
+ }
+
+ var workspaceConfigFile models.WorkspaceConfigFile
+ err = json.Unmarshal(configFileAsBytes, &workspaceConfigFile)
+ if err != nil {
+ return models.WorkspaceConfigFile{}, err
+ }
+
+ return workspaceConfigFile, nil
+}
+
// FindWorkspaceConfigFile searches for a .infisical.json file in the current directory and all parent directories.
func FindWorkspaceConfigFile() (string, error) {
dir, err := os.Getwd()
diff --git a/cli/packages/util/helper.go b/cli/packages/util/helper.go
index d96ac3899..043db468e 100644
--- a/cli/packages/util/helper.go
+++ b/cli/packages/util/helper.go
@@ -105,6 +105,17 @@ func RequireLocalWorkspaceFile() {
}
}
+func ValidateWorkspaceFile(projectConfigFilePath string) {
+ workspaceFilePath, err := GetWorkSpaceFromFilePath(projectConfigFilePath)
+ if err != nil {
+ PrintErrorMessageAndExit(fmt.Sprintf("error reading your project config %v", err))
+ }
+
+ if workspaceFilePath.WorkspaceId == "" {
+ PrintErrorMessageAndExit("Your project id is missing in your local config file. Please add it or run again [infisical init]")
+ }
+}
+
func GetHashFromStringList(list []string) string {
hash := sha256.New()
diff --git a/cli/packages/util/secrets.go b/cli/packages/util/secrets.go
index 2eec803d1..a260aaaec 100644
--- a/cli/packages/util/secrets.go
+++ b/cli/packages/util/secrets.go
@@ -220,7 +220,7 @@ func InjectImportedSecret(plainTextWorkspaceKey []byte, secrets []models.SingleE
return secrets, nil
}
-func GetAllEnvironmentVariables(params models.GetAllSecretsParameters) ([]models.SingleEnvironmentVariable, error) {
+func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectConfigFilePath string) ([]models.SingleEnvironmentVariable, error) {
var infisicalToken string
if params.InfisicalToken == "" {
infisicalToken = os.Getenv(INFISICAL_TOKEN_NAME)
@@ -236,7 +236,13 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters) ([]models
if infisicalToken == "" {
if isConnected {
log.Debug().Msg("GetAllEnvironmentVariables: Connected to internet, checking logged in creds")
- RequireLocalWorkspaceFile()
+
+ if projectConfigFilePath == "" {
+ RequireLocalWorkspaceFile()
+ } else {
+ ValidateWorkspaceFile(projectConfigFilePath)
+ }
+
RequireLogin()
}
@@ -251,13 +257,26 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters) ([]models
PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
}
- workspaceFile, err := GetWorkSpaceFromFile()
- if err != nil {
- return nil, err
+ var infisicalDotJson models.WorkspaceConfigFile
+
+ if projectConfigFilePath == "" {
+ projectConfig, err := GetWorkSpaceFromFile()
+ if err != nil {
+ return nil, err
+ }
+
+ infisicalDotJson = projectConfig
+ } else {
+ projectConfig, err := GetWorkSpaceFromFilePath(projectConfigFilePath)
+ if err != nil {
+ return nil, err
+ }
+
+ infisicalDotJson = projectConfig
}
if params.WorkspaceId != "" {
- workspaceFile.WorkspaceId = params.WorkspaceId
+ infisicalDotJson.WorkspaceId = params.WorkspaceId
}
// // Verify environment
@@ -266,18 +285,18 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters) ([]models
// return nil, fmt.Errorf("unable to validate environment name because [err=%s]", err)
// }
- secretsToReturn, errorToReturn = GetPlainTextSecretsViaJTW(loggedInUserDetails.UserCredentials.JTWToken, loggedInUserDetails.UserCredentials.PrivateKey, workspaceFile.WorkspaceId,
+ secretsToReturn, errorToReturn = GetPlainTextSecretsViaJTW(loggedInUserDetails.UserCredentials.JTWToken, loggedInUserDetails.UserCredentials.PrivateKey, infisicalDotJson.WorkspaceId,
params.Environment, params.TagSlugs, params.SecretsPath, params.IncludeImport)
log.Debug().Msgf("GetAllEnvironmentVariables: Trying to fetch secrets JTW token [err=%s]", errorToReturn)
backupSecretsEncryptionKey := []byte(loggedInUserDetails.UserCredentials.PrivateKey)[0:32]
if errorToReturn == nil {
- WriteBackupSecrets(workspaceFile.WorkspaceId, params.Environment, backupSecretsEncryptionKey, secretsToReturn)
+ WriteBackupSecrets(infisicalDotJson.WorkspaceId, params.Environment, backupSecretsEncryptionKey, secretsToReturn)
}
// only attempt to serve cached secrets if no internet connection and if at least one secret cached
if !isConnected {
- backedSecrets, err := ReadBackupSecrets(workspaceFile.WorkspaceId, params.Environment, backupSecretsEncryptionKey)
+ backedSecrets, err := ReadBackupSecrets(infisicalDotJson.WorkspaceId, params.Environment, backupSecretsEncryptionKey)
if len(backedSecrets) > 0 {
PrintWarning("Unable to fetch latest secret(s) due to connection error, serving secrets from last successful fetch. For more info, run with --debug")
secretsToReturn = backedSecrets
@@ -421,7 +440,7 @@ func getSecretsByKeys(secrets []models.SingleEnvironmentVariable) map[string]mod
return secretMapByName
}
-func ExpandSecrets(secrets []models.SingleEnvironmentVariable, infisicalToken string) []models.SingleEnvironmentVariable {
+func ExpandSecrets(secrets []models.SingleEnvironmentVariable, infisicalToken string, projectConfigPathDir string) []models.SingleEnvironmentVariable {
expandedSecs := make(map[string]string)
interpolatedSecs := make(map[string]string)
// map[env.secret-path][keyname]Secret
@@ -454,7 +473,7 @@ func ExpandSecrets(secrets []models.SingleEnvironmentVariable, infisicalToken st
if crossRefSec, ok := crossEnvRefSecs[uniqKey]; !ok {
// if not in cross reference cache, fetch it from server
- refSecs, err := GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: env, InfisicalToken: infisicalToken, SecretsPath: secPath})
+ refSecs, err := GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: env, InfisicalToken: infisicalToken, SecretsPath: secPath}, projectConfigPathDir)
if err != nil {
HandleError(err, fmt.Sprintf("Could not fetch secrets in environment: %s secret-path: %s", env, secPath), "If you are using a service token to fetch secrets, please ensure it is valid")
}
diff --git a/docs/cli/commands/run.mdx b/docs/cli/commands/run.mdx
index 62d0c5104..8078342a5 100644
--- a/docs/cli/commands/run.mdx
+++ b/docs/cli/commands/run.mdx
@@ -62,6 +62,16 @@ Inject secrets from Infisical into your application process.
### Flags
+
+
+ Explicitly set the directory where the .infisical.json resides. This is useful for some monorepo setups.
+
+ ```bash
+ # Example
+ infisical run --project-config-dir=/some-dir -- printenv
+ ```
+
+
Pass secrets into multiple commands at once