1
0
mirror of https://github.com/taigrr/yq synced 2025-01-18 04:53:17 -08:00

Compare commits

...

34 Commits

Author SHA1 Message Date
Mike Farah
2937645fcd wip 2020-02-27 10:06:43 +11:00
Mike Farah
1a97b27041 wip - array length 2020-02-26 10:44:58 +11:00
Pascal Sochacki
da398765b8 added files for github action 2020-02-26 09:09:37 +11:00
Roberto Mier Escandon
d356fa0d0b Bump debian package to version 3.1-2
Updated all files to be more Debian compliant
Update release instructions for get mod vendor before releasing
2020-02-25 08:57:41 +11:00
Roberto Mier Escandon
d22bfc241b Add changelog 2020-02-25 08:57:41 +11:00
Mike Farah
954affea23 Create go.yml 2020-02-21 21:19:09 +11:00
Mike Farah
b0d1afb601 Update CONTRIBUTING.md 2020-02-21 21:16:47 +11:00
Mike Farah
b286636909 Create CODE_OF_CONDUCT.md 2020-02-21 21:13:14 +11:00
Mike Farah
bdf47c9797 Separated contribution notes 2020-02-21 21:12:09 +11:00
Mike Farah
1cc20d52bb Create CONTRIBUTING.md 2020-02-21 21:11:34 +11:00
Mike Farah
651d9edf88 Updating readme 2020-02-21 21:08:28 +11:00
Mike Farah
903605df39 Updating readme 2020-02-21 21:07:59 +11:00
Mike Farah
0f9facc84b Update issue templates 2020-02-21 21:04:54 +11:00
Mike Farah
5af86b1333 Update issue templates 2020-02-21 21:00:36 +11:00
Mike Farah
2bd2a85a4c Fixed trailing empty docs 2020-02-21 11:37:59 +11:00
Mike Farah
ceb76e5c17 Fixed trailing empty docs 2020-02-21 11:34:26 +11:00
Mike Farah
44322f0248 Fixed writing to null document 2020-02-21 11:02:10 +11:00
Mike Farah
0347516d82 Always print new line so wc works properly 2020-02-21 10:29:37 +11:00
Mike Farah
a46386e093 Fixed special characters in path for merging 2020-02-18 20:18:49 +11:00
Mike Farah
f5c3beb159 Added test for https://github.com/mikefarah/yq/issues/361 2020-02-18 20:02:09 +11:00
Mike Farah
9864afc4e7 Fixed empty merge problem 2020-02-18 09:15:46 +11:00
Roberto Mier Escandon
69fae2d9cb Inc Deb version 2020-02-17 09:29:02 +11:00
Mike Farah
83c13ce392 Fixed empty merge problem - need to visit empty arrays and objects 2020-02-13 14:56:58 +11:00
Mike Farah
d83c46eec2 Uncomment line in publish script 2020-02-13 10:22:52 +11:00
Mike Farah
65802f9e0e updated readme 2020-02-12 16:28:24 +11:00
Mike Farah
07309e1685 Inc snapcraft version 2020-02-12 16:27:25 +11:00
Mike Farah
24e906bae6 Fixed numeric map key issue 2020-02-12 15:40:21 +11:00
Mike Farah
f084f2bb23 Inc version for next release 2020-02-12 12:04:41 +11:00
Mike Farah
96a4161a92 Fixed explode anchors for array roots 2020-02-12 11:03:40 +11:00
Mike Farah
5cc01e43bc Can supply value for write from file 2020-02-08 14:04:54 +11:00
Mike Farah
9de2573009 Fixed merge append arrays 2020-02-07 16:32:39 +11:00
Mike Farah
29521f2e3e Simplified when to visit a node 2020-02-07 14:52:37 +11:00
Mike Farah
af5724ba29 Updated readme 2020-02-07 11:28:56 +11:00
Mike Farah
0a39d29c53 Updated readme 2020-02-07 11:26:29 +11:00
38 changed files with 1110 additions and 329 deletions

45
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,45 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**Input Yaml**
Concise yaml document(s) (as simple as possible to show the bug)
data1.yml:
```yaml
this: should really work
```
data2.yml:
```yaml
but: it strangely didn't
```
**Command**
The command you ran:
```
yq merge data1.yml data2.yml
```
**Actual behavior**
```yaml
cat: meow
```
**Expected behavior**
```yaml
this: should really work
but: it strangely didn't
```
**Additional context**
Add any other context about the problem here.

View File

@@ -0,0 +1,36 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
If we have data1.yml like:
```yaml
country: Australia
```
And we run a command:
```bash
yq predictWeather data1.yml
```
it could output
```yaml
temp: 32
```
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

28
.github/workflows/go.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: Go
on: [push]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.13
uses: actions/setup-go@v1
with:
go-version: 1.13
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Get dependencies
run: |
go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
- name: Build
run: make local test

76
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at mikefarah@gmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

8
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,8 @@
1. Install (golang)[https://golang.org/]
1. Run `scripts/devtools.sh` to install the required devtools
2. Run `make [local] vendor` to install the vendor dependencies
2. Run `make [local] test` to ensure you can run the existing tests
3. Write unit tests - (see existing examples). Changes will not be accepted without corresponding unit tests.
4. Make the code changes.
5. `make [local] test` to lint code and run tests
6. Profit! ok no profit, but raise a PR and get kudos :)

View File

@@ -12,15 +12,13 @@ V3 is officially out - if you've been using v2 and want/need to upgrade, checkou
## Install ## Install
### Download the latest binary ### [Download the latest binary](https://github.com/mikefarah/yq/releases/latest)
[Here](https://github.com/mikefarah/yq/releases/latest) ### MacOS:
### On MacOS:
``` ```
brew install yq brew install yq
``` ```
### On Ubuntu and other Linux distros supporting `snap` packages: ### Ubuntu and other Linux distros supporting `snap` packages:
``` ```
snap install yq snap install yq
``` ```
@@ -44,11 +42,14 @@ rm /etc/myfile.tmp
``` ```
### On Ubuntu 16.04 or higher from Debian package: ### 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 add-apt-repository ppa:rmescandon/yq
sudo apt update sudo apt update
sudo apt install yq -y sudo apt install yq -y
``` ```
Supported by @rmescandon
### Go Get: ### Go Get:
``` ```
GO111MODULE=on go get github.com/mikefarah/yq/v3 GO111MODULE=on go get github.com/mikefarah/yq/v3
@@ -82,7 +83,7 @@ yq() {
- [List matching paths of a given path expression](https://mikefarah.gitbook.io/yq/commands/read#path-only) - [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 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 - 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 - Keeps yaml formatting and comments when updating
- [Validate a yaml file](https://mikefarah.gitbook.io/yq/commands/validate) - [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) - 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,21 +115,11 @@ Available Commands:
Flags: Flags:
-h, --help help for yq -h, --help help for yq
-I, --indent int sets indent level for output (default 2)
-P, --prettyPrint pretty print -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, --verbose verbose mode
-V, --version Print version information and quit -V, --version Print version information and quit
Use "yq [command] --help" for more information about a command. 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
4. apply changes to go.mod
5. `make [local] build`
7. profit

View File

@@ -1,74 +0,0 @@
Major release! Upgraded underlying yaml parser, re-written majority of yq. This has brought on a number of features that have been in demand for a while (see below).
This is in beta and needs some community feedback and testing :)
# New Features
- Keeps yaml comments and formatting, can specify yaml tags when updating. https://github.com/mikefarah/yq/issues/19, https://github.com/mikefarah/yq/issues/169, https://github.com/mikefarah/yq/issues/107, https://github.com/mikefarah/yq/issues/171, https://github.com/mikefarah/yq/issues/245, https://github.com/mikefarah/yq/issues/303,https://github.com/mikefarah/yq/issues/308,https://github.com/mikefarah/yq/issues/314
- 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
# Breaking changes
## Update scripts file format has changed to be more powerful.
Comments can be added, and delete commands have been introduced.
Before:
```yaml
b.e[+].name: Mike Farah
```
After:
```yaml
- command: update
path: b.e[+].thing
value:
#great
things: frog # wow!
- command: delete
path: b.d
```
https://github.com/mikefarah/yq/issues/305
## Reading and splatting, matching results are printed once per line.
e.g:
```json
parent:
childA:
no: matches here
childB:
there: matches
hi: no match
there2: also matches
```
```bash
yq r sample.yaml 'parent.*.there*'
```
old
```yaml
- null
- - matches
- also matches
```
new
```yaml
matches
also matches
```
and you can print the matching paths:
yq r --printMode pv sample.yaml 'parent.*.there*'
```yaml
parent.childB.there: matches
parent.childB.there2: also matches
```

11
action.yml Normal file
View File

@@ -0,0 +1,11 @@
name: 'YAML processor'
description: 'YAML processor for running in Github action'
inputs:
cmd:
description: 'The Command which should be run'
required: true
runs:
using: 'docker'
image: 'github-action/Dockerfile'
args:
- ${{ inputs.cmd }}

View File

@@ -91,7 +91,7 @@ func TestReadCmd(t *testing.T) {
if result.Error != nil { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
test.AssertResult(t, "2", result.Output) test.AssertResult(t, "2\n", result.Output)
} }
func TestCompareCmd(t *testing.T) { func TestCompareCmd(t *testing.T) {
@@ -153,16 +153,16 @@ func TestValidateCmd(t *testing.T) {
func TestReadWithAdvancedFilterCmd(t *testing.T) { func TestReadWithAdvancedFilterCmd(t *testing.T) {
cmd := getRootCommand() 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 { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
test.AssertResult(t, "4", result.Output) test.AssertResult(t, "4\n", result.Output)
} }
func TestReadWithAdvancedFilterMapCmd(t *testing.T) { func TestReadWithAdvancedFilterMapCmd(t *testing.T) {
cmd := getRootCommand() 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 { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
@@ -183,11 +183,162 @@ func TestReadWithKeyAndValueCmd(t *testing.T) {
func TestReadArrayCmd(t *testing.T) { func TestReadArrayCmd(t *testing.T) {
cmd := getRootCommand() 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 { if result.Error != nil {
t.Error(result.Error) 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 TestReadArrayLengthCmd(t *testing.T) {
content := `- things
- whatever
`
filename := test.WriteTempYamlFile(content)
defer test.RemoveTempYamlFile(filename)
cmd := getRootCommand()
result := test.RunCmd(cmd, fmt.Sprintf("read -l %s", filename))
if result.Error != nil {
t.Error(result.Error)
}
test.AssertResult(t, "2\n", result.Output)
}
func TestReadArrayLengthDeepCmd(t *testing.T) {
content := `holder:
- things
- whatever
`
filename := test.WriteTempYamlFile(content)
defer test.RemoveTempYamlFile(filename)
cmd := getRootCommand()
result := test.RunCmd(cmd, fmt.Sprintf("read -l %s holder", filename))
if result.Error != nil {
t.Error(result.Error)
}
test.AssertResult(t, "2\n", result.Output)
}
func TestReadArrayLengthDeepMultipleCmd(t *testing.T) {
content := `holderA:
- things
- whatever
holderB:
- other things
- cool
`
filename := test.WriteTempYamlFile(content)
defer test.RemoveTempYamlFile(filename)
cmd := getRootCommand()
result := test.RunCmd(cmd, fmt.Sprintf("read -l %s holder*", filename))
if result.Error != nil {
t.Error(result.Error)
}
test.AssertResult(t, "4\n", result.Output)
}
func TestReadArrayLengthDeepMultipleWithPathCmd(t *testing.T) {
content := `holderA:
- things
- whatever
holderB:
- other things
- cool
`
filename := test.WriteTempYamlFile(content)
defer test.RemoveTempYamlFile(filename)
cmd := getRootCommand()
result := test.RunCmd(cmd, fmt.Sprintf("read -l %s -ppv holder*", filename))
if result.Error != nil {
t.Error(result.Error)
}
test.AssertResult(t, "holderA: 2\nholderB: 2", result.Output)
}
func TestReadObjectLengthCmd(t *testing.T) {
content := `cat: meow
dog: bark
`
filename := test.WriteTempYamlFile(content)
defer test.RemoveTempYamlFile(filename)
cmd := getRootCommand()
result := test.RunCmd(cmd, fmt.Sprintf("read -l %s", filename))
if result.Error != nil {
t.Error(result.Error)
}
test.AssertResult(t, "2\n", result.Output)
}
func TestReadObjectLengthDeepCmd(t *testing.T) {
content := `holder:
cat: meow
dog: bark
`
filename := test.WriteTempYamlFile(content)
defer test.RemoveTempYamlFile(filename)
cmd := getRootCommand()
result := test.RunCmd(cmd, fmt.Sprintf("read -l %s holder", filename))
if result.Error != nil {
t.Error(result.Error)
}
test.AssertResult(t, "2\n", result.Output)
}
func TestReadObjectLengthDeepMultipleCmd(t *testing.T) {
content := `holderA:
cat: meow
dog: bark
holderB:
elephant: meow
zebra: bark
`
filename := test.WriteTempYamlFile(content)
defer test.RemoveTempYamlFile(filename)
cmd := getRootCommand()
result := test.RunCmd(cmd, fmt.Sprintf("read -l %s holder*", filename))
if result.Error != nil {
t.Error(result.Error)
}
test.AssertResult(t, "4\n", result.Output)
}
func TestReadObjectLengthDeepMultipleWithPathsCmd(t *testing.T) {
content := `holderA:
cat: meow
dog: bark
holderB:
elephant: meow
zebra: bark
`
filename := test.WriteTempYamlFile(content)
defer test.RemoveTempYamlFile(filename)
cmd := getRootCommand()
result := test.RunCmd(cmd, fmt.Sprintf("read -l -ppv %s holder*", filename))
if result.Error != nil {
t.Error(result.Error)
}
test.AssertResult(t, "holderA: 2\nholderB: 2\n", result.Output)
}
func TestReadScalarLengthCmd(t *testing.T) {
content := `meow`
filename := test.WriteTempYamlFile(content)
defer test.RemoveTempYamlFile(filename)
cmd := getRootCommand()
result := test.RunCmd(cmd, fmt.Sprintf("read -l %s", filename))
if result.Error != nil {
t.Error(result.Error)
}
test.AssertResult(t, "2\n", result.Output)
} }
func TestReadDeepSplatCmd(t *testing.T) { func TestReadDeepSplatCmd(t *testing.T) {
@@ -226,7 +377,7 @@ func TestReadWithKeyCmd(t *testing.T) {
if result.Error != nil { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
test.AssertResult(t, "b.c", result.Output) test.AssertResult(t, "b.c\n", result.Output)
} }
func TestReadAnchorsCmd(t *testing.T) { func TestReadAnchorsCmd(t *testing.T) {
@@ -235,7 +386,7 @@ func TestReadAnchorsCmd(t *testing.T) {
if result.Error != nil { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
test.AssertResult(t, "1", result.Output) test.AssertResult(t, "1\n", result.Output)
} }
func TestReadAnchorsWithKeyAndValueCmd(t *testing.T) { func TestReadAnchorsWithKeyAndValueCmd(t *testing.T) {
@@ -279,7 +430,7 @@ func TestReadMergeAnchorsOriginalCmd(t *testing.T) {
if result.Error != nil { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
test.AssertResult(t, "original", result.Output) test.AssertResult(t, "original\n", result.Output)
} }
func TestReadMergeAnchorsExplodeJsonCmd(t *testing.T) { func TestReadMergeAnchorsExplodeJsonCmd(t *testing.T) {
@@ -318,7 +469,52 @@ pointer: *value-pointer`
if result.Error != nil { if result.Error != nil {
t.Error(result.Error) 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) test.AssertResult(t, expectedOutput, result.Output)
} }
@@ -333,7 +529,7 @@ pointer: *value-pointer`
if result.Error != nil { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
expectedOutput := `the value` expectedOutput := "the value\n"
test.AssertResult(t, expectedOutput, result.Output) test.AssertResult(t, expectedOutput, result.Output)
} }
@@ -388,7 +584,7 @@ func TestReadMergeAnchorsOverrideCmd(t *testing.T) {
if result.Error != nil { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
test.AssertResult(t, "ice", result.Output) test.AssertResult(t, "ice\n", result.Output)
} }
func TestReadMergeAnchorsPrefixMatchCmd(t *testing.T) { func TestReadMergeAnchorsPrefixMatchCmd(t *testing.T) {
@@ -410,7 +606,7 @@ func TestReadMergeAnchorsListOriginalCmd(t *testing.T) {
if result.Error != nil { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
test.AssertResult(t, "original", result.Output) test.AssertResult(t, "original\n", result.Output)
} }
func TestReadMergeAnchorsListOverrideInListCmd(t *testing.T) { func TestReadMergeAnchorsListOverrideInListCmd(t *testing.T) {
@@ -419,7 +615,7 @@ func TestReadMergeAnchorsListOverrideInListCmd(t *testing.T) {
if result.Error != nil { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
test.AssertResult(t, "coconut", result.Output) test.AssertResult(t, "coconut\n", result.Output)
} }
func TestReadMergeAnchorsListOverrideCmd(t *testing.T) { func TestReadMergeAnchorsListOverrideCmd(t *testing.T) {
@@ -428,7 +624,7 @@ func TestReadMergeAnchorsListOverrideCmd(t *testing.T) {
if result.Error != nil { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
test.AssertResult(t, "newbar", result.Output) test.AssertResult(t, "newbar\n", result.Output)
} }
func TestReadInvalidDocumentIndexCmd(t *testing.T) { func TestReadInvalidDocumentIndexCmd(t *testing.T) {
@@ -470,7 +666,7 @@ func TestReadMultiCmd(t *testing.T) {
if result.Error != nil { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
test.AssertResult(t, "here", result.Output) test.AssertResult(t, "here\n", result.Output)
} }
func TestReadMultiWithKeyAndValueCmd(t *testing.T) { func TestReadMultiWithKeyAndValueCmd(t *testing.T) {
@@ -491,7 +687,8 @@ func TestReadMultiAllCmd(t *testing.T) {
test.AssertResult(t, test.AssertResult(t,
`first document `first document
second document second document
third document`, result.Output) third document
`, result.Output)
} }
func TestReadMultiAllWithKeyAndValueCmd(t *testing.T) { func TestReadMultiAllWithKeyAndValueCmd(t *testing.T) {
@@ -513,7 +710,7 @@ func TestReadCmd_ArrayYaml(t *testing.T) {
if result.Error != nil { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
test.AssertResult(t, "false", result.Output) test.AssertResult(t, "false\n", result.Output)
} }
func TestReadEmptyContentCmd(t *testing.T) { func TestReadEmptyContentCmd(t *testing.T) {
@@ -530,6 +727,28 @@ func TestReadEmptyContentCmd(t *testing.T) {
test.AssertResult(t, expectedOutput, result.Output) 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) { func TestReadEmptyContentWithDefaultValueCmd(t *testing.T) {
content := `` content := ``
filename := test.WriteTempYamlFile(content) filename := test.WriteTempYamlFile(content)
@@ -673,7 +892,8 @@ func TestReadCmd_ArrayYaml_SplatWithKeyCmd(t *testing.T) {
t.Error(result.Error) t.Error(result.Error)
} }
expectedOutput := `[0] expectedOutput := `[0]
[1]` [1]
`
test.AssertResult(t, expectedOutput, result.Output) test.AssertResult(t, expectedOutput, result.Output)
} }
@@ -684,7 +904,8 @@ func TestReadCmd_ArrayYaml_SplatKey(t *testing.T) {
t.Error(result.Error) t.Error(result.Error)
} }
expectedOutput := `false expectedOutput := `false
true` true
`
test.AssertResult(t, expectedOutput, result.Output) test.AssertResult(t, expectedOutput, result.Output)
} }
@@ -765,15 +986,6 @@ func TestReadCmd_ErrorBadPath(t *testing.T) {
test.AssertResult(t, expectedOutput, result.Output) 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) { func TestReadToJsonCmd(t *testing.T) {
cmd := getRootCommand() cmd := getRootCommand()
result := test.RunCmd(cmd, "read -j ../examples/sample.yaml b") result := test.RunCmd(cmd, "read -j ../examples/sample.yaml b")
@@ -892,7 +1104,8 @@ b:
} }
expectedOutput := `more things expectedOutput := `more things
more things also` more things also
`
test.AssertResult(t, expectedOutput, result.Output) test.AssertResult(t, expectedOutput, result.Output)
} }
@@ -947,7 +1160,8 @@ b:
} }
expectedOutput := `b.there.c expectedOutput := `b.there.c
b.there2.c` b.there2.c
`
test.AssertResult(t, expectedOutput, result.Output) test.AssertResult(t, expectedOutput, result.Output)
} }
@@ -1111,25 +1325,6 @@ func TestPrefixCmd_ErrorUnreadableFile(t *testing.T) {
test.AssertResult(t, expectedOutput, result.Error.Error()) 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) { func TestPrefixCmd_Inplace(t *testing.T) {
content := `b: content := `b:
c: 3 c: 3
@@ -1201,6 +1396,79 @@ func TestWriteCmd(t *testing.T) {
test.AssertResult(t, expectedOutput, result.Output) 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) { func TestWriteEmptyCmd(t *testing.T) {
content := `` content := ``
filename := test.WriteTempYamlFile(content) filename := test.WriteTempYamlFile(content)
@@ -1217,6 +1485,26 @@ func TestWriteEmptyCmd(t *testing.T) {
test.AssertResult(t, expectedOutput, result.Output) 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) { func TestWriteCmdScript(t *testing.T) {
content := `b: content := `b:
c: 3 c: 3
@@ -1519,7 +1807,7 @@ func TestWriteCmd_SplatMapEmpty(t *testing.T) {
t.Error(result.Error) t.Error(result.Error)
} }
expectedOutput := `b: expectedOutput := `b:
c: thing c: {}
d: another thing d: another thing
` `
test.AssertResult(t, expectedOutput, result.Output) test.AssertResult(t, expectedOutput, result.Output)
@@ -1812,6 +2100,33 @@ c:
test.AssertResult(t, expectedOutput, result.Output) 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) { func TestMergeOverwriteAndAppendCmd(t *testing.T) {
cmd := getRootCommand() cmd := getRootCommand()
result := test.RunCmd(cmd, "merge --autocreate=false --append --overwrite ../examples/data1.yaml ../examples/data2.yaml") result := test.RunCmd(cmd, "merge --autocreate=false --append --overwrite ../examples/data1.yaml ../examples/data2.yaml")
@@ -1890,6 +2205,27 @@ apples: red
test.AssertResult(t, expectedOutput, result.Output) 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) { func TestMergeYamlMultiAllOverwriteCmd(t *testing.T) {
content := `b: content := `b:
c: 3 c: 3
@@ -1920,6 +2256,25 @@ apples: red
test.AssertResult(t, expectedOutput, result.Output) 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) { func TestMergeCmd_Error(t *testing.T) {
cmd := getRootCommand() cmd := getRootCommand()
result := test.RunCmd(cmd, "merge") result := test.RunCmd(cmd, "merge")

View File

@@ -7,8 +7,11 @@ import (
var customTag = "" var customTag = ""
var printMode = "v" var printMode = "v"
var resultsAsArray = false
var printLength = false
var writeInplace = false var writeInplace = false
var writeScript = "" var writeScript = ""
var sourceYamlFile = ""
var outputToJSON = false var outputToJSON = false
var prettyPrint = false var prettyPrint = false
var explodeAnchors = false var explodeAnchors = false

View File

@@ -4,6 +4,7 @@ import (
"github.com/mikefarah/yq/v3/pkg/yqlib" "github.com/mikefarah/yq/v3/pkg/yqlib"
errors "github.com/pkg/errors" errors "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
yaml "gopkg.in/yaml.v3"
) )
func createMergeCmd() *cobra.Command { 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 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 { func mergeProperties(cmd *cobra.Command, args []string) error {
var updateCommands []yqlib.UpdateCommand = make([]yqlib.UpdateCommand, 0) var updateCommands []yqlib.UpdateCommand = make([]yqlib.UpdateCommand, 0)
@@ -48,7 +59,7 @@ func mergeProperties(cmd *cobra.Command, args []string) error {
var filesToMerge = args[1:] var filesToMerge = args[1:]
for _, fileToMerge := range filesToMerge { for _, fileToMerge := range filesToMerge {
matchingNodes, errorProcessingFile := readYamlFile(fileToMerge, "**", false, 0) matchingNodes, errorProcessingFile := doReadYamlFile(fileToMerge, createReadFunctionForMerge(), false, 0)
if errorProcessingFile != nil { if errorProcessingFile != nil {
return errorProcessingFile return errorProcessingFile
} }

View File

@@ -26,6 +26,8 @@ yq r -- things.yaml '--key-starting-with-dashes.blah'
cmdRead.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") cmdRead.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)")
cmdRead.PersistentFlags().StringVarP(&printMode, "printMode", "p", "v", "print mode (v (values, default), p (paths), pv (path and value pairs)") cmdRead.PersistentFlags().StringVarP(&printMode, "printMode", "p", "v", "print mode (v (values, default), p (paths), pv (path and value pairs)")
cmdRead.PersistentFlags().StringVarP(&defaultValue, "defaultValue", "D", "", "default value printed when there are no results") cmdRead.PersistentFlags().StringVarP(&defaultValue, "defaultValue", "D", "", "default value printed when there are no results")
cmdRead.PersistentFlags().BoolVarP(&resultsAsArray, "asArray", "a", false, "print results as array")
cmdRead.PersistentFlags().BoolVarP(&printLength, "length", "l", false, "print length of results")
cmdRead.PersistentFlags().BoolVarP(&explodeAnchors, "explodeAnchors", "X", false, "explode anchors") cmdRead.PersistentFlags().BoolVarP(&explodeAnchors, "explodeAnchors", "X", false, "explode anchors")
return cmdRead return cmdRead
} }

View File

@@ -13,7 +13,19 @@ import (
yaml "gopkg.in/yaml.v3" 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) { 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 matchingNodes []*yqlib.NodeContext
var currentIndex = 0 var currentIndex = 0
@@ -29,7 +41,7 @@ func readYamlFile(filename string, path string, updateAll bool, docIndexInt int)
} }
var errorParsing error var errorParsing error
matchingNodes, errorParsing = appendDocument(matchingNodes, dataBucket, path, updateAll, docIndexInt, currentIndex) matchingNodes, errorParsing = appendDocument(matchingNodes, dataBucket, readFn, updateAll, docIndexInt, currentIndex)
if errorParsing != nil { if errorParsing != nil {
return errorParsing return errorParsing
} }
@@ -51,23 +63,43 @@ func handleEOF(updateAll bool, docIndexInt int, currentIndex int) error {
return nil 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) log.Debugf("processing document %v - requested index %v", currentIndex, docIndexInt)
yqlib.DebugNode(&dataBucket) yqlib.DebugNode(&dataBucket)
if !updateAll && currentIndex != docIndexInt { if !updateAll && currentIndex != docIndexInt {
return originalMatchingNodes, nil return originalMatchingNodes, nil
} }
log.Debugf("reading %v in document %v", path, currentIndex) log.Debugf("reading in document %v", currentIndex)
matchingNodes, errorParsing := lib.Get(&dataBucket, path) matchingNodes, errorParsing := readFn(&dataBucket)
if errorParsing != nil { if errorParsing != nil {
return nil, errors.Wrapf(errorParsing, "Error reading path in document index %v", currentIndex) return nil, errors.Wrapf(errorParsing, "Error reading path in document index %v", currentIndex)
} }
return append(originalMatchingNodes, matchingNodes...), nil return append(originalMatchingNodes, matchingNodes...), nil
} }
func printValue(node *yaml.Node, writer io.Writer) error { func lengthOf(node *yaml.Node) int {
kindToCheck := node.Kind
if node.Kind == yaml.DocumentNode && len(node.Content) == 1 {
log.Debugf("length of document node, calculating length of child")
kindToCheck = node.Content[0].Kind
}
switch kindToCheck {
case yaml.ScalarNode:
return len(node.Value)
case yaml.MappingNode:
return len(node.Content) / 2
default:
return len(node.Content)
}
}
func printValue(node *yaml.Node, writer io.Writer, shouldPrintLength bool) error {
if shouldPrintLength {
return writeString(writer, fmt.Sprintf("%v\n", lengthOf(node)))
}
if node.Kind == yaml.ScalarNode { if node.Kind == yaml.ScalarNode {
_, errorWriting := writer.Write([]byte(node.Value)) _, errorWriting := writer.Write([]byte(node.Value + "\n"))
return errorWriting return errorWriting
} }
return printNode(node, writer) return printNode(node, writer)
@@ -103,10 +135,10 @@ func writeString(writer io.Writer, txt string) error {
} }
func explode(matchingNodes []*yqlib.NodeContext) error { func explode(matchingNodes []*yqlib.NodeContext) error {
log.Debug("exploding nodes")
for _, nodeContext := range matchingNodes { for _, nodeContext := range matchingNodes {
var targetNode = yaml.Node{Kind: yaml.MappingNode} var targetNode = yaml.Node{Kind: nodeContext.Node.Kind}
explodedNodes, errorRetrieving := lib.Get(nodeContext.Node, "**") explodedNodes, errorRetrieving := lib.Get(nodeContext.Node, "**", true)
if errorRetrieving != nil { if errorRetrieving != nil {
return errorRetrieving return errorRetrieving
} }
@@ -120,6 +152,7 @@ func explode(matchingNodes []*yqlib.NodeContext) error {
} }
nodeContext.Node = &targetNode nodeContext.Node = &targetNode
} }
log.Debug("done exploding nodes")
return nil return nil
} }
@@ -146,41 +179,42 @@ func printResults(matchingNodes []*yqlib.NodeContext, writer io.Writer) error {
} }
return nil return nil
} }
var counter = 0
var errorWriting error var errorWriting error
for index, mappedDoc := range matchingNodes { for _, mappedDoc := range matchingNodes {
switch printMode { switch printMode {
case "p": case "p":
errorWriting = writeString(bufferedWriter, lib.PathStackToString(mappedDoc.PathStack)) errorWriting = writeString(bufferedWriter, lib.PathStackToString(mappedDoc.PathStack)+"\n")
if errorWriting != nil { if errorWriting != nil {
return errorWriting return errorWriting
} }
if index < len(matchingNodes)-1 {
errorWriting = writeString(bufferedWriter, "\n")
if errorWriting != nil {
return errorWriting
}
}
case "pv", "vp": case "pv", "vp":
// put it into a node and print that. // put it into a node and print that.
var parentNode = yaml.Node{Kind: yaml.MappingNode} var parentNode = yaml.Node{Kind: yaml.MappingNode}
parentNode.Content = make([]*yaml.Node, 2) parentNode.Content = make([]*yaml.Node, 2)
parentNode.Content[0] = &yaml.Node{Kind: yaml.ScalarNode, Value: lib.PathStackToString(mappedDoc.PathStack)} parentNode.Content[0] = &yaml.Node{Kind: yaml.ScalarNode, Value: lib.PathStackToString(mappedDoc.PathStack)}
parentNode.Content[1] = mappedDoc.Node if printLength {
if err := printValue(&parentNode, bufferedWriter); err != nil { parentNode.Content[1] = &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", lengthOf(mappedDoc.Node))}
} else {
parentNode.Content[1] = mappedDoc.Node
}
if err := printValue(&parentNode, bufferedWriter, false); err != nil {
return err return err
} }
default: default:
if err := printValue(mappedDoc.Node, bufferedWriter); err != nil { if printLength {
counter = counter + lengthOf(mappedDoc.Node)
} else if err := printValue(mappedDoc.Node, bufferedWriter, false); err != nil {
return err 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 printLength {
if errorWriting != nil { if err := writeString(bufferedWriter, fmt.Sprintf("%v\n", counter)); err != nil {
return errorWriting return err
}
}
} }
} }
@@ -200,6 +234,11 @@ func parseDocumentIndex() (bool, int, error) {
type updateDataFn func(dataBucket *yaml.Node, currentIndex 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 { func mapYamlDecoder(updateData updateDataFn, encoder yqlib.Encoder) yamlDecoderFn {
return func(decoder *yaml.Decoder) error { return func(decoder *yaml.Decoder) error {
var dataBucket yaml.Node var dataBucket yaml.Node
@@ -219,8 +258,11 @@ func mapYamlDecoder(updateData updateDataFn, encoder yqlib.Encoder) yamlDecoderF
if errorReading == io.EOF && docIndexInt == 0 && currentIndex == 0 { if errorReading == io.EOF && docIndexInt == 0 && currentIndex == 0 {
//empty document, lets just make one //empty document, lets just make one
child := yaml.Node{Kind: yaml.MappingNode}
dataBucket = yaml.Node{Kind: yaml.DocumentNode, Content: make([]*yaml.Node, 1)} 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 dataBucket.Content[0] = &child
} else if errorReading == io.EOF { } else if errorReading == io.EOF {
if !updateAll && currentIndex <= docIndexInt { if !updateAll && currentIndex <= docIndexInt {
@@ -355,6 +397,17 @@ func readUpdateCommands(args []string, expectedArgs int, badArgsMessage string)
} }
log.Debugf("Read write commands file '%v'", updateCommands) 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 { } else if len(args) < expectedArgs {
return nil, errors.New(badArgsMessage) return nil, errors.New(badArgsMessage)
} else { } else {

View File

@@ -11,7 +11,7 @@ var (
GitDescribe string GitDescribe string
// Version is main version number that is being run at the moment. // 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) // 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 // then it means that it is a final release. Otherwise, this is a pre-release

View File

@@ -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().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace")
cmdWrite.PersistentFlags().StringVarP(&writeScript, "script", "s", "", "yaml script for updating yaml") 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(&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)") cmdWrite.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)")
return cmdWrite return cmdWrite

28
debian/changelog vendored
View File

@@ -1,3 +1,31 @@
yq (3.1-2) eoan; urgency=medium
* Bug fix: yq 3 was removing empty inline-style objects and arrays (#355)
* Bug fix: Merge option returned different output when switching order of
merging files(#347)
* Bug fix: Add new object to existing array object was failing in 3.1.1 (#361)
* Bug fix: yq 3 empty keys did not allow merging of values (#356)
* Bug fix: keys quoted during merge (#363)
* Bug fix: Correct length with wc -l (#362)
* Bug fix: Write to empty document removed path (#359)
-- Roberto Mier Escandon <rmescandon@gmail.com> Mon, 24 Feb 2020 20:31:58 +0100
yq (3.1-1) eoan; urgency=medium
* Keeps yaml comments and formatting, can specify yaml tags when updating.
* Handles anchors
* Can print out matching paths and values when splatting
* 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 yq (2.2-1) bionic; urgency=medium
* Added Windows support for the "--inplace" command flag * Added Windows support for the "--inplace" command flag

2
debian/compat vendored
View File

@@ -1 +1 @@
9 10

22
debian/control vendored
View File

@@ -1,22 +1,22 @@
Source: yq Source: yq
Section: devel Section: devel
Priority: extra Priority: optional
Maintainer: Roberto Mier EscandĂłn <rmescandon@gmail.com> Maintainer: Roberto Mier EscandĂłn <rmescandon@gmail.com>
Build-Depends: debhelper (>= 9), Build-Depends: debhelper (>=10),
dh-golang, dh-golang (>=1.34),
golang-1.10-go, golang-1.13,
rsync rsync
Standards-Version: 3.9.6 Standards-Version: 4.1.4
Homepage: https://github.com/mikefarah/yq.git Homepage: https://github.com/mikefarah/yq.git
Vcs-Browser: https://github.com/mikefarah/yq.git Vcs-Browser: https://github.com/mikefarah/yq.git
Vcs-Git: 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 Package: yq
Architecture: any Architecture: any
Built-Using: ${misc:Built-Using} Depends: ${shlibs:Depends}, ${misc:Depends}
Depends: ${shlibs:Depends}, Description: lightweight and portable command-line YAML processor
${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.

21
debian/copyright vendored
View File

@@ -3,5 +3,22 @@ Upstream-Name: yq
Source: https://github.com/mikefarah/yq.git Source: https://github.com/mikefarah/yq.git
Files: * Files: *
Copyright: 2017 Mike Farah Ltd. All rights reserved Copyright: 2017 Mike Farah
License: Proprietary License: Expat
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1
debian/files vendored Normal file
View File

@@ -0,0 +1 @@
yq_3.1-2_source.buildinfo devel optional

26
debian/rules vendored
View File

@@ -14,46 +14,44 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
PROJECT := yq PROJECT := yq
OWNER := mikefarah OWNER := mikefarah
REPO := github.com REPO := github.com
GOVERSION := 1.10 GOVERSION := 1.13
export DH_OPTIONS export DH_OPTIONS
export DH_GOPKG := ${REPO}/${OWNER}/${PROJECT} export DH_GOPKG := ${REPO}/${OWNER}/${PROJECT}
export GOROOT := /usr/lib/go-${GOVERSION} export GOROOT := /usr/lib/go-${GOVERSION}
export GOPATH := ${CURDIR}/_build export GOPATH := ${CURDIR}/_build
export GOBIN := ${GOPATH}/bin export GOBIN := ${GOPATH}/bin
export PATH := ${GOROOT}/bin:${GOBIN}:${PATH} export PATH := ${GOROOT}/bin:${GOBIN}:${PATH}
BLDPATH := $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) export GOCACHE := /tmp/gocache
SRCDIR := ${CURDIR}/_build/src/${DH_GOPKG} export GOFLAGS := -mod=vendor
SRCDIR := ${GOPATH}/src/${DH_GOPKG}
DESTDIR := ${CURDIR}/debian/${PROJECT} DESTDIR := ${CURDIR}/debian/${PROJECT}
BINDIR := /usr/bin BINDIR := /usr/bin
ASSETSDIR := /usr/share/${PROJECT} ASSETSDIR := /usr/share/${PROJECT}
%: %:
dh $@ --buildsystem=golang --with=golang dh $@ --builddirectory=${GOPATH} --buildsystem=golang
override_dh_auto_build: override_dh_auto_build:
mkdir -p ${SRCDIR} mkdir -p ${SRCDIR}
mkdir -p ${GOBIN} mkdir -p ${GOBIN}
# copy project to local srcdir to build from there # 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 --exclude=docs . $(SRCDIR)
# build go code # build go code
(cd ${SRCDIR} && go install ./...) (cd ${SRCDIR} && go install -buildmode=pie ./...)
override_dh_auto_test: override_dh_auto_test:
(cd ${SRCDIR} && go test -v ./...) (cd ${SRCDIR} && go test -v ./...)
override_dh_auto_install: override_dh_auto_install:
mkdir -p ${DESTDIR}/${BINDIR} cp ${GOBIN}/yq ${DESTDIR}/${BINDIR}
mkdir -p ${DESTDIR}/${ASSETSDIR} cp -f ${SRCDIR}/LICENSE ${DESTDIR}/${ASSETSDIR}
cp ${CURDIR}/_build/bin/yq ${DESTDIR}/${BINDIR}
cp -rf ${SRCDIR}/LICENSE ${DESTDIR}/${ASSETSDIR}
cp -rf ${SRCDIR}/README.md ${DESTDIR}/${PLUGINSDIR}
chmod a+x ${DESTDIR}/${BINDIR}/yq chmod a+x ${DESTDIR}/${BINDIR}/yq
override_dh_auto_clean: override_dh_auto_clean:
dh_clean dh_clean
rm -rf ${CURDIR}/obj-${BLDPATH}
rm -rf ${CURDIR}/_build rm -rf ${CURDIR}/_build

3
debian/yq.dirs vendored Normal file
View File

@@ -0,0 +1,3 @@
usr/bin
usr/share/yq
usr/share/man/man1

5
github-action/Dockerfile Normal file
View File

@@ -0,0 +1,5 @@
FROM mikefarah/yq:3
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

4
github-action/entrypoint.sh Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/sh -l
echo "$1"
eval $1

15
go.mod
View File

@@ -1,13 +1,26 @@
module github.com/mikefarah/yq/v3 module github.com/mikefarah/yq/v3
require ( 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/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/pkg/errors v0.8.1
github.com/sirupsen/logrus v1.4.2 // indirect
github.com/spf13/cobra v0.0.5 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 golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935 // indirect
gopkg.in/imdario/mergo.v0 v0.3.7 // indirect gopkg.in/imdario/mergo.v0 v0.3.7 // indirect
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 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 go 1.13

86
go.sum
View File

@@ -1,17 +1,50 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 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/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/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-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/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/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/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/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/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 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 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 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 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/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.1.0+incompatible h1:nu2cqmzk4WlWJNgnevY88faMcdrDzYGcsUjYFxEpB7Y=
github.com/mikefarah/yaml/v2 v2.4.0 h1:eYqfooY0BnvKTJxr7+ABJs13n3dg9n347GScDaU2Lww= github.com/mikefarah/yaml/v2 v2.4.0 h1:eYqfooY0BnvKTJxr7+ABJs13n3dg9n347GScDaU2Lww=
github.com/mikefarah/yaml/v2 v2.4.0/go.mod h1:ahVqZF4n1W4NqwvVnZzC4es67xsW9uR/RRf2RRxieJU= 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/mikefarah/yq/v2 v2.4.1 h1:tajDonaFK6WqitSZExB6fKlWQy/yCkptqxh2AXEe3N4=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 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/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/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 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/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/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/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 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 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= 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/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 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 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/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.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/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= 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-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-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-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/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-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/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/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-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-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-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/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 h1:s5lp4ug7qHzUccgyFdjsX7OZDzHXRaePrF3B3vmUiuM=
golang.org/x/tools v0.0.0-20191030203535-5e247c9ad0a0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 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 h1:kJQZhwFzSwJS2BxboKjdZzWczQOZx8VuH7Y8hhuGUtM=
golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 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-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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 h1:QDotlIZtaO/p+Um0ok18HRTpq5i5/SAk/qprsor+9c8=
gopkg.in/imdario/mergo.v0 v0.3.7/go.mod h1:9qPP6AGrlC1G2PTNXko614FwGZvorN7MiBU0Eppok+U= 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 h1:6D+BvnJ/j6e222UW8s2qTSe3wGBtvo0MbVQG/c5k8RE=
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473/go.mod h1:N1eN2tsCx0Ydtgjl4cqmbRCsY4/+z4cYDeqwZTk6zog= 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 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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=

View File

@@ -1,13 +1,14 @@
package yqlib package yqlib
import ( import (
"fmt"
"strconv" "strconv"
yaml "gopkg.in/yaml.v3" yaml "gopkg.in/yaml.v3"
) )
type DataNavigator interface { type DataNavigator interface {
Traverse(value *yaml.Node, path []string) error Traverse(value *yaml.Node, path []interface{}) error
} }
type navigator struct { 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 realValue := value
emptyArray := make([]interface{}, 0) emptyArray := make([]interface{}, 0)
if realValue.Kind == yaml.DocumentNode { 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) return n.doTraverse(value, "", path, emptyArray)
} }
func (n *navigator) doTraverse(value *yaml.Node, head string, tail []string, pathStack []interface{}) error { func (n *navigator) doTraverse(value *yaml.Node, head interface{}, tail []interface{}, pathStack []interface{}) error {
if value.Kind == yaml.ScalarNode {
return n.navigationStrategy.Visit(NewNodeContext(value, head, tail, pathStack))
}
log.Debug("head %v", head) log.Debug("head %v", head)
DebugNode(value) DebugNode(value)
var nodeContext = NewNodeContext(value, head, tail, pathStack)
var errorDeepSplatting error var errorDeepSplatting error
if head == "**" { if head == "**" && value.Kind != yaml.ScalarNode && n.navigationStrategy.ShouldDeeplyTraverse(nodeContext) {
if len(pathStack) == 0 || pathStack[len(pathStack)-1] != "<<" { if len(pathStack) == 0 || pathStack[len(pathStack)-1] != "<<" {
errorDeepSplatting = n.recurse(value, head, tail, pathStack) 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 return errorDeepSplatting
} }
if len(tail) > 0 { if len(tail) > 0 && value.Kind != yaml.ScalarNode {
log.Debugf("diving into %v", tail[0]) log.Debugf("diving into %v", tail[0])
DebugNode(value) DebugNode(value)
return n.recurse(value, tail[0], tail[1:], pathStack) 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 { func (n *navigator) getOrReplace(original *yaml.Node, expectedKind yaml.Kind) *yaml.Node {
if original.Kind != expectedKind { 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 &yaml.Node{Kind: expectedKind}
} }
return original 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)) log.Debug("recursing, processing %v, pathStack %v", head, pathStackToString(pathStack))
switch value.Kind { switch value.Kind {
case yaml.MappingNode: case yaml.MappingNode:
log.Debug("its a map with %v entries", len(value.Content)/2) 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: case yaml.SequenceNode:
log.Debug("its a sequence of %v things!", len(value.Content)) log.Debug("its a sequence of %v things!", len(value.Content))
var index, errorParsingIndex = strconv.ParseInt(head, 10, 64) // nolint switch head := head.(type) {
if errorParsingIndex == nil { case int64:
return n.recurseArray(value, index, head, tail, pathStack) return n.recurseArray(value, head, head, tail, pathStack)
} else if head == "+" { default:
return n.appendArray(value, head, tail, pathStack)
}
return n.splatArray(value, head, tail, pathStack)
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: case yaml.AliasNode:
log.Debug("its an alias!") log.Debug("its an alias!")
DebugNode(value.Alias) DebugNode(value.Alias)
if n.navigationStrategy.FollowAlias(NewNodeContext(value, head, tail, pathStack)) { if n.navigationStrategy.FollowAlias(NewNodeContext(value, head, tail, pathStack)) {
log.Debug("following the alias")
if value.Alias.Kind == yaml.ScalarNode { return n.recurse(value.Alias, head, tail, pathStack)
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)
}
} }
return nil return nil
default: 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 traversedEntry := false
errorVisiting := n.visitMatchingEntries(value, head, tail, pathStack, func(contents []*yaml.Node, indexInMap int) error { errorVisiting := n.visitMatchingEntries(value, head, tail, pathStack, func(contents []*yaml.Node, indexInMap int) error {
log.Debug("recurseMap: visitMatchingEntries for %v", contents[indexInMap].Value) 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) { if n.navigationStrategy.ShouldTraverse(NewNodeContext(contents[indexInMap+1], head, tail, newPathStack), contents[indexInMap].Value) {
log.Debug("recurseMap: Going to traverse") log.Debug("recurseMap: Going to traverse")
traversedEntry = true 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) errorTraversing := n.doTraverse(contents[indexInMap+1], head, tail, newPathStack)
log.Debug("recurseMap: Finished traversing") log.Debug("recurseMap: Finished traversing")
n.navigationStrategy.DebugVisitedNodes() n.navigationStrategy.DebugVisitedNodes()
@@ -130,22 +128,33 @@ func (n *navigator) recurseMap(value *yaml.Node, head string, tail []string, pat
return errorVisiting 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 return nil
} }
_, errorParsingInt := strconv.ParseInt(head, 10, 64)
mapEntryKey := yaml.Node{Value: head, Kind: yaml.ScalarNode} 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) value.Content = append(value.Content, &mapEntryKey)
mapEntryValue := yaml.Node{Kind: guessKind(head, tail, 0)} mapEntryValue := yaml.Node{Kind: guessKind(head, tail, 0)}
value.Content = append(value.Content, &mapEntryValue) 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)) return n.doTraverse(&mapEntryValue, head, tail, append(pathStack, head))
} }
// need to pass the node in, as it may be aliased // need to pass the node in, as it may be aliased
type mapVisitorFn func(contents []*yaml.Node, index int) error 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 var contents = node.Content
for index := 0; index < len(contents); index = index + 2 { for index := 0; index < len(contents); index = index + 2 {
content := contents[index] content := contents[index]
@@ -160,7 +169,7 @@ func (n *navigator) visitDirectMatchingEntries(node *yaml.Node, head string, tai
return nil 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 var contents = node.Content
log.Debug("visitMatchingEntries %v", head) log.Debug("visitMatchingEntries %v", head)
DebugNode(node) 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) 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 // merge aliases are defined first, but we only want to traverse them
// if we don't find a match on this node first. // if we don't find a match on this node first.
// traverse them backwards so that the last alias overrides the preceding. // 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 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. // need to search this backwards too, so that aliases defined last override the preceding.
for aliasIndex := len(possibleAliasArray) - 1; aliasIndex >= 0; aliasIndex = aliasIndex - 1 { for aliasIndex := len(possibleAliasArray) - 1; aliasIndex >= 0; aliasIndex = aliasIndex - 1 {
child := possibleAliasArray[aliasIndex] child := possibleAliasArray[aliasIndex]
@@ -223,7 +232,7 @@ func (n *navigator) visitAliasSequence(possibleAliasArray []*yaml.Node, head str
return nil 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 { for index, childValue := range value.Content {
log.Debug("processing") log.Debug("processing")
DebugNode(childValue) DebugNode(childValue)
@@ -231,6 +240,9 @@ func (n *navigator) splatArray(value *yaml.Node, head string, tail []string, pat
newPathStack := append(pathStack, index) newPathStack := append(pathStack, index)
if n.navigationStrategy.ShouldTraverse(NewNodeContext(childValue, head, tail, newPathStack), childValue.Value) { 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) var err = n.doTraverse(childValue, head, tail, newPathStack)
if err != nil { if err != nil {
return err return err
@@ -240,14 +252,14 @@ func (n *navigator) splatArray(value *yaml.Node, head string, tail []string, pat
return nil 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)} var newNode = yaml.Node{Kind: guessKind(head, tail, 0)}
value.Content = append(value.Content, &newNode) value.Content = append(value.Content, &newNode)
log.Debug("appending a new node, %v", value.Content) log.Debug("appending a new node, %v", value.Content)
return n.doTraverse(&newNode, head, tail, append(pathStack, len(value.Content)-1)) 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 { for int64(len(value.Content)) <= index {
value.Content = append(value.Content, &yaml.Node{Kind: guessKind(head, tail, 0)}) value.Content = append(value.Content, &yaml.Node{Kind: guessKind(head, tail, 0)})
} }

View File

@@ -1,12 +1,10 @@
package yqlib package yqlib
import ( import (
"strconv"
yaml "gopkg.in/yaml.v3" yaml "gopkg.in/yaml.v3"
) )
func DeleteNavigationStrategy(pathElementToDelete string) NavigationStrategy { func DeleteNavigationStrategy(pathElementToDelete interface{}) NavigationStrategy {
parser := NewPathParser() parser := NewPathParser()
return &NavigationStrategyImpl{ return &NavigationStrategyImpl{
visitedNodes: []*NodeContext{}, visitedNodes: []*NodeContext{},
@@ -17,6 +15,9 @@ func DeleteNavigationStrategy(pathElementToDelete string) NavigationStrategy {
autoCreateMap: func(nodeContext NodeContext) bool { autoCreateMap: func(nodeContext NodeContext) bool {
return false return false
}, },
shouldDeeplyTraverse: func(nodeContext NodeContext) bool {
return true
},
visit: func(nodeContext NodeContext) error { visit: func(nodeContext NodeContext) error {
node := nodeContext.Node node := nodeContext.Node
log.Debug("need to find and delete %v in here", pathElementToDelete) 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) newContents := make([]*yaml.Node, 0)
for index := 0; index < len(contents); index = index + 2 { for index := 0; index < len(contents); index = index + 2 {
keyNode := contents[index] keyNode := contents[index]
valueNode := contents[index+1] 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) log.Debug("adding node %v", keyNode.Value)
newContents = append(newContents, keyNode, valueNode) newContents = append(newContents, keyNode, valueNode)
} else { } else {
@@ -46,21 +47,23 @@ func deleteFromMap(pathParser PathParser, contents []*yaml.Node, pathStack []int
return newContents 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 switch pathElementToDelete := pathElementToDelete.(type) {
if err == nil { case int64:
return deleteIndexInArray(content, indexToDelete) return deleteIndexInArray(content, pathElementToDelete)
} default:
log.Debug("%v is not a numeric index, finding matching patterns", pathElementToDelete) log.Debug("%v is not a numeric index, finding matching patterns", pathElementToDelete)
var newArray = make([]*yaml.Node, 0) var newArray = make([]*yaml.Node, 0)
for _, childValue := range content { for _, childValue := range content {
if !pathParser.MatchesNextPathElement(NewNodeContext(childValue, pathElementToDelete, []string{}, pathStack), childValue.Value) { if !pathParser.MatchesNextPathElement(NewNodeContext(childValue, pathElementToDelete, make([]interface{}, 0), pathStack), childValue.Value) {
newArray = append(newArray, childValue) newArray = append(newArray, childValue)
}
} }
return newArray
} }
return newArray
} }
func deleteIndexInArray(content []*yaml.Node, index int64) []*yaml.Node { func deleteIndexInArray(content []*yaml.Node, index int64) []*yaml.Node {

View File

@@ -19,6 +19,23 @@ type UpdateCommand struct {
Overwrite bool 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) { func DebugNode(value *yaml.Node) {
if value == nil { if value == nil {
log.Debug("-- node is nil --") log.Debug("-- node is nil --")
@@ -30,7 +47,7 @@ func DebugNode(value *yaml.Node) {
log.Error("Error debugging node, %v", errorEncoding.Error()) log.Error("Error debugging node, %v", errorEncoding.Error())
} }
encoder.Close() encoder.Close()
log.Debug("Tag: %v", value.Tag) log.Debug("Tag: %v, Kind: %v", value.Tag, KindString(value.Kind))
log.Debug("%v", buf.String()) log.Debug("%v", buf.String())
} }
} }
@@ -43,7 +60,7 @@ func mergePathStackToString(pathStack []interface{}, appendArrays bool) string {
var sb strings.Builder var sb strings.Builder
for index, path := range pathStack { for index, path := range pathStack {
switch path.(type) { switch path.(type) {
case int: case int, int64:
if appendArrays { if appendArrays {
sb.WriteString("[+]") sb.WriteString("[+]")
} else { } else {
@@ -52,13 +69,22 @@ func mergePathStackToString(pathStack []interface{}, appendArrays bool) string {
default: default:
s := fmt.Sprintf("%v", path) s := fmt.Sprintf("%v", path)
hasDot := strings.Contains(s, ".") var _, errParsingInt = strconv.ParseInt(s, 10, 64) // nolint
if hasDot {
sb.WriteString("[") 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) sb.WriteString(s)
if hasDot { if hasSpecial || errParsingInt == nil {
sb.WriteString("]") sb.WriteString(wrappingCharacterEnd)
} }
} }
@@ -66,38 +92,47 @@ func mergePathStackToString(pathStack []interface{}, appendArrays bool) string {
sb.WriteString(".") 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 { func guessKind(head interface{}, tail []interface{}, guess yaml.Kind) yaml.Kind {
log.Debug("tail %v", tail) log.Debug("guessKind: tail %v", tail)
if len(tail) == 0 && guess == 0 { if len(tail) == 0 && guess == 0 {
log.Debug("end of path, must be a scalar") log.Debug("end of path, must be a scalar")
return yaml.ScalarNode return yaml.ScalarNode
} else if len(tail) == 0 { } else if len(tail) == 0 {
return guess return guess
} }
var next = tail[0]
var _, errorParsingInt = strconv.ParseInt(tail[0], 10, 64) switch next.(type) {
if tail[0] == "+" || errorParsingInt == nil { case int64:
return yaml.SequenceNode 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 { 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 Update(rootNode *yaml.Node, updateCommand UpdateCommand, autoCreate bool) error
New(path string) yaml.Node 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) var paths = l.parser.ParsePath(path)
navigationStrategy := ReadNavigationStrategy() navigationStrategy := ReadNavigationStrategy(deeplyTraverseArrays)
navigator := NewDataNavigator(navigationStrategy) navigator := NewDataNavigator(navigationStrategy)
error := navigator.Traverse(rootNode, paths) error := navigator.Traverse(rootNode, paths)
return navigationStrategy.GetVisitedNodes(), error return navigationStrategy.GetVisitedNodes(), error

View File

@@ -8,13 +8,13 @@ import (
type NodeContext struct { type NodeContext struct {
Node *yaml.Node Node *yaml.Node
Head string Head interface{}
Tail []string Tail []interface{}
PathStack []interface{} PathStack []interface{}
} }
func NewNodeContext(node *yaml.Node, head string, tail []string, pathStack []interface{}) NodeContext { func NewNodeContext(node *yaml.Node, head interface{}, tail []interface{}, pathStack []interface{}) NodeContext {
newTail := make([]string, len(tail)) newTail := make([]interface{}, len(tail))
copy(newTail, tail) copy(newTail, tail)
newPathStack := make([]interface{}, len(pathStack)) 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 // node key is the string value of the last element in the path stack
// we use it to match against the pathExpression in head. // we use it to match against the pathExpression in head.
ShouldTraverse(nodeContext NodeContext, nodeKey string) bool ShouldTraverse(nodeContext NodeContext, nodeKey string) bool
ShouldDeeplyTraverse(nodeContext NodeContext) bool
GetVisitedNodes() []*NodeContext GetVisitedNodes() []*NodeContext
DebugVisitedNodes() DebugVisitedNodes()
GetPathParser() PathParser GetPathParser() PathParser
} }
type NavigationStrategyImpl struct { type NavigationStrategyImpl struct {
followAlias func(nodeContext NodeContext) bool followAlias func(nodeContext NodeContext) bool
autoCreateMap func(nodeContext NodeContext) bool autoCreateMap func(nodeContext NodeContext) bool
visit func(nodeContext NodeContext) error visit func(nodeContext NodeContext) error
shouldVisitExtraFn func(nodeContext NodeContext) bool shouldVisitExtraFn func(nodeContext NodeContext) bool
visitedNodes []*NodeContext shouldDeeplyTraverse func(nodeContext NodeContext) bool
pathParser PathParser visitedNodes []*NodeContext
pathParser PathParser
} }
func (ns *NavigationStrategyImpl) GetPathParser() PathParser { func (ns *NavigationStrategyImpl) GetPathParser() PathParser {
@@ -64,6 +66,10 @@ func (ns *NavigationStrategyImpl) AutoCreateMap(nodeContext NodeContext) bool {
return ns.autoCreateMap(nodeContext) return ns.autoCreateMap(nodeContext)
} }
func (ns *NavigationStrategyImpl) ShouldDeeplyTraverse(nodeContext NodeContext) bool {
return ns.shouldDeeplyTraverse(nodeContext)
}
func (ns *NavigationStrategyImpl) ShouldTraverse(nodeContext NodeContext, nodeKey string) bool { func (ns *NavigationStrategyImpl) ShouldTraverse(nodeContext NodeContext, nodeKey string) bool {
// we should traverse aliases (if enabled), but not visit them :/ // we should traverse aliases (if enabled), but not visit them :/
if len(nodeContext.PathStack) == 0 { if len(nodeContext.PathStack) == 0 {
@@ -84,7 +90,6 @@ func (ns *NavigationStrategyImpl) shouldVisit(nodeContext NodeContext) bool {
return true return true
} }
log.Debug("tail len %v", len(nodeContext.Tail)) log.Debug("tail len %v", len(nodeContext.Tail))
// SOMETHING HERE!
if ns.alreadyVisited(pathStack) || len(nodeContext.Tail) != 0 { if ns.alreadyVisited(pathStack) || len(nodeContext.Tail) != 0 {
return false return false

View File

@@ -1,12 +1,13 @@
package yqlib package yqlib
import ( import (
"fmt"
"strconv" "strconv"
"strings" "strings"
) )
type PathParser interface { type PathParser interface {
ParsePath(path string) []string ParsePath(path string) []interface{}
MatchesNextPathElement(nodeContext NodeContext, nodeKey string) bool MatchesNextPathElement(nodeContext NodeContext, nodeKey string) bool
IsPathExpression(pathElement string) bool IsPathExpression(pathElement string) bool
} }
@@ -42,9 +43,11 @@ func (p *pathParser) MatchesNextPathElement(nodeContext NodeContext, nodeKey str
if head == "**" || head == "*" { if head == "**" || head == "*" {
return true return true
} }
if strings.Contains(head, "==") { var headString = fmt.Sprintf("%v", head)
if strings.Contains(headString, "==") {
log.Debug("ooh deep recursion time") log.Debug("ooh deep recursion time")
result := strings.SplitN(head, "==", 2) result := strings.SplitN(headString, "==", 2)
path := strings.TrimSpace(result[0]) path := strings.TrimSpace(result[0])
value := strings.TrimSpace(result[1]) value := strings.TrimSpace(result[1])
log.Debug("path %v", path) 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 == "" { 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) head, tail := p.nextYamlPath(remaining)
if tail == "" { if tail == "" {
return append(paths, head) return append(paths, head)
@@ -88,11 +92,16 @@ func (p *pathParser) parsePathAccum(paths []string, remaining string) []string {
return p.parsePathAccum(append(paths, head), tail) 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] { switch path[0] {
case '[': case '[':
// e.g [0].blah.cat -> we need to return "0" and "blah.cat" // 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 '"': case '"':
// e.g "a.b".blah.cat -> we need to return "a.b" and "blah.cat" // e.g "a.b".blah.cat -> we need to return "a.b" and "blah.cat"
return p.search(path[1:], []uint8{'"'}, true) return p.search(path[1:], []uint8{'"'}, true)

View File

@@ -10,20 +10,21 @@ var parser = NewPathParser()
var parsePathsTests = []struct { var parsePathsTests = []struct {
path string path string
expectedPaths []string expectedPaths []interface{}
}{ }{
{"a.b", []string{"a", "b"}}, {"a.b", append(make([]interface{}, 0), "a", "b")},
{"a.b.**", []string{"a", "b", "**"}}, {"a.b.**", append(make([]interface{}, 0), "a", "b", "**")},
{"a.b.*", []string{"a", "b", "*"}}, {"a.b.*", append(make([]interface{}, 0), "a", "b", "*")},
{"a.b[0]", []string{"a", "b", "0"}}, {"a.b[0]", append(make([]interface{}, 0), "a", "b", int64(0))},
{"a.b.d[+]", []string{"a", "b", "d", "+"}}, {"a.b.0", append(make([]interface{}, 0), "a", "b", "0")},
{"a", []string{"a"}}, {"a.b.d[+]", append(make([]interface{}, 0), "a", "b", "d", "+")},
{"a.b.c", []string{"a", "b", "c"}}, {"a", append(make([]interface{}, 0), "a")},
{"\"a.b\".c", []string{"a.b", "c"}}, {"a.b.c", append(make([]interface{}, 0), "a", "b", "c")},
{"a.\"b.c\".d", []string{"a", "b.c", "d"}}, {"\"a.b\".c", append(make([]interface{}, 0), "a.b", "c")},
{"[1].a.d", []string{"1", "a", "d"}}, {"a.\"b.c\".d", append(make([]interface{}, 0), "a", "b.c", "d")},
{"a[0].c", []string{"a", "0", "c"}}, {"[1].a.d", append(make([]interface{}, 0), int64(1), "a", "d")},
{"[0]", []string{"0"}}, {"a[0].c", append(make([]interface{}, 0), "a", int64(0), "c")},
{"[0]", append(make([]interface{}, 0), int64(0))},
} }
func TestPathParserParsePath(t *testing.T) { func TestPathParserParsePath(t *testing.T) {

View File

@@ -1,6 +1,6 @@
package yqlib package yqlib
func ReadNavigationStrategy() NavigationStrategy { func ReadNavigationStrategy(deeplyTraverseArrays bool) NavigationStrategy {
return &NavigationStrategyImpl{ return &NavigationStrategyImpl{
visitedNodes: []*NodeContext{}, visitedNodes: []*NodeContext{},
pathParser: NewPathParser(), pathParser: NewPathParser(),
@@ -13,5 +13,18 @@ func ReadNavigationStrategy() NavigationStrategy {
visit: func(nodeContext NodeContext) error { visit: func(nodeContext NodeContext) error {
return nil 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
},
} }
} }

View File

@@ -10,6 +10,9 @@ func UpdateNavigationStrategy(updateCommand UpdateCommand, autoCreate bool) Navi
autoCreateMap: func(nodeContext NodeContext) bool { autoCreateMap: func(nodeContext NodeContext) bool {
return autoCreate return autoCreate
}, },
shouldDeeplyTraverse: func(nodeContext NodeContext) bool {
return true
},
visit: func(nodeContext NodeContext) error { visit: func(nodeContext NodeContext) error {
node := nodeContext.Node node := nodeContext.Node
changesToApply := updateCommand.Value changesToApply := updateCommand.Value

View File

@@ -33,6 +33,8 @@
- docker build . -t mikefarah/yq:latest -t mikefarah/yq:VERSION - docker build . -t mikefarah/yq:latest -t mikefarah/yq:VERSION
- debian package - debian package
- ensure you get all vendor dependencies before packaging
```go mod vendor```
- execute - execute
```dch -i``` ```dch -i```
- fill debian/changelog with changes from last version - fill debian/changelog with changes from last version
@@ -42,4 +44,4 @@
- put to PPA - put to PPA
```dput ppa:<REPOSITORY> ../yq_<VERSION>_source.changes``` ```dput ppa:<REPOSITORY> ../yq_<VERSION>_source.changes```
(current distro repository is ppa:rmescandon/yq. In case that a new version (current distro repository is ppa:rmescandon/yq. In case that a new version
is released, please contact rmescandon@gmail.com to bump debian package) is released, please contact rmescandon@gmail.com to bump debian package)

View File

@@ -32,5 +32,5 @@ upload() {
done < <(find ./build -mindepth 1 -maxdepth 1 -print0) done < <(find ./build -mindepth 1 -maxdepth 1 -print0)
} }
# release release
upload upload

View File

@@ -1,5 +1,5 @@
name: yq name: yq
version: '3.1.0' version: '3.1.2'
summary: A lightweight and portable command-line YAML processor summary: A lightweight and portable command-line YAML processor
description: | description: |
The aim of the project is to be the jq or sed of yaml files. The aim of the project is to be the jq or sed of yaml files.

View File

@@ -1,3 +0,0 @@
b:
c: thing
d: another thing