diff --git a/cli/cmd/set-env-vars.go b/cli/cmd/set-env-vars.go index 4c63f9b..6cdbe0f 100644 --- a/cli/cmd/set-env-vars.go +++ b/cli/cmd/set-env-vars.go @@ -5,9 +5,12 @@ package cmd import ( "fmt" + "os" + "strconv" "github.com/codesphere-cloud/cs-go/pkg/cs" "github.com/codesphere-cloud/cs-go/pkg/io" + "github.com/joho/godotenv" "github.com/spf13/cobra" ) @@ -18,7 +21,8 @@ type SetEnvVarCmd struct { type SetEnvVarOptions struct { GlobalOptions - EnvVar *[]string + EnvVar *[]string + EnvFile *string } func AddSetEnvVarCmd(p *cobra.Command, opts GlobalOptions) { @@ -26,10 +30,12 @@ func AddSetEnvVarCmd(p *cobra.Command, opts GlobalOptions) { cmd: &cobra.Command{ Use: "set-env", Short: "Set environment varariables", - Long: `Set environment variables in a workspace`, + Long: `Set environment variables in a workspace from flags or a .env file.`, Example: io.FormatExampleCommands("set-env", []io.Example{ - {Cmd: "--workspace --env-var foo=bar", Desc: "Set single environment variable"}, - {Cmd: "--workspace --env-var foo=bar --env-var hello=world", Desc: "Set multiple environment variables"}, + {Cmd: "--workspace --env-var FOO=bar", Desc: "Set a single environment variable"}, + {Cmd: "--workspace --env-var FOO=bar --env-var HELLO=world", Desc: "Set multiple environment variables"}, + {Cmd: "--workspace --env-file ./.env", Desc: "Set environment variables from a .env file"}, + {Cmd: "--workspace --env-file ./.env --env-var FOO=new_value", Desc: "Set from a file and override/add a specific variable"}, }), }, Opts: SetEnvVarOptions{GlobalOptions: opts}, @@ -41,6 +47,7 @@ func AddSetEnvVarCmd(p *cobra.Command, opts GlobalOptions) { func (l *SetEnvVarCmd) parseFlags() { l.Opts.EnvVar = l.cmd.Flags().StringArrayP("env-var", "e", []string{}, "env vars to set in form key=val") + l.Opts.EnvFile = l.cmd.Flags().StringP("env-file", "f", "", "path to a .env file") } func (l *SetEnvVarCmd) RunE(_ *cobra.Command, args []string) (err error) { @@ -51,21 +58,73 @@ func (l *SetEnvVarCmd) RunE(_ *cobra.Command, args []string) (err error) { return l.SetEnvironmentVariables(client) } +func ReadEnvFile(l *SetEnvVarCmd, finalEnvVarMap map[string]string) error { -func (l *SetEnvVarCmd) SetEnvironmentVariables(client Client) (err error) { - envVarMap, err := cs.ArgToEnvVarMap(*l.Opts.EnvVar) + envFile := *l.Opts.EnvFile + if _, err := os.Stat(envFile); os.IsNotExist(err) { + return fmt.Errorf("env file does not exist: %s", envFile) + } + + fileEnvMap, err := godotenv.Read(envFile) if err != nil { - return fmt.Errorf("failed to parse environment variables: %w", err) + return fmt.Errorf("failed to parse env file %s: %w", envFile, err) + } + + for key, value := range fileEnvMap { + finalEnvVarMap[key] = value + } + + return nil +} + +func ReadEnvVarArg(l *SetEnvVarCmd, finalEnvVarMap map[string]string) error { + + flagEnvVarMap, err := cs.ArgToEnvVarMap(*l.Opts.EnvVar) + if err != nil { + return fmt.Errorf("failed to parse environment variables from flags: %w", err) + } + + for key, value := range flagEnvVarMap { + finalEnvVarMap[key] = value } + + return nil +} + +func (l *SetEnvVarCmd) SetEnvironmentVariables(client Client) (err error) { + finalEnvVarMap := make(map[string]string) + + // Check if the environment file is provided and read it + if l.Opts.EnvFile != nil && *l.Opts.EnvFile != "" { + err := ReadEnvFile(l, finalEnvVarMap) + if err != nil { + return fmt.Errorf("failed to read environment variables from file: %w", err) + } + } + + // Check if the environment variables are provided as flags and read them + if l.Opts.EnvVar != nil && len(*l.Opts.EnvVar) > 0 { + err := ReadEnvVarArg(l, finalEnvVarMap) + if err != nil { + return fmt.Errorf("failed to read environment variables from flags: %w", err) + } + } + + if len(finalEnvVarMap) == 0 { + fmt.Println("No environment variables provided to set.") + return nil + } + wsId, err := l.Opts.GetWorkspaceId() if err != nil { return fmt.Errorf("failed to get workspace ID: %w", err) } - err = client.SetEnvVarOnWorkspace(wsId, envVarMap) + err = client.SetEnvVarOnWorkspace(wsId, finalEnvVarMap) if err != nil { - return fmt.Errorf("failed to set environment variables %v: %w", envVarMap, err) + return fmt.Errorf("failed to set environment variables %v: %w", finalEnvVarMap, err) } + fmt.Printf("Successfully set %d environment variable(s) on workspace %s\n", len(finalEnvVarMap), strconv.Itoa(wsId)) return nil } diff --git a/cli/cmd/set_env_vars_test.go b/cli/cmd/set_env_vars_test.go index 75f6b5d..5083103 100644 --- a/cli/cmd/set_env_vars_test.go +++ b/cli/cmd/set_env_vars_test.go @@ -68,7 +68,7 @@ var _ = Describe("SetEnvVars", func() { }) It("doesn't set environment variables", func() { err := e.SetEnvironmentVariables(mockClient) - Expect(err).To(MatchError("failed to parse environment variables: invalid environment variable argument: helloworld")) + Expect(err).To(MatchError("failed to parse environment variables from flags: invalid environment variable argument: helloworld")) }) }) diff --git a/docs/cs_set-env.md b/docs/cs_set-env.md index 74a9ddf..ebfb732 100644 --- a/docs/cs_set-env.md +++ b/docs/cs_set-env.md @@ -14,10 +14,10 @@ cs set-env [flags] ``` # Set single environment variable -$ /home/runner/.cache/go-build/14/142566baf6f47b381ccbdff9218d5f9501b1be7b016f5d73eacb871e2ca47b86-d/main set-env --workspace --env-var foo=bar +$ cs set-env --workspace --env-var foo=bar # Set multiple environment variables -$ /home/runner/.cache/go-build/14/142566baf6f47b381ccbdff9218d5f9501b1be7b016f5d73eacb871e2ca47b86-d/main set-env --workspace --env-var foo=bar --env-var hello=world +$ cs set-env --workspace --env-var foo=bar --env-var hello=world ``` ### Options @@ -39,4 +39,4 @@ $ /home/runner/.cache/go-build/14/142566baf6f47b381ccbdff9218d5f9501b1be7b016f5d * [cs](cs.md) - The Codesphere CLI -###### Auto generated by spf13/cobra on 6-Jun-2025 +###### Auto generated by spf13/cobra on 5-Jun-2025 diff --git a/go.mod b/go.mod index 77fd2a6..bf7ea8c 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( github.com/google/go-cmp v0.7.0 // indirect github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/joho/godotenv v1.5.1 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect diff --git a/go.sum b/go.sum index 3cc7642..a351f66 100644 --- a/go.sum +++ b/go.sum @@ -14,6 +14,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jedib0t/go-pretty/v6 v6.6.7 h1:m+LbHpm0aIAPLzLbMfn8dc3Ht8MW7lsSO4MPItz/Uuo= github.com/jedib0t/go-pretty/v6 v6.6.7/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=