mirror of
https://github.com/taigrr/yq
synced 2025-01-18 04:53:17 -08:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2bd2a85a4c | ||
|
|
ceb76e5c17 | ||
|
|
44322f0248 | ||
|
|
0347516d82 | ||
|
|
a46386e093 | ||
|
|
f5c3beb159 | ||
|
|
9864afc4e7 | ||
|
|
69fae2d9cb | ||
|
|
83c13ce392 | ||
|
|
d83c46eec2 | ||
|
|
65802f9e0e | ||
|
|
07309e1685 | ||
|
|
24e906bae6 | ||
|
|
f084f2bb23 | ||
|
|
96a4161a92 | ||
|
|
5cc01e43bc | ||
|
|
9de2573009 | ||
|
|
29521f2e3e | ||
|
|
af5724ba29 | ||
|
|
0a39d29c53 |
10
README.md
10
README.md
@@ -44,7 +44,8 @@ rm /etc/myfile.tmp
|
||||
```
|
||||
|
||||
### On Ubuntu 16.04 or higher from Debian package:
|
||||
```
|
||||
```sh
|
||||
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CC86BB64
|
||||
sudo add-apt-repository ppa:rmescandon/yq
|
||||
sudo apt update
|
||||
sudo apt install yq -y
|
||||
@@ -82,7 +83,7 @@ yq() {
|
||||
- [List matching paths of a given path expression](https://mikefarah.gitbook.io/yq/commands/read#path-only)
|
||||
- Update a yaml file given a [path expression](https://mikefarah.gitbook.io/yq/commands/write-update#basic) or [script file](https://mikefarah.gitbook.io/yq/commands/write-update#basic)
|
||||
- Update creates any missing entries in the path on the fly
|
||||
- Deeply compare yaml files
|
||||
- Deeply [compare](https://mikefarah.gitbook.io/yq/commands/compare) yaml files
|
||||
- Keeps yaml formatting and comments when updating
|
||||
- [Validate a yaml file](https://mikefarah.gitbook.io/yq/commands/validate)
|
||||
- Create a yaml file given a [deep path and value](https://mikefarah.gitbook.io/yq/commands/create#creating-a-simple-yaml-file) or a [script file](https://mikefarah.gitbook.io/yq/commands/create#creating-using-a-create-script)
|
||||
@@ -114,8 +115,9 @@ Available Commands:
|
||||
|
||||
Flags:
|
||||
-h, --help help for yq
|
||||
-I, --indent int sets indent level for output (default 2)
|
||||
-P, --prettyPrint pretty print
|
||||
-j, --tojson output as json
|
||||
-j, --tojson output as json. By default it prints a json document in one line, use the prettyPrint flag to print a formatted doc.
|
||||
-v, --verbose verbose mode
|
||||
-V, --version Print version information and quit
|
||||
|
||||
@@ -124,8 +126,6 @@ Use "yq [command] --help" for more information about a command.
|
||||
|
||||
## Contribute
|
||||
|
||||
**Note: v3 is currently in progress - for the moment I won't be accepting new feature PRs until v3 is ready :)**
|
||||
|
||||
1. `scripts/devtools.sh`
|
||||
2. `make [local] vendor`
|
||||
3. add unit tests
|
||||
|
||||
@@ -91,7 +91,7 @@ func TestReadCmd(t *testing.T) {
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
test.AssertResult(t, "2", result.Output)
|
||||
test.AssertResult(t, "2\n", result.Output)
|
||||
}
|
||||
|
||||
func TestCompareCmd(t *testing.T) {
|
||||
@@ -153,16 +153,16 @@ func TestValidateCmd(t *testing.T) {
|
||||
|
||||
func TestReadWithAdvancedFilterCmd(t *testing.T) {
|
||||
cmd := getRootCommand()
|
||||
result := test.RunCmd(cmd, "read -v ../examples/sample.yaml b.e(name==sam).value")
|
||||
result := test.RunCmd(cmd, "read ../examples/sample.yaml b.e(name==sam).value")
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
test.AssertResult(t, "4", result.Output)
|
||||
test.AssertResult(t, "4\n", result.Output)
|
||||
}
|
||||
|
||||
func TestReadWithAdvancedFilterMapCmd(t *testing.T) {
|
||||
cmd := getRootCommand()
|
||||
result := test.RunCmd(cmd, "read -v ../examples/sample.yaml b.e[name==fr*]")
|
||||
result := test.RunCmd(cmd, "read ../examples/sample.yaml b.e[name==fr*]")
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
@@ -183,11 +183,11 @@ func TestReadWithKeyAndValueCmd(t *testing.T) {
|
||||
|
||||
func TestReadArrayCmd(t *testing.T) {
|
||||
cmd := getRootCommand()
|
||||
result := test.RunCmd(cmd, "read -p pv ../examples/sample.yaml b.e.1.name")
|
||||
result := test.RunCmd(cmd, "read -p pv ../examples/sample.yaml b.e[1].name")
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
test.AssertResult(t, "b.e.1.name: sam\n", result.Output)
|
||||
test.AssertResult(t, "b.e.[1].name: sam\n", result.Output)
|
||||
}
|
||||
|
||||
func TestReadDeepSplatCmd(t *testing.T) {
|
||||
@@ -226,7 +226,7 @@ func TestReadWithKeyCmd(t *testing.T) {
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
test.AssertResult(t, "b.c", result.Output)
|
||||
test.AssertResult(t, "b.c\n", result.Output)
|
||||
}
|
||||
|
||||
func TestReadAnchorsCmd(t *testing.T) {
|
||||
@@ -235,7 +235,7 @@ func TestReadAnchorsCmd(t *testing.T) {
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
test.AssertResult(t, "1", result.Output)
|
||||
test.AssertResult(t, "1\n", result.Output)
|
||||
}
|
||||
|
||||
func TestReadAnchorsWithKeyAndValueCmd(t *testing.T) {
|
||||
@@ -279,7 +279,7 @@ func TestReadMergeAnchorsOriginalCmd(t *testing.T) {
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
test.AssertResult(t, "original", result.Output)
|
||||
test.AssertResult(t, "original\n", result.Output)
|
||||
}
|
||||
|
||||
func TestReadMergeAnchorsExplodeJsonCmd(t *testing.T) {
|
||||
@@ -318,7 +318,52 @@ pointer: *value-pointer`
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
expectedOutput := `the value`
|
||||
expectedOutput := "the value\n"
|
||||
test.AssertResult(t, expectedOutput, result.Output)
|
||||
}
|
||||
|
||||
func TestReadMergeAnchorsExplodeSimpleArrayCmd(t *testing.T) {
|
||||
content := `- things`
|
||||
filename := test.WriteTempYamlFile(content)
|
||||
defer test.RemoveTempYamlFile(filename)
|
||||
|
||||
cmd := getRootCommand()
|
||||
result := test.RunCmd(cmd, fmt.Sprintf("read -X %s", filename))
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
expectedOutput := `- things
|
||||
`
|
||||
test.AssertResult(t, expectedOutput, result.Output)
|
||||
}
|
||||
|
||||
func TestReadNumberKeyJsonCmd(t *testing.T) {
|
||||
content := `data: {"40433437326": 10.833332}`
|
||||
filename := test.WriteTempYamlFile(content)
|
||||
defer test.RemoveTempYamlFile(filename)
|
||||
|
||||
cmd := getRootCommand()
|
||||
result := test.RunCmd(cmd, fmt.Sprintf("read -j %s", filename))
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
expectedOutput := `{"data":{"40433437326":10.833332}}
|
||||
`
|
||||
test.AssertResult(t, expectedOutput, result.Output)
|
||||
}
|
||||
|
||||
func TestReadMergeAnchorsExplodeSimpleArrayJsonCmd(t *testing.T) {
|
||||
content := `- things`
|
||||
filename := test.WriteTempYamlFile(content)
|
||||
defer test.RemoveTempYamlFile(filename)
|
||||
|
||||
cmd := getRootCommand()
|
||||
result := test.RunCmd(cmd, fmt.Sprintf("read -j %s", filename))
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
expectedOutput := `["things"]
|
||||
`
|
||||
test.AssertResult(t, expectedOutput, result.Output)
|
||||
}
|
||||
|
||||
@@ -333,7 +378,7 @@ pointer: *value-pointer`
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
expectedOutput := `the value`
|
||||
expectedOutput := "the value\n"
|
||||
test.AssertResult(t, expectedOutput, result.Output)
|
||||
}
|
||||
|
||||
@@ -388,7 +433,7 @@ func TestReadMergeAnchorsOverrideCmd(t *testing.T) {
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
test.AssertResult(t, "ice", result.Output)
|
||||
test.AssertResult(t, "ice\n", result.Output)
|
||||
}
|
||||
|
||||
func TestReadMergeAnchorsPrefixMatchCmd(t *testing.T) {
|
||||
@@ -410,7 +455,7 @@ func TestReadMergeAnchorsListOriginalCmd(t *testing.T) {
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
test.AssertResult(t, "original", result.Output)
|
||||
test.AssertResult(t, "original\n", result.Output)
|
||||
}
|
||||
|
||||
func TestReadMergeAnchorsListOverrideInListCmd(t *testing.T) {
|
||||
@@ -419,7 +464,7 @@ func TestReadMergeAnchorsListOverrideInListCmd(t *testing.T) {
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
test.AssertResult(t, "coconut", result.Output)
|
||||
test.AssertResult(t, "coconut\n", result.Output)
|
||||
}
|
||||
|
||||
func TestReadMergeAnchorsListOverrideCmd(t *testing.T) {
|
||||
@@ -428,7 +473,7 @@ func TestReadMergeAnchorsListOverrideCmd(t *testing.T) {
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
test.AssertResult(t, "newbar", result.Output)
|
||||
test.AssertResult(t, "newbar\n", result.Output)
|
||||
}
|
||||
|
||||
func TestReadInvalidDocumentIndexCmd(t *testing.T) {
|
||||
@@ -470,7 +515,7 @@ func TestReadMultiCmd(t *testing.T) {
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
test.AssertResult(t, "here", result.Output)
|
||||
test.AssertResult(t, "here\n", result.Output)
|
||||
}
|
||||
|
||||
func TestReadMultiWithKeyAndValueCmd(t *testing.T) {
|
||||
@@ -491,7 +536,8 @@ func TestReadMultiAllCmd(t *testing.T) {
|
||||
test.AssertResult(t,
|
||||
`first document
|
||||
second document
|
||||
third document`, result.Output)
|
||||
third document
|
||||
`, result.Output)
|
||||
}
|
||||
|
||||
func TestReadMultiAllWithKeyAndValueCmd(t *testing.T) {
|
||||
@@ -513,7 +559,7 @@ func TestReadCmd_ArrayYaml(t *testing.T) {
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
test.AssertResult(t, "false", result.Output)
|
||||
test.AssertResult(t, "false\n", result.Output)
|
||||
}
|
||||
|
||||
func TestReadEmptyContentCmd(t *testing.T) {
|
||||
@@ -530,6 +576,28 @@ func TestReadEmptyContentCmd(t *testing.T) {
|
||||
test.AssertResult(t, expectedOutput, result.Output)
|
||||
}
|
||||
|
||||
func TestReadEmptyNodesPrintPathCmd(t *testing.T) {
|
||||
content := `map:
|
||||
that: {}
|
||||
array:
|
||||
great: []
|
||||
null:
|
||||
indeed: ~`
|
||||
filename := test.WriteTempYamlFile(content)
|
||||
defer test.RemoveTempYamlFile(filename)
|
||||
|
||||
cmd := getRootCommand()
|
||||
result := test.RunCmd(cmd, fmt.Sprintf("read %s -ppv **", filename))
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
expectedOutput := `map.that: {}
|
||||
array.great: []
|
||||
null.indeed: ~
|
||||
`
|
||||
test.AssertResult(t, expectedOutput, result.Output)
|
||||
}
|
||||
|
||||
func TestReadEmptyContentWithDefaultValueCmd(t *testing.T) {
|
||||
content := ``
|
||||
filename := test.WriteTempYamlFile(content)
|
||||
@@ -673,7 +741,8 @@ func TestReadCmd_ArrayYaml_SplatWithKeyCmd(t *testing.T) {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
expectedOutput := `[0]
|
||||
[1]`
|
||||
[1]
|
||||
`
|
||||
test.AssertResult(t, expectedOutput, result.Output)
|
||||
}
|
||||
|
||||
@@ -684,7 +753,8 @@ func TestReadCmd_ArrayYaml_SplatKey(t *testing.T) {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
expectedOutput := `false
|
||||
true`
|
||||
true
|
||||
`
|
||||
test.AssertResult(t, expectedOutput, result.Output)
|
||||
}
|
||||
|
||||
@@ -765,15 +835,6 @@ func TestReadCmd_ErrorBadPath(t *testing.T) {
|
||||
test.AssertResult(t, expectedOutput, result.Output)
|
||||
}
|
||||
|
||||
func TestReadCmd_Verbose(t *testing.T) {
|
||||
cmd := getRootCommand()
|
||||
result := test.RunCmd(cmd, "read -v ../examples/sample.yaml b.c")
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
test.AssertResult(t, "2", result.Output)
|
||||
}
|
||||
|
||||
func TestReadToJsonCmd(t *testing.T) {
|
||||
cmd := getRootCommand()
|
||||
result := test.RunCmd(cmd, "read -j ../examples/sample.yaml b")
|
||||
@@ -892,7 +953,8 @@ b:
|
||||
}
|
||||
|
||||
expectedOutput := `more things
|
||||
more things also`
|
||||
more things also
|
||||
`
|
||||
test.AssertResult(t, expectedOutput, result.Output)
|
||||
}
|
||||
|
||||
@@ -947,7 +1009,8 @@ b:
|
||||
}
|
||||
|
||||
expectedOutput := `b.there.c
|
||||
b.there2.c`
|
||||
b.there2.c
|
||||
`
|
||||
test.AssertResult(t, expectedOutput, result.Output)
|
||||
}
|
||||
|
||||
@@ -1111,25 +1174,6 @@ func TestPrefixCmd_ErrorUnreadableFile(t *testing.T) {
|
||||
test.AssertResult(t, expectedOutput, result.Error.Error())
|
||||
}
|
||||
|
||||
func TestPrefixCmd_Verbose(t *testing.T) {
|
||||
content := `b:
|
||||
c: 3
|
||||
`
|
||||
filename := test.WriteTempYamlFile(content)
|
||||
defer test.RemoveTempYamlFile(filename)
|
||||
|
||||
cmd := getRootCommand()
|
||||
result := test.RunCmd(cmd, fmt.Sprintf("prefix %s x", filename))
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
expectedOutput := `x:
|
||||
b:
|
||||
c: 3
|
||||
`
|
||||
test.AssertResult(t, expectedOutput, result.Output)
|
||||
}
|
||||
|
||||
func TestPrefixCmd_Inplace(t *testing.T) {
|
||||
content := `b:
|
||||
c: 3
|
||||
@@ -1201,6 +1245,79 @@ func TestWriteCmd(t *testing.T) {
|
||||
test.AssertResult(t, expectedOutput, result.Output)
|
||||
}
|
||||
|
||||
func TestWriteEmptyMultiDocCmd(t *testing.T) {
|
||||
content := `# this is empty
|
||||
---
|
||||
`
|
||||
filename := test.WriteTempYamlFile(content)
|
||||
defer test.RemoveTempYamlFile(filename)
|
||||
|
||||
cmd := getRootCommand()
|
||||
result := test.RunCmd(cmd, fmt.Sprintf("write %s c 7", filename))
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
expectedOutput := `c: 7
|
||||
|
||||
# this is empty
|
||||
`
|
||||
test.AssertResult(t, expectedOutput, result.Output)
|
||||
}
|
||||
|
||||
func TestWriteSurroundingEmptyMultiDocCmd(t *testing.T) {
|
||||
content := `---
|
||||
# empty
|
||||
---
|
||||
cat: frog
|
||||
---
|
||||
|
||||
# empty
|
||||
`
|
||||
filename := test.WriteTempYamlFile(content)
|
||||
defer test.RemoveTempYamlFile(filename)
|
||||
|
||||
cmd := getRootCommand()
|
||||
result := test.RunCmd(cmd, fmt.Sprintf("write %s -d1 c 7", filename))
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
expectedOutput := `
|
||||
|
||||
# empty
|
||||
---
|
||||
cat: frog
|
||||
c: 7
|
||||
---
|
||||
|
||||
|
||||
# empty
|
||||
`
|
||||
test.AssertResult(t, expectedOutput, result.Output)
|
||||
}
|
||||
|
||||
func TestWriteFromFileCmd(t *testing.T) {
|
||||
content := `b:
|
||||
c: 3
|
||||
`
|
||||
filename := test.WriteTempYamlFile(content)
|
||||
defer test.RemoveTempYamlFile(filename)
|
||||
|
||||
source := `kittens: are cute # sure are!`
|
||||
fromFilename := test.WriteTempYamlFile(source)
|
||||
defer test.RemoveTempYamlFile(fromFilename)
|
||||
|
||||
cmd := getRootCommand()
|
||||
result := test.RunCmd(cmd, fmt.Sprintf("write %s b.c -f %s", filename, fromFilename))
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
expectedOutput := `b:
|
||||
c:
|
||||
kittens: are cute # sure are!
|
||||
`
|
||||
test.AssertResult(t, expectedOutput, result.Output)
|
||||
}
|
||||
|
||||
func TestWriteEmptyCmd(t *testing.T) {
|
||||
content := ``
|
||||
filename := test.WriteTempYamlFile(content)
|
||||
@@ -1217,6 +1334,26 @@ func TestWriteEmptyCmd(t *testing.T) {
|
||||
test.AssertResult(t, expectedOutput, result.Output)
|
||||
}
|
||||
|
||||
func TestWriteAutoCreateCmd(t *testing.T) {
|
||||
content := `applications:
|
||||
- name: app
|
||||
env:`
|
||||
filename := test.WriteTempYamlFile(content)
|
||||
defer test.RemoveTempYamlFile(filename)
|
||||
|
||||
cmd := getRootCommand()
|
||||
result := test.RunCmd(cmd, fmt.Sprintf("write %s applications[0].env.hello world", filename))
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
expectedOutput := `applications:
|
||||
- name: app
|
||||
env:
|
||||
hello: world
|
||||
`
|
||||
test.AssertResult(t, expectedOutput, result.Output)
|
||||
}
|
||||
|
||||
func TestWriteCmdScript(t *testing.T) {
|
||||
content := `b:
|
||||
c: 3
|
||||
@@ -1519,7 +1656,7 @@ func TestWriteCmd_SplatMapEmpty(t *testing.T) {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
expectedOutput := `b:
|
||||
c: thing
|
||||
c: {}
|
||||
d: another thing
|
||||
`
|
||||
test.AssertResult(t, expectedOutput, result.Output)
|
||||
@@ -1812,6 +1949,33 @@ c:
|
||||
test.AssertResult(t, expectedOutput, result.Output)
|
||||
}
|
||||
|
||||
func TestMergeAppendArraysCmd(t *testing.T) {
|
||||
content := `people:
|
||||
- name: Barry
|
||||
age: 21`
|
||||
filename := test.WriteTempYamlFile(content)
|
||||
defer test.RemoveTempYamlFile(filename)
|
||||
|
||||
mergeContent := `people:
|
||||
- name: Roger
|
||||
age: 44`
|
||||
mergeFilename := test.WriteTempYamlFile(mergeContent)
|
||||
defer test.RemoveTempYamlFile(mergeFilename)
|
||||
|
||||
cmd := getRootCommand()
|
||||
result := test.RunCmd(cmd, fmt.Sprintf("merge --append -d* %s %s", filename, mergeFilename))
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
expectedOutput := `people:
|
||||
- name: Barry
|
||||
age: 21
|
||||
- name: Roger
|
||||
age: 44
|
||||
`
|
||||
test.AssertResult(t, expectedOutput, result.Output)
|
||||
}
|
||||
|
||||
func TestMergeOverwriteAndAppendCmd(t *testing.T) {
|
||||
cmd := getRootCommand()
|
||||
result := test.RunCmd(cmd, "merge --autocreate=false --append --overwrite ../examples/data1.yaml ../examples/data2.yaml")
|
||||
@@ -1890,6 +2054,27 @@ apples: red
|
||||
test.AssertResult(t, expectedOutput, result.Output)
|
||||
}
|
||||
|
||||
func TestMergeSpecialCharacterKeysCmd(t *testing.T) {
|
||||
content := ``
|
||||
filename := test.WriteTempYamlFile(content)
|
||||
defer test.RemoveTempYamlFile(filename)
|
||||
|
||||
mergeContent := `key[bracket]: value
|
||||
key.bracket: value
|
||||
key"value": value
|
||||
key'value': value
|
||||
`
|
||||
mergeFilename := test.WriteTempYamlFile(mergeContent)
|
||||
defer test.RemoveTempYamlFile(mergeFilename)
|
||||
|
||||
cmd := getRootCommand()
|
||||
result := test.RunCmd(cmd, fmt.Sprintf("merge %s %s", filename, mergeFilename))
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
test.AssertResult(t, mergeContent, result.Output)
|
||||
}
|
||||
|
||||
func TestMergeYamlMultiAllOverwriteCmd(t *testing.T) {
|
||||
content := `b:
|
||||
c: 3
|
||||
@@ -1920,6 +2105,25 @@ apples: red
|
||||
test.AssertResult(t, expectedOutput, result.Output)
|
||||
}
|
||||
|
||||
func TestMergeYamlNullMapCmd(t *testing.T) {
|
||||
content := `b:`
|
||||
filename := test.WriteTempYamlFile(content)
|
||||
defer test.RemoveTempYamlFile(filename)
|
||||
|
||||
mergeContent := `b:
|
||||
thing: a frog
|
||||
`
|
||||
mergeFilename := test.WriteTempYamlFile(mergeContent)
|
||||
defer test.RemoveTempYamlFile(mergeFilename)
|
||||
|
||||
cmd := getRootCommand()
|
||||
result := test.RunCmd(cmd, fmt.Sprintf("merge %s %s", filename, mergeFilename))
|
||||
if result.Error != nil {
|
||||
t.Error(result.Error)
|
||||
}
|
||||
test.AssertResult(t, mergeContent, result.Output)
|
||||
}
|
||||
|
||||
func TestMergeCmd_Error(t *testing.T) {
|
||||
cmd := getRootCommand()
|
||||
result := test.RunCmd(cmd, "merge")
|
||||
|
||||
@@ -9,6 +9,7 @@ var customTag = ""
|
||||
var printMode = "v"
|
||||
var writeInplace = false
|
||||
var writeScript = ""
|
||||
var sourceYamlFile = ""
|
||||
var outputToJSON = false
|
||||
var prettyPrint = false
|
||||
var explodeAnchors = false
|
||||
|
||||
13
cmd/merge.go
13
cmd/merge.go
@@ -4,6 +4,7 @@ import (
|
||||
"github.com/mikefarah/yq/v3/pkg/yqlib"
|
||||
errors "github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func createMergeCmd() *cobra.Command {
|
||||
@@ -36,6 +37,16 @@ If append flag is set then existing arrays will be merged with the arrays from e
|
||||
return cmdMerge
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't deeply traverse arrays when appending a merge, instead we want to
|
||||
* append the entire array element.
|
||||
*/
|
||||
func createReadFunctionForMerge() func(*yaml.Node) ([]*yqlib.NodeContext, error) {
|
||||
return func(dataBucket *yaml.Node) ([]*yqlib.NodeContext, error) {
|
||||
return lib.Get(dataBucket, "**", !appendFlag)
|
||||
}
|
||||
}
|
||||
|
||||
func mergeProperties(cmd *cobra.Command, args []string) error {
|
||||
var updateCommands []yqlib.UpdateCommand = make([]yqlib.UpdateCommand, 0)
|
||||
|
||||
@@ -48,7 +59,7 @@ func mergeProperties(cmd *cobra.Command, args []string) error {
|
||||
var filesToMerge = args[1:]
|
||||
|
||||
for _, fileToMerge := range filesToMerge {
|
||||
matchingNodes, errorProcessingFile := readYamlFile(fileToMerge, "**", false, 0)
|
||||
matchingNodes, errorProcessingFile := doReadYamlFile(fileToMerge, createReadFunctionForMerge(), false, 0)
|
||||
if errorProcessingFile != nil {
|
||||
return errorProcessingFile
|
||||
}
|
||||
|
||||
68
cmd/utils.go
68
cmd/utils.go
@@ -13,7 +13,19 @@ import (
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type readDataFn func(dataBucket *yaml.Node) ([]*yqlib.NodeContext, error)
|
||||
|
||||
func createReadFunction(path string) func(*yaml.Node) ([]*yqlib.NodeContext, error) {
|
||||
return func(dataBucket *yaml.Node) ([]*yqlib.NodeContext, error) {
|
||||
return lib.Get(dataBucket, path, true)
|
||||
}
|
||||
}
|
||||
|
||||
func readYamlFile(filename string, path string, updateAll bool, docIndexInt int) ([]*yqlib.NodeContext, error) {
|
||||
return doReadYamlFile(filename, createReadFunction(path), updateAll, docIndexInt)
|
||||
}
|
||||
|
||||
func doReadYamlFile(filename string, readFn readDataFn, updateAll bool, docIndexInt int) ([]*yqlib.NodeContext, error) {
|
||||
var matchingNodes []*yqlib.NodeContext
|
||||
|
||||
var currentIndex = 0
|
||||
@@ -29,7 +41,7 @@ func readYamlFile(filename string, path string, updateAll bool, docIndexInt int)
|
||||
}
|
||||
|
||||
var errorParsing error
|
||||
matchingNodes, errorParsing = appendDocument(matchingNodes, dataBucket, path, updateAll, docIndexInt, currentIndex)
|
||||
matchingNodes, errorParsing = appendDocument(matchingNodes, dataBucket, readFn, updateAll, docIndexInt, currentIndex)
|
||||
if errorParsing != nil {
|
||||
return errorParsing
|
||||
}
|
||||
@@ -51,14 +63,14 @@ func handleEOF(updateAll bool, docIndexInt int, currentIndex int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func appendDocument(originalMatchingNodes []*yqlib.NodeContext, dataBucket yaml.Node, path string, updateAll bool, docIndexInt int, currentIndex int) ([]*yqlib.NodeContext, error) {
|
||||
func appendDocument(originalMatchingNodes []*yqlib.NodeContext, dataBucket yaml.Node, readFn readDataFn, updateAll bool, docIndexInt int, currentIndex int) ([]*yqlib.NodeContext, error) {
|
||||
log.Debugf("processing document %v - requested index %v", currentIndex, docIndexInt)
|
||||
yqlib.DebugNode(&dataBucket)
|
||||
if !updateAll && currentIndex != docIndexInt {
|
||||
return originalMatchingNodes, nil
|
||||
}
|
||||
log.Debugf("reading %v in document %v", path, currentIndex)
|
||||
matchingNodes, errorParsing := lib.Get(&dataBucket, path)
|
||||
log.Debugf("reading in document %v", currentIndex)
|
||||
matchingNodes, errorParsing := readFn(&dataBucket)
|
||||
if errorParsing != nil {
|
||||
return nil, errors.Wrapf(errorParsing, "Error reading path in document index %v", currentIndex)
|
||||
}
|
||||
@@ -67,7 +79,7 @@ func appendDocument(originalMatchingNodes []*yqlib.NodeContext, dataBucket yaml.
|
||||
|
||||
func printValue(node *yaml.Node, writer io.Writer) error {
|
||||
if node.Kind == yaml.ScalarNode {
|
||||
_, errorWriting := writer.Write([]byte(node.Value))
|
||||
_, errorWriting := writer.Write([]byte(node.Value + "\n"))
|
||||
return errorWriting
|
||||
}
|
||||
return printNode(node, writer)
|
||||
@@ -103,10 +115,10 @@ func writeString(writer io.Writer, txt string) error {
|
||||
}
|
||||
|
||||
func explode(matchingNodes []*yqlib.NodeContext) error {
|
||||
|
||||
log.Debug("exploding nodes")
|
||||
for _, nodeContext := range matchingNodes {
|
||||
var targetNode = yaml.Node{Kind: yaml.MappingNode}
|
||||
explodedNodes, errorRetrieving := lib.Get(nodeContext.Node, "**")
|
||||
var targetNode = yaml.Node{Kind: nodeContext.Node.Kind}
|
||||
explodedNodes, errorRetrieving := lib.Get(nodeContext.Node, "**", true)
|
||||
if errorRetrieving != nil {
|
||||
return errorRetrieving
|
||||
}
|
||||
@@ -120,6 +132,7 @@ func explode(matchingNodes []*yqlib.NodeContext) error {
|
||||
}
|
||||
nodeContext.Node = &targetNode
|
||||
}
|
||||
log.Debug("done exploding nodes")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -147,19 +160,13 @@ func printResults(matchingNodes []*yqlib.NodeContext, writer io.Writer) error {
|
||||
return nil
|
||||
}
|
||||
var errorWriting error
|
||||
for index, mappedDoc := range matchingNodes {
|
||||
for _, mappedDoc := range matchingNodes {
|
||||
switch printMode {
|
||||
case "p":
|
||||
errorWriting = writeString(bufferedWriter, lib.PathStackToString(mappedDoc.PathStack))
|
||||
errorWriting = writeString(bufferedWriter, lib.PathStackToString(mappedDoc.PathStack)+"\n")
|
||||
if errorWriting != nil {
|
||||
return errorWriting
|
||||
}
|
||||
if index < len(matchingNodes)-1 {
|
||||
errorWriting = writeString(bufferedWriter, "\n")
|
||||
if errorWriting != nil {
|
||||
return errorWriting
|
||||
}
|
||||
}
|
||||
case "pv", "vp":
|
||||
// put it into a node and print that.
|
||||
var parentNode = yaml.Node{Kind: yaml.MappingNode}
|
||||
@@ -173,14 +180,6 @@ func printResults(matchingNodes []*yqlib.NodeContext, writer io.Writer) error {
|
||||
if err := printValue(mappedDoc.Node, bufferedWriter); err != nil {
|
||||
return err
|
||||
}
|
||||
// Printing our Scalars does not print a new line at the end
|
||||
// we only want to do that if there are more values (so users can easily script extraction of values in the yaml)
|
||||
if index < len(matchingNodes)-1 && mappedDoc.Node.Kind == yaml.ScalarNode {
|
||||
errorWriting = writeString(bufferedWriter, "\n")
|
||||
if errorWriting != nil {
|
||||
return errorWriting
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,6 +199,11 @@ func parseDocumentIndex() (bool, int, error) {
|
||||
|
||||
type updateDataFn func(dataBucket *yaml.Node, currentIndex int) error
|
||||
|
||||
func isNullDocument(dataBucket *yaml.Node) bool {
|
||||
return dataBucket.Kind == yaml.DocumentNode && (len(dataBucket.Content) == 0 ||
|
||||
dataBucket.Content[0].Kind == yaml.ScalarNode && dataBucket.Content[0].Tag == "!!null")
|
||||
}
|
||||
|
||||
func mapYamlDecoder(updateData updateDataFn, encoder yqlib.Encoder) yamlDecoderFn {
|
||||
return func(decoder *yaml.Decoder) error {
|
||||
var dataBucket yaml.Node
|
||||
@@ -219,8 +223,11 @@ func mapYamlDecoder(updateData updateDataFn, encoder yqlib.Encoder) yamlDecoderF
|
||||
|
||||
if errorReading == io.EOF && docIndexInt == 0 && currentIndex == 0 {
|
||||
//empty document, lets just make one
|
||||
child := yaml.Node{Kind: yaml.MappingNode}
|
||||
dataBucket = yaml.Node{Kind: yaml.DocumentNode, Content: make([]*yaml.Node, 1)}
|
||||
child := yaml.Node{Kind: yaml.MappingNode}
|
||||
dataBucket.Content[0] = &child
|
||||
} else if isNullDocument(&dataBucket) && (updateAll || docIndexInt == currentIndex) {
|
||||
child := yaml.Node{Kind: yaml.MappingNode}
|
||||
dataBucket.Content[0] = &child
|
||||
} else if errorReading == io.EOF {
|
||||
if !updateAll && currentIndex <= docIndexInt {
|
||||
@@ -355,6 +362,17 @@ func readUpdateCommands(args []string, expectedArgs int, badArgsMessage string)
|
||||
}
|
||||
|
||||
log.Debugf("Read write commands file '%v'", updateCommands)
|
||||
} else if sourceYamlFile != "" && len(args) == expectedArgs-1 {
|
||||
log.Debugf("Reading value from %v", sourceYamlFile)
|
||||
var value yaml.Node
|
||||
err := readData(sourceYamlFile, 0, &value)
|
||||
if err != nil && err != io.EOF {
|
||||
return nil, err
|
||||
}
|
||||
log.Debug("args %v", args[expectedArgs-2])
|
||||
updateCommands = make([]yqlib.UpdateCommand, 1)
|
||||
updateCommands[0] = yqlib.UpdateCommand{Command: "update", Path: args[expectedArgs-2], Value: value.Content[0], Overwrite: true}
|
||||
|
||||
} else if len(args) < expectedArgs {
|
||||
return nil, errors.New(badArgsMessage)
|
||||
} else {
|
||||
|
||||
@@ -11,7 +11,7 @@ var (
|
||||
GitDescribe string
|
||||
|
||||
// Version is main version number that is being run at the moment.
|
||||
Version = "3.1.0"
|
||||
Version = "3.1.2"
|
||||
|
||||
// VersionPrerelease is a pre-release marker for the version. If this is "" (empty string)
|
||||
// then it means that it is a final release. Otherwise, this is a pre-release
|
||||
|
||||
@@ -43,6 +43,7 @@ format is list of update commands (update or delete) like so:
|
||||
}
|
||||
cmdWrite.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace")
|
||||
cmdWrite.PersistentFlags().StringVarP(&writeScript, "script", "s", "", "yaml script for updating yaml")
|
||||
cmdWrite.PersistentFlags().StringVarP(&sourceYamlFile, "from", "f", "", "yaml file for updating yaml (as-is)")
|
||||
cmdWrite.PersistentFlags().StringVarP(&customTag, "tag", "t", "", "set yaml tag (e.g. !!int)")
|
||||
cmdWrite.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)")
|
||||
return cmdWrite
|
||||
|
||||
13
debian/changelog
vendored
13
debian/changelog
vendored
@@ -1,3 +1,16 @@
|
||||
yq (3.1.1) eoan; urgency=medium
|
||||
|
||||
* Keeps yaml comments and formatting, can specify yaml tags when updating.
|
||||
* Handles anchors! https://github.com/mikefarah/yq/issues/310, https://github.com/mikefarah/yq/issues/178
|
||||
* Can print out matching paths and values when splatting https://github.com/mikefarah/yq/issues/20
|
||||
* JSON output works for all commands! Yaml files with multiple documents are printed out as one JSON document per line.
|
||||
* Deep splat (**) to match arbitrary paths
|
||||
* Update scripts file format has changed to be more powerful
|
||||
* Reading and splatting, matching results are printed once per line
|
||||
* Bugfixing
|
||||
|
||||
-- Roberto Mier Escandon <rmescandon@gmail.com> Tue, 11 Feb 2020 22:18:24 +0100
|
||||
|
||||
yq (2.2-1) bionic; urgency=medium
|
||||
|
||||
* Added Windows support for the "--inplace" command flag
|
||||
|
||||
21
debian/control
vendored
21
debian/control
vendored
@@ -1,22 +1,23 @@
|
||||
Source: yq
|
||||
Section: devel
|
||||
Priority: extra
|
||||
Priority: optional
|
||||
Maintainer: Roberto Mier EscandĂłn <rmescandon@gmail.com>
|
||||
Build-Depends: debhelper (>= 9),
|
||||
dh-golang,
|
||||
golang-1.10-go,
|
||||
Build-Depends: debhelper (>=10),
|
||||
dh-golang (>=1.34),
|
||||
golang-1.13,
|
||||
dpkg-dev,
|
||||
rsync
|
||||
Standards-Version: 3.9.6
|
||||
Homepage: https://github.com/mikefarah/yq.git
|
||||
Vcs-Browser: https://github.com/mikefarah/yq.git
|
||||
Vcs-Git: https://github.com/mikefarah/yq.git
|
||||
XS-Go-Import-Path: github.com/mikefarah/yq
|
||||
XSBC-Original-Maintainer: Roberto Mier EscandĂłn <rmescandon@gmail.com>
|
||||
|
||||
Package: yq
|
||||
Architecture: any
|
||||
Built-Using: ${misc:Built-Using}
|
||||
Depends: ${shlibs:Depends},
|
||||
${misc:Depends}
|
||||
Description:
|
||||
a lightweight and portable command-line YAML processor
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Description: a lightweight and portable command-line YAML processor
|
||||
.
|
||||
The aim of the project is to be the [jq](https://github.com/stedolan/jq) or sed of yaml files.
|
||||
The aim of the project is to be the
|
||||
[jq](https://github.com/stedolan/jq) or sed of yaml files.
|
||||
|
||||
24
debian/rules
vendored
24
debian/rules
vendored
@@ -14,46 +14,46 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
PROJECT := yq
|
||||
OWNER := mikefarah
|
||||
REPO := github.com
|
||||
GOVERSION := 1.10
|
||||
GOVERSION := 1.13
|
||||
|
||||
export DH_OPTIONS
|
||||
export DH_GOPKG := ${REPO}/${OWNER}/${PROJECT}
|
||||
export GOROOT := /usr/lib/go-${GOVERSION}
|
||||
export GOPATH := ${CURDIR}/_build
|
||||
export GOBIN := ${GOPATH}/bin
|
||||
export PATH := ${GOROOT}/bin:${GOBIN}:${PATH}
|
||||
BLDPATH := $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
|
||||
SRCDIR := ${CURDIR}/_build/src/${DH_GOPKG}
|
||||
export GOCACHE := /tmp/gocache
|
||||
export GOFLAGS := -mod=vendor
|
||||
|
||||
SRCDIR := ${GOPATH}/src/${DH_GOPKG}
|
||||
DESTDIR := ${CURDIR}/debian/${PROJECT}
|
||||
BINDIR := /usr/bin
|
||||
ASSETSDIR := /usr/share/${PROJECT}
|
||||
|
||||
%:
|
||||
dh $@ --buildsystem=golang --with=golang
|
||||
dh $@ --builddirectory=${GOPATH} --buildsystem=golang
|
||||
|
||||
override_dh_auto_build:
|
||||
mkdir -p ${SRCDIR}
|
||||
mkdir -p ${GOBIN}
|
||||
# copy project to local srcdir to build from there
|
||||
rsync -avz --progress --exclude=obj-${BLDPATH} --exclude=debian . $(SRCDIR)
|
||||
rsync -avz --progress --exclude=_build --exclude=debian --exclude=tmp. --exclude=go.mod . $(SRCDIR)
|
||||
# build go code
|
||||
(cd ${SRCDIR} && go install ./...)
|
||||
|
||||
(cd ${SRCDIR} && go install -buildmode=pie ./...)
|
||||
|
||||
override_dh_auto_test:
|
||||
(cd ${SRCDIR} && go test -v ./...)
|
||||
|
||||
override_dh_auto_install:
|
||||
mkdir -p ${DESTDIR}/${BINDIR}
|
||||
mkdir -p ${DESTDIR}/${ASSETSDIR}
|
||||
cp ${CURDIR}/_build/bin/yq ${DESTDIR}/${BINDIR}
|
||||
cp -rf ${SRCDIR}/LICENSE ${DESTDIR}/${ASSETSDIR}
|
||||
cp -rf ${SRCDIR}/README.md ${DESTDIR}/${PLUGINSDIR}
|
||||
cp ${GOBIN}/yq ${DESTDIR}/${BINDIR}
|
||||
cp -f ${SRCDIR}/LICENSE ${DESTDIR}/${ASSETSDIR}
|
||||
chmod a+x ${DESTDIR}/${BINDIR}/yq
|
||||
|
||||
override_dh_auto_clean:
|
||||
dh_clean
|
||||
rm -rf ${CURDIR}/obj-${BLDPATH}
|
||||
rm -rf ${CURDIR}/_build
|
||||
|
||||
15
go.mod
15
go.mod
@@ -1,13 +1,26 @@
|
||||
module github.com/mikefarah/yq/v3
|
||||
|
||||
require (
|
||||
github.com/cosiner/argv v0.0.1 // indirect
|
||||
github.com/go-delve/delve v1.4.0 // indirect
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0
|
||||
github.com/mattn/go-colorable v0.1.4 // indirect
|
||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.8 // indirect
|
||||
github.com/peterh/liner v1.2.0 // indirect
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/sirupsen/logrus v1.4.2 // indirect
|
||||
github.com/spf13/cobra v0.0.5
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
go.starlark.net v0.0.0-20200203144150-6677ee5c7211 // indirect
|
||||
golang.org/x/arch v0.0.0-20191126211547-368ea8f32fff // indirect
|
||||
golang.org/x/crypto v0.0.0-20200210222208-86ce3cb69678 // indirect
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 // indirect
|
||||
golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935 // indirect
|
||||
gopkg.in/imdario/mergo.v0 v0.3.7 // indirect
|
||||
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2
|
||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71
|
||||
)
|
||||
|
||||
go 1.13
|
||||
|
||||
86
go.sum
86
go.sum
@@ -1,17 +1,50 @@
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5 h1:rIXlvz2IWiupMFlC45cZCXZFvKX/ExBcSLrDy2G0Lp8=
|
||||
github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5/go.mod h1:p/NrK5tF6ICIly4qwEDsf6VDirFiWWz0FenfYBwJaKQ=
|
||||
github.com/cosiner/argv v0.0.1 h1:2iAFN+sWPktbZ4tvxm33Ei8VY66FPCxdOxpncUGpAXE=
|
||||
github.com/cosiner/argv v0.0.1/go.mod h1:p/NrK5tF6ICIly4qwEDsf6VDirFiWWz0FenfYBwJaKQ=
|
||||
github.com/cpuguy83/go-md2man v1.0.8/go.mod h1:N6JayAiVKtlHSnuTCeuLSQVs75hb8q+dYQLjr7cDsKY=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-delve/delve v1.4.0 h1:O+1dw1XBZXqhC6fIPQwGxLlbd2wDRau7NxNhVpw02ag=
|
||||
github.com/go-delve/delve v1.4.0/go.mod h1:gQM0ReOJLNAvPuKAXfjHngtE93C2yc/ekTbo7YbAHSo=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561 h1:isR/L+BIZ+rqODWYR/f526ygrBMGKZYFhaaFRDGvuZ8=
|
||||
github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4=
|
||||
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=
|
||||
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mikefarah/yaml v2.1.0+incompatible h1:nu2cqmzk4WlWJNgnevY88faMcdrDzYGcsUjYFxEpB7Y=
|
||||
github.com/mikefarah/yaml/v2 v2.4.0 h1:eYqfooY0BnvKTJxr7+ABJs13n3dg9n347GScDaU2Lww=
|
||||
github.com/mikefarah/yaml/v2 v2.4.0/go.mod h1:ahVqZF4n1W4NqwvVnZzC4es67xsW9uR/RRf2RRxieJU=
|
||||
@@ -19,46 +52,99 @@ github.com/mikefarah/yq v2.4.0+incompatible h1:oBxbWy8R9hI3BIUUxEf0CzikWa2AgnGrG
|
||||
github.com/mikefarah/yq/v2 v2.4.1 h1:tajDonaFK6WqitSZExB6fKlWQy/yCkptqxh2AXEe3N4=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b h1:8uaXtUkxiy+T/zdLWuxa/PG4so0TPZDZfafFNNSaptE=
|
||||
github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
|
||||
github.com/peterh/liner v1.2.0 h1:w/UPXyl5GfahFxcTOz2j9wCIHNI+pUPr2laqpojKNCg=
|
||||
github.com/peterh/liner v1.2.0/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/profile v0.0.0-20170413231811-06b906832ed0/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday v0.0.0-20180428102519-11635eb403ff/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/sirupsen/logrus v0.0.0-20180523074243-ea8897e79973 h1:3AJZYTzw3gm3TNTt30x0CCKD7GOn2sdd50Hn35fQkGY=
|
||||
github.com/sirupsen/logrus v0.0.0-20180523074243-ea8897e79973/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.0-20170417170307-b6cb39589372/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v0.0.0-20170417173400-9e4c21054fa1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
go.starlark.net v0.0.0-20190702223751-32f345186213 h1:lkYv5AKwvvduv5XWP6szk/bvvgO6aDeUujhZQXIFTes=
|
||||
go.starlark.net v0.0.0-20190702223751-32f345186213/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
|
||||
go.starlark.net v0.0.0-20200203144150-6677ee5c7211 h1:Qoe+9POtDT51UBQ8XEnS9QKeHDQzEl2QRh3eok9R4aw=
|
||||
go.starlark.net v0.0.0-20200203144150-6677ee5c7211/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
|
||||
golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4 h1:QlVATYS7JBoZMVaf+cNjb90WD/beKVHnIxFKT4QaHVI=
|
||||
golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
|
||||
golang.org/x/arch v0.0.0-20191126211547-368ea8f32fff h1:k/MrR0lKiCokRu1JUDDAWhWZinfBAOZRzz3LkPOkFMs=
|
||||
golang.org/x/arch v0.0.0-20191126211547-368ea8f32fff/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200210222208-86ce3cb69678/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20191030203535-5e247c9ad0a0 h1:s5lp4ug7qHzUccgyFdjsX7OZDzHXRaePrF3B3vmUiuM=
|
||||
golang.org/x/tools v0.0.0-20191030203535-5e247c9ad0a0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935 h1:kJQZhwFzSwJS2BxboKjdZzWczQOZx8VuH7Y8hhuGUtM=
|
||||
golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
||||
gopkg.in/imdario/mergo.v0 v0.3.7 h1:QDotlIZtaO/p+Um0ok18HRTpq5i5/SAk/qprsor+9c8=
|
||||
gopkg.in/imdario/mergo.v0 v0.3.7/go.mod h1:9qPP6AGrlC1G2PTNXko614FwGZvorN7MiBU0Eppok+U=
|
||||
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 h1:6D+BvnJ/j6e222UW8s2qTSe3wGBtvo0MbVQG/c5k8RE=
|
||||
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473/go.mod h1:N1eN2tsCx0Ydtgjl4cqmbRCsY4/+z4cYDeqwZTk6zog=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type DataNavigator interface {
|
||||
Traverse(value *yaml.Node, path []string) error
|
||||
Traverse(value *yaml.Node, path []interface{}) error
|
||||
}
|
||||
|
||||
type navigator struct {
|
||||
@@ -20,7 +21,7 @@ func NewDataNavigator(NavigationStrategy NavigationStrategy) DataNavigator {
|
||||
}
|
||||
}
|
||||
|
||||
func (n *navigator) Traverse(value *yaml.Node, path []string) error {
|
||||
func (n *navigator) Traverse(value *yaml.Node, path []interface{}) error {
|
||||
realValue := value
|
||||
emptyArray := make([]interface{}, 0)
|
||||
if realValue.Kind == yaml.DocumentNode {
|
||||
@@ -30,17 +31,14 @@ func (n *navigator) Traverse(value *yaml.Node, path []string) error {
|
||||
return n.doTraverse(value, "", path, emptyArray)
|
||||
}
|
||||
|
||||
func (n *navigator) doTraverse(value *yaml.Node, head string, tail []string, pathStack []interface{}) error {
|
||||
|
||||
if value.Kind == yaml.ScalarNode {
|
||||
return n.navigationStrategy.Visit(NewNodeContext(value, head, tail, pathStack))
|
||||
}
|
||||
func (n *navigator) doTraverse(value *yaml.Node, head interface{}, tail []interface{}, pathStack []interface{}) error {
|
||||
|
||||
log.Debug("head %v", head)
|
||||
DebugNode(value)
|
||||
var nodeContext = NewNodeContext(value, head, tail, pathStack)
|
||||
|
||||
var errorDeepSplatting error
|
||||
if head == "**" {
|
||||
if head == "**" && value.Kind != yaml.ScalarNode && n.navigationStrategy.ShouldDeeplyTraverse(nodeContext) {
|
||||
if len(pathStack) == 0 || pathStack[len(pathStack)-1] != "<<" {
|
||||
errorDeepSplatting = n.recurse(value, head, tail, pathStack)
|
||||
}
|
||||
@@ -52,59 +50,59 @@ func (n *navigator) doTraverse(value *yaml.Node, head string, tail []string, pat
|
||||
return errorDeepSplatting
|
||||
}
|
||||
|
||||
if len(tail) > 0 {
|
||||
if len(tail) > 0 && value.Kind != yaml.ScalarNode {
|
||||
log.Debugf("diving into %v", tail[0])
|
||||
DebugNode(value)
|
||||
return n.recurse(value, tail[0], tail[1:], pathStack)
|
||||
}
|
||||
return n.navigationStrategy.Visit(NewNodeContext(value, head, tail, pathStack))
|
||||
return n.navigationStrategy.Visit(nodeContext)
|
||||
}
|
||||
|
||||
func (n *navigator) getOrReplace(original *yaml.Node, expectedKind yaml.Kind) *yaml.Node {
|
||||
if original.Kind != expectedKind {
|
||||
log.Debug("wanted %v but it was %v, overriding", expectedKind, original.Kind)
|
||||
log.Debug("wanted %v but it was %v, overriding", KindString(expectedKind), KindString(original.Kind))
|
||||
return &yaml.Node{Kind: expectedKind}
|
||||
}
|
||||
return original
|
||||
}
|
||||
|
||||
func (n *navigator) recurse(value *yaml.Node, head string, tail []string, pathStack []interface{}) error {
|
||||
func (n *navigator) recurse(value *yaml.Node, head interface{}, tail []interface{}, pathStack []interface{}) error {
|
||||
log.Debug("recursing, processing %v, pathStack %v", head, pathStackToString(pathStack))
|
||||
switch value.Kind {
|
||||
case yaml.MappingNode:
|
||||
log.Debug("its a map with %v entries", len(value.Content)/2)
|
||||
return n.recurseMap(value, head, tail, pathStack)
|
||||
headString := fmt.Sprintf("%v", head)
|
||||
return n.recurseMap(value, headString, tail, pathStack)
|
||||
|
||||
case yaml.SequenceNode:
|
||||
log.Debug("its a sequence of %v things!", len(value.Content))
|
||||
|
||||
var index, errorParsingIndex = strconv.ParseInt(head, 10, 64) // nolint
|
||||
if errorParsingIndex == nil {
|
||||
return n.recurseArray(value, index, head, tail, pathStack)
|
||||
} else if head == "+" {
|
||||
return n.appendArray(value, head, tail, pathStack)
|
||||
}
|
||||
return n.splatArray(value, head, tail, pathStack)
|
||||
switch head := head.(type) {
|
||||
case int64:
|
||||
return n.recurseArray(value, head, head, tail, pathStack)
|
||||
default:
|
||||
|
||||
if head == "+" {
|
||||
return n.appendArray(value, head, tail, pathStack)
|
||||
} else if len(value.Content) == 0 && head == "**" {
|
||||
return n.navigationStrategy.Visit(NewNodeContext(value, head, tail, pathStack))
|
||||
}
|
||||
return n.splatArray(value, head, tail, pathStack)
|
||||
}
|
||||
case yaml.AliasNode:
|
||||
log.Debug("its an alias!")
|
||||
DebugNode(value.Alias)
|
||||
if n.navigationStrategy.FollowAlias(NewNodeContext(value, head, tail, pathStack)) {
|
||||
|
||||
if value.Alias.Kind == yaml.ScalarNode {
|
||||
log.Debug("alias to a scalar")
|
||||
return n.navigationStrategy.Visit(NewNodeContext(value.Alias, head, tail, pathStack))
|
||||
} else {
|
||||
log.Debug("following the alias")
|
||||
return n.recurse(value.Alias, head, tail, pathStack)
|
||||
}
|
||||
log.Debug("following the alias")
|
||||
return n.recurse(value.Alias, head, tail, pathStack)
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return nil
|
||||
return n.navigationStrategy.Visit(NewNodeContext(value, head, tail, pathStack))
|
||||
}
|
||||
}
|
||||
|
||||
func (n *navigator) recurseMap(value *yaml.Node, head string, tail []string, pathStack []interface{}) error {
|
||||
func (n *navigator) recurseMap(value *yaml.Node, head string, tail []interface{}, pathStack []interface{}) error {
|
||||
traversedEntry := false
|
||||
errorVisiting := n.visitMatchingEntries(value, head, tail, pathStack, func(contents []*yaml.Node, indexInMap int) error {
|
||||
log.Debug("recurseMap: visitMatchingEntries for %v", contents[indexInMap].Value)
|
||||
@@ -115,7 +113,7 @@ func (n *navigator) recurseMap(value *yaml.Node, head string, tail []string, pat
|
||||
if n.navigationStrategy.ShouldTraverse(NewNodeContext(contents[indexInMap+1], head, tail, newPathStack), contents[indexInMap].Value) {
|
||||
log.Debug("recurseMap: Going to traverse")
|
||||
traversedEntry = true
|
||||
// contents[indexInMap+1] = n.getOrReplace(contents[indexInMap+1], guessKind(head, tail, contents[indexInMap+1].Kind))
|
||||
contents[indexInMap+1] = n.getOrReplace(contents[indexInMap+1], guessKind(head, tail, contents[indexInMap+1].Kind))
|
||||
errorTraversing := n.doTraverse(contents[indexInMap+1], head, tail, newPathStack)
|
||||
log.Debug("recurseMap: Finished traversing")
|
||||
n.navigationStrategy.DebugVisitedNodes()
|
||||
@@ -130,22 +128,33 @@ func (n *navigator) recurseMap(value *yaml.Node, head string, tail []string, pat
|
||||
return errorVisiting
|
||||
}
|
||||
|
||||
if traversedEntry || n.navigationStrategy.GetPathParser().IsPathExpression(head) || !n.navigationStrategy.AutoCreateMap(NewNodeContext(value, head, tail, pathStack)) {
|
||||
if len(value.Content) == 0 && head == "**" {
|
||||
return n.navigationStrategy.Visit(NewNodeContext(value, head, tail, pathStack))
|
||||
} else if traversedEntry || n.navigationStrategy.GetPathParser().IsPathExpression(head) || !n.navigationStrategy.AutoCreateMap(NewNodeContext(value, head, tail, pathStack)) {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, errorParsingInt := strconv.ParseInt(head, 10, 64)
|
||||
|
||||
mapEntryKey := yaml.Node{Value: head, Kind: yaml.ScalarNode}
|
||||
|
||||
if errorParsingInt == nil {
|
||||
// fixes a json encoding problem where keys that look like numbers
|
||||
// get treated as numbers and cannot be used in a json map
|
||||
mapEntryKey.Style = yaml.LiteralStyle
|
||||
}
|
||||
|
||||
value.Content = append(value.Content, &mapEntryKey)
|
||||
mapEntryValue := yaml.Node{Kind: guessKind(head, tail, 0)}
|
||||
value.Content = append(value.Content, &mapEntryValue)
|
||||
log.Debug("adding new node %v", head)
|
||||
log.Debug("adding a new node %v - def a string", head)
|
||||
return n.doTraverse(&mapEntryValue, head, tail, append(pathStack, head))
|
||||
}
|
||||
|
||||
// need to pass the node in, as it may be aliased
|
||||
type mapVisitorFn func(contents []*yaml.Node, index int) error
|
||||
|
||||
func (n *navigator) visitDirectMatchingEntries(node *yaml.Node, head string, tail []string, pathStack []interface{}, visit mapVisitorFn) error {
|
||||
func (n *navigator) visitDirectMatchingEntries(node *yaml.Node, head string, tail []interface{}, pathStack []interface{}, visit mapVisitorFn) error {
|
||||
var contents = node.Content
|
||||
for index := 0; index < len(contents); index = index + 2 {
|
||||
content := contents[index]
|
||||
@@ -160,7 +169,7 @@ func (n *navigator) visitDirectMatchingEntries(node *yaml.Node, head string, tai
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *navigator) visitMatchingEntries(node *yaml.Node, head string, tail []string, pathStack []interface{}, visit mapVisitorFn) error {
|
||||
func (n *navigator) visitMatchingEntries(node *yaml.Node, head string, tail []interface{}, pathStack []interface{}, visit mapVisitorFn) error {
|
||||
var contents = node.Content
|
||||
log.Debug("visitMatchingEntries %v", head)
|
||||
DebugNode(node)
|
||||
@@ -176,7 +185,7 @@ func (n *navigator) visitMatchingEntries(node *yaml.Node, head string, tail []st
|
||||
return n.visitAliases(contents, head, tail, pathStack, visit)
|
||||
}
|
||||
|
||||
func (n *navigator) visitAliases(contents []*yaml.Node, head string, tail []string, pathStack []interface{}, visit mapVisitorFn) error {
|
||||
func (n *navigator) visitAliases(contents []*yaml.Node, head string, tail []interface{}, pathStack []interface{}, visit mapVisitorFn) error {
|
||||
// merge aliases are defined first, but we only want to traverse them
|
||||
// if we don't find a match on this node first.
|
||||
// traverse them backwards so that the last alias overrides the preceding.
|
||||
@@ -207,7 +216,7 @@ func (n *navigator) visitAliases(contents []*yaml.Node, head string, tail []stri
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *navigator) visitAliasSequence(possibleAliasArray []*yaml.Node, head string, tail []string, pathStack []interface{}, visit mapVisitorFn) error {
|
||||
func (n *navigator) visitAliasSequence(possibleAliasArray []*yaml.Node, head string, tail []interface{}, pathStack []interface{}, visit mapVisitorFn) error {
|
||||
// need to search this backwards too, so that aliases defined last override the preceding.
|
||||
for aliasIndex := len(possibleAliasArray) - 1; aliasIndex >= 0; aliasIndex = aliasIndex - 1 {
|
||||
child := possibleAliasArray[aliasIndex]
|
||||
@@ -223,7 +232,7 @@ func (n *navigator) visitAliasSequence(possibleAliasArray []*yaml.Node, head str
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *navigator) splatArray(value *yaml.Node, head string, tail []string, pathStack []interface{}) error {
|
||||
func (n *navigator) splatArray(value *yaml.Node, head interface{}, tail []interface{}, pathStack []interface{}) error {
|
||||
for index, childValue := range value.Content {
|
||||
log.Debug("processing")
|
||||
DebugNode(childValue)
|
||||
@@ -231,6 +240,9 @@ func (n *navigator) splatArray(value *yaml.Node, head string, tail []string, pat
|
||||
|
||||
newPathStack := append(pathStack, index)
|
||||
if n.navigationStrategy.ShouldTraverse(NewNodeContext(childValue, head, tail, newPathStack), childValue.Value) {
|
||||
// here we should not deeply traverse the array if we are appending..not sure how to do that.
|
||||
// need to visit instead...
|
||||
// easiest way is to pop off the head and pass the rest of the tail in.
|
||||
var err = n.doTraverse(childValue, head, tail, newPathStack)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -240,14 +252,14 @@ func (n *navigator) splatArray(value *yaml.Node, head string, tail []string, pat
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *navigator) appendArray(value *yaml.Node, head string, tail []string, pathStack []interface{}) error {
|
||||
func (n *navigator) appendArray(value *yaml.Node, head interface{}, tail []interface{}, pathStack []interface{}) error {
|
||||
var newNode = yaml.Node{Kind: guessKind(head, tail, 0)}
|
||||
value.Content = append(value.Content, &newNode)
|
||||
log.Debug("appending a new node, %v", value.Content)
|
||||
return n.doTraverse(&newNode, head, tail, append(pathStack, len(value.Content)-1))
|
||||
}
|
||||
|
||||
func (n *navigator) recurseArray(value *yaml.Node, index int64, head string, tail []string, pathStack []interface{}) error {
|
||||
func (n *navigator) recurseArray(value *yaml.Node, index int64, head interface{}, tail []interface{}, pathStack []interface{}) error {
|
||||
for int64(len(value.Content)) <= index {
|
||||
value.Content = append(value.Content, &yaml.Node{Kind: guessKind(head, tail, 0)})
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func DeleteNavigationStrategy(pathElementToDelete string) NavigationStrategy {
|
||||
func DeleteNavigationStrategy(pathElementToDelete interface{}) NavigationStrategy {
|
||||
parser := NewPathParser()
|
||||
return &NavigationStrategyImpl{
|
||||
visitedNodes: []*NodeContext{},
|
||||
@@ -17,6 +15,9 @@ func DeleteNavigationStrategy(pathElementToDelete string) NavigationStrategy {
|
||||
autoCreateMap: func(nodeContext NodeContext) bool {
|
||||
return false
|
||||
},
|
||||
shouldDeeplyTraverse: func(nodeContext NodeContext) bool {
|
||||
return true
|
||||
},
|
||||
visit: func(nodeContext NodeContext) error {
|
||||
node := nodeContext.Node
|
||||
log.Debug("need to find and delete %v in here", pathElementToDelete)
|
||||
@@ -31,12 +32,12 @@ func DeleteNavigationStrategy(pathElementToDelete string) NavigationStrategy {
|
||||
},
|
||||
}
|
||||
}
|
||||
func deleteFromMap(pathParser PathParser, contents []*yaml.Node, pathStack []interface{}, pathElementToDelete string) []*yaml.Node {
|
||||
func deleteFromMap(pathParser PathParser, contents []*yaml.Node, pathStack []interface{}, pathElementToDelete interface{}) []*yaml.Node {
|
||||
newContents := make([]*yaml.Node, 0)
|
||||
for index := 0; index < len(contents); index = index + 2 {
|
||||
keyNode := contents[index]
|
||||
valueNode := contents[index+1]
|
||||
if !pathParser.MatchesNextPathElement(NewNodeContext(keyNode, pathElementToDelete, []string{}, pathStack), keyNode.Value) {
|
||||
if !pathParser.MatchesNextPathElement(NewNodeContext(keyNode, pathElementToDelete, make([]interface{}, 0), pathStack), keyNode.Value) {
|
||||
log.Debug("adding node %v", keyNode.Value)
|
||||
newContents = append(newContents, keyNode, valueNode)
|
||||
} else {
|
||||
@@ -46,21 +47,23 @@ func deleteFromMap(pathParser PathParser, contents []*yaml.Node, pathStack []int
|
||||
return newContents
|
||||
}
|
||||
|
||||
func deleteFromArray(pathParser PathParser, content []*yaml.Node, pathStack []interface{}, pathElementToDelete string) []*yaml.Node {
|
||||
func deleteFromArray(pathParser PathParser, content []*yaml.Node, pathStack []interface{}, pathElementToDelete interface{}) []*yaml.Node {
|
||||
|
||||
var indexToDelete, err = strconv.ParseInt(pathElementToDelete, 10, 64) // nolint
|
||||
if err == nil {
|
||||
return deleteIndexInArray(content, indexToDelete)
|
||||
}
|
||||
log.Debug("%v is not a numeric index, finding matching patterns", pathElementToDelete)
|
||||
var newArray = make([]*yaml.Node, 0)
|
||||
switch pathElementToDelete := pathElementToDelete.(type) {
|
||||
case int64:
|
||||
return deleteIndexInArray(content, pathElementToDelete)
|
||||
default:
|
||||
log.Debug("%v is not a numeric index, finding matching patterns", pathElementToDelete)
|
||||
var newArray = make([]*yaml.Node, 0)
|
||||
|
||||
for _, childValue := range content {
|
||||
if !pathParser.MatchesNextPathElement(NewNodeContext(childValue, pathElementToDelete, []string{}, pathStack), childValue.Value) {
|
||||
newArray = append(newArray, childValue)
|
||||
for _, childValue := range content {
|
||||
if !pathParser.MatchesNextPathElement(NewNodeContext(childValue, pathElementToDelete, make([]interface{}, 0), pathStack), childValue.Value) {
|
||||
newArray = append(newArray, childValue)
|
||||
}
|
||||
}
|
||||
return newArray
|
||||
}
|
||||
return newArray
|
||||
|
||||
}
|
||||
|
||||
func deleteIndexInArray(content []*yaml.Node, index int64) []*yaml.Node {
|
||||
|
||||
@@ -19,6 +19,23 @@ type UpdateCommand struct {
|
||||
Overwrite bool
|
||||
}
|
||||
|
||||
func KindString(kind yaml.Kind) string {
|
||||
switch kind {
|
||||
case yaml.ScalarNode:
|
||||
return "ScalarNode"
|
||||
case yaml.SequenceNode:
|
||||
return "SequenceNode"
|
||||
case yaml.MappingNode:
|
||||
return "MappingNode"
|
||||
case yaml.DocumentNode:
|
||||
return "DocumentNode"
|
||||
case yaml.AliasNode:
|
||||
return "AliasNode"
|
||||
default:
|
||||
return "unknown!"
|
||||
}
|
||||
}
|
||||
|
||||
func DebugNode(value *yaml.Node) {
|
||||
if value == nil {
|
||||
log.Debug("-- node is nil --")
|
||||
@@ -30,7 +47,7 @@ func DebugNode(value *yaml.Node) {
|
||||
log.Error("Error debugging node, %v", errorEncoding.Error())
|
||||
}
|
||||
encoder.Close()
|
||||
log.Debug("Tag: %v", value.Tag)
|
||||
log.Debug("Tag: %v, Kind: %v", value.Tag, KindString(value.Kind))
|
||||
log.Debug("%v", buf.String())
|
||||
}
|
||||
}
|
||||
@@ -43,7 +60,7 @@ func mergePathStackToString(pathStack []interface{}, appendArrays bool) string {
|
||||
var sb strings.Builder
|
||||
for index, path := range pathStack {
|
||||
switch path.(type) {
|
||||
case int:
|
||||
case int, int64:
|
||||
if appendArrays {
|
||||
sb.WriteString("[+]")
|
||||
} else {
|
||||
@@ -52,13 +69,22 @@ func mergePathStackToString(pathStack []interface{}, appendArrays bool) string {
|
||||
|
||||
default:
|
||||
s := fmt.Sprintf("%v", path)
|
||||
hasDot := strings.Contains(s, ".")
|
||||
if hasDot {
|
||||
sb.WriteString("[")
|
||||
var _, errParsingInt = strconv.ParseInt(s, 10, 64) // nolint
|
||||
|
||||
hasSpecial := strings.Contains(s, ".") || strings.Contains(s, "[") || strings.Contains(s, "]") || strings.Contains(s, "\"")
|
||||
hasDoubleQuotes := strings.Contains(s, "\"")
|
||||
wrappingCharacterStart := "\""
|
||||
wrappingCharacterEnd := "\""
|
||||
if hasDoubleQuotes {
|
||||
wrappingCharacterStart = "("
|
||||
wrappingCharacterEnd = ")"
|
||||
}
|
||||
if hasSpecial || errParsingInt == nil {
|
||||
sb.WriteString(wrappingCharacterStart)
|
||||
}
|
||||
sb.WriteString(s)
|
||||
if hasDot {
|
||||
sb.WriteString("]")
|
||||
if hasSpecial || errParsingInt == nil {
|
||||
sb.WriteString(wrappingCharacterEnd)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,38 +92,47 @@ func mergePathStackToString(pathStack []interface{}, appendArrays bool) string {
|
||||
sb.WriteString(".")
|
||||
}
|
||||
}
|
||||
return sb.String()
|
||||
var pathString = sb.String()
|
||||
log.Debug("got a path string: %v", pathString)
|
||||
return pathString
|
||||
}
|
||||
|
||||
func guessKind(head string, tail []string, guess yaml.Kind) yaml.Kind {
|
||||
log.Debug("tail %v", tail)
|
||||
func guessKind(head interface{}, tail []interface{}, guess yaml.Kind) yaml.Kind {
|
||||
log.Debug("guessKind: tail %v", tail)
|
||||
if len(tail) == 0 && guess == 0 {
|
||||
log.Debug("end of path, must be a scalar")
|
||||
return yaml.ScalarNode
|
||||
} else if len(tail) == 0 {
|
||||
return guess
|
||||
}
|
||||
|
||||
var _, errorParsingInt = strconv.ParseInt(tail[0], 10, 64)
|
||||
if tail[0] == "+" || errorParsingInt == nil {
|
||||
var next = tail[0]
|
||||
switch next.(type) {
|
||||
case int64:
|
||||
return yaml.SequenceNode
|
||||
default:
|
||||
var nextString = fmt.Sprintf("%v", next)
|
||||
if nextString == "+" {
|
||||
return yaml.SequenceNode
|
||||
}
|
||||
pathParser := NewPathParser()
|
||||
if pathParser.IsPathExpression(nextString) && (guess == yaml.SequenceNode || guess == yaml.MappingNode) {
|
||||
return guess
|
||||
} else if guess == yaml.AliasNode {
|
||||
log.Debug("guess was an alias, okey doke.")
|
||||
return guess
|
||||
} else if head == "**" {
|
||||
log.Debug("deep wildcard, go with the guess")
|
||||
return guess
|
||||
}
|
||||
log.Debug("forcing a mapping node")
|
||||
log.Debug("yaml.SequenceNode %v", guess == yaml.SequenceNode)
|
||||
log.Debug("yaml.ScalarNode %v", guess == yaml.ScalarNode)
|
||||
return yaml.MappingNode
|
||||
}
|
||||
pathParser := NewPathParser()
|
||||
if (pathParser.IsPathExpression(tail[0]) || head == "**") && (guess == yaml.SequenceNode || guess == yaml.MappingNode) {
|
||||
return guess
|
||||
}
|
||||
if guess == yaml.AliasNode {
|
||||
log.Debug("guess was an alias, okey doke.")
|
||||
return guess
|
||||
}
|
||||
log.Debug("forcing a mapping node")
|
||||
log.Debug("yaml.SequenceNode %v", guess == yaml.SequenceNode)
|
||||
log.Debug("yaml.ScalarNode %v", guess == yaml.ScalarNode)
|
||||
return yaml.MappingNode
|
||||
}
|
||||
|
||||
type YqLib interface {
|
||||
Get(rootNode *yaml.Node, path string) ([]*NodeContext, error)
|
||||
Get(rootNode *yaml.Node, path string, deeplyTraverseArrays bool) ([]*NodeContext, error)
|
||||
Update(rootNode *yaml.Node, updateCommand UpdateCommand, autoCreate bool) error
|
||||
New(path string) yaml.Node
|
||||
|
||||
@@ -115,9 +150,9 @@ func NewYqLib() YqLib {
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lib) Get(rootNode *yaml.Node, path string) ([]*NodeContext, error) {
|
||||
func (l *lib) Get(rootNode *yaml.Node, path string, deeplyTraverseArrays bool) ([]*NodeContext, error) {
|
||||
var paths = l.parser.ParsePath(path)
|
||||
navigationStrategy := ReadNavigationStrategy()
|
||||
navigationStrategy := ReadNavigationStrategy(deeplyTraverseArrays)
|
||||
navigator := NewDataNavigator(navigationStrategy)
|
||||
error := navigator.Traverse(rootNode, paths)
|
||||
return navigationStrategy.GetVisitedNodes(), error
|
||||
|
||||
@@ -8,13 +8,13 @@ import (
|
||||
|
||||
type NodeContext struct {
|
||||
Node *yaml.Node
|
||||
Head string
|
||||
Tail []string
|
||||
Head interface{}
|
||||
Tail []interface{}
|
||||
PathStack []interface{}
|
||||
}
|
||||
|
||||
func NewNodeContext(node *yaml.Node, head string, tail []string, pathStack []interface{}) NodeContext {
|
||||
newTail := make([]string, len(tail))
|
||||
func NewNodeContext(node *yaml.Node, head interface{}, tail []interface{}, pathStack []interface{}) NodeContext {
|
||||
newTail := make([]interface{}, len(tail))
|
||||
copy(newTail, tail)
|
||||
|
||||
newPathStack := make([]interface{}, len(pathStack))
|
||||
@@ -34,18 +34,20 @@ type NavigationStrategy interface {
|
||||
// node key is the string value of the last element in the path stack
|
||||
// we use it to match against the pathExpression in head.
|
||||
ShouldTraverse(nodeContext NodeContext, nodeKey string) bool
|
||||
ShouldDeeplyTraverse(nodeContext NodeContext) bool
|
||||
GetVisitedNodes() []*NodeContext
|
||||
DebugVisitedNodes()
|
||||
GetPathParser() PathParser
|
||||
}
|
||||
|
||||
type NavigationStrategyImpl struct {
|
||||
followAlias func(nodeContext NodeContext) bool
|
||||
autoCreateMap func(nodeContext NodeContext) bool
|
||||
visit func(nodeContext NodeContext) error
|
||||
shouldVisitExtraFn func(nodeContext NodeContext) bool
|
||||
visitedNodes []*NodeContext
|
||||
pathParser PathParser
|
||||
followAlias func(nodeContext NodeContext) bool
|
||||
autoCreateMap func(nodeContext NodeContext) bool
|
||||
visit func(nodeContext NodeContext) error
|
||||
shouldVisitExtraFn func(nodeContext NodeContext) bool
|
||||
shouldDeeplyTraverse func(nodeContext NodeContext) bool
|
||||
visitedNodes []*NodeContext
|
||||
pathParser PathParser
|
||||
}
|
||||
|
||||
func (ns *NavigationStrategyImpl) GetPathParser() PathParser {
|
||||
@@ -64,6 +66,10 @@ func (ns *NavigationStrategyImpl) AutoCreateMap(nodeContext NodeContext) bool {
|
||||
return ns.autoCreateMap(nodeContext)
|
||||
}
|
||||
|
||||
func (ns *NavigationStrategyImpl) ShouldDeeplyTraverse(nodeContext NodeContext) bool {
|
||||
return ns.shouldDeeplyTraverse(nodeContext)
|
||||
}
|
||||
|
||||
func (ns *NavigationStrategyImpl) ShouldTraverse(nodeContext NodeContext, nodeKey string) bool {
|
||||
// we should traverse aliases (if enabled), but not visit them :/
|
||||
if len(nodeContext.PathStack) == 0 {
|
||||
@@ -84,7 +90,6 @@ func (ns *NavigationStrategyImpl) shouldVisit(nodeContext NodeContext) bool {
|
||||
return true
|
||||
}
|
||||
log.Debug("tail len %v", len(nodeContext.Tail))
|
||||
// SOMETHING HERE!
|
||||
|
||||
if ns.alreadyVisited(pathStack) || len(nodeContext.Tail) != 0 {
|
||||
return false
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type PathParser interface {
|
||||
ParsePath(path string) []string
|
||||
ParsePath(path string) []interface{}
|
||||
MatchesNextPathElement(nodeContext NodeContext, nodeKey string) bool
|
||||
IsPathExpression(pathElement string) bool
|
||||
}
|
||||
@@ -42,9 +43,11 @@ func (p *pathParser) MatchesNextPathElement(nodeContext NodeContext, nodeKey str
|
||||
if head == "**" || head == "*" {
|
||||
return true
|
||||
}
|
||||
if strings.Contains(head, "==") {
|
||||
var headString = fmt.Sprintf("%v", head)
|
||||
|
||||
if strings.Contains(headString, "==") {
|
||||
log.Debug("ooh deep recursion time")
|
||||
result := strings.SplitN(head, "==", 2)
|
||||
result := strings.SplitN(headString, "==", 2)
|
||||
path := strings.TrimSpace(result[0])
|
||||
value := strings.TrimSpace(result[1])
|
||||
log.Debug("path %v", path)
|
||||
@@ -70,17 +73,18 @@ func (p *pathParser) MatchesNextPathElement(nodeContext NodeContext, nodeKey str
|
||||
}
|
||||
}
|
||||
|
||||
return matchesString(head, nodeKey)
|
||||
return matchesString(headString, nodeKey)
|
||||
}
|
||||
|
||||
func (p *pathParser) ParsePath(path string) []string {
|
||||
func (p *pathParser) ParsePath(path string) []interface{} {
|
||||
var paths = make([]interface{}, 0)
|
||||
if path == "" {
|
||||
return []string{}
|
||||
return paths
|
||||
}
|
||||
return p.parsePathAccum([]string{}, path)
|
||||
return p.parsePathAccum(paths, path)
|
||||
}
|
||||
|
||||
func (p *pathParser) parsePathAccum(paths []string, remaining string) []string {
|
||||
func (p *pathParser) parsePathAccum(paths []interface{}, remaining string) []interface{} {
|
||||
head, tail := p.nextYamlPath(remaining)
|
||||
if tail == "" {
|
||||
return append(paths, head)
|
||||
@@ -88,11 +92,16 @@ func (p *pathParser) parsePathAccum(paths []string, remaining string) []string {
|
||||
return p.parsePathAccum(append(paths, head), tail)
|
||||
}
|
||||
|
||||
func (p *pathParser) nextYamlPath(path string) (pathElement string, remaining string) {
|
||||
func (p *pathParser) nextYamlPath(path string) (pathElement interface{}, remaining string) {
|
||||
switch path[0] {
|
||||
case '[':
|
||||
// e.g [0].blah.cat -> we need to return "0" and "blah.cat"
|
||||
return p.search(path[1:], []uint8{']'}, true)
|
||||
var value, remainingBit = p.search(path[1:], []uint8{']'}, true)
|
||||
var number, errParsingInt = strconv.ParseInt(value, 10, 64) // nolint
|
||||
if errParsingInt == nil {
|
||||
return number, remainingBit
|
||||
}
|
||||
return value, remainingBit
|
||||
case '"':
|
||||
// e.g "a.b".blah.cat -> we need to return "a.b" and "blah.cat"
|
||||
return p.search(path[1:], []uint8{'"'}, true)
|
||||
|
||||
@@ -10,20 +10,21 @@ var parser = NewPathParser()
|
||||
|
||||
var parsePathsTests = []struct {
|
||||
path string
|
||||
expectedPaths []string
|
||||
expectedPaths []interface{}
|
||||
}{
|
||||
{"a.b", []string{"a", "b"}},
|
||||
{"a.b.**", []string{"a", "b", "**"}},
|
||||
{"a.b.*", []string{"a", "b", "*"}},
|
||||
{"a.b[0]", []string{"a", "b", "0"}},
|
||||
{"a.b.d[+]", []string{"a", "b", "d", "+"}},
|
||||
{"a", []string{"a"}},
|
||||
{"a.b.c", []string{"a", "b", "c"}},
|
||||
{"\"a.b\".c", []string{"a.b", "c"}},
|
||||
{"a.\"b.c\".d", []string{"a", "b.c", "d"}},
|
||||
{"[1].a.d", []string{"1", "a", "d"}},
|
||||
{"a[0].c", []string{"a", "0", "c"}},
|
||||
{"[0]", []string{"0"}},
|
||||
{"a.b", append(make([]interface{}, 0), "a", "b")},
|
||||
{"a.b.**", append(make([]interface{}, 0), "a", "b", "**")},
|
||||
{"a.b.*", append(make([]interface{}, 0), "a", "b", "*")},
|
||||
{"a.b[0]", append(make([]interface{}, 0), "a", "b", int64(0))},
|
||||
{"a.b.0", append(make([]interface{}, 0), "a", "b", "0")},
|
||||
{"a.b.d[+]", append(make([]interface{}, 0), "a", "b", "d", "+")},
|
||||
{"a", append(make([]interface{}, 0), "a")},
|
||||
{"a.b.c", append(make([]interface{}, 0), "a", "b", "c")},
|
||||
{"\"a.b\".c", append(make([]interface{}, 0), "a.b", "c")},
|
||||
{"a.\"b.c\".d", append(make([]interface{}, 0), "a", "b.c", "d")},
|
||||
{"[1].a.d", append(make([]interface{}, 0), int64(1), "a", "d")},
|
||||
{"a[0].c", append(make([]interface{}, 0), "a", int64(0), "c")},
|
||||
{"[0]", append(make([]interface{}, 0), int64(0))},
|
||||
}
|
||||
|
||||
func TestPathParserParsePath(t *testing.T) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package yqlib
|
||||
|
||||
func ReadNavigationStrategy() NavigationStrategy {
|
||||
func ReadNavigationStrategy(deeplyTraverseArrays bool) NavigationStrategy {
|
||||
return &NavigationStrategyImpl{
|
||||
visitedNodes: []*NodeContext{},
|
||||
pathParser: NewPathParser(),
|
||||
@@ -13,5 +13,18 @@ func ReadNavigationStrategy() NavigationStrategy {
|
||||
visit: func(nodeContext NodeContext) error {
|
||||
return nil
|
||||
},
|
||||
shouldDeeplyTraverse: func(nodeContext NodeContext) bool {
|
||||
var isInArray = false
|
||||
if len(nodeContext.PathStack) > 0 {
|
||||
var lastElement = nodeContext.PathStack[len(nodeContext.PathStack)-1]
|
||||
switch lastElement.(type) {
|
||||
case int:
|
||||
isInArray = true
|
||||
default:
|
||||
isInArray = false
|
||||
}
|
||||
}
|
||||
return deeplyTraverseArrays || !isInArray
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ func UpdateNavigationStrategy(updateCommand UpdateCommand, autoCreate bool) Navi
|
||||
autoCreateMap: func(nodeContext NodeContext) bool {
|
||||
return autoCreate
|
||||
},
|
||||
shouldDeeplyTraverse: func(nodeContext NodeContext) bool {
|
||||
return true
|
||||
},
|
||||
visit: func(nodeContext NodeContext) error {
|
||||
node := nodeContext.Node
|
||||
changesToApply := updateCommand.Value
|
||||
|
||||
@@ -32,5 +32,5 @@ upload() {
|
||||
done < <(find ./build -mindepth 1 -maxdepth 1 -print0)
|
||||
}
|
||||
|
||||
# release
|
||||
release
|
||||
upload
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: yq
|
||||
version: '3.1.0'
|
||||
version: '3.1.2'
|
||||
summary: A lightweight and portable command-line YAML processor
|
||||
description: |
|
||||
The aim of the project is to be the jq or sed of yaml files.
|
||||
|
||||
Reference in New Issue
Block a user