This commit is contained in:
Mike Farah 2021-01-09 12:06:19 +11:00
parent 34bc33d5c5
commit 7a184bef78
8 changed files with 173 additions and 26 deletions

View File

@ -1,21 +1,76 @@
## Read string environment variable
Running
```bash
myenv="cat meow" yq eval --null-input '.a = env(myenv)'
```
will output
```yaml
a: cat meow
```
## Read boolean environment variable
Running
```bash
myenv="true" yq eval --null-input '.a = env(myenv)'
```
will output
```yaml
a: true
```
## Read numeric environment variable
Running
```bash
myenv="12" yq eval --null-input '.a = env(myenv)'
```
will output
```yaml
a: 12
```
## Read yaml environment variable
Running
```bash
myenv="{b: fish}" yq eval --null-input '.a = env(myenv)'
```
will output
```yaml
a: {b: fish}
```
## Read boolean environment variable as a string ## Read boolean environment variable as a string
Running Running
```bash ```bash
myenv="true" yq eval --null-input 'strenv(myenv)' myenv="true" yq eval --null-input '.a = strenv(myenv)'
``` ```
will output will output
```yaml ```yaml
12 a: "true"
``` ```
## Read numeric environment variable as a string ## Read numeric environment variable as a string
Running Running
```bash ```bash
myenv="12" yq eval --null-input 'strenv(myenv)' myenv="12" yq eval --null-input '.a = strenv(myenv)'
``` ```
will output will output
```yaml ```yaml
12 a: "12"
```
## Dynamic key lookup with environment variable
Given a sample.yml file of:
```yaml
cat: meow
dog: woof
```
then
```bash
myenv="cat" yq eval '.[env(myenv)]' sample.yml
```
will output
```yaml
meow
``` ```

View File

@ -48,6 +48,24 @@ will output
frog frog
``` ```
## Dynamic keys
Expressions within [] can be used to dynamically lookup / calculate keys
Given a sample.yml file of:
```yaml
b: apple
apple: crispy yum
banana: soft yum
```
then
```bash
yq eval '.[.b]' sample.yml
```
will output
```yaml
crispy yum
```
## Children don't exist ## Children don't exist
Nodes are added dynamically while traversing Nodes are added dynamically while traversing

View File

@ -3,13 +3,14 @@ package yqlib
import ( import (
"container/list" "container/list"
"os" "os"
"strings"
yaml "gopkg.in/yaml.v3" yaml "gopkg.in/yaml.v3"
) )
// type EnvOpPreferences struct { type EnvOpPreferences struct {
// StringValue bool StringValue bool
// } }
func EnvOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNode) (*list.List, error) { func EnvOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNode) (*list.List, error) {
envName := pathNode.Operation.CandidateNode.Node.Value envName := pathNode.Operation.CandidateNode.Node.Value
@ -17,15 +18,34 @@ func EnvOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNo
rawValue := os.Getenv(envName) rawValue := os.Getenv(envName)
preferences := pathNode.Operation.Preferences.(*EnvOpPreferences)
var node *yaml.Node
if preferences.StringValue {
node = &yaml.Node{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: rawValue,
}
} else {
var dataBucket yaml.Node
decoder := yaml.NewDecoder(strings.NewReader(rawValue))
errorReading := decoder.Decode(&dataBucket)
if errorReading != nil {
return nil, errorReading
}
//first node is a doc
node = UnwrapDoc(&dataBucket)
}
log.Debug("ENV tag", node.Tag)
log.Debug("ENV value", node.Value)
log.Debug("ENV Kind", node.Kind)
target := &CandidateNode{ target := &CandidateNode{
Path: make([]interface{}, 0), Path: make([]interface{}, 0),
Document: 0, Document: 0,
Filename: "", Filename: "",
Node: &yaml.Node{ Node: node,
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: rawValue,
},
} }
return nodeToMap(target), nil return nodeToMap(target), nil

View File

@ -5,20 +5,61 @@ import (
) )
var envOperatorScenarios = []expressionScenario{ var envOperatorScenarios = []expressionScenario{
{
description: "Read string environment variable",
environmentVariable: "cat meow",
expression: `.a = env(myenv)`,
expected: []string{
"D0, P[], ()::a: cat meow\n",
},
},
{
description: "Read boolean environment variable",
environmentVariable: "true",
expression: `.a = env(myenv)`,
expected: []string{
"D0, P[], ()::a: true\n",
},
},
{
description: "Read numeric environment variable",
environmentVariable: "12",
expression: `.a = env(myenv)`,
expected: []string{
"D0, P[], ()::a: 12\n",
},
},
{
description: "Read yaml environment variable",
environmentVariable: "{b: fish}",
expression: `.a = env(myenv)`,
expected: []string{
"D0, P[], ()::a: {b: fish}\n",
},
},
{ {
description: "Read boolean environment variable as a string", description: "Read boolean environment variable as a string",
environmentVariable: "true", environmentVariable: "true",
expression: `strenv(myenv)`, expression: `.a = strenv(myenv)`,
expected: []string{ expected: []string{
"D0, P[], (!!str)::\"true\"\n", "D0, P[], ()::a: \"true\"\n",
}, },
}, },
{ {
description: "Read numeric environment variable as a string", description: "Read numeric environment variable as a string",
environmentVariable: "12", environmentVariable: "12",
expression: `strenv(myenv)`, expression: `.a = strenv(myenv)`,
expected: []string{ expected: []string{
"D0, P[], (!!str)::\"12\"\n", "D0, P[], ()::a: \"12\"\n",
},
},
{
description: "Dynamic key lookup with environment variable",
environmentVariable: "cat",
document: `{cat: meow, dog: woof}`,
expression: `.[env(myenv)]`,
expected: []string{
"D0, P[cat], (!!str)::meow\n",
}, },
}, },
} }

View File

@ -54,6 +54,15 @@ var traversePathOperatorScenarios = []expressionScenario{
"D0, P[{}], (!!str)::frog\n", "D0, P[{}], (!!str)::frog\n",
}, },
}, },
{
description: "Dynamic keys",
subdescription: `Expressions within [] can be used to dynamically lookup / calculate keys`,
document: `{b: apple, apple: crispy yum, banana: soft yum}`,
expression: `.[.b]`,
expected: []string{
"D0, P[apple], (!!str)::crispy yum\n",
},
},
{ {
description: "Children don't exist", description: "Children don't exist",
subdescription: "Nodes are added dynamically while traversing", subdescription: "Nodes are added dynamically while traversing",

View File

@ -17,7 +17,7 @@ import (
type expressionScenario struct { type expressionScenario struct {
description string description string
subdescription string subdescription string
environmentVariable string environmentVariable string
document string document string
document2 string document2 string
expression string expression string
@ -170,9 +170,10 @@ func documentInput(w *bufio.Writer, s expressionScenario) (string, string) {
envCommand := "" envCommand := ""
if(s.environmentVariable != "") { if s.environmentVariable != "" {
envCommand = fmt.Sprintf("myenv=\"%v\" ", s.environmentVariable) envCommand = fmt.Sprintf("myenv=\"%v\" ", s.environmentVariable)
} os.Setenv("myenv", s.environmentVariable)
}
if s.document != "" { if s.document != "" {
if s.dontFormatInputForDoc { if s.dontFormatInputForDoc {
@ -201,7 +202,6 @@ func documentInput(w *bufio.Writer, s expressionScenario) (string, string) {
writeOrPanic(w, "then\n") writeOrPanic(w, "then\n")
if s.expression != "" { if s.expression != "" {
writeOrPanic(w, fmt.Sprintf("```bash\n%vyq %v '%v' %v\n```\n", envCommand, command, s.expression, files)) writeOrPanic(w, fmt.Sprintf("```bash\n%vyq %v '%v' %v\n```\n", envCommand, command, s.expression, files))
} else { } else {

View File

@ -181,17 +181,20 @@ func stringValue(wrapped bool) lex.Action {
func envOp(strenv bool) lex.Action { func envOp(strenv bool) lex.Action {
return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { return func(s *lex.Scanner, m *machines.Match) (interface{}, error) {
value := string(m.Bytes) value := string(m.Bytes)
preferences := &EnvOpPreferences{}
if strenv { if strenv {
// strenv( ) // strenv( )
value = value[7:len(value)-1] value = value[7 : len(value)-1]
} else { preferences.StringValue = true
} else {
//env( ) //env( )
value = value[4:len(value)-1] value = value[4 : len(value)-1]
} }
envOperation := CreateValueOperation(value, value) envOperation := CreateValueOperation(value, value)
envOperation.OperationType = EnvOp envOperation.OperationType = EnvOp
envOperation.Preferences = preferences
return &Token{TokenType: OperationToken, Operation: envOperation}, nil return &Token{TokenType: OperationToken, Operation: envOperation}, nil
} }
@ -286,6 +289,7 @@ func initLexer() (*lex.Lexer, error) {
lexer.Add([]byte(`"[^"]*"`), stringValue(true)) lexer.Add([]byte(`"[^"]*"`), stringValue(true))
lexer.Add([]byte(`strenv\([^\)]+\)`), envOp(true)) lexer.Add([]byte(`strenv\([^\)]+\)`), envOp(true))
lexer.Add([]byte(`env\([^\)]+\)`), envOp(false))
lexer.Add([]byte(`\[`), literalToken(OpenCollect, false)) lexer.Add([]byte(`\[`), literalToken(OpenCollect, false))
lexer.Add([]byte(`\]`), literalToken(CloseCollect, true)) lexer.Add([]byte(`\]`), literalToken(CloseCollect, true))