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

Compare commits

...

16 Commits
3.3.0 ... 3.3.1

Author SHA1 Message Date
Mike Farah
b0fa0e5b86 added shell completion instructions 2020-06-11 18:50:38 +10:00
Mike Farah
6777d639c0 Fixed error handling 2020-06-11 18:30:45 +10:00
Mike Farah
de8dcff803 Added shell completions 2020-06-11 18:27:01 +10:00
Mike Farah
765ada4dc6 Bumping version 2020-06-11 15:01:18 +10:00
Mike Farah
1405584892 New,Update now support anchors and aliases 2020-06-11 13:57:13 +10:00
Mike Farah
8c9c326342 Usage error messages now go to StdErr 2020-06-11 10:14:33 +10:00
Mike Farah
e90b00957b Added missing flow style 2020-06-11 09:58:10 +10:00
Mike Farah
71f5f76213 Delete now works with deep splat 2020-06-11 09:53:36 +10:00
Mike Farah
b9e304e7a4 Can stripComments and explodeAnchors for compare 2020-06-11 09:13:55 +10:00
Mike Farah
d473c39a44 Added exit flag 2020-06-10 16:55:20 +10:00
Mike Farah
9624410add Significantly improved performance of exploding anchors (improves to json speed) 2020-06-10 16:36:33 +10:00
Mike Farah
b55fe48bd8 Update to latest go-yaml library, updated test w.r.t. formatting and comment handling fixes 2020-06-10 16:02:12 +10:00
adripo
721dd57ed4 Fixed typo
removed repeated word
2020-06-02 09:02:20 +10:00
Roberto Mier Escandon
6fc3566acd Changelog updated for version 3.3-0 2020-05-01 10:31:53 +10:00
Mike Farah
4b63d92a3c Added choco install instructions 2020-04-22 23:29:44 +10:00
M. Minot
3ccd32a47e docs(readme): protect parameter expansions
Avoid unwanted word-splitting, including in the working directory path.
2020-04-19 00:18:08 +10:00
34 changed files with 425 additions and 105 deletions

View File

@@ -18,13 +18,20 @@ V3 is officially out - if you've been using v2 and want/need to upgrade, checkou
``` ```
brew install yq brew install yq
``` ```
### Windows:
```
choco install yq
```
Supported by @chillum
### Ubuntu and other Linux distros supporting `snap` packages: ### Ubuntu and other Linux distros supporting `snap` packages:
``` ```
snap install yq snap install yq
``` ```
#### Snap notes #### Snap notes
`yq` installs with with [_strict confinement_](https://docs.snapcraft.io/snap-confinement/6233) in snap, this means it doesn't have direct access to root files. To read root files you can: `yq` installs with [_strict confinement_](https://docs.snapcraft.io/snap-confinement/6233) in snap, this means it doesn't have direct access to root files. To read root files you can:
``` ```
sudo cat /etc/myfile | yq r - a.path sudo cat /etc/myfile | yq r - a.path
@@ -60,20 +67,20 @@ GO111MODULE=on go get github.com/mikefarah/yq/v3
Oneshot use: Oneshot use:
```bash ```bash
docker run --rm -v ${PWD}:/workdir mikefarah/yq yq [flags] <command> FILE... docker run --rm -v "${PWD}":/workdir mikefarah/yq yq [flags] <command> FILE...
``` ```
Run commands interactively: Run commands interactively:
```bash ```bash
docker run --rm -it -v ${PWD}:/workdir mikefarah/yq sh docker run --rm -it -v "${PWD}":/workdir mikefarah/yq sh
``` ```
It can be useful to have a bash function to avoid typing the whole docker command: It can be useful to have a bash function to avoid typing the whole docker command:
```bash ```bash
yq() { yq() {
docker run --rm -i -v ${PWD}:/workdir mikefarah/yq yq $@ docker run --rm -i -v "${PWD}":/workdir mikefarah/yq yq "$@"
} }
``` ```

View File

@@ -31,6 +31,8 @@ yq x -d1 dataA.yaml dataB.yaml 'a.b.c'
cmdCompare.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") cmdCompare.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)")
cmdCompare.PersistentFlags().StringVarP(&printMode, "printMode", "p", "v", "print mode (v (values, default), p (paths), pv (path and value pairs)") cmdCompare.PersistentFlags().StringVarP(&printMode, "printMode", "p", "v", "print mode (v (values, default), p (paths), pv (path and value pairs)")
cmdCompare.PersistentFlags().StringVarP(&defaultValue, "defaultValue", "D", "", "default value printed when there are no results") cmdCompare.PersistentFlags().StringVarP(&defaultValue, "defaultValue", "D", "", "default value printed when there are no results")
cmdCompare.PersistentFlags().BoolVarP(&stripComments, "stripComments", "", false, "strip comments out before comparing")
cmdCompare.PersistentFlags().BoolVarP(&explodeAnchors, "explodeAnchors", "X", false, "explode anchors")
return cmdCompare return cmdCompare
} }

View File

@@ -16,6 +16,55 @@ func TestCompareSameCmd(t *testing.T) {
test.AssertResult(t, expectedOutput, result.Output) test.AssertResult(t, expectedOutput, result.Output)
} }
func TestCompareIgnoreCommentsCmd(t *testing.T) {
cmd := getRootCommand()
result := test.RunCmd(cmd, "compare --stripComments ../examples/data1.yaml ../examples/data1-no-comments.yaml")
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := ``
test.AssertResult(t, expectedOutput, result.Output)
}
func TestCompareDontIgnoreCommentsCmd(t *testing.T) {
forceOsExit = false
cmd := getRootCommand()
result := test.RunCmd(cmd, "compare ../examples/data1.yaml ../examples/data1-no-comments.yaml")
expectedOutput := `-a: simple # just the best
+a: simple
b: [1, 2]
c:
test: 1
`
test.AssertResult(t, expectedOutput, result.Output)
}
func TestCompareExplodeAnchorsCommentsCmd(t *testing.T) {
cmd := getRootCommand()
result := test.RunCmd(cmd, "compare --explodeAnchors ../examples/simple-anchor.yaml ../examples/simple-anchor-exploded.yaml")
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := ``
test.AssertResult(t, expectedOutput, result.Output)
}
func TestCompareDontExplodeAnchorsCmd(t *testing.T) {
forceOsExit = false
cmd := getRootCommand()
result := test.RunCmd(cmd, "compare ../examples/simple-anchor.yaml ../examples/simple-anchor-exploded.yaml")
expectedOutput := `-foo: &foo
+foo:
a: 1
foobar:
- !!merge <<: *foo
+ a: 1
`
test.AssertResult(t, expectedOutput, result.Output)
}
func TestCompareDifferentCmd(t *testing.T) { func TestCompareDifferentCmd(t *testing.T) {
forceOsExit = false forceOsExit = false
cmd := getRootCommand() cmd := getRootCommand()
@@ -40,9 +89,9 @@ func TestComparePrettyCmd(t *testing.T) {
} }
expectedOutput := ` a: simple # just the best expectedOutput := ` a: simple # just the best
b: b:
- 1 - 1
-- 2 - - 2
+- 3 + - 3
c: c:
test: 1 test: 1
` `

View File

@@ -10,12 +10,15 @@ var printMode = "v"
var printLength = false var printLength = false
var unwrapScalar = true var unwrapScalar = true
var customStyle = "" var customStyle = ""
var anchorName = ""
var makeAlias = false
var stripComments = false var stripComments = false
var collectIntoArray = false var collectIntoArray = false
var writeInplace = false var writeInplace = false
var writeScript = "" var writeScript = ""
var sourceYamlFile = "" var sourceYamlFile = ""
var outputToJSON = false var outputToJSON = false
var exitStatus = false
var prettyPrint = false var prettyPrint = false
var explodeAnchors = false var explodeAnchors = false
var colorsEnabled = false var colorsEnabled = false

View File

@@ -94,8 +94,31 @@ b:
expectedOutput := `a: 2 expectedOutput := `a: 2
b: b:
hi: hi:
- name: fred - name: fred
- name: sam - name: sam
`
test.AssertResult(t, expectedOutput, result.Output)
}
func TestDeleteDeepSplatArrayYaml(t *testing.T) {
content := `thing: 123
b:
hi:
- thing: item1
name: fred
`
filename := test.WriteTempYamlFile(content)
defer test.RemoveTempYamlFile(filename)
cmd := getRootCommand()
result := test.RunCmd(cmd, fmt.Sprintf("delete %s **.thing", filename))
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `b:
hi:
- name: fred
` `
test.AssertResult(t, expectedOutput, result.Output) test.AssertResult(t, expectedOutput, result.Output)
} }

View File

@@ -101,10 +101,10 @@ func TestMergeAppendArraysCmd(t *testing.T) {
t.Error(result.Error) t.Error(result.Error)
} }
expectedOutput := `people: expectedOutput := `people:
- name: Barry - name: Barry
age: 21 age: 21
- name: Roger - name: Roger
age: 44 age: 44
` `
test.AssertResult(t, expectedOutput, result.Output) test.AssertResult(t, expectedOutput, result.Output)
} }
@@ -146,8 +146,8 @@ another:
document: here document: here
a: simple # just the best a: simple # just the best
b: b:
- 1 - 1
- 2 - 2
c: c:
test: 1 test: 1
--- ---
@@ -317,8 +317,8 @@ func TestMergeAllowEmptyTargetCmd(t *testing.T) {
} }
expectedOutput := `a: simple # just the best expectedOutput := `a: simple # just the best
b: b:
- 1 - 1
- 2 - 2
c: c:
test: 1 test: 1
` `

View File

@@ -29,6 +29,8 @@ Note that you can give a create script to perform more sophisticated yaml. This
cmdNew.PersistentFlags().StringVarP(&writeScript, "script", "s", "", "yaml script for creating yaml") cmdNew.PersistentFlags().StringVarP(&writeScript, "script", "s", "", "yaml script for creating yaml")
cmdNew.PersistentFlags().StringVarP(&customTag, "tag", "t", "", "set yaml tag (e.g. !!int)") cmdNew.PersistentFlags().StringVarP(&customTag, "tag", "t", "", "set yaml tag (e.g. !!int)")
cmdNew.PersistentFlags().StringVarP(&customStyle, "style", "", "", "formatting style of the value: single, double, folded, flow, literal, tagged") cmdNew.PersistentFlags().StringVarP(&customStyle, "style", "", "", "formatting style of the value: single, double, folded, flow, literal, tagged")
cmdNew.PersistentFlags().StringVarP(&anchorName, "anchorName", "", "", "anchor name")
cmdNew.PersistentFlags().BoolVarP(&makeAlias, "makeAlias", "", false, "create an alias using the value as the anchor name")
return cmdNew return cmdNew
} }

View File

@@ -18,6 +18,30 @@ func TestNewCmd(t *testing.T) {
test.AssertResult(t, expectedOutput, result.Output) test.AssertResult(t, expectedOutput, result.Output)
} }
func TestNewAnchorCmd(t *testing.T) {
cmd := getRootCommand()
result := test.RunCmd(cmd, "new b.c 3 --anchorName=fred")
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `b:
c: &fred 3
`
test.AssertResult(t, expectedOutput, result.Output)
}
func TestNewAliasCmd(t *testing.T) {
cmd := getRootCommand()
result := test.RunCmd(cmd, "new b.c foo --makeAlias")
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `b:
c: *foo
`
test.AssertResult(t, expectedOutput, result.Output)
}
func TestNewArrayCmd(t *testing.T) { func TestNewArrayCmd(t *testing.T) {
cmd := getRootCommand() cmd := getRootCommand()
result := test.RunCmd(cmd, "new b[0] 3") result := test.RunCmd(cmd, "new b[0] 3")
@@ -25,7 +49,7 @@ func TestNewArrayCmd(t *testing.T) {
t.Error(result.Error) t.Error(result.Error)
} }
expectedOutput := `b: expectedOutput := `b:
- 3 - 3
` `
test.AssertResult(t, expectedOutput, result.Output) test.AssertResult(t, expectedOutput, result.Output)
} }

View File

@@ -41,8 +41,8 @@ func TestPrefixCmdArray(t *testing.T) {
t.Error(result.Error) t.Error(result.Error)
} }
expectedOutput := `- d: expectedOutput := `- d:
- b: - b:
c: 3 c: 3
` `
test.AssertResult(t, expectedOutput, result.Output) test.AssertResult(t, expectedOutput, result.Output)
} }

View File

@@ -31,6 +31,7 @@ yq r -- things.yaml '--key-starting-with-dashes.blah'
cmdRead.PersistentFlags().BoolVarP(&unwrapScalar, "unwrapScalar", "", true, "unwrap scalar, print the value with no quotes, colors or comments") cmdRead.PersistentFlags().BoolVarP(&unwrapScalar, "unwrapScalar", "", true, "unwrap scalar, print the value with no quotes, colors or comments")
cmdRead.PersistentFlags().BoolVarP(&stripComments, "stripComments", "", false, "print yaml without any comments") cmdRead.PersistentFlags().BoolVarP(&stripComments, "stripComments", "", false, "print yaml without any comments")
cmdRead.PersistentFlags().BoolVarP(&explodeAnchors, "explodeAnchors", "X", false, "explode anchors") cmdRead.PersistentFlags().BoolVarP(&explodeAnchors, "explodeAnchors", "X", false, "explode anchors")
cmdRead.PersistentFlags().BoolVarP(&exitStatus, "exitStatus", "e", false, "set exit status if no matches are found")
return cmdRead return cmdRead
} }
@@ -50,7 +51,13 @@ func readProperty(cmd *cobra.Command, args []string) error {
matchingNodes, errorReadingStream := readYamlFile(args[0], path, updateAll, docIndexInt) matchingNodes, errorReadingStream := readYamlFile(args[0], path, updateAll, docIndexInt)
if exitStatus {
cmd.SilenceUsage = true
return errors.New("No matches found")
}
if errorReadingStream != nil { if errorReadingStream != nil {
cmd.SilenceUsage = true
return errorReadingStream return errorReadingStream
} }
out := cmd.OutOrStdout() out := cmd.OutOrStdout()

View File

@@ -871,17 +871,35 @@ func TestReadPrettyPrintCmd(t *testing.T) {
b: b:
c: 2 c: 2
d: d:
- 3 - 3
- 4 - 4
e: e:
- name: fred - name: fred
value: 3 value: 3
- name: sam - name: sam
value: 4 value: 4
` `
test.AssertResult(t, expectedOutput, result.Output) test.AssertResult(t, expectedOutput, result.Output)
} }
func TestReadNotFoundWithExitStatus(t *testing.T) {
cmd := getRootCommand()
result := test.RunCmd(cmd, "read ../examples/sample.yaml adsf -e")
if result.Error == nil {
t.Error("Expected command to fail")
}
expectedOutput := `No matches found`
test.AssertResult(t, expectedOutput, result.Error.Error())
}
func TestReadNotFoundWithoutExitStatus(t *testing.T) {
cmd := getRootCommand()
result := test.RunCmd(cmd, "read ../examples/sample.yaml adsf")
if result.Error != nil {
t.Error("Expected command to succeed!")
}
}
func TestReadPrettyPrintWithIndentCmd(t *testing.T) { func TestReadPrettyPrintWithIndentCmd(t *testing.T) {
cmd := getRootCommand() cmd := getRootCommand()
result := test.RunCmd(cmd, "read -P -I4 ../examples/sample.json") result := test.RunCmd(cmd, "read -P -I4 ../examples/sample.json")
@@ -892,13 +910,13 @@ func TestReadPrettyPrintWithIndentCmd(t *testing.T) {
b: b:
c: 2 c: 2
d: d:
- 3 - 3
- 4 - 4
e: e:
- name: fred - name: fred
value: 3 value: 3
- name: sam - name: sam
value: 4 value: 4
` `
test.AssertResult(t, expectedOutput, result.Output) test.AssertResult(t, expectedOutput, result.Output)
} }
@@ -914,8 +932,8 @@ func TestReadCmd_ArrayYaml_NoPath(t *testing.T) {
hosts: lalaland hosts: lalaland
name: "Apply smth" name: "Apply smth"
roles: roles:
- lala - lala
- land - land
serial: 1 serial: 1
- become: false - become: false
gather_facts: true gather_facts: true
@@ -934,8 +952,8 @@ gather_facts: false
hosts: lalaland hosts: lalaland
name: "Apply smth" name: "Apply smth"
roles: roles:
- lala - lala
- land - land
serial: 1 serial: 1
` `
test.AssertResult(t, expectedOutput, result.Output) test.AssertResult(t, expectedOutput, result.Output)
@@ -952,8 +970,8 @@ gather_facts: false
hosts: lalaland hosts: lalaland
name: "Apply smth" name: "Apply smth"
roles: roles:
- lala - lala
- land - land
serial: 1 serial: 1
become: false become: false
gather_facts: true gather_facts: true
@@ -973,8 +991,8 @@ func TestReadCmd_ArrayYaml_SplatWithKeyAndValueCmd(t *testing.T) {
hosts: lalaland hosts: lalaland
name: "Apply smth" name: "Apply smth"
roles: roles:
- lala - lala
- land - land
serial: 1 serial: 1
'[1]': '[1]':
become: false become: false

View File

@@ -54,8 +54,7 @@ func New() *cobra.Command {
createDeleteCmd(), createDeleteCmd(),
createNewCmd(), createNewCmd(),
createMergeCmd(), createMergeCmd(),
createBashCompletionCmd(rootCmd),
) )
rootCmd.SetOutput(os.Stdout)
return rootCmd return rootCmd
} }

57
cmd/shell_completion.go Normal file
View File

@@ -0,0 +1,57 @@
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
var shellVariant = "bash"
func createBashCompletionCmd(rootCmd *cobra.Command) *cobra.Command {
var completionCmd = &cobra.Command{
Use: "shell-completion",
Short: "Generates shell completion scripts",
Long: `To load completion for:
bash:
Run
. <(yq bash-completion)
To configure your bash shell to load completions for each session add to
your bashrc
# ~/.bashrc or ~/.profile
. <(yq bash-completion)
zsh:
The generated completion script should be put somewhere in your $fpath named _yq
powershell:
Users need PowerShell version 5.0 or above, which comes with Windows 10 and
can be downloaded separately for Windows 7 or 8.1. They can then write the
completions to a file and source this file from their PowerShell profile,
which is referenced by the $Profile environment variable.
fish:
Save the output to a fish file and add it to your completions directory.
`,
RunE: func(cmd *cobra.Command, args []string) error {
switch shellVariant {
case "bash", "":
return rootCmd.GenBashCompletion(os.Stdout)
case "zsh":
return rootCmd.GenZshCompletion(os.Stdout)
case "fish":
return rootCmd.GenFishCompletion(os.Stdout, true)
case "powershell":
return rootCmd.GenPowerShellCompletion(os.Stdout)
default:
return fmt.Errorf("Unknown variant %v", shellVariant)
}
},
}
completionCmd.PersistentFlags().StringVarP(&shellVariant, "variation", "V", "", "shell variation: bash (default), zsh, fish, powershell")
return completionCmd
}

View File

@@ -149,25 +149,91 @@ func writeString(writer io.Writer, txt string) error {
return errorWriting return errorWriting
} }
func setIfNotThere(node *yaml.Node, key string, value *yaml.Node) {
for index := 0; index < len(node.Content); index = index + 2 {
keyNode := node.Content[index]
if keyNode.Value == key {
return
}
}
// need to add it to the map
mapEntryKey := yaml.Node{Value: key, Kind: yaml.ScalarNode}
node.Content = append(node.Content, &mapEntryKey)
node.Content = append(node.Content, value)
}
func applyAlias(node *yaml.Node, alias *yaml.Node) {
for index := 0; index < len(alias.Content); index = index + 2 {
keyNode := alias.Content[index]
log.Debugf("applying alias key %v", keyNode.Value)
valueNode := alias.Content[index+1]
setIfNotThere(node, keyNode.Value, valueNode)
}
}
func explodeNode(node *yaml.Node) error {
node.Anchor = ""
switch node.Kind {
case yaml.SequenceNode, yaml.DocumentNode:
for index, contentNode := range node.Content {
log.Debugf("exploding index %v", index)
errorInContent := explodeNode(contentNode)
if errorInContent != nil {
return errorInContent
}
}
return nil
case yaml.AliasNode:
log.Debugf("its an alias!")
node.Kind = node.Alias.Kind
node.Style = node.Alias.Style
node.Tag = node.Alias.Tag
node.Content = node.Alias.Content
node.Value = node.Alias.Value
node.Alias = nil
return nil
case yaml.MappingNode:
for index := 0; index < len(node.Content); index = index + 2 {
keyNode := node.Content[index]
valueNode := node.Content[index+1]
log.Debugf("traversing %v", keyNode.Value)
if keyNode.Value != "<<" {
errorInContent := explodeNode(valueNode)
if errorInContent != nil {
return errorInContent
}
} else {
if valueNode.Kind == yaml.SequenceNode {
log.Debugf("an alias merge list!")
for index := len(valueNode.Content) - 1; index >= 0; index = index - 1 {
aliasNode := valueNode.Content[index]
applyAlias(node, aliasNode.Alias)
}
} else {
log.Debugf("an alias merge!")
applyAlias(node, valueNode.Alias)
}
node.Content = append(node.Content[:index], node.Content[index+2:]...)
//replay that index, since the array is shorter now.
index = index - 2
}
}
return nil
default:
return nil
}
}
func explode(matchingNodes []*yqlib.NodeContext) error { func explode(matchingNodes []*yqlib.NodeContext) error {
log.Debug("exploding nodes") log.Debug("exploding nodes")
for _, nodeContext := range matchingNodes { for _, nodeContext := range matchingNodes {
var targetNode = yaml.Node{Kind: nodeContext.Node.Kind} log.Debugf("exploding %v", nodeContext.Head)
explodedNodes, errorRetrieving := lib.Get(nodeContext.Node, "**", true) errorExplodingNode := explodeNode(nodeContext.Node)
if errorRetrieving != nil { if errorExplodingNode != nil {
return errorRetrieving return errorExplodingNode
} }
for _, matchingNode := range explodedNodes {
mergePath := lib.MergePathStackToString(matchingNode.PathStack, appendFlag)
updateCommand := yqlib.UpdateCommand{Command: "update", Path: mergePath, Value: matchingNode.Node, Overwrite: overwriteFlag}
errorUpdating := lib.Update(&targetNode, updateCommand, true)
if errorUpdating != nil {
return errorUpdating
}
}
nodeContext.Node = &targetNode
} }
log.Debug("done exploding nodes")
return nil return nil
} }
@@ -435,13 +501,13 @@ func readUpdateCommands(args []string, expectedArgs int, badArgsMessage string)
log.Debug("args %v", args) log.Debug("args %v", args)
log.Debug("path %v", args[expectedArgs-2]) log.Debug("path %v", args[expectedArgs-2])
log.Debug("Value %v", args[expectedArgs-1]) log.Debug("Value %v", args[expectedArgs-1])
updateCommands[0] = yqlib.UpdateCommand{Command: "update", Path: args[expectedArgs-2], Value: valueParser.Parse(args[expectedArgs-1], customTag, customStyle), Overwrite: true} updateCommands[0] = yqlib.UpdateCommand{Command: "update", Path: args[expectedArgs-2], Value: valueParser.Parse(args[expectedArgs-1], customTag, customStyle, anchorName, makeAlias), Overwrite: true}
} else if len(args) == expectedArgs-1 { } else if len(args) == expectedArgs-1 {
// don't update the value // don't update the value
updateCommands = make([]yqlib.UpdateCommand, 1) updateCommands = make([]yqlib.UpdateCommand, 1)
log.Debug("args %v", args) log.Debug("args %v", args)
log.Debug("path %v", args[expectedArgs-2]) log.Debug("path %v", args[expectedArgs-2])
updateCommands[0] = yqlib.UpdateCommand{Command: "update", Path: args[expectedArgs-2], Value: valueParser.Parse("", customTag, customStyle), Overwrite: true, DontUpdateNodeValue: true} updateCommands[0] = yqlib.UpdateCommand{Command: "update", Path: args[expectedArgs-2], Value: valueParser.Parse("", customTag, customStyle, anchorName, makeAlias), Overwrite: true, DontUpdateNodeValue: true}
} else { } else {
return nil, errors.New(badArgsMessage) return nil, errors.New(badArgsMessage)
} }

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.3.0" Version = "3.3.1"
// 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

@@ -47,6 +47,8 @@ format is list of update commands (update or delete) like so:
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)")
cmdWrite.PersistentFlags().StringVarP(&customStyle, "style", "", "", "formatting style of the value: single, double, folded, flow, literal, tagged") cmdWrite.PersistentFlags().StringVarP(&customStyle, "style", "", "", "formatting style of the value: single, double, folded, flow, literal, tagged")
cmdWrite.PersistentFlags().StringVarP(&anchorName, "anchorName", "", "", "anchor name")
cmdWrite.PersistentFlags().BoolVarP(&makeAlias, "makeAlias", "", false, "create an alias using the value as the anchor name")
return cmdWrite return cmdWrite
} }

View File

@@ -261,9 +261,9 @@ func TestWriteAutoCreateCmd(t *testing.T) {
t.Error(result.Error) t.Error(result.Error)
} }
expectedOutput := `applications: expectedOutput := `applications:
- name: app - name: app
env: env:
hello: world hello: world
` `
test.AssertResult(t, expectedOutput, result.Output) test.AssertResult(t, expectedOutput, result.Output)
} }
@@ -476,8 +476,8 @@ func TestWriteCmd_Append(t *testing.T) {
t.Error(result.Error) t.Error(result.Error)
} }
expectedOutput := `b: expectedOutput := `b:
- foo - foo
- 7 - 7
` `
test.AssertResult(t, expectedOutput, result.Output) test.AssertResult(t, expectedOutput, result.Output)
} }
@@ -508,8 +508,8 @@ func TestWriteCmd_AppendInlinePretty(t *testing.T) {
t.Error(result.Error) t.Error(result.Error)
} }
expectedOutput := `b: expectedOutput := `b:
- foo - foo
- 7 - 7
` `
test.AssertResult(t, expectedOutput, result.Output) test.AssertResult(t, expectedOutput, result.Output)
} }
@@ -527,7 +527,7 @@ func TestWriteCmd_AppendEmptyArray(t *testing.T) {
} }
expectedOutput := `a: 2 expectedOutput := `a: 2
b: b:
- v - v
` `
test.AssertResult(t, expectedOutput, result.Output) test.AssertResult(t, expectedOutput, result.Output)
} }
@@ -546,8 +546,8 @@ func TestWriteCmd_SplatArray(t *testing.T) {
t.Error(result.Error) t.Error(result.Error)
} }
expectedOutput := `b: expectedOutput := `b:
- c: new - c: new
- c: new - c: new
` `
test.AssertResult(t, expectedOutput, result.Output) test.AssertResult(t, expectedOutput, result.Output)
} }

10
debian/changelog vendored
View File

@@ -1,3 +1,13 @@
yq (3.3-0) focal; urgency=medium
* You can control string styles (quotes) using the new --style flag
* String values now always have quotes when outputting to json
* Negative array indices now traverse the array backwards
* Added a --stripComments flag to print yaml without any comments
* Bumped go to version 1.14
-- Roberto Mier Escandon <rmescandon@gmail.com> Thu, 30 Apr 2020 20:45:44 +0200
yq (3.1-2) eoan; urgency=medium yq (3.1-2) eoan; urgency=medium
* Bug fix: yq 3 was removing empty inline-style objects and arrays (#355) * Bug fix: yq 3 was removing empty inline-style objects and arrays (#355)

1
debian/files vendored
View File

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

View File

@@ -0,0 +1,4 @@
a: simple
b: [1, 2]
c:
test: 1

View File

@@ -1,2 +0,0 @@
b:
c: things

View File

@@ -0,0 +1,5 @@
foo:
a: 1
foobar:
a: 1

6
go.mod
View File

@@ -2,16 +2,16 @@ module github.com/mikefarah/yq/v3
require ( require (
github.com/fatih/color v1.9.0 github.com/fatih/color v1.9.0
github.com/goccy/go-yaml v1.4.3 github.com/goccy/go-yaml v1.7.5
github.com/kylelemons/godebug v1.1.0 github.com/kylelemons/godebug v1.1.0
github.com/mattn/go-colorable v0.1.6 // indirect github.com/mattn/go-colorable v0.1.6 // indirect
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/spf13/cobra v1.0.0 github.com/spf13/cobra v1.0.0
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 // indirect golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 // indirect
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // 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-20200313102051-9f266ea9e77c gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c
) )
go 1.14 go 1.14

13
go.sum
View File

@@ -29,8 +29,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/goccy/go-yaml v1.4.3 h1:+1jK1ost1TBEfWjciIMU8rciBq0poxurgS7XvLgQInM= github.com/goccy/go-yaml v1.7.5 h1:dWvj+p3BG11S/GlUzwzt1WZz0lhBTzTIDtmXT/ZOaPY=
github.com/goccy/go-yaml v1.4.3/go.mod h1:PsEEJ29nIFZL07P/c8dv4P6rQkVFFXafQee85U+ERHA= github.com/goccy/go-yaml v1.7.5/go.mod h1:wS4gNoLalDSJxo/SpngzPQ2BN4uuZVLCmbM4S3vd4+Y=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -144,8 +144,8 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 h1:opSr2sbRXk5X5/givKrrKj9HXxFpW2sdCiP8MJSKLQY= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 h1:OjiUf46hAmXblsZdnoSXsEUSKU8r1UEzcL5RVZ4gO9Y=
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/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/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -175,7 +175,6 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c h1:grhR+C34yXImVGp7EzNk+DTIk+323eIUWOmEevy6bDo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -24,6 +24,7 @@ func NewDataNavigator(NavigationStrategy NavigationStrategy) DataNavigator {
func (n *navigator) Traverse(value *yaml.Node, path []interface{}) error { func (n *navigator) Traverse(value *yaml.Node, path []interface{}) error {
realValue := value realValue := value
emptyArray := make([]interface{}, 0) emptyArray := make([]interface{}, 0)
log.Debugf("Traversing path %v", pathStackToString(path))
if realValue.Kind == yaml.DocumentNode { if realValue.Kind == yaml.DocumentNode {
log.Debugf("its a document! returning the first child") log.Debugf("its a document! returning the first child")
return n.doTraverse(value.Content[0], "", path, emptyArray) return n.doTraverse(value.Content[0], "", path, emptyArray)
@@ -68,6 +69,16 @@ func (n *navigator) getOrReplace(original *yaml.Node, expectedKind yaml.Kind) *y
func (n *navigator) recurse(value *yaml.Node, head interface{}, tail []interface{}, 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))
nodeContext := NewNodeContext(value, head, tail, pathStack)
if head == "**" && !n.navigationStrategy.ShouldOnlyDeeplyVisitLeaves(nodeContext) {
errorVisitingDeeply := n.navigationStrategy.Visit(nodeContext)
if errorVisitingDeeply != nil {
return errorVisitingDeeply
}
}
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)
@@ -85,20 +96,20 @@ func (n *navigator) recurse(value *yaml.Node, head interface{}, tail []interface
if head == "+" { if head == "+" {
return n.appendArray(value, head, tail, pathStack) return n.appendArray(value, head, tail, pathStack)
} else if len(value.Content) == 0 && head == "**" { } else if len(value.Content) == 0 && head == "**" {
return n.navigationStrategy.Visit(NewNodeContext(value, head, tail, pathStack)) return n.navigationStrategy.Visit(nodeContext)
} }
return n.splatArray(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(nodeContext) {
log.Debug("following the alias") log.Debug("following the alias")
return n.recurse(value.Alias, head, tail, pathStack) return n.recurse(value.Alias, head, tail, pathStack)
} }
return nil return nil
default: default:
return n.navigationStrategy.Visit(NewNodeContext(value, head, tail, pathStack)) return n.navigationStrategy.Visit(nodeContext)
} }
} }

View File

@@ -18,9 +18,12 @@ func DeleteNavigationStrategy(pathElementToDelete interface{}) NavigationStrateg
shouldDeeplyTraverse: func(nodeContext NodeContext) bool { shouldDeeplyTraverse: func(nodeContext NodeContext) bool {
return true return true
}, },
shouldOnlyDeeplyVisitLeaves: func(nodeContext NodeContext) bool {
return false
},
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 (%v)", pathElementToDelete, pathStackToString(nodeContext.PathStack))
DebugNode(node) DebugNode(node)
if node.Kind == yaml.SequenceNode { if node.Kind == yaml.SequenceNode {
newContent := deleteFromArray(parser, node.Content, nodeContext.PathStack, pathElementToDelete) newContent := deleteFromArray(parser, node.Content, nodeContext.PathStack, pathElementToDelete)

View File

@@ -48,7 +48,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, Kind: %v", value.Tag, KindString(value.Kind)) log.Debug("Tag: %v, Kind: %v, Anchor: %v", value.Tag, KindString(value.Kind), value.Anchor)
log.Debug("%v", buf.String()) log.Debug("%v", buf.String())
} }
} }

View File

@@ -35,19 +35,22 @@ type NavigationStrategy interface {
// 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 ShouldDeeplyTraverse(nodeContext NodeContext) bool
// when deeply traversing, should we visit all matching nodes, or just leaves?
ShouldOnlyDeeplyVisitLeaves(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
shouldDeeplyTraverse func(nodeContext NodeContext) bool shouldDeeplyTraverse func(nodeContext NodeContext) bool
visitedNodes []*NodeContext shouldOnlyDeeplyVisitLeaves func(nodeContext NodeContext) bool
pathParser PathParser visitedNodes []*NodeContext
pathParser PathParser
} }
func (ns *NavigationStrategyImpl) GetPathParser() PathParser { func (ns *NavigationStrategyImpl) GetPathParser() PathParser {
@@ -70,6 +73,10 @@ func (ns *NavigationStrategyImpl) ShouldDeeplyTraverse(nodeContext NodeContext)
return ns.shouldDeeplyTraverse(nodeContext) return ns.shouldDeeplyTraverse(nodeContext)
} }
func (ns *NavigationStrategyImpl) ShouldOnlyDeeplyVisitLeaves(nodeContext NodeContext) bool {
return ns.shouldOnlyDeeplyVisitLeaves(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 {

View File

@@ -26,5 +26,8 @@ func ReadNavigationStrategy(deeplyTraverseArrays bool) NavigationStrategy {
} }
return deeplyTraverseArrays || !isInArray return deeplyTraverseArrays || !isInArray
}, },
shouldOnlyDeeplyVisitLeaves: func(nodeContext NodeContext) bool {
return true
},
} }
} }

View File

@@ -13,6 +13,9 @@ func UpdateNavigationStrategy(updateCommand UpdateCommand, autoCreate bool) Navi
shouldDeeplyTraverse: func(nodeContext NodeContext) bool { shouldDeeplyTraverse: func(nodeContext NodeContext) bool {
return true return true
}, },
shouldOnlyDeeplyVisitLeaves: 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
@@ -28,6 +31,8 @@ func UpdateNavigationStrategy(updateCommand UpdateCommand, autoCreate bool) Navi
node.Kind = changesToApply.Kind node.Kind = changesToApply.Kind
node.Style = changesToApply.Style node.Style = changesToApply.Style
node.Content = changesToApply.Content node.Content = changesToApply.Content
node.Anchor = changesToApply.Anchor
node.Alias = changesToApply.Alias
node.HeadComment = changesToApply.HeadComment node.HeadComment = changesToApply.HeadComment
node.LineComment = changesToApply.LineComment node.LineComment = changesToApply.LineComment
node.FootComment = changesToApply.FootComment node.FootComment = changesToApply.FootComment

View File

@@ -5,7 +5,7 @@ import (
) )
type ValueParser interface { type ValueParser interface {
Parse(argument string, customTag string, customStyle string) *yaml.Node Parse(argument string, customTag string, customStyle string, anchorName string, createAlias bool) *yaml.Node
} }
type valueParser struct { type valueParser struct {
@@ -15,7 +15,7 @@ func NewValueParser() ValueParser {
return &valueParser{} return &valueParser{}
} }
func (v *valueParser) Parse(argument string, customTag string, customStyle string) *yaml.Node { func (v *valueParser) Parse(argument string, customTag string, customStyle string, anchorName string, createAlias bool) *yaml.Node {
var style yaml.Style var style yaml.Style
if customStyle == "tagged" { if customStyle == "tagged" {
style = yaml.TaggedStyle style = yaml.TaggedStyle
@@ -27,12 +27,20 @@ func (v *valueParser) Parse(argument string, customTag string, customStyle strin
style = yaml.LiteralStyle style = yaml.LiteralStyle
} else if customStyle == "folded" { } else if customStyle == "folded" {
style = yaml.FoldedStyle style = yaml.FoldedStyle
} else if customStyle == "flow" {
style = yaml.FlowStyle
} else if customStyle != "" { } else if customStyle != "" {
log.Error("Unknown style %v, ignoring", customStyle) log.Error("Unknown style %v, ignoring", customStyle)
} }
if argument == "[]" { if argument == "[]" {
return &yaml.Node{Tag: "!!seq", Kind: yaml.SequenceNode, Style: style} return &yaml.Node{Tag: "!!seq", Kind: yaml.SequenceNode, Style: style}
} }
return &yaml.Node{Value: argument, Tag: customTag, Kind: yaml.ScalarNode, Style: style}
kind := yaml.ScalarNode
if createAlias {
kind = yaml.AliasNode
}
return &yaml.Node{Value: argument, Tag: customTag, Kind: kind, Style: style, Anchor: anchorName}
} }

View File

@@ -16,12 +16,13 @@ var parseStyleTests = []struct {
{"double", yaml.DoubleQuotedStyle}, {"double", yaml.DoubleQuotedStyle},
{"single", yaml.SingleQuotedStyle}, {"single", yaml.SingleQuotedStyle},
{"folded", yaml.FoldedStyle}, {"folded", yaml.FoldedStyle},
{"flow", yaml.FlowStyle},
{"literal", yaml.LiteralStyle}, {"literal", yaml.LiteralStyle},
} }
func TestValueParserStyleTag(t *testing.T) { func TestValueParserStyleTag(t *testing.T) {
for _, tt := range parseStyleTests { for _, tt := range parseStyleTests {
actual := NewValueParser().Parse("cat", "", tt.customStyle) actual := NewValueParser().Parse("cat", "", tt.customStyle, "", false)
test.AssertResultWithContext(t, tt.expectedStyle, actual.Style, tt.customStyle) test.AssertResultWithContext(t, tt.expectedStyle, actual.Style, tt.customStyle)
} }
} }
@@ -39,7 +40,7 @@ var parseValueTests = []struct {
func TestValueParserParse(t *testing.T) { func TestValueParserParse(t *testing.T) {
for _, tt := range parseValueTests { for _, tt := range parseValueTests {
actual := NewValueParser().Parse(tt.argument, tt.customTag, "") actual := NewValueParser().Parse(tt.argument, tt.customTag, "", "", false)
test.AssertResultWithContext(t, tt.argument, actual.Value, tt.testDescription) test.AssertResultWithContext(t, tt.argument, actual.Value, tt.testDescription)
test.AssertResultWithContext(t, tt.expectedTag, actual.Tag, tt.testDescription) test.AssertResultWithContext(t, tt.expectedTag, actual.Tag, tt.testDescription)
test.AssertResult(t, yaml.ScalarNode, actual.Kind) test.AssertResult(t, yaml.ScalarNode, actual.Kind)
@@ -47,7 +48,18 @@ func TestValueParserParse(t *testing.T) {
} }
func TestValueParserParseEmptyArray(t *testing.T) { func TestValueParserParseEmptyArray(t *testing.T) {
actual := NewValueParser().Parse("[]", "", "") actual := NewValueParser().Parse("[]", "", "", "", false)
test.AssertResult(t, "!!seq", actual.Tag) test.AssertResult(t, "!!seq", actual.Tag)
test.AssertResult(t, yaml.SequenceNode, actual.Kind) test.AssertResult(t, yaml.SequenceNode, actual.Kind)
} }
func TestValueParserParseAlias(t *testing.T) {
actual := NewValueParser().Parse("bob", "", "", "", true)
test.AssertResult(t, "bob", actual.Value)
test.AssertResult(t, yaml.AliasNode, actual.Kind)
}
func TestValueParserAnchorname(t *testing.T) {
actual := NewValueParser().Parse("caterpillar", "", "", "foo", false)
test.AssertResult(t, "foo", actual.Anchor)
}

View File

@@ -1,5 +1,5 @@
name: yq name: yq
version: '3.3.0' version: '3.3.1'
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.

3
yq.go
View File

@@ -4,14 +4,11 @@ import (
"os" "os"
command "github.com/mikefarah/yq/v3/cmd" command "github.com/mikefarah/yq/v3/cmd"
logging "gopkg.in/op/go-logging.v1"
) )
func main() { func main() {
cmd := command.New() cmd := command.New()
log := logging.MustGetLogger("yq")
if err := cmd.Execute(); err != nil { if err := cmd.Execute(); err != nil {
log.Error(err.Error())
os.Exit(1) os.Exit(1)
} }
} }