From 9e9da8897440bd826278c780f8d2ade1b5321ddb Mon Sep 17 00:00:00 2001 From: Pablo Terradillos Date: Wed, 16 Jul 2025 09:15:42 -0300 Subject: [PATCH 01/14] Added support for passing values read from a file using --from-file --- cmd/esc/cli/env_set.go | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/cmd/esc/cli/env_set.go b/cmd/esc/cli/env_set.go index 6caf57e8..bdd8b143 100644 --- a/cmd/esc/cli/env_set.go +++ b/cmd/esc/cli/env_set.go @@ -5,9 +5,11 @@ package cli import ( "context" "fmt" + "os" "regexp" "strconv" "strings" + "unicode/utf8" "github.com/ccojocar/zxcvbn-go" "github.com/spf13/cobra" @@ -23,6 +25,7 @@ func newEnvSetCmd(env *envCommand) *cobra.Command { var plaintext bool var rawString bool var draft bool + var fromFile bool cmd := &cobra.Command{ Use: "set [/][/] ", @@ -60,7 +63,25 @@ func newEnvSetCmd(env *envCommand) *cobra.Command { return fmt.Errorf("path must contain at least one element") } - input := args[1] + var input string + if fromFile { + fileContent, readFileErr := os.ReadFile(args[1]) + + if readFileErr != nil { + return fmt.Errorf("could not read file: %w", readFileErr) + } + + if !utf8.Valid(fileContent) { + return fmt.Errorf("file contents must be valid UTF-8") + } + + rawString = true + + input = string(fileContent) + } else { + input = args[1] + } + var yamlValue yaml.Node if rawString { yamlValue.SetString(input) @@ -170,6 +191,7 @@ func newEnvSetCmd(env *envCommand) *cobra.Command { if err != nil { panic(err) } + cmd.Flags().BoolVar(&fromFile, "from-file", false, "If set, the key value is read from the specified file.") return cmd } From d369eef5523fb88a29a8b95aa4ee71ce45112b2a Mon Sep 17 00:00:00 2001 From: Pablo Terradillos Date: Sat, 19 Jul 2025 09:25:40 -0300 Subject: [PATCH 02/14] Use --file parameter in env set to be consistent with env edit behavior --- cmd/esc/cli/env_set.go | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/cmd/esc/cli/env_set.go b/cmd/esc/cli/env_set.go index bdd8b143..f7b4d741 100644 --- a/cmd/esc/cli/env_set.go +++ b/cmd/esc/cli/env_set.go @@ -5,6 +5,7 @@ package cli import ( "context" "fmt" + "io" "os" "regexp" "strconv" @@ -25,11 +26,11 @@ func newEnvSetCmd(env *envCommand) *cobra.Command { var plaintext bool var rawString bool var draft bool - var fromFile bool + var file string cmd := &cobra.Command{ Use: "set [/][/] ", - Args: cobra.RangeArgs(2, 3), + Args: cobra.RangeArgs(1, 3), Short: "Set a value within an environment.", Long: "Set a value within an environment\n" + "\n" + @@ -51,12 +52,14 @@ func newEnvSetCmd(env *envCommand) *cobra.Command { if ref.version != "" { return fmt.Errorf("the set command does not accept versions") } - if len(args) < 2 { + + if len(args) < 2 && file == "" { return fmt.Errorf("expected a path and a value") } path, err := resource.ParsePropertyPath(args[0]) if err != nil { + return fmt.Errorf("invalid path: %w", err) } if len(path) == 0 { @@ -64,20 +67,23 @@ func newEnvSetCmd(env *envCommand) *cobra.Command { } var input string - if fromFile { - fileContent, readFileErr := os.ReadFile(args[1]) - - if readFileErr != nil { - return fmt.Errorf("could not read file: %w", readFileErr) - } + if file != "" { + var content []byte + switch file { + case "-": + content, err = io.ReadAll(env.esc.stdin) + default: + content, err = os.ReadFile(file) + if err != nil { + return fmt.Errorf("could not read file: %w", err) + } - if !utf8.Valid(fileContent) { - return fmt.Errorf("file contents must be valid UTF-8") + if !utf8.Valid(content) { + return fmt.Errorf("file content must be valid UTF-8") + } } - rawString = true - - input = string(fileContent) + input = string(content) } else { input = args[1] } @@ -191,7 +197,7 @@ func newEnvSetCmd(env *envCommand) *cobra.Command { if err != nil { panic(err) } - cmd.Flags().BoolVar(&fromFile, "from-file", false, "If set, the key value is read from the specified file.") + cmd.Flags().StringVar(&file, "file", "", "If set, the key value is read from the specified file. Pass `-` to read from standard input.") return cmd } From 0fde71c5eedfc8fdee36ab2c5151288e2535caba Mon Sep 17 00:00:00 2001 From: Pablo Terradillos Date: Sat, 19 Jul 2025 09:26:51 -0300 Subject: [PATCH 03/14] Do not add an extra \n if the value already contains one when using --value string --- cmd/esc/cli/env_open.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cmd/esc/cli/env_open.go b/cmd/esc/cli/env_open.go index 8c06eeff..2da66143 100644 --- a/cmd/esc/cli/env_open.go +++ b/cmd/esc/cli/env_open.go @@ -7,6 +7,7 @@ import ( "encoding/json" "fmt" "io" + "strings" "time" "github.com/pulumi/esc" @@ -138,7 +139,12 @@ func (env *envCommand) renderValue( } return nil case "string": - fmt.Fprintf(out, "%v\n", val.ToString(!showSecrets)) + s := val.ToString(!showSecrets) + if strings.HasSuffix(s, "\n") { + fmt.Fprintf(out, "%v", s) + } else { + fmt.Fprintf(out, "%v\n", s) + } return nil default: // NOTE: we shouldn't get here. This was checked at the beginning of the function. From 41a8665fac37a0943bc954f31c775de5b941bc33 Mon Sep 17 00:00:00 2001 From: Pablo Terradillos Date: Sat, 19 Jul 2025 09:31:45 -0300 Subject: [PATCH 04/14] Add string value option to help for --value in get and open --- cmd/esc/cli/env_get.go | 2 +- cmd/esc/cli/env_open.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/esc/cli/env_get.go b/cmd/esc/cli/env_get.go index c568a832..125d4e91 100644 --- a/cmd/esc/cli/env_get.go +++ b/cmd/esc/cli/env_get.go @@ -132,7 +132,7 @@ func newEnvGetCmd(env *envCommand) *cobra.Command { "Set to print just the definition.") cmd.Flags().StringVar( &value, "value", "", - "Set to print just the value in the given format. May be 'dotenv', 'json', 'detailed', or 'shell'") + "Set to print just the value in the given format. May be 'dotenv', 'json', 'detailed', 'shell' or 'string'") cmd.Flags().BoolVar( &showSecrets, "show-secrets", false, "Show static secrets in plaintext rather than ciphertext") diff --git a/cmd/esc/cli/env_open.go b/cmd/esc/cli/env_open.go index 2da66143..7b225d5a 100644 --- a/cmd/esc/cli/env_open.go +++ b/cmd/esc/cli/env_open.go @@ -79,7 +79,7 @@ func newEnvOpenCmd(envcmd *envCommand) *cobra.Command { "the lifetime of the opened environment in the form HhMm (e.g. 2h, 1h30m, 15m)") cmd.Flags().StringVarP( &format, "format", "f", "json", - "the output format to use. May be 'dotenv', 'json', 'yaml', 'detailed', or 'shell'") + "the output format to use. May be 'dotenv', 'json', 'yaml', 'detailed', 'shell' or 'string'") return cmd } From 438af61c19d613d204525fea134c4a3a36d96c76 Mon Sep 17 00:00:00 2001 From: Pablo Terradillos Date: Sat, 19 Jul 2025 09:49:46 -0300 Subject: [PATCH 05/14] Added break to prevent default execution --- cmd/esc/cli/env_set.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/esc/cli/env_set.go b/cmd/esc/cli/env_set.go index f7b4d741..f29ceb1f 100644 --- a/cmd/esc/cli/env_set.go +++ b/cmd/esc/cli/env_set.go @@ -72,6 +72,7 @@ func newEnvSetCmd(env *envCommand) *cobra.Command { switch file { case "-": content, err = io.ReadAll(env.esc.stdin) + break default: content, err = os.ReadFile(file) if err != nil { From 584f6de603d02cd2ef33f359cd4b00830daeaf48 Mon Sep 17 00:00:00 2001 From: Pablo Terradillos Date: Sat, 19 Jul 2025 09:52:23 -0300 Subject: [PATCH 06/14] added --file from esc env set to changelog --- CHANGELOG_PENDING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index c0bba8b3..b6d3acec 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -2,6 +2,7 @@ - `--draft` flag for `esc env set`, `esc env edit`, `esc env versions rollback` to create a change request rather than updating directly. **Warning: this feature is in preview, limited to specific orgs, and subject to change.** [#552](https://github.com/pulumi/esc/pull/552) +- `esc env set` now supports --file parameter to read content from a file or stdin [#556](https://github.com/pulumi/esc/pull/556) ### Bug Fixes From e4affaaed11a502e9a78a28e144af31f392a9670 Mon Sep 17 00:00:00 2001 From: Pablo Terradillos Date: Sat, 19 Jul 2025 09:54:48 -0300 Subject: [PATCH 07/14] Handle err when reading from stdin --- cmd/esc/cli/env_set.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/esc/cli/env_set.go b/cmd/esc/cli/env_set.go index f29ceb1f..96fa4d70 100644 --- a/cmd/esc/cli/env_set.go +++ b/cmd/esc/cli/env_set.go @@ -72,7 +72,9 @@ func newEnvSetCmd(env *envCommand) *cobra.Command { switch file { case "-": content, err = io.ReadAll(env.esc.stdin) - break + if err != nil { + return fmt.Errorf("could not read from stdin: %w", err) + } default: content, err = os.ReadFile(file) if err != nil { From 922d40468a00252508167eee1ab9f61afdc32f09 Mon Sep 17 00:00:00 2001 From: Pablo Terradillos Date: Sat, 19 Jul 2025 14:24:37 -0300 Subject: [PATCH 08/14] Check that content is utf-8 valid also when coming from stdin --- cmd/esc/cli/env_set.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/esc/cli/env_set.go b/cmd/esc/cli/env_set.go index 96fa4d70..89219312 100644 --- a/cmd/esc/cli/env_set.go +++ b/cmd/esc/cli/env_set.go @@ -80,10 +80,10 @@ func newEnvSetCmd(env *envCommand) *cobra.Command { if err != nil { return fmt.Errorf("could not read file: %w", err) } + } - if !utf8.Valid(content) { - return fmt.Errorf("file content must be valid UTF-8") - } + if !utf8.Valid(content) { + return fmt.Errorf("file content must be valid UTF-8") } input = string(content) From 089db3230fce1243dc065083f60703ab0b4c5080 Mon Sep 17 00:00:00 2001 From: Pablo Terradillos Date: Tue, 22 Jul 2025 09:09:13 -0300 Subject: [PATCH 09/14] Improved handling args when using --file and added tests" --- cmd/esc/cli/env_set.go | 8 ++-- cmd/esc/cli/fs.go | 5 +++ cmd/esc/cli/testdata/env-set-file.yaml | 56 ++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 cmd/esc/cli/testdata/env-set-file.yaml diff --git a/cmd/esc/cli/env_set.go b/cmd/esc/cli/env_set.go index 89219312..6e520526 100644 --- a/cmd/esc/cli/env_set.go +++ b/cmd/esc/cli/env_set.go @@ -6,7 +6,6 @@ import ( "context" "fmt" "io" - "os" "regexp" "strconv" "strings" @@ -53,8 +52,11 @@ func newEnvSetCmd(env *envCommand) *cobra.Command { return fmt.Errorf("the set command does not accept versions") } - if len(args) < 2 && file == "" { + switch { + case file == "" && len(args) < 2: return fmt.Errorf("expected a path and a value") + case file != "" && len(args) < 1: + return fmt.Errorf("expected a path") } path, err := resource.ParsePropertyPath(args[0]) @@ -76,7 +78,7 @@ func newEnvSetCmd(env *envCommand) *cobra.Command { return fmt.Errorf("could not read from stdin: %w", err) } default: - content, err = os.ReadFile(file) + content, err = env.esc.fs.ReadFile(file) if err != nil { return fmt.Errorf("could not read file: %w", err) } diff --git a/cmd/esc/cli/fs.go b/cmd/esc/cli/fs.go index b16df08c..d157b13c 100644 --- a/cmd/esc/cli/fs.go +++ b/cmd/esc/cli/fs.go @@ -15,6 +15,7 @@ type escFS interface { fs.FS CreateTemp(dir, pattern string) (string, io.ReadWriteCloser, error) + ReadFile(filename string) ([]byte, error) Remove(name string) error } @@ -41,3 +42,7 @@ func (defaultFS) CreateTemp(dir, pattern string) (string, io.ReadWriteCloser, er func (defaultFS) Remove(name string) error { return os.Remove(name) } + +func (defaultFS) ReadFile(name string) ([]byte, error) { + return os.ReadFile(name) +} diff --git a/cmd/esc/cli/testdata/env-set-file.yaml b/cmd/esc/cli/testdata/env-set-file.yaml new file mode 100644 index 00000000..101fc07b --- /dev/null +++ b/cmd/esc/cli/testdata/env-set-file.yaml @@ -0,0 +1,56 @@ +run: | + echo 'hello' | esc env set default/test foo -f=- + esc env get default/test + echo 'hello world' >test.file + esc env set default/test foo -f=test.file + esc env get default/test + esc env set default/test -f=test.file || echo -n "" + esc env set -f=test.file || echo -n "" +environments: + test-user/default/test: + values: + foo: bar + +--- +> esc env set default/test foo -f=- +> esc env get default/test +# Value +```json +{ + "foo": "hello" +} +``` +# Definition +```yaml +values: + foo: hello + +``` + +> esc env set default/test foo -f=test.file +> esc env get default/test +# Value +```json +{ + "foo": "hello world" +} +``` +# Definition +```yaml +values: + foo: hello world + +``` + +> esc env set default/test -f=test.file +> esc env set -f=test.file + +--- +> esc env set default/test foo -f=- +> esc env get default/test +> esc env set default/test foo -f=test.file +> esc env get default/test +> esc env set default/test -f=test.file +Error: expected a path +> esc env set -f=test.file +Error: accepts between 1 and 3 arg(s), received 0 From 681595420755236e866e276a3ece719585f7f126 Mon Sep 17 00:00:00 2001 From: Pablo Terradillos Date: Tue, 22 Jul 2025 09:31:59 -0300 Subject: [PATCH 10/14] added test for --file with --string --- cmd/esc/cli/testdata/env-set-file.yaml | 28 ++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/cmd/esc/cli/testdata/env-set-file.yaml b/cmd/esc/cli/testdata/env-set-file.yaml index 101fc07b..0807e700 100644 --- a/cmd/esc/cli/testdata/env-set-file.yaml +++ b/cmd/esc/cli/testdata/env-set-file.yaml @@ -1,9 +1,12 @@ run: | - echo 'hello' | esc env set default/test foo -f=- + echo "hello" | esc env set default/test foo -f=- esc env get default/test - echo 'hello world' >test.file + echo "hello world" >test.file esc env set default/test foo -f=test.file esc env get default/test + esc env get default/test --value string + esc env set default/test foo -f=test.file --string + esc env get default/test esc env set default/test -f=test.file || echo -n "" esc env set -f=test.file || echo -n "" environments: @@ -42,6 +45,24 @@ values: ``` +> esc env get default/test --value string +"foo"="hello world" +> esc env set default/test foo -f=test.file --string +> esc env get default/test +# Value +```json +{ + "foo": "hello world\n" +} +``` +# Definition +```yaml +values: + foo: | + hello world + +``` + > esc env set default/test -f=test.file > esc env set -f=test.file @@ -50,6 +71,9 @@ values: > esc env get default/test > esc env set default/test foo -f=test.file > esc env get default/test +> esc env get default/test --value string +> esc env set default/test foo -f=test.file --string +> esc env get default/test > esc env set default/test -f=test.file Error: expected a path > esc env set -f=test.file From 17c7a464083d94b27f701dec12403acf3c686598 Mon Sep 17 00:00:00 2001 From: Pablo Terradillos Date: Wed, 23 Jul 2025 17:25:27 -0300 Subject: [PATCH 11/14] Added tests for the case of trying to use binary files in esc env set --file --- cmd/esc/cli/testdata/env-set-file.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cmd/esc/cli/testdata/env-set-file.yaml b/cmd/esc/cli/testdata/env-set-file.yaml index 0807e700..86f66701 100644 --- a/cmd/esc/cli/testdata/env-set-file.yaml +++ b/cmd/esc/cli/testdata/env-set-file.yaml @@ -9,6 +9,11 @@ run: | esc env get default/test esc env set default/test -f=test.file || echo -n "" esc env set -f=test.file || echo -n "" + esc env set default/test -f=binary foo || echo -n "" + esc env set -f=- default/test foo esc env set default/test -f=test.file > esc env set -f=test.file +> esc env set default/test -f=binary foo +> esc env set -f=- default/test foo --- > esc env set default/test foo -f=- @@ -78,3 +85,7 @@ values: Error: expected a path > esc env set -f=test.file Error: accepts between 1 and 3 arg(s), received 0 +> esc env set default/test -f=binary foo +Error: file content must be valid UTF-8 +> esc env set -f=- default/test foo +Error: file content must be valid UTF-8 From 089237e308048d13810b6ddc00a800f56cfe81d2 Mon Sep 17 00:00:00 2001 From: Pablo Terradillos Date: Wed, 23 Jul 2025 17:31:21 -0300 Subject: [PATCH 12/14] removed pending changes that were already merged to changelog --- CHANGELOG_PENDING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index b6d3acec..415118ab 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -1,8 +1,8 @@ ### Improvements +- `esc env set` now supports --file parameter to read content from a file or stdin [#556](https://github.com/pulumi/esc/pull/556) - `--draft` flag for `esc env set`, `esc env edit`, `esc env versions rollback` to create a change request rather than updating directly. **Warning: this feature is in preview, limited to specific orgs, and subject to change.** [#552](https://github.com/pulumi/esc/pull/552) -- `esc env set` now supports --file parameter to read content from a file or stdin [#556](https://github.com/pulumi/esc/pull/556) ### Bug Fixes From cf3499e2b39774998752b456db78d8185aa16af7 Mon Sep 17 00:00:00 2001 From: Pablo Terradillos Date: Wed, 23 Jul 2025 17:57:32 -0300 Subject: [PATCH 13/14] Added tests with multiline files for esc env set --file --- cmd/esc/cli/testdata/env-set-file.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/cmd/esc/cli/testdata/env-set-file.yaml b/cmd/esc/cli/testdata/env-set-file.yaml index 86f66701..2f1ba0a6 100644 --- a/cmd/esc/cli/testdata/env-set-file.yaml +++ b/cmd/esc/cli/testdata/env-set-file.yaml @@ -11,9 +11,15 @@ run: | esc env set -f=test.file || echo -n "" esc env set default/test -f=binary foo || echo -n "" esc env set -f=- default/test foo esc env set -f=test.file > esc env set default/test -f=binary foo > esc env set -f=- default/test foo +> esc env set --string -f=multiline default/test foo +> esc env get default/test foo --value string +this is a +multiline +file --- > esc env set default/test foo -f=- @@ -89,3 +100,5 @@ Error: accepts between 1 and 3 arg(s), received 0 Error: file content must be valid UTF-8 > esc env set -f=- default/test foo Error: file content must be valid UTF-8 +> esc env set --string -f=multiline default/test foo +> esc env get default/test foo --value string From 5c487976ad2b7e0188a38d5b83751e022deed763 Mon Sep 17 00:00:00 2001 From: Pablo Terradillos Date: Wed, 23 Jul 2025 18:01:14 -0300 Subject: [PATCH 14/14] changed help test for --file in env set --- cmd/esc/cli/env_set.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/esc/cli/env_set.go b/cmd/esc/cli/env_set.go index 6e520526..04ae01e2 100644 --- a/cmd/esc/cli/env_set.go +++ b/cmd/esc/cli/env_set.go @@ -195,6 +195,7 @@ func newEnvSetCmd(env *envCommand) *cobra.Command { cmd.Flags().BoolVar( &rawString, "string", false, "true to treat the value as a string rather than attempting to parse it as YAML") + cmd.Flags().StringVarP(&file, "file", "f", "", "If set, the value is read from the specified file. Pass `-` to read from standard input.") cmd.Flags().BoolVar( &draft, "draft", false, "true to create a draft rather than saving changes directly, returns a submitted Change Request ID and its URL") @@ -202,7 +203,6 @@ func newEnvSetCmd(env *envCommand) *cobra.Command { if err != nil { panic(err) } - cmd.Flags().StringVar(&file, "file", "", "If set, the key value is read from the specified file. Pass `-` to read from standard input.") return cmd }