first cli

This commit is contained in:
Mike Farah 2020-10-27 16:45:16 +11:00
parent badd476730
commit 85d059340b
42 changed files with 362 additions and 3826 deletions

View File

@ -1,83 +1,83 @@
package cmd package cmd
import ( // import (
"strings" // "strings"
"testing" // "testing"
"github.com/mikefarah/yq/v3/test" // "github.com/mikefarah/yq/v3/test"
"github.com/spf13/cobra" // "github.com/spf13/cobra"
) // )
func getRootCommand() *cobra.Command { // func getRootCommand() *cobra.Command {
return New() // return New()
} // }
func TestRootCmd(t *testing.T) { // func TestRootCmd(t *testing.T) {
cmd := getRootCommand() // cmd := getRootCommand()
result := test.RunCmd(cmd, "") // result := test.RunCmd(cmd, "")
if result.Error != nil { // if result.Error != nil {
t.Error(result.Error) // t.Error(result.Error)
} // }
if !strings.Contains(result.Output, "Usage:") { // if !strings.Contains(result.Output, "Usage:") {
t.Error("Expected usage message to be printed out, but the usage message was not found.") // t.Error("Expected usage message to be printed out, but the usage message was not found.")
} // }
} // }
func TestRootCmd_Help(t *testing.T) { // func TestRootCmd_Help(t *testing.T) {
cmd := getRootCommand() // cmd := getRootCommand()
result := test.RunCmd(cmd, "--help") // result := test.RunCmd(cmd, "--help")
if result.Error != nil { // if result.Error != nil {
t.Error(result.Error) // t.Error(result.Error)
} // }
if !strings.Contains(result.Output, "yq is a lightweight and portable command-line YAML processor. It aims to be the jq or sed of yaml files.") { // if !strings.Contains(result.Output, "yq is a lightweight and portable command-line YAML processor. It aims to be the jq or sed of yaml files.") {
t.Error("Expected usage message to be printed out, but the usage message was not found.") // t.Error("Expected usage message to be printed out, but the usage message was not found.")
} // }
} // }
func TestRootCmd_VerboseLong(t *testing.T) { // func TestRootCmd_VerboseLong(t *testing.T) {
cmd := getRootCommand() // cmd := getRootCommand()
result := test.RunCmd(cmd, "--verbose") // result := test.RunCmd(cmd, "--verbose")
if result.Error != nil { // if result.Error != nil {
t.Error(result.Error) // t.Error(result.Error)
} // }
if !verbose { // if !verbose {
t.Error("Expected verbose to be true") // t.Error("Expected verbose to be true")
} // }
} // }
func TestRootCmd_VerboseShort(t *testing.T) { // func TestRootCmd_VerboseShort(t *testing.T) {
cmd := getRootCommand() // cmd := getRootCommand()
result := test.RunCmd(cmd, "-v") // result := test.RunCmd(cmd, "-v")
if result.Error != nil { // if result.Error != nil {
t.Error(result.Error) // t.Error(result.Error)
} // }
if !verbose { // if !verbose {
t.Error("Expected verbose to be true") // t.Error("Expected verbose to be true")
} // }
} // }
func TestRootCmd_VersionShort(t *testing.T) { // func TestRootCmd_VersionShort(t *testing.T) {
cmd := getRootCommand() // cmd := getRootCommand()
result := test.RunCmd(cmd, "-V") // result := test.RunCmd(cmd, "-V")
if result.Error != nil { // if result.Error != nil {
t.Error(result.Error) // t.Error(result.Error)
} // }
if !strings.Contains(result.Output, "yq version") { // if !strings.Contains(result.Output, "yq version") {
t.Error("expected version message to be printed out, but the message was not found.") // t.Error("expected version message to be printed out, but the message was not found.")
} // }
} // }
func TestRootCmd_VersionLong(t *testing.T) { // func TestRootCmd_VersionLong(t *testing.T) {
cmd := getRootCommand() // cmd := getRootCommand()
result := test.RunCmd(cmd, "--version") // result := test.RunCmd(cmd, "--version")
if result.Error != nil { // if result.Error != nil {
t.Error(result.Error) // t.Error(result.Error)
} // }
if !strings.Contains(result.Output, "yq version") { // if !strings.Contains(result.Output, "yq version") {
t.Error("expected version message to be printed out, but the message was not found.") // t.Error("expected version message to be printed out, but the message was not found.")
} // }
} // }

View File

@ -1,89 +0,0 @@
package cmd
// import (
// "bufio"
// "bytes"
// "os"
// "strings"
// "github.com/kylelemons/godebug/diff"
// "github.com/mikefarah/yq/v3/pkg/yqlib"
// errors "github.com/pkg/errors"
// "github.com/spf13/cobra"
// )
// // turn off for unit tests :(
// var forceOsExit = true
// func createCompareCmd() *cobra.Command {
// var cmdCompare = &cobra.Command{
// Use: "compare [yaml_file_a] [yaml_file_b]",
// Aliases: []string{"x"},
// Short: "yq x [--prettyPrint/-P] dataA.yaml dataB.yaml 'b.e(name==fr*).value'",
// Example: `
// yq x - data2.yml # reads from stdin
// yq x -pp dataA.yaml dataB.yaml '**' # compare paths
// yq x -d1 dataA.yaml dataB.yaml 'a.b.c'
// `,
// Long: "Deeply compares two yaml files, prints the difference. Use with prettyPrint flag to ignore formatting differences.",
// RunE: compareDocuments,
// }
// 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(&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
// }
// func compareDocuments(cmd *cobra.Command, args []string) error {
// var path = ""
// if len(args) < 2 {
// return errors.New("Must provide at 2 yaml files")
// } else if len(args) > 2 {
// path = args[2]
// }
// var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex()
// if errorParsingDocIndex != nil {
// return errorParsingDocIndex
// }
// var matchingNodesA []*yqlib.NodeContext
// var matchingNodesB []*yqlib.NodeContext
// var errorDoingThings error
// matchingNodesA, errorDoingThings = readYamlFile(args[0], path, updateAll, docIndexInt)
// if errorDoingThings != nil {
// return errorDoingThings
// }
// matchingNodesB, errorDoingThings = readYamlFile(args[1], path, updateAll, docIndexInt)
// if errorDoingThings != nil {
// return errorDoingThings
// }
// var dataBufferA bytes.Buffer
// var dataBufferB bytes.Buffer
// errorDoingThings = printResults(matchingNodesA, bufio.NewWriter(&dataBufferA))
// if errorDoingThings != nil {
// return errorDoingThings
// }
// errorDoingThings = printResults(matchingNodesB, bufio.NewWriter(&dataBufferB))
// if errorDoingThings != nil {
// return errorDoingThings
// }
// diffString := diff.Diff(strings.TrimSuffix(dataBufferA.String(), "\n"), strings.TrimSuffix(dataBufferB.String(), "\n"))
// if len(diffString) > 1 {
// cmd.Print(diffString)
// cmd.Print("\n")
// if forceOsExit {
// os.Exit(1)
// }
// }
// return nil
// }

View File

@ -1,115 +0,0 @@
package cmd
// import (
// "testing"
// "github.com/mikefarah/yq/v3/test"
// )
// func TestCompareSameCmd(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "compare ../examples/data1.yaml ../examples/data1.yaml")
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := ``
// 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) {
// forceOsExit = false
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "compare ../examples/data1.yaml ../examples/data3.yaml")
// expectedOutput := `-a: simple # just the best
// -b: [1, 2]
// +a: "simple" # just the best
// +b: [1, 3]
// c:
// test: 1
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestComparePrettyCmd(t *testing.T) {
// forceOsExit = false
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "compare -P ../examples/data1.yaml ../examples/data3.yaml")
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := ` a: simple # just the best
// b:
// - 1
// - - 2
// + - 3
// c:
// test: 1
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestComparePathsCmd(t *testing.T) {
// forceOsExit = false
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "compare -P -ppv ../examples/data1.yaml ../examples/data3.yaml **")
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := ` a: simple # just the best
// b.[0]: 1
// -b.[1]: 2
// +b.[1]: 3
// c.test: 1
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }

View File

@ -1,8 +1,6 @@
package cmd package cmd
import ( import (
"github.com/mikefarah/yq/v3/pkg/yqlib"
"github.com/mikefarah/yq/v3/pkg/yqlib/treeops"
logging "gopkg.in/op/go-logging.v1" logging "gopkg.in/op/go-logging.v1"
) )
@ -14,7 +12,6 @@ var customStyle = ""
var anchorName = "" var anchorName = ""
var makeAlias = false var makeAlias = false
var stripComments = false var stripComments = false
var collectIntoArray = false
var writeInplace = false var writeInplace = false
var writeScript = "" var writeScript = ""
var sourceYamlFile = "" var sourceYamlFile = ""
@ -22,6 +19,8 @@ var outputToJSON = false
var exitStatus = false var exitStatus = false
var prettyPrint = false var prettyPrint = false
var explodeAnchors = false var explodeAnchors = false
var forceColor = false
var forceNoColor = false
var colorsEnabled = false var colorsEnabled = false
var defaultValue = "" var defaultValue = ""
var indent = 2 var indent = 2
@ -31,7 +30,5 @@ var arrayMergeStrategyFlag = "update"
var commentsMergeStrategyFlag = "setWhenBlank" var commentsMergeStrategyFlag = "setWhenBlank"
var verbose = false var verbose = false
var version = false var version = false
var docIndex = "0" var shellCompletion = ""
var log = logging.MustGetLogger("yq") var log = logging.MustGetLogger("yq")
var lib = treeops.NewYqTreeLib()
var valueParser = yqlib.NewValueParser()

View File

@ -1,41 +0,0 @@
package cmd
// import (
// "github.com/mikefarah/yq/v3/pkg/yqlib"
// errors "github.com/pkg/errors"
// "github.com/spf13/cobra"
// )
// func createDeleteCmd() *cobra.Command {
// var cmdDelete = &cobra.Command{
// Use: "delete [yaml_file] [path_expression]",
// Aliases: []string{"d"},
// Short: "yq d [--inplace/-i] [--doc/-d index] sample.yaml 'b.e(name==fred)'",
// Example: `
// yq delete things.yaml 'a.b.c'
// yq delete things.yaml 'a.*.c'
// yq delete things.yaml 'a.(child.subchild==co*).c'
// yq delete things.yaml 'a.**'
// yq delete --inplace things.yaml 'a.b.c'
// yq delete --inplace -- things.yaml '--key-starting-with-dash' # need to use '--' to stop processing arguments as flags
// yq d -i things.yaml 'a.b.c'
// `,
// Long: `Deletes the nodes matching the given path expression from the YAML file.
// Outputs to STDOUT unless the inplace flag is used, in which case the file is updated instead.
// `,
// RunE: deleteProperty,
// }
// cmdDelete.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace")
// cmdDelete.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)")
// return cmdDelete
// }
// func deleteProperty(cmd *cobra.Command, args []string) error {
// if len(args) < 2 {
// return errors.New("Must provide <filename> <path_to_delete>")
// }
// var updateCommands []yqlib.UpdateCommand = make([]yqlib.UpdateCommand, 1)
// updateCommands[0] = yqlib.UpdateCommand{Command: "delete", Path: args[1]}
// return updateDoc(args[0], updateCommands, cmd.OutOrStdout())
// }

View File

@ -1,246 +0,0 @@
package cmd
// import (
// "fmt"
// "strings"
// "testing"
// "github.com/mikefarah/yq/v3/test"
// )
// func TestDeleteYamlCmd(t *testing.T) {
// content := `a: 2
// b:
// c: things
// d: something else
// `
// filename := test.WriteTempYamlFile(content)
// defer test.RemoveTempYamlFile(filename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("delete %s b.c", filename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `a: 2
// b:
// d: something else
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestDeleteDeepDoesNotExistCmd(t *testing.T) {
// content := `a: 2`
// filename := test.WriteTempYamlFile(content)
// defer test.RemoveTempYamlFile(filename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("delete %s b.c", filename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `a: 2
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestDeleteSplatYaml(t *testing.T) {
// content := `a: other
// b: [3, 4]
// c:
// toast: leave
// test: 1
// tell: 1
// tasty.taco: cool
// `
// filename := test.WriteTempYamlFile(content)
// defer test.RemoveTempYamlFile(filename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("delete %s c.te*", filename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `a: other
// b: [3, 4]
// c:
// toast: leave
// tasty.taco: cool
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestDeleteSplatArrayYaml(t *testing.T) {
// content := `a: 2
// b:
// hi:
// - thing: item1
// name: fred
// - thing: item2
// name: sam
// `
// filename := test.WriteTempYamlFile(content)
// defer test.RemoveTempYamlFile(filename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("delete %s b.hi[*].thing", filename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `a: 2
// b:
// hi:
// - name: fred
// - 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)
// }
// func TestDeleteSplatPrefixYaml(t *testing.T) {
// content := `a: 2
// b:
// hi:
// c: things
// d: something else
// there:
// c: more things
// d: more something else
// there2:
// c: more things also
// d: more something else also
// `
// filename := test.WriteTempYamlFile(content)
// defer test.RemoveTempYamlFile(filename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("delete %s b.there*.c", filename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `a: 2
// b:
// hi:
// c: things
// d: something else
// there:
// d: more something else
// there2:
// d: more something else also
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestDeleteYamlArrayCmd(t *testing.T) {
// content := `- 1
// - 2
// - 3
// `
// filename := test.WriteTempYamlFile(content)
// defer test.RemoveTempYamlFile(filename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("delete %s [1]", filename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `- 1
// - 3
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestDeleteYamlArrayExpressionCmd(t *testing.T) {
// content := `- name: fred
// - name: cat
// - name: thing
// `
// filename := test.WriteTempYamlFile(content)
// defer test.RemoveTempYamlFile(filename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("delete %s (name==cat)", filename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `- name: fred
// - name: thing
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestDeleteYamlMulti(t *testing.T) {
// content := `apples: great
// ---
// - 1
// - 2
// - 3
// `
// filename := test.WriteTempYamlFile(content)
// defer test.RemoveTempYamlFile(filename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("delete -d 1 %s [1]", filename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `apples: great
// ---
// - 1
// - 3
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestDeleteYamlMultiAllCmd(t *testing.T) {
// content := `b:
// c: 3
// apples: great
// ---
// apples: great
// something: else
// `
// filename := test.WriteTempYamlFile(content)
// defer test.RemoveTempYamlFile(filename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("delete %s -d * apples", filename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `b:
// c: 3
// ---
// something: else`
// test.AssertResult(t, expectedOutput, strings.Trim(result.Output, "\n "))
// }

View File

@ -1,124 +0,0 @@
package cmd
// import (
// "github.com/mikefarah/yq/v3/pkg/yqlib"
// errors "github.com/pkg/errors"
// "github.com/spf13/cobra"
// yaml "gopkg.in/yaml.v3"
// )
// func createMergeCmd() *cobra.Command {
// var cmdMerge = &cobra.Command{
// Use: "merge [initial_yaml_file] [additional_yaml_file]...",
// Aliases: []string{"m"},
// Short: "yq m [--inplace/-i] [--doc/-d index] [--overwrite/-x] [--arrayMerge/-a strategy] sample.yaml sample2.yaml",
// Example: `
// yq merge things.yaml other.yaml
// yq merge --inplace things.yaml other.yaml
// yq m -i things.yaml other.yaml
// yq m --overwrite things.yaml other.yaml
// yq m -i -x things.yaml other.yaml
// yq m -i -a=append things.yaml other.yaml
// yq m -i --autocreate=false things.yaml other.yaml
// `,
// Long: `Updates the yaml file by adding/updating the path(s) and value(s) from additional yaml file(s).
// Outputs to STDOUT unless the inplace flag is used, in which case the file is updated instead.
// If overwrite flag is set then existing values will be overwritten using the values from each additional yaml file.
// If append flag is set then existing arrays will be merged with the arrays from each additional yaml file.
// `,
// RunE: mergeProperties,
// }
// cmdMerge.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace")
// cmdMerge.PersistentFlags().BoolVarP(&overwriteFlag, "overwrite", "x", false, "update the yaml file by overwriting existing values")
// cmdMerge.PersistentFlags().BoolVarP(&autoCreateFlag, "autocreate", "c", true, "automatically create any missing entries")
// cmdMerge.PersistentFlags().StringVarP(&arrayMergeStrategyFlag, "arrays", "a", "update", `array merge strategy (update/append/overwrite)
// update: recursively update arrays by their index
// append: concatenate arrays together
// overwrite: replace arrays
// `)
// cmdMerge.PersistentFlags().StringVarP(&commentsMergeStrategyFlag, "comments", "", "setWhenBlank", `comments merge strategy (setWhenBlank/ignore/append/overwrite)
// setWhenBlank: set comment if the original document has no comment at that node
// ignore: leave comments as-is in the original
// append: append comments together
// overwrite: overwrite comments completely
// `)
// cmdMerge.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)")
// return cmdMerge
// }
// /*
// * We don't deeply traverse arrays when appending a merge, instead we want to
// * append the entire array element.
// */
// func createReadFunctionForMerge(arrayMergeStrategy yqlib.ArrayMergeStrategy) func(*yaml.Node) ([]*yqlib.NodeContext, error) {
// return func(dataBucket *yaml.Node) ([]*yqlib.NodeContext, error) {
// return lib.GetForMerge(dataBucket, "**", arrayMergeStrategy)
// }
// }
// func mergeProperties(cmd *cobra.Command, args []string) error {
// var updateCommands []yqlib.UpdateCommand = make([]yqlib.UpdateCommand, 0)
// if len(args) < 1 {
// return errors.New("Must provide at least 1 yaml file")
// }
// var arrayMergeStrategy yqlib.ArrayMergeStrategy
// switch arrayMergeStrategyFlag {
// case "update":
// arrayMergeStrategy = yqlib.UpdateArrayMergeStrategy
// case "append":
// arrayMergeStrategy = yqlib.AppendArrayMergeStrategy
// case "overwrite":
// arrayMergeStrategy = yqlib.OverwriteArrayMergeStrategy
// default:
// return errors.New("Array merge strategy must be one of: update/append/overwrite")
// }
// var commentsMergeStrategy yqlib.CommentsMergeStrategy
// switch commentsMergeStrategyFlag {
// case "setWhenBlank":
// commentsMergeStrategy = yqlib.SetWhenBlankCommentsMergeStrategy
// case "ignore":
// commentsMergeStrategy = yqlib.IgnoreCommentsMergeStrategy
// case "append":
// commentsMergeStrategy = yqlib.AppendCommentsMergeStrategy
// case "overwrite":
// commentsMergeStrategy = yqlib.OverwriteCommentsMergeStrategy
// default:
// return errors.New("Comments merge strategy must be one of: setWhenBlank/ignore/append/overwrite")
// }
// if len(args) > 1 {
// // first generate update commands from the file
// var filesToMerge = args[1:]
// for _, fileToMerge := range filesToMerge {
// matchingNodes, errorProcessingFile := doReadYamlFile(fileToMerge, createReadFunctionForMerge(arrayMergeStrategy), false, 0)
// if errorProcessingFile != nil {
// return errorProcessingFile
// }
// log.Debugf("finished reading for merge!")
// for _, matchingNode := range matchingNodes {
// log.Debugf("matched node %v", lib.PathStackToString(matchingNode.PathStack))
// yqlib.DebugNode(matchingNode.Node)
// }
// for _, matchingNode := range matchingNodes {
// mergePath := lib.MergePathStackToString(matchingNode.PathStack, arrayMergeStrategy)
// updateCommands = append(updateCommands, yqlib.UpdateCommand{
// Command: "merge",
// Path: mergePath,
// Value: matchingNode.Node,
// Overwrite: overwriteFlag,
// CommentsMergeStrategy: commentsMergeStrategy,
// // dont update the content for nodes midway, only leaf nodes
// DontUpdateNodeContent: matchingNode.IsMiddleNode && (arrayMergeStrategy != yqlib.OverwriteArrayMergeStrategy || matchingNode.Node.Kind != yaml.SequenceNode),
// })
// }
// }
// }
// return updateDoc(args[0], updateCommands, cmd.OutOrStdout())
// }

View File

@ -1,551 +0,0 @@
package cmd
// import (
// "fmt"
// "os"
// "runtime"
// "testing"
// "github.com/mikefarah/yq/v3/test"
// )
// func TestMergeCmd(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "merge ../examples/data1.yaml ../examples/data2.yaml")
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `a: simple # just the best
// b: [1, 2]
// c:
// test: 1
// toast: leave
// tell: 1
// tasty.taco: cool
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestMergeOneFileCmd(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "merge ../examples/data1.yaml")
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `a: simple # just the best
// b: [1, 2]
// c:
// test: 1
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestMergeNoAutoCreateCmd(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "merge -c=false ../examples/data1.yaml ../examples/data2.yaml")
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `a: simple # just the best
// b: [1, 2]
// c:
// test: 1
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestMergeOverwriteCmd(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "merge -c=false --overwrite ../examples/data1.yaml ../examples/data2.yaml")
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `a: other # just the best
// b: [3, 4]
// c:
// test: 1
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestMergeOverwriteDeepExampleCmd(t *testing.T) {
// content := `c:
// test: 1
// thing: whatever
// `
// filename := test.WriteTempYamlFile(content)
// defer test.RemoveTempYamlFile(filename)
// mergeContent := `c:
// test: 5
// `
// mergeFilename := test.WriteTempYamlFile(mergeContent)
// defer test.RemoveTempYamlFile(mergeFilename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("merge --autocreate=false --overwrite %s %s", filename, mergeFilename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `c:
// test: 5
// thing: whatever
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestMergeAppendCmd(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "merge --autocreate=false --arrays=append ../examples/data1.yaml ../examples/data2.yaml")
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `a: simple # just the best
// b: [1, 2, 3, 4]
// c:
// test: 1
// `
// 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 --arrays=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 TestMergeAliasArraysCmd(t *testing.T) {
// content := `
// vars:
// variable1: &var1 cat
// usage:
// value1: *var1
// valueAnother: *var1
// valuePlain: thing
// `
// filename := test.WriteTempYamlFile(content)
// defer test.RemoveTempYamlFile(filename)
// mergeContent := `
// vars:
// variable2: &var2 puppy
// usage:
// value2: *var2
// valueAnother: *var2
// valuePlain: *var2
// `
// mergeFilename := test.WriteTempYamlFile(mergeContent)
// defer test.RemoveTempYamlFile(mergeFilename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("merge -x %s %s", filename, mergeFilename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `vars:
// variable1: &var1 cat
// variable2: &var2 puppy
// usage:
// value1: *var1
// valueAnother: *var2
// valuePlain: *var2
// value2: *var2
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestMergeOverwriteAndAppendCmd(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "merge --autocreate=false --arrays=append --overwrite ../examples/data1.yaml ../examples/data2.yaml")
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `a: other # just the best
// b: [1, 2, 3, 4]
// c:
// test: 1
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// var commentContentA = `
// a: valueA1 # commentA1
// b: valueB1
// `
// var commentContentB = `
// a: valueA2 # commentA2
// b: valueB2 # commentB2
// c: valueC2 # commentC2
// `
// func TestMergeCommentsSetWhenBlankCmd(t *testing.T) {
// filename := test.WriteTempYamlFile(commentContentA)
// defer test.RemoveTempYamlFile(filename)
// mergeFilename := test.WriteTempYamlFile(commentContentB)
// defer test.RemoveTempYamlFile(mergeFilename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("merge --comments=setWhenBlank %s %s", filename, mergeFilename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `a: valueA1 # commentA1
// b: valueB1 # commentB2
// c: valueC2 # commentC2
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestMergeCommentsIgnoreCmd(t *testing.T) {
// filename := test.WriteTempYamlFile(commentContentA)
// defer test.RemoveTempYamlFile(filename)
// mergeFilename := test.WriteTempYamlFile(commentContentB)
// defer test.RemoveTempYamlFile(mergeFilename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("merge --comments=ignore %s %s", filename, mergeFilename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `a: valueA1 # commentA1
// b: valueB1
// c: valueC2
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestMergeCommentsAppendCmd(t *testing.T) {
// filename := test.WriteTempYamlFile(commentContentA)
// defer test.RemoveTempYamlFile(filename)
// mergeFilename := test.WriteTempYamlFile(commentContentB)
// defer test.RemoveTempYamlFile(mergeFilename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("merge --comments=append %s %s", filename, mergeFilename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `a: valueA1 # commentA1 # commentA2
// b: valueB1 # commentB2
// c: valueC2 # commentC2
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestMergeCommentsOverwriteCmd(t *testing.T) {
// filename := test.WriteTempYamlFile(commentContentA)
// defer test.RemoveTempYamlFile(filename)
// mergeFilename := test.WriteTempYamlFile(commentContentB)
// defer test.RemoveTempYamlFile(mergeFilename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("merge --comments=overwrite %s %s", filename, mergeFilename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `a: valueA1 # commentA2
// b: valueB1 # commentB2
// c: valueC2 # commentC2
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestMergeOverwriteArraysTooCmd(t *testing.T) {
// content := `a: simple # just the best
// b: [1, 2]
// c:
// test: 1
// `
// filename := test.WriteTempYamlFile(content)
// defer test.RemoveTempYamlFile(filename)
// mergeContent := `a: things
// b: [6]`
// mergeFilename := test.WriteTempYamlFile(mergeContent)
// defer test.RemoveTempYamlFile(mergeFilename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("merge --autocreate=false --arrays=overwrite --overwrite %s %s", filename, mergeFilename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `a: things # just the best
// b: [6]
// c:
// test: 1
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestMergeRootArraysCmd(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "merge --arrays=append ../examples/sample_array.yaml ../examples/sample_array_2.yaml")
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `- 1
// - 2
// - 3
// - 4
// - 5
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestMergeOverwriteArraysCmd(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "merge --arrays=overwrite ../examples/sample_array.yaml ../examples/sample_array_2.yaml")
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `- 4
// - 5
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestMergeUpdateArraysCmd(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "merge -x --arrays=update ../examples/sample_array.yaml ../examples/sample_array_2.yaml")
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `- 4
// - 5
// - 3
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestMergeCmd_Multi(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "merge -d1 ../examples/multiple_docs_small.yaml ../examples/data1.yaml")
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `a: Easy! as one two three
// ---
// another:
// document: here
// a: simple # just the best
// b: [1, 2]
// c:
// test: 1
// ---
// - 1
// - 2
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestMergeYamlMultiAllCmd(t *testing.T) {
// content := `b:
// c: 3
// apples: green
// ---
// something: else`
// filename := test.WriteTempYamlFile(content)
// defer test.RemoveTempYamlFile(filename)
// mergeContent := `apples: red
// something: good`
// mergeFilename := test.WriteTempYamlFile(mergeContent)
// defer test.RemoveTempYamlFile(mergeFilename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("merge -d* %s %s", filename, mergeFilename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `b:
// c: 3
// apples: green
// something: good
// ---
// something: else
// apples: red
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestMergeSpecialCharacterKeysCmd(t *testing.T) {
// content := ``
// filename := test.WriteTempYamlFile(content)
// defer test.RemoveTempYamlFile(filename)
// mergeContent := `key[bracket]: value
// key.bracket: value
// key"value": value
// key'value': value
// `
// mergeFilename := test.WriteTempYamlFile(mergeContent)
// defer test.RemoveTempYamlFile(mergeFilename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("merge %s %s", filename, mergeFilename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// test.AssertResult(t, mergeContent, result.Output)
// }
// func TestMergeYamlMultiAllOverwriteCmd(t *testing.T) {
// content := `b:
// c: 3
// apples: green
// ---
// something: else`
// filename := test.WriteTempYamlFile(content)
// defer test.RemoveTempYamlFile(filename)
// mergeContent := `apples: red
// something: good`
// mergeFilename := test.WriteTempYamlFile(mergeContent)
// defer test.RemoveTempYamlFile(mergeFilename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("merge --overwrite -d* %s %s", filename, mergeFilename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `b:
// c: 3
// apples: red
// something: good
// ---
// something: good
// apples: red
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestMergeYamlNullMapCmd(t *testing.T) {
// content := `b:`
// filename := test.WriteTempYamlFile(content)
// defer test.RemoveTempYamlFile(filename)
// mergeContent := `b:
// thing: a frog
// `
// mergeFilename := test.WriteTempYamlFile(mergeContent)
// defer test.RemoveTempYamlFile(mergeFilename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("merge %s %s", filename, mergeFilename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// test.AssertResult(t, mergeContent, result.Output)
// }
// func TestMergeCmd_Error(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "merge")
// if result.Error == nil {
// t.Error("Expected command to fail due to missing arg")
// }
// expectedOutput := `Must provide at least 1 yaml file`
// test.AssertResult(t, expectedOutput, result.Error.Error())
// }
// func TestMergeCmd_ErrorUnreadableFile(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "merge ../examples/data1.yaml fake-unknown")
// if result.Error == nil {
// t.Error("Expected command to fail due to unknown file")
// }
// var expectedOutput string
// if runtime.GOOS == "windows" {
// expectedOutput = `open fake-unknown: The system cannot find the file specified.`
// } else {
// expectedOutput = `open fake-unknown: no such file or directory`
// }
// test.AssertResult(t, expectedOutput, result.Error.Error())
// }
// func TestMergeCmd_Inplace(t *testing.T) {
// filename := test.WriteTempYamlFile(test.ReadTempYamlFile("../examples/data1.yaml"))
// err := os.Chmod(filename, os.FileMode(int(0666)))
// if err != nil {
// t.Error(err)
// }
// defer test.RemoveTempYamlFile(filename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("merge -i %s ../examples/data2.yaml", filename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// info, _ := os.Stat(filename)
// gotOutput := test.ReadTempYamlFile(filename)
// expectedOutput := `a: simple # just the best
// b: [1, 2]
// c:
// test: 1
// toast: leave
// tell: 1
// tasty.taco: cool
// `
// test.AssertResult(t, expectedOutput, gotOutput)
// test.AssertResult(t, os.FileMode(int(0666)), info.Mode())
// }
// func TestMergeAllowEmptyTargetCmd(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "merge ../examples/empty.yaml ../examples/data1.yaml")
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `a: simple # just the best
// b: [1, 2]
// c:
// test: 1
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestMergeAllowEmptyMergeCmd(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "merge ../examples/data1.yaml ../examples/empty.yaml")
// expectedOutput := `a: simple # just the best
// b: [1, 2]
// c:
// test: 1
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }

View File

@ -1,55 +0,0 @@
package cmd
// import (
// "github.com/mikefarah/yq/v3/pkg/yqlib"
// "github.com/spf13/cobra"
// )
// func createNewCmd() *cobra.Command {
// var cmdNew = &cobra.Command{
// Use: "new [path] [value]",
// Aliases: []string{"n"},
// Short: "yq n [--script/-s script_file] a.b.c newValue",
// Example: `
// yq new 'a.b.c' cat
// yq n 'a.b.c' --tag '!!str' true # force 'true' to be interpreted as a string instead of bool
// yq n 'a.b[+]' cat
// yq n -- '--key-starting-with-dash' cat # need to use '--' to stop processing arguments as flags
// yq n --script create_script.yaml
// `,
// Long: `Creates a new yaml w.r.t the given path and value.
// Outputs to STDOUT
// Create Scripts:
// Note that you can give a create script to perform more sophisticated yaml. This follows the same format as the update script.
// `,
// RunE: newProperty,
// }
// 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(&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
// }
// func newProperty(cmd *cobra.Command, args []string) error {
// var badArgsMessage = "Must provide <path_to_update> <value>"
// var updateCommands, updateCommandsError = readUpdateCommands(args, 2, badArgsMessage, false)
// if updateCommandsError != nil {
// return updateCommandsError
// }
// newNode := lib.New(updateCommands[0].Path)
// for _, updateCommand := range updateCommands {
// errorUpdating := lib.Update(&newNode, updateCommand, true)
// if errorUpdating != nil {
// return errorUpdating
// }
// }
// var encoder = yqlib.NewYamlEncoder(cmd.OutOrStdout(), indent, colorsEnabled)
// return encoder.Encode(&newNode)
// }

View File

@ -1,120 +0,0 @@
package cmd
// import (
// "fmt"
// "testing"
// "github.com/mikefarah/yq/v3/test"
// )
// func TestNewCmd(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "new b.c 3")
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `b:
// c: 3
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestNewCmdScript(t *testing.T) {
// updateScript := `- command: update
// path: b.c
// value: 7`
// scriptFilename := test.WriteTempYamlFile(updateScript)
// defer test.RemoveTempYamlFile(scriptFilename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("new --script %s", scriptFilename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `b:
// c: 7
// `
// 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) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "new b[0] 3")
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `b:
// - 3
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestNewCmd_Error(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "new b.c")
// if result.Error == nil {
// t.Error("Expected command to fail due to missing arg")
// }
// expectedOutput := `Must provide <path_to_update> <value>`
// test.AssertResult(t, expectedOutput, result.Error.Error())
// }
// func TestNewWithTaggedStyleCmd(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "new b.c cat --tag=!!str --style=tagged")
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `b:
// c: !!str cat
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestNewWithDoubleQuotedStyleCmd(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "new b.c cat --style=double")
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `b:
// c: "cat"
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestNewWithSingleQuotedStyleCmd(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "new b.c cat --style=single")
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `b:
// c: 'cat'
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }

View File

@ -1,50 +0,0 @@
package cmd
// import (
// "github.com/mikefarah/yq/v3/pkg/yqlib"
// errors "github.com/pkg/errors"
// "github.com/spf13/cobra"
// yaml "gopkg.in/yaml.v3"
// )
// func createPrefixCmd() *cobra.Command {
// var cmdPrefix = &cobra.Command{
// Use: "prefix [yaml_file] [path]",
// Aliases: []string{"p"},
// Short: "yq p [--inplace/-i] [--doc/-d index] sample.yaml a.b.c",
// Example: `
// yq prefix things.yaml 'a.b.c'
// yq prefix --inplace things.yaml 'a.b.c'
// yq prefix --inplace -- things.yaml '--key-starting-with-dash' # need to use '--' to stop processing arguments as flags
// yq p -i things.yaml 'a.b.c'
// yq p --doc 2 things.yaml 'a.b.d'
// yq p -d2 things.yaml 'a.b.d'
// `,
// Long: `Prefixes w.r.t to the yaml file at the given path.
// Outputs to STDOUT unless the inplace flag is used, in which case the file is updated instead.
// `,
// RunE: prefixProperty,
// }
// cmdPrefix.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace")
// cmdPrefix.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)")
// return cmdPrefix
// }
// func prefixProperty(cmd *cobra.Command, args []string) error {
// if len(args) < 2 {
// return errors.New("Must provide <filename> <prefixed_path>")
// }
// updateCommand := yqlib.UpdateCommand{Command: "update", Path: args[1]}
// log.Debugf("args %v", args)
// var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex()
// if errorParsingDocIndex != nil {
// return errorParsingDocIndex
// }
// var updateData = func(dataBucket *yaml.Node, currentIndex int) error {
// return prefixDocument(updateAll, docIndexInt, currentIndex, dataBucket, updateCommand)
// }
// return readAndUpdate(cmd.OutOrStdout(), args[0], updateData)
// }

View File

@ -1,189 +0,0 @@
package cmd
// import (
// "fmt"
// "runtime"
// "strings"
// "testing"
// "github.com/mikefarah/yq/v3/test"
// )
// func TestPrefixCmd(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 d", filename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `d:
// b:
// c: 3
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestPrefixCmdArray(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 [+].d.[+]", filename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `- d:
// - b:
// c: 3
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestPrefixCmd_MultiLayer(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 d.e.f", filename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `d:
// e:
// f:
// b:
// c: 3
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestPrefixMultiCmd(t *testing.T) {
// content := `b:
// c: 3
// ---
// apples: great
// `
// filename := test.WriteTempYamlFile(content)
// defer test.RemoveTempYamlFile(filename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("prefix %s -d 1 d", filename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `b:
// c: 3
// ---
// d:
// apples: great
// `
// test.AssertResult(t, expectedOutput, result.Output)
// }
// func TestPrefixInvalidDocumentIndexCmd(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 -df d", filename))
// if result.Error == nil {
// t.Error("Expected command to fail due to invalid path")
// }
// expectedOutput := `Document index f is not a integer or *: strconv.ParseInt: parsing "f": invalid syntax`
// test.AssertResult(t, expectedOutput, result.Error.Error())
// }
// func TestPrefixBadDocumentIndexCmd(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 -d 1 d", filename))
// if result.Error == nil {
// t.Error("Expected command to fail due to invalid path")
// }
// expectedOutput := `asked to process document index 1 but there are only 1 document(s)`
// test.AssertResult(t, expectedOutput, result.Error.Error())
// }
// func TestPrefixMultiAllCmd(t *testing.T) {
// content := `b:
// c: 3
// ---
// apples: great
// `
// filename := test.WriteTempYamlFile(content)
// defer test.RemoveTempYamlFile(filename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("prefix %s -d * d", filename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// expectedOutput := `d:
// b:
// c: 3
// ---
// d:
// apples: great`
// test.AssertResult(t, expectedOutput, strings.Trim(result.Output, "\n "))
// }
// func TestPrefixCmd_Error(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "prefix")
// if result.Error == nil {
// t.Error("Expected command to fail due to missing arg")
// }
// expectedOutput := `Must provide <filename> <prefixed_path>`
// test.AssertResult(t, expectedOutput, result.Error.Error())
// }
// func TestPrefixCmd_ErrorUnreadableFile(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "prefix fake-unknown a.b")
// if result.Error == nil {
// t.Error("Expected command to fail due to unknown file")
// }
// var expectedOutput string
// if runtime.GOOS == "windows" {
// expectedOutput = `open fake-unknown: The system cannot find the file specified.`
// } else {
// expectedOutput = `open fake-unknown: no such file or directory`
// }
// test.AssertResult(t, expectedOutput, result.Error.Error())
// }
// func TestPrefixCmd_Inplace(t *testing.T) {
// content := `b:
// c: 3
// `
// filename := test.WriteTempYamlFile(content)
// defer test.RemoveTempYamlFile(filename)
// cmd := getRootCommand()
// result := test.RunCmd(cmd, fmt.Sprintf("prefix -i %s d", filename))
// if result.Error != nil {
// t.Error(result.Error)
// }
// gotOutput := test.ReadTempYamlFile(filename)
// expectedOutput := `d:
// b:
// c: 3`
// test.AssertResult(t, expectedOutput, strings.Trim(gotOutput, "\n "))
// }

View File

@ -1,65 +0,0 @@
package cmd
import (
errors "github.com/pkg/errors"
"github.com/spf13/cobra"
)
func createReadCmd() *cobra.Command {
var cmdRead = &cobra.Command{
Use: "read [yaml_file] [path_expression]",
Aliases: []string{"r"},
Short: "yq r [--printMode/-p pv] sample.yaml 'b.e(name==fr*).value'",
Example: `
yq read things.yaml 'a.b.c'
yq r - 'a.b.c' # reads from stdin
yq r things.yaml 'a.*.c'
yq r things.yaml 'a.**.c' # deep splat
yq r things.yaml 'a.(child.subchild==co*).c'
yq r -d1 things.yaml 'a.array[0].blah'
yq r things.yaml 'a.array[*].blah'
yq r -- things.yaml '--key-starting-with-dashes.blah'
`,
Long: "Outputs the value of the given path in the yaml file to STDOUT",
RunE: readProperty,
}
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(&defaultValue, "defaultValue", "D", "", "default value printed when there are no results")
cmdRead.PersistentFlags().BoolVarP(&collectIntoArray, "collect", "c", false, "collect results into array")
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(&explodeAnchors, "explodeAnchors", "X", false, "explode anchors")
cmdRead.PersistentFlags().BoolVarP(&exitStatus, "exitStatus", "e", false, "set exit status if no matches are found")
return cmdRead
}
func readProperty(cmd *cobra.Command, args []string) error {
var path = ""
if len(args) < 1 {
return errors.New("Must provide filename")
} else if len(args) > 1 {
path = args[1]
}
var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex()
if errorParsingDocIndex != nil {
return errorParsingDocIndex
}
matchingNodes, errorReadingStream := readYamlFile(args[0], path, updateAll, docIndexInt)
if exitStatus && len(matchingNodes) == 0 {
cmd.SilenceUsage = true
return errors.New("No matches found")
}
if errorReadingStream != nil {
cmd.SilenceUsage = true
return errorReadingStream
}
out := cmd.OutOrStdout()
return printResults(matchingNodes, out)
}

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,11 @@
package cmd package cmd
import ( import (
"errors"
"fmt"
"os" "os"
"github.com/mikefarah/yq/v4/pkg/yqlib/treeops"
"github.com/spf13/cobra" "github.com/spf13/cobra"
logging "gopkg.in/op/go-logging.v1" logging "gopkg.in/op/go-logging.v1"
) )
@ -17,9 +20,51 @@ func New() *cobra.Command {
cmd.Print(GetVersionDisplay()) cmd.Print(GetVersionDisplay())
return nil return nil
} }
cmd.Println(cmd.UsageString()) if shellCompletion != "" {
switch shellCompletion {
case "bash", "":
return cmd.GenBashCompletion(os.Stdout)
case "zsh":
return cmd.GenZshCompletion(os.Stdout)
case "fish":
return cmd.GenFishCompletion(os.Stdout, true)
case "powershell":
return cmd.GenPowerShellCompletion(os.Stdout)
default:
return fmt.Errorf("Unknown variant %v", shellCompletion)
}
}
// if len(args) == 0 {
// cmd.Println(cmd.UsageString())
// return nil
// }
cmd.SilenceUsage = true
return nil var treeCreator = treeops.NewPathTreeCreator()
expression := ""
if len(args) > 0 {
expression = args[0]
}
pathNode, err := treeCreator.ParsePath(expression)
if err != nil {
return err
}
matchingNodes, err := evaluate("-", pathNode)
if err != nil {
return err
}
if exitStatus && matchingNodes.Len() == 0 {
cmd.SilenceUsage = true
return errors.New("No matches found")
}
out := cmd.OutOrStdout()
return printResults(matchingNodes, out)
}, },
PersistentPreRun: func(cmd *cobra.Command, args []string) { PersistentPreRun: func(cmd *cobra.Command, args []string) {
cmd.SetOut(cmd.OutOrStdout()) cmd.SetOut(cmd.OutOrStdout())
@ -44,18 +89,15 @@ func New() *cobra.Command {
rootCmd.PersistentFlags().BoolVarP(&prettyPrint, "prettyPrint", "P", false, "pretty print") rootCmd.PersistentFlags().BoolVarP(&prettyPrint, "prettyPrint", "P", false, "pretty print")
rootCmd.PersistentFlags().IntVarP(&indent, "indent", "I", 2, "sets indent level for output") rootCmd.PersistentFlags().IntVarP(&indent, "indent", "I", 2, "sets indent level for output")
rootCmd.Flags().BoolVarP(&version, "version", "V", false, "Print version information and quit") rootCmd.Flags().BoolVarP(&version, "version", "V", false, "Print version information and quit")
rootCmd.PersistentFlags().BoolVarP(&colorsEnabled, "colors", "C", false, "print with colors")
rootCmd.AddCommand( rootCmd.Flags().StringVarP(&shellCompletion, "shellCompletion", "", "", "[bash/zsh/powershell/fish] prints shell completion script")
createReadCmd(),
// createCompareCmd(), rootCmd.PersistentFlags().BoolVarP(&forceColor, "colors", "C", false, "force print with colors")
createValidateCmd(), rootCmd.PersistentFlags().BoolVarP(&forceNoColor, "no-colors", "M", false, "force print with no colors")
// createWriteCmd(),
// createPrefixCmd(), // rootCmd.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)")
// createDeleteCmd(), rootCmd.PersistentFlags().StringVarP(&printMode, "printMode", "p", "v", "print mode (v (values, default), p (paths), pv (path and value pairs)")
// createNewCmd(), rootCmd.PersistentFlags().StringVarP(&defaultValue, "defaultValue", "D", "", "default value printed when there are no results")
// createMergeCmd(),
createBashCompletionCmd(rootCmd),
)
return rootCmd return rootCmd
} }

View File

@ -1,57 +0,0 @@
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 shell-completion)
To configure your bash shell to load completions for each session add to
your bashrc
# ~/.bashrc or ~/.profile
. <(yq shell-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

@ -2,103 +2,73 @@ package cmd
import ( import (
"bufio" "bufio"
"fmt" "container/list"
"errors"
"io" "io"
"os" "os"
"strconv"
"github.com/mikefarah/yq/v3/pkg/yqlib" "github.com/mikefarah/yq/v4/pkg/yqlib"
"github.com/mikefarah/yq/v3/pkg/yqlib/treeops" "github.com/mikefarah/yq/v4/pkg/yqlib/treeops"
errors "github.com/pkg/errors"
yaml "gopkg.in/yaml.v3" yaml "gopkg.in/yaml.v3"
) )
type readDataFn func(document int, dataBucket *yaml.Node) ([]*treeops.CandidateNode, error) func readStream(filename string) (*yaml.Decoder, error) {
if filename == "" {
func createReadFunction(path string) func(int, *yaml.Node) ([]*treeops.CandidateNode, error) { return nil, errors.New("Must provide filename")
return func(document int, dataBucket *yaml.Node) ([]*treeops.CandidateNode, error) {
return lib.Get(document, dataBucket, path)
} }
var stream io.Reader
if filename == "-" {
stream = bufio.NewReader(os.Stdin)
} else {
file, err := os.Open(filename) // nolint gosec
if err != nil {
return nil, err
}
defer safelyCloseFile(file)
stream = file
}
return yaml.NewDecoder(stream), nil
} }
func readYamlFile(filename string, path string, updateAll bool, docIndexInt int) ([]*treeops.CandidateNode, error) { func evaluate(filename string, node *treeops.PathTreeNode) (*list.List, error) {
return doReadYamlFile(filename, createReadFunction(path), updateAll, docIndexInt)
}
func doReadYamlFile(filename string, readFn readDataFn, updateAll bool, docIndexInt int) ([]*treeops.CandidateNode, error) { var treeNavigator = treeops.NewDataTreeNavigator(treeops.NavigationPrefs{})
var matchingNodes []*treeops.CandidateNode
var matchingNodes = list.New()
var currentIndex uint = 0
var decoder, err = readStream(filename)
if err != nil {
return nil, err
}
var currentIndex = 0
var errorReadingStream = readStream(filename, func(decoder *yaml.Decoder) error {
for { for {
var dataBucket yaml.Node var dataBucket yaml.Node
errorReading := decoder.Decode(&dataBucket) errorReading := decoder.Decode(&dataBucket)
if errorReading == io.EOF { if errorReading == io.EOF {
return handleEOF(updateAll, docIndexInt, currentIndex) return matchingNodes, nil
} else if errorReading != nil { } else if errorReading != nil {
return errorReading return nil, errorReading
} }
candidateNode := &treeops.CandidateNode{
Document: currentIndex,
Filename: filename,
Node: &dataBucket,
}
inputList := list.New()
inputList.PushBack(candidateNode)
var errorParsing error newMatches, errorParsing := treeNavigator.GetMatchingNodes(inputList, node)
matchingNodes, errorParsing = appendDocument(matchingNodes, dataBucket, readFn, updateAll, docIndexInt, currentIndex)
if errorParsing != nil { if errorParsing != nil {
return errorParsing return nil, errorParsing
}
if !updateAll && currentIndex == docIndexInt {
log.Debug("all done")
return nil
} }
matchingNodes.PushBackList(newMatches)
currentIndex = currentIndex + 1 currentIndex = currentIndex + 1
} }
})
return matchingNodes, errorReadingStream
}
func handleEOF(updateAll bool, docIndexInt int, currentIndex int) error { return matchingNodes, nil
log.Debugf("done %v / %v", currentIndex, docIndexInt)
if !updateAll && currentIndex <= docIndexInt && docIndexInt != 0 {
return fmt.Errorf("Could not process document index %v as there are only %v document(s)", docIndex, currentIndex)
}
return nil
}
func appendDocument(originalMatchingNodes []*treeops.CandidateNode, dataBucket yaml.Node, readFn readDataFn, updateAll bool, docIndexInt int, currentIndex int) ([]*treeops.CandidateNode, error) {
log.Debugf("processing document %v - requested index %v", currentIndex, docIndexInt)
// yqlib.DebugNode(&dataBucket)
if !updateAll && currentIndex != docIndexInt {
return originalMatchingNodes, nil
}
log.Debugf("reading in document %v", currentIndex)
matchingNodes, errorParsing := readFn(currentIndex, &dataBucket)
if errorParsing != nil {
return nil, errors.Wrapf(errorParsing, "Error reading path in document index %v", currentIndex)
}
return append(originalMatchingNodes, matchingNodes...), nil
}
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)
}
}
// transforms node before printing, if required
func transformNode(node *yaml.Node) *yaml.Node {
if printLength {
return &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", lengthOf(node))}
}
return node
} }
func printNode(node *yaml.Node, writer io.Writer) error { func printNode(node *yaml.Node, writer io.Writer) error {
@ -114,9 +84,10 @@ func printNode(node *yaml.Node, writer io.Writer) error {
return encoder.Encode(node) return encoder.Encode(node)
} }
func removeComments(matchingNodes []*treeops.CandidateNode) { func removeComments(matchingNodes *list.List) {
for _, nodeContext := range matchingNodes { for el := matchingNodes.Front(); el != nil; el = el.Next() {
removeCommentOfNode(nodeContext.Node) candidate := el.Value.(*treeops.CandidateNode)
removeCommentOfNode(candidate.Node)
} }
} }
@ -130,9 +101,10 @@ func removeCommentOfNode(node *yaml.Node) {
} }
} }
func setStyle(matchingNodes []*treeops.CandidateNode, style yaml.Style) { func setStyle(matchingNodes *list.List, style yaml.Style) {
for _, nodeContext := range matchingNodes { for el := matchingNodes.Front(); el != nil; el = el.Next() {
updateStyleOfNode(nodeContext.Node, style) candidate := el.Value.(*treeops.CandidateNode)
updateStyleOfNode(candidate.Node, style)
} }
} }
@ -234,9 +206,10 @@ func explodeNode(node *yaml.Node) error {
} }
} }
func explode(matchingNodes []*treeops.CandidateNode) error { func explode(matchingNodes *list.List) error {
log.Debug("exploding nodes") log.Debug("exploding nodes")
for _, nodeContext := range matchingNodes { for el := matchingNodes.Front(); el != nil; el = el.Next() {
nodeContext := el.Value.(*treeops.CandidateNode)
log.Debugf("exploding %v", nodeContext.GetKey()) log.Debugf("exploding %v", nodeContext.GetKey())
errorExplodingNode := explodeNode(nodeContext.Node) errorExplodingNode := explodeNode(nodeContext.Node)
if errorExplodingNode != nil { if errorExplodingNode != nil {
@ -246,7 +219,7 @@ func explode(matchingNodes []*treeops.CandidateNode) error {
return nil return nil
} }
func printResults(matchingNodes []*treeops.CandidateNode, writer io.Writer) error { func printResults(matchingNodes *list.List, writer io.Writer) error {
if prettyPrint { if prettyPrint {
setStyle(matchingNodes, 0) setStyle(matchingNodes, 0)
} }
@ -255,6 +228,12 @@ func printResults(matchingNodes []*treeops.CandidateNode, writer io.Writer) erro
removeComments(matchingNodes) removeComments(matchingNodes)
} }
fileInfo, _ := os.Stdout.Stat()
if forceColor || (!forceNoColor && (fileInfo.Mode()&os.ModeCharDevice) != 0) {
colorsEnabled = true
}
//always explode anchors when printing json //always explode anchors when printing json
if explodeAnchors || outputToJSON { if explodeAnchors || outputToJSON {
errorExploding := explode(matchingNodes) errorExploding := explode(matchingNodes)
@ -266,7 +245,7 @@ func printResults(matchingNodes []*treeops.CandidateNode, writer io.Writer) erro
bufferedWriter := bufio.NewWriter(writer) bufferedWriter := bufio.NewWriter(writer)
defer safelyFlush(bufferedWriter) defer safelyFlush(bufferedWriter)
if len(matchingNodes) == 0 { if matchingNodes.Len() == 0 {
log.Debug("no matching results, nothing to print") log.Debug("no matching results, nothing to print")
if defaultValue != "" { if defaultValue != "" {
return writeString(bufferedWriter, defaultValue) return writeString(bufferedWriter, defaultValue)
@ -275,9 +254,9 @@ func printResults(matchingNodes []*treeops.CandidateNode, writer io.Writer) erro
} }
var errorWriting error var errorWriting error
var arrayCollection = yaml.Node{Kind: yaml.SequenceNode} for el := matchingNodes.Front(); el != nil; el = el.Next() {
mappedDoc := el.Value.(*treeops.CandidateNode)
for _, mappedDoc := range matchingNodes {
switch printMode { switch printMode {
case "p": case "p":
errorWriting = writeString(bufferedWriter, mappedDoc.PathStackToString()+"\n") errorWriting = writeString(bufferedWriter, mappedDoc.PathStackToString()+"\n")
@ -289,251 +268,24 @@ func printResults(matchingNodes []*treeops.CandidateNode, writer io.Writer) erro
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: mappedDoc.PathStackToString()} parentNode.Content[0] = &yaml.Node{Kind: yaml.ScalarNode, Value: mappedDoc.PathStackToString()}
parentNode.Content[1] = transformNode(mappedDoc.Node) if mappedDoc.Node.Kind == yaml.DocumentNode {
if collectIntoArray { parentNode.Content[1] = mappedDoc.Node.Content[0]
arrayCollection.Content = append(arrayCollection.Content, &parentNode) } else {
} else if err := printNode(&parentNode, bufferedWriter); err != nil { parentNode.Content[1] = mappedDoc.Node
}
if err := printNode(&parentNode, bufferedWriter); err != nil {
return err return err
} }
default: default:
if collectIntoArray { if err := printNode(mappedDoc.Node, bufferedWriter); err != nil {
arrayCollection.Content = append(arrayCollection.Content, mappedDoc.Node)
} else if err := printNode(transformNode(mappedDoc.Node), bufferedWriter); err != nil {
return err return err
} }
} }
} }
if collectIntoArray {
if err := printNode(transformNode(&arrayCollection), bufferedWriter); err != nil {
return err
}
}
return nil return nil
} }
func parseDocumentIndex() (bool, int, error) {
if docIndex == "*" {
return true, -1, nil
}
docIndexInt64, err := strconv.ParseInt(docIndex, 10, 32)
if err != nil {
return false, -1, errors.Wrapf(err, "Document index %v is not a integer or *", docIndex)
}
return false, int(docIndexInt64), nil
}
type updateDataFn func(dataBucket *yaml.Node, currentIndex int) error
func isNullDocument(dataBucket *yaml.Node) bool {
return dataBucket.Kind == yaml.DocumentNode && (len(dataBucket.Content) == 0 ||
dataBucket.Content[0].Kind == yaml.ScalarNode && dataBucket.Content[0].Tag == "!!null")
}
func mapYamlDecoder(updateData updateDataFn, encoder yqlib.Encoder) yamlDecoderFn {
return func(decoder *yaml.Decoder) error {
var dataBucket yaml.Node
var errorReading error
var errorWriting error
var errorUpdating error
var currentIndex = 0
var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex()
if errorParsingDocIndex != nil {
return errorParsingDocIndex
}
for {
log.Debugf("Read doc %v", currentIndex)
errorReading = decoder.Decode(&dataBucket)
if errorReading == io.EOF && docIndexInt == 0 && currentIndex == 0 {
//empty document, lets just make one
dataBucket = yaml.Node{Kind: yaml.DocumentNode, Content: make([]*yaml.Node, 1)}
child := yaml.Node{Kind: yaml.MappingNode}
dataBucket.Content[0] = &child
} else if isNullDocument(&dataBucket) && (updateAll || docIndexInt == currentIndex) {
child := yaml.Node{Kind: yaml.MappingNode}
dataBucket.Content[0] = &child
} else if errorReading == io.EOF {
if !updateAll && currentIndex <= docIndexInt {
return fmt.Errorf("asked to process document index %v but there are only %v document(s)", docIndex, currentIndex)
}
return nil
} else if errorReading != nil {
return errors.Wrapf(errorReading, "Error reading document at index %v, %v", currentIndex, errorReading)
}
errorUpdating = updateData(&dataBucket, currentIndex)
if errorUpdating != nil {
return errors.Wrapf(errorUpdating, "Error updating document at index %v", currentIndex)
}
if prettyPrint {
updateStyleOfNode(&dataBucket, 0)
}
errorWriting = encoder.Encode(&dataBucket)
if errorWriting != nil {
return errors.Wrapf(errorWriting, "Error writing document at index %v, %v", currentIndex, errorWriting)
}
currentIndex = currentIndex + 1
}
}
}
// func prefixDocument(updateAll bool, docIndexInt int, currentIndex int, dataBucket *yaml.Node, updateCommand yqlib.UpdateCommand) error {
// if updateAll || currentIndex == docIndexInt {
// log.Debugf("Prefixing document %v", currentIndex)
// // yqlib.DebugNode(dataBucket)
// updateCommand.Value = dataBucket.Content[0]
// dataBucket.Content = make([]*yaml.Node, 1)
// newNode := lib.New(updateCommand.Path)
// dataBucket.Content[0] = &newNode
// errorUpdating := lib.Update(dataBucket, updateCommand, true)
// if errorUpdating != nil {
// return errorUpdating
// }
// }
// return nil
// }
// func updateDoc(inputFile string, updateCommands []yqlib.UpdateCommand, writer io.Writer) error {
// var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex()
// if errorParsingDocIndex != nil {
// return errorParsingDocIndex
// }
// var updateData = func(dataBucket *yaml.Node, currentIndex int) error {
// if updateAll || currentIndex == docIndexInt {
// log.Debugf("Updating doc %v", currentIndex)
// for _, updateCommand := range updateCommands {
// log.Debugf("Processing update to Path %v", updateCommand.Path)
// errorUpdating := lib.Update(dataBucket, updateCommand, autoCreateFlag)
// if errorUpdating != nil {
// return errorUpdating
// }
// }
// }
// return nil
// }
// return readAndUpdate(writer, inputFile, updateData)
// }
// func readAndUpdate(stdOut io.Writer, inputFile string, updateData updateDataFn) error {
// var destination io.Writer
// var destinationName string
// var completedSuccessfully = false
// if writeInplace {
// info, err := os.Stat(inputFile)
// if err != nil {
// return err
// }
// // mkdir temp dir as some docker images does not have temp dir
// _, err = os.Stat(os.TempDir())
// if os.IsNotExist(err) {
// err = os.Mkdir(os.TempDir(), 0700)
// if err != nil {
// return err
// }
// } else if err != nil {
// return err
// }
// tempFile, err := ioutil.TempFile("", "temp")
// if err != nil {
// return err
// }
// destinationName = tempFile.Name()
// err = os.Chmod(destinationName, info.Mode())
// if err != nil {
// return err
// }
// destination = tempFile
// defer func() {
// safelyCloseFile(tempFile)
// if completedSuccessfully {
// safelyRenameFile(tempFile.Name(), inputFile)
// }
// }()
// } else {
// destination = stdOut
// destinationName = "Stdout"
// }
// log.Debugf("Writing to %v from %v", destinationName, inputFile)
// bufferedWriter := bufio.NewWriter(destination)
// defer safelyFlush(bufferedWriter)
// var encoder yqlib.Encoder
// if outputToJSON {
// encoder = yqlib.NewJsonEncoder(bufferedWriter, prettyPrint, indent)
// } else {
// encoder = yqlib.NewYamlEncoder(bufferedWriter, indent, colorsEnabled)
// }
// var errorProcessing = readStream(inputFile, mapYamlDecoder(updateData, encoder))
// completedSuccessfully = errorProcessing == nil
// return errorProcessing
// }
type updateCommandParsed struct {
Command string
Path string
Value yaml.Node
}
// func readUpdateCommands(args []string, expectedArgs int, badArgsMessage string, allowNoValue bool) ([]yqlib.UpdateCommand, error) {
// var updateCommands []yqlib.UpdateCommand = make([]yqlib.UpdateCommand, 0)
// if writeScript != "" {
// var parsedCommands = make([]updateCommandParsed, 0)
// err := readData(writeScript, 0, &parsedCommands)
// if err != nil && err != io.EOF {
// return nil, err
// }
// log.Debugf("Read write commands file '%v'", parsedCommands)
// for index := range parsedCommands {
// parsedCommand := parsedCommands[index]
// updateCommand := yqlib.UpdateCommand{Command: parsedCommand.Command, Path: parsedCommand.Path, Value: &parsedCommand.Value, Overwrite: true}
// updateCommands = append(updateCommands, updateCommand)
// }
// 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 {
// updateCommands = make([]yqlib.UpdateCommand, 1)
// log.Debug("args %v", args)
// log.Debug("path %v", args[expectedArgs-2])
// log.Debug("Value %v", args[expectedArgs-1])
// value := valueParser.Parse(args[expectedArgs-1], customTag, customStyle, anchorName, makeAlias)
// updateCommands[0] = yqlib.UpdateCommand{Command: "update", Path: args[expectedArgs-2], Value: value, Overwrite: true, CommentsMergeStrategy: yqlib.IgnoreCommentsMergeStrategy}
// } else if len(args) == expectedArgs-1 && allowNoValue {
// // don't update the value
// updateCommands = make([]yqlib.UpdateCommand, 1)
// log.Debug("args %v", args)
// log.Debug("path %v", args[expectedArgs-2])
// updateCommands[0] = yqlib.UpdateCommand{Command: "update", Path: args[expectedArgs-2], Value: valueParser.Parse("", customTag, customStyle, anchorName, makeAlias), Overwrite: true, DontUpdateNodeValue: true}
// } else {
// return nil, errors.New(badArgsMessage)
// }
// return updateCommands, nil
// }
func safelyRenameFile(from string, to string) { func safelyRenameFile(from string, to string) {
if renameError := os.Rename(from, to); renameError != nil { if renameError := os.Rename(from, to); renameError != nil {
log.Debugf("Error renaming from %v to %v, attempting to copy contents", from, to) log.Debugf("Error renaming from %v to %v, attempting to copy contents", from, to)
@ -584,36 +336,3 @@ func safelyCloseFile(file *os.File) {
log.Error(err.Error()) log.Error(err.Error())
} }
} }
type yamlDecoderFn func(*yaml.Decoder) error
func readStream(filename string, yamlDecoder yamlDecoderFn) error {
if filename == "" {
return errors.New("Must provide filename")
}
var stream io.Reader
if filename == "-" {
stream = bufio.NewReader(os.Stdin)
} else {
file, err := os.Open(filename) // nolint gosec
if err != nil {
return err
}
defer safelyCloseFile(file)
stream = file
}
return yamlDecoder(yaml.NewDecoder(stream))
}
func readData(filename string, indexToRead int, parsedData interface{}) error {
return readStream(filename, func(decoder *yaml.Decoder) error {
for currentIndex := 0; currentIndex < indexToRead; currentIndex++ {
errorSkipping := decoder.Decode(parsedData)
if errorSkipping != nil {
return errors.Wrapf(errorSkipping, "Error processing document at index %v, %v", currentIndex, errorSkipping)
}
}
return decoder.Decode(parsedData)
})
}

View File

@ -1,37 +0,0 @@
package cmd
import (
errors "github.com/pkg/errors"
"github.com/spf13/cobra"
)
func createValidateCmd() *cobra.Command {
var cmdRead = &cobra.Command{
Use: "validate [yaml_file]",
Aliases: []string{"v"},
Short: "yq v sample.yaml",
Example: `
yq v - # reads from stdin
`,
RunE: validateProperty,
SilenceUsage: true,
SilenceErrors: false,
}
cmdRead.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)")
return cmdRead
}
func validateProperty(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("Must provide filename")
}
var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex()
if errorParsingDocIndex != nil {
return errorParsingDocIndex
}
_, errorReadingStream := readYamlFile(args[0], "", updateAll, docIndexInt)
return errorReadingStream
}

View File

@ -1,31 +0,0 @@
package cmd
import (
"fmt"
"testing"
"github.com/mikefarah/yq/v3/test"
)
func TestValidateCmd(t *testing.T) {
cmd := getRootCommand()
result := test.RunCmd(cmd, "validate ../examples/sample.yaml b.c")
if result.Error != nil {
t.Error(result.Error)
}
test.AssertResult(t, "", result.Output)
}
func TestValidateBadDataCmd(t *testing.T) {
content := `[!Whatever]`
filename := test.WriteTempYamlFile(content)
defer test.RemoveTempYamlFile(filename)
cmd := getRootCommand()
result := test.RunCmd(cmd, fmt.Sprintf("validate %s", filename))
if result.Error == nil {
t.Error("Expected command to fail")
}
expectedOutput := `yaml: line 1: did not find expected ',' or ']'`
test.AssertResult(t, expectedOutput, result.Error.Error())
}

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.4.0" Version = "4.0.0-beta"
// 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

@ -1,3 +1,5 @@
# Some doc
a: true a: true
b: b:
c: 2 c: 2

2
go.mod
View File

@ -1,4 +1,4 @@
module github.com/mikefarah/yq/v3 module github.com/mikefarah/yq/v4
require ( require (
github.com/elliotchance/orderedmap v1.3.0 // indirect github.com/elliotchance/orderedmap v1.3.0 // indirect

4
go.sum
View File

@ -77,6 +77,10 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 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-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mikefarah/yq v1.15.0 h1:ViMYNRG5UB7hzm8olxMFqPtkpMXXKO4g32/v9JUa62o=
github.com/mikefarah/yq v2.4.0+incompatible h1:oBxbWy8R9hI3BIUUxEf0CzikWa2AgnGrGhvGQt5jgjk=
github.com/mikefarah/yq/v3 v3.0.0-20201020025845-ccb718cd0f59 h1:6nvF+EEFIVD4KT64CgvAzWaMVC283Huno59khWs9r1A=
github.com/mikefarah/yq/v3 v3.0.0-20201020025845-ccb718cd0f59/go.mod h1:7eVjFf5bgozMuHk+oKpyxR2zCN3iEN1tF0/bM5jvtKo=
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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=

View File

@ -13,6 +13,7 @@ type CandidateNode struct {
Node *yaml.Node // the actual node Node *yaml.Node // the actual node
Path []interface{} /// the path we took to get to this node Path []interface{} /// the path we took to get to this node
Document uint // the document index of this node Document uint // the document index of this node
Filename string
} }
func (n *CandidateNode) GetKey() string { func (n *CandidateNode) GetKey() string {

View File

@ -17,34 +17,17 @@ type NavigationPrefs struct {
} }
type DataTreeNavigator interface { type DataTreeNavigator interface {
GetMatchingNodes(matchingNodes []*CandidateNode, pathNode *PathTreeNode) ([]*CandidateNode, error) // given a list of CandidateEntities and a pathNode,
// this will process the list against the given pathNode and return
// a new list of matching candidates
GetMatchingNodes(matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error)
} }
func NewDataTreeNavigator(navigationPrefs NavigationPrefs) DataTreeNavigator { func NewDataTreeNavigator(navigationPrefs NavigationPrefs) DataTreeNavigator {
return &dataTreeNavigator{navigationPrefs} return &dataTreeNavigator{navigationPrefs}
} }
func (d *dataTreeNavigator) GetMatchingNodes(matchingNodes []*CandidateNode, pathNode *PathTreeNode) ([]*CandidateNode, error) { func (d *dataTreeNavigator) GetMatchingNodes(matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
var matchingNodeMap = list.New()
for _, n := range matchingNodes {
matchingNodeMap.PushBack(n)
}
matchedNodes, err := d.getMatchingNodes(matchingNodeMap, pathNode)
if err != nil {
return nil, err
}
values := make([]*CandidateNode, 0, matchedNodes.Len())
for el := matchedNodes.Front(); el != nil; el = el.Next() {
values = append(values, el.Value.(*CandidateNode))
}
return values, nil
}
func (d *dataTreeNavigator) getMatchingNodes(matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
if pathNode == nil { if pathNode == nil {
log.Debugf("getMatchingNodes - nothing to do") log.Debugf("getMatchingNodes - nothing to do")
return matchingNodes, nil return matchingNodes, nil

View File

@ -1,6 +1,7 @@
package treeops package treeops
import ( import (
"container/list"
"strings" "strings"
"testing" "testing"
@ -10,9 +11,10 @@ import (
var treeNavigator = NewDataTreeNavigator(NavigationPrefs{}) var treeNavigator = NewDataTreeNavigator(NavigationPrefs{})
var treeCreator = NewPathTreeCreator() var treeCreator = NewPathTreeCreator()
func readDoc(t *testing.T, content string) []*CandidateNode { func readDoc(t *testing.T, content string) *list.List {
inputList := list.New()
if content == "" { if content == "" {
return []*CandidateNode{} return inputList
} }
decoder := yaml.NewDecoder(strings.NewReader(content)) decoder := yaml.NewDecoder(strings.NewReader(content))
var dataBucket yaml.Node var dataBucket yaml.Node
@ -21,12 +23,19 @@ func readDoc(t *testing.T, content string) []*CandidateNode {
t.Error(content) t.Error(content)
t.Error(err) t.Error(err)
} }
return []*CandidateNode{&CandidateNode{Node: dataBucket.Content[0], Document: 0}}
inputList.PushBack(&CandidateNode{
Document: 0,
Filename: "test.yml",
Node: &dataBucket,
})
return inputList
} }
func resultsToString(results []*CandidateNode) []string { func resultsToString(results *list.List) []string {
var pretty []string = make([]string, 0) var pretty []string = make([]string, 0)
for _, n := range results { for el := results.Front(); el != nil; el = el.Next() {
n := el.Value.(*CandidateNode)
pretty = append(pretty, NodeToString(n)) pretty = append(pretty, NodeToString(n))
} }
return pretty return pretty

View File

@ -101,34 +101,6 @@ func (p *Operation) toString() string {
} }
} }
type YqTreeLib interface {
Get(document int, documentNode *yaml.Node, path string) ([]*CandidateNode, error)
// GetForMerge(rootNode *yaml.Node, path string, arrayMergeStrategy ArrayMergeStrategy) ([]*NodeContext, error)
// Update(rootNode *yaml.Node, updateCommand UpdateCommand, autoCreate bool) error
// New(path string) yaml.Node
// PathStackToString(pathStack []interface{}) string
// MergePathStackToString(pathStack []interface{}, arrayMergeStrategy ArrayMergeStrategy) string
}
func NewYqTreeLib() YqTreeLib {
return &lib{treeCreator: NewPathTreeCreator()}
}
type lib struct {
treeCreator PathTreeCreator
}
func (l *lib) Get(document int, documentNode *yaml.Node, path string) ([]*CandidateNode, error) {
nodes := []*CandidateNode{&CandidateNode{Node: documentNode.Content[0], Document: 0}}
navigator := NewDataTreeNavigator(NavigationPrefs{})
pathNode, errPath := l.treeCreator.ParsePath(path)
if errPath != nil {
return nil, errPath
}
return navigator.GetMatchingNodes(nodes, pathNode)
}
//use for debugging only //use for debugging only
func NodesToString(collection *list.List) string { func NodesToString(collection *list.List) string {
if !log.IsEnabledFor(logging.DEBUG) { if !log.IsEnabledFor(logging.DEBUG) {
@ -157,7 +129,11 @@ func NodeToString(node *CandidateNode) string {
log.Error("Error debugging node, %v", errorEncoding.Error()) log.Error("Error debugging node, %v", errorEncoding.Error())
} }
encoder.Close() encoder.Close()
return fmt.Sprintf(`D%v, P%v, (%v)::%v`, node.Document, node.Path, value.Tag, buf.String()) tag := value.Tag
if value.Kind == yaml.DocumentNode {
tag = "doc"
}
return fmt.Sprintf(`D%v, P%v, (%v)::%v`, node.Document, node.Path, tag, buf.String())
} }
func KindString(kind yaml.Kind) string { func KindString(kind yaml.Kind) string {

View File

@ -3,14 +3,14 @@ package treeops
import "container/list" import "container/list"
func AssignOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { func AssignOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs) lhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Lhs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
for el := lhs.Front(); el != nil; el = el.Next() { for el := lhs.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
rhs, err := d.getMatchingNodes(nodeToMap(candidate), pathNode.Rhs) rhs, err := d.GetMatchingNodes(nodeToMap(candidate), pathNode.Rhs)
if err != nil { if err != nil {
return nil, err return nil, err
@ -28,14 +28,14 @@ func AssignOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *Pa
// does not update content or values // does not update content or values
func AssignAttributesOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { func AssignAttributesOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs) lhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Lhs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
for el := lhs.Front(); el != nil; el = el.Next() { for el := lhs.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
rhs, err := d.getMatchingNodes(nodeToMap(candidate), pathNode.Rhs) rhs, err := d.GetMatchingNodes(nodeToMap(candidate), pathNode.Rhs)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -9,61 +9,70 @@ var assignOperatorScenarios = []expressionScenario{
document: `{a: {b: apple}}`, document: `{a: {b: apple}}`,
expression: `.a.b |= "frog"`, expression: `.a.b |= "frog"`,
expected: []string{ expected: []string{
"D0, P[], (!!map)::{a: {b: frog}}\n", "D0, P[], (doc)::{a: {b: frog}}\n",
}, },
}, { },
{
document: `{a: {b: apple}}`, document: `{a: {b: apple}}`,
expression: `.a.b | (. |= "frog")`, expression: `.a.b | (. |= "frog")`,
expected: []string{ expected: []string{
"D0, P[a b], (!!str)::frog\n", "D0, P[a b], (!!str)::frog\n",
}, },
}, { },
{
document: `{a: {b: apple}}`, document: `{a: {b: apple}}`,
expression: `.a.b |= 5`, expression: `.a.b |= 5`,
expected: []string{ expected: []string{
"D0, P[], (!!map)::{a: {b: 5}}\n", "D0, P[], (doc)::{a: {b: 5}}\n",
}, },
}, { },
{
document: `{a: {b: apple}}`, document: `{a: {b: apple}}`,
expression: `.a.b |= 3.142`, expression: `.a.b |= 3.142`,
expected: []string{ expected: []string{
"D0, P[], (!!map)::{a: {b: 3.142}}\n", "D0, P[], (doc)::{a: {b: 3.142}}\n",
}, },
}, { },
{
document: `{a: {b: {g: foof}}}`, document: `{a: {b: {g: foof}}}`,
expression: `.a |= .b`, expression: `.a |= .b`,
expected: []string{ expected: []string{
"D0, P[], (!!map)::{a: {g: foof}}\n", "D0, P[], (doc)::{a: {g: foof}}\n",
}, },
}, { },
{
document: `{a: {b: apple, c: cactus}}`, document: `{a: {b: apple, c: cactus}}`,
expression: `.a[] | select(. == "apple") |= "frog"`, expression: `.a[] | select(. == "apple") |= "frog"`,
expected: []string{ expected: []string{
"D0, P[], (!!map)::{a: {b: frog, c: cactus}}\n", "D0, P[], (doc)::{a: {b: frog, c: cactus}}\n",
}, },
}, { },
{
document: `[candy, apple, sandy]`, document: `[candy, apple, sandy]`,
expression: `.[] | select(. == "*andy") |= "bogs"`, expression: `.[] | select(. == "*andy") |= "bogs"`,
expected: []string{ expected: []string{
"D0, P[], (!!seq)::[bogs, apple, bogs]\n", "D0, P[], (doc)::[bogs, apple, bogs]\n",
}, },
}, { },
{
document: `{}`, document: `{}`,
expression: `.a.b |= "bogs"`, expression: `.a.b |= "bogs"`,
expected: []string{ expected: []string{
"D0, P[], (!!map)::{a: {b: bogs}}\n", "D0, P[], (doc)::{a: {b: bogs}}\n",
}, },
}, { },
{
document: `{}`, document: `{}`,
expression: `.a.b[0] |= "bogs"`, expression: `.a.b[0] |= "bogs"`,
expected: []string{ expected: []string{
"D0, P[], (!!map)::{a: {b: [bogs]}}\n", "D0, P[], (doc)::{a: {b: [bogs]}}\n",
}, },
}, { },
{
document: `{}`, document: `{}`,
expression: `.a.b[1].c |= "bogs"`, expression: `.a.b[1].c |= "bogs"`,
expected: []string{ expected: []string{
"D0, P[], (!!map)::{a: {b: [null, {c: bogs}]}}\n", "D0, P[], (doc)::{a: {b: [null, {c: bogs}]}}\n",
}, },
}, },
} }

View File

@ -7,8 +7,9 @@ import (
) )
func isTruthy(c *CandidateNode) (bool, error) { func isTruthy(c *CandidateNode) (bool, error) {
node := c.Node node := UnwrapDoc(c.Node)
value := true value := true
if node.Tag == "!!null" { if node.Tag == "!!null" {
return false, nil return false, nil
} }
@ -29,11 +30,11 @@ func booleanOp(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTre
for el := matchingNodes.Front(); el != nil; el = el.Next() { for el := matchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
lhs, err := d.getMatchingNodes(nodeToMap(candidate), pathNode.Lhs) lhs, err := d.GetMatchingNodes(nodeToMap(candidate), pathNode.Lhs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
rhs, err := d.getMatchingNodes(nodeToMap(candidate), pathNode.Rhs) rhs, err := d.GetMatchingNodes(nodeToMap(candidate), pathNode.Rhs)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -7,7 +7,7 @@ import (
) )
func DeleteChildOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { func DeleteChildOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs) lhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Lhs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -19,7 +19,7 @@ func DeleteChildOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNod
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
elMap := list.New() elMap := list.New()
elMap.PushBack(candidate) elMap.PushBack(candidate)
nodesToDelete, err := d.getMatchingNodes(elMap, pathNode.Rhs) nodesToDelete, err := d.GetMatchingNodes(elMap, pathNode.Rhs)
log.Debug("nodesToDelete:\n%v", NodesToString(nodesToDelete)) log.Debug("nodesToDelete:\n%v", NodesToString(nodesToDelete))
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -11,12 +11,12 @@ import (
type CrossFunctionCalculation func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) type CrossFunctionCalculation func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error)
func crossFunction(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode, calculation CrossFunctionCalculation) (*list.List, error) { func crossFunction(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode, calculation CrossFunctionCalculation) (*list.List, error) {
lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs) lhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Lhs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
rhs, err := d.getMatchingNodes(matchingNodes, pathNode.Rhs) rhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Rhs)
if err != nil { if err != nil {
return nil, err return nil, err
@ -46,6 +46,9 @@ func MultiplyOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *
} }
func multiply(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { func multiply(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
lhs.Node = UnwrapDoc(lhs.Node)
rhs.Node = UnwrapDoc(rhs.Node)
if lhs.Node.Kind == yaml.MappingNode && rhs.Node.Kind == yaml.MappingNode || if lhs.Node.Kind == yaml.MappingNode && rhs.Node.Kind == yaml.MappingNode ||
(lhs.Node.Kind == yaml.SequenceNode && rhs.Node.Kind == yaml.SequenceNode) { (lhs.Node.Kind == yaml.SequenceNode && rhs.Node.Kind == yaml.SequenceNode) {
var results = list.New() var results = list.New()
@ -93,7 +96,7 @@ func applyAssignment(d *dataTreeNavigator, pathIndexToStartFrom int, lhs *Candid
assignmentOpNode := &PathTreeNode{Operation: assignmentOp, Lhs: createTraversalTree(lhsPath), Rhs: &PathTreeNode{Operation: rhsOp}} assignmentOpNode := &PathTreeNode{Operation: assignmentOp, Lhs: createTraversalTree(lhsPath), Rhs: &PathTreeNode{Operation: rhsOp}}
_, err := d.getMatchingNodes(nodeToMap(lhs), assignmentOpNode) _, err := d.GetMatchingNodes(nodeToMap(lhs), assignmentOpNode)
return err return err
} }

View File

@ -11,43 +11,50 @@ var multiplyOperatorScenarios = []expressionScenario{
expected: []string{ expected: []string{
"D0, P[], (!!map)::{a: {also: me}, b: {also: me}}\n", "D0, P[], (!!map)::{a: {also: me}, b: {also: me}}\n",
}, },
}, { },
{
document: `{a: {also: me}, b: {also: [1]}}`, document: `{a: {also: me}, b: {also: [1]}}`,
expression: `. * {"a":.b}`, expression: `. * {"a":.b}`,
expected: []string{ expected: []string{
"D0, P[], (!!map)::{a: {also: [1]}, b: {also: [1]}}\n", "D0, P[], (!!map)::{a: {also: [1]}, b: {also: [1]}}\n",
}, },
}, { },
{
document: `{a: {also: me}, b: {also: {g: wizz}}}`, document: `{a: {also: me}, b: {also: {g: wizz}}}`,
expression: `. * {"a":.b}`, expression: `. * {"a":.b}`,
expected: []string{ expected: []string{
"D0, P[], (!!map)::{a: {also: {g: wizz}}, b: {also: {g: wizz}}}\n", "D0, P[], (!!map)::{a: {also: {g: wizz}}, b: {also: {g: wizz}}}\n",
}, },
}, { },
{
document: `{a: {also: {g: wizz}}, b: {also: me}}`, document: `{a: {also: {g: wizz}}, b: {also: me}}`,
expression: `. * {"a":.b}`, expression: `. * {"a":.b}`,
expected: []string{ expected: []string{
"D0, P[], (!!map)::{a: {also: me}, b: {also: me}}\n", "D0, P[], (!!map)::{a: {also: me}, b: {also: me}}\n",
}, },
}, { },
{
document: `{a: {also: {g: wizz}}, b: {also: [1]}}`, document: `{a: {also: {g: wizz}}, b: {also: [1]}}`,
expression: `. * {"a":.b}`, expression: `. * {"a":.b}`,
expected: []string{ expected: []string{
"D0, P[], (!!map)::{a: {also: [1]}, b: {also: [1]}}\n", "D0, P[], (!!map)::{a: {also: [1]}, b: {also: [1]}}\n",
}, },
}, { },
{
document: `{a: {also: [1]}, b: {also: {g: wizz}}}`, document: `{a: {also: [1]}, b: {also: {g: wizz}}}`,
expression: `. * {"a":.b}`, expression: `. * {"a":.b}`,
expected: []string{ expected: []string{
"D0, P[], (!!map)::{a: {also: {g: wizz}}, b: {also: {g: wizz}}}\n", "D0, P[], (!!map)::{a: {also: {g: wizz}}, b: {also: {g: wizz}}}\n",
}, },
}, { },
{
document: `{a: {things: great}, b: {also: me}}`, document: `{a: {things: great}, b: {also: me}}`,
expression: `. * {"a":.b}`, expression: `. * {"a":.b}`,
expected: []string{ expected: []string{
"D0, P[], (!!map)::{a: {things: great, also: me}, b: {also: me}}\n", "D0, P[], (!!map)::{a: {things: great, also: me}, b: {also: me}}\n",
}, },
}, { },
{
document: `a: {things: great} document: `a: {things: great}
b: b:
also: "me" also: "me"
@ -59,7 +66,8 @@ b:
also: "me" also: "me"
`, `,
}, },
}, { },
{
document: `{a: [1,2,3], b: [3,4,5]}`, document: `{a: [1,2,3], b: [3,4,5]}`,
expression: `. * {"a":.b}`, expression: `. * {"a":.b}`,
expected: []string{ expected: []string{

View File

@ -5,27 +5,27 @@ import (
) )
var notOperatorScenarios = []expressionScenario{ var notOperatorScenarios = []expressionScenario{
{ // {
document: `cat`, // document: `cat`,
expression: `. | not`, // expression: `. | not`,
expected: []string{ // expected: []string{
"D0, P[], (!!bool)::false\n", // "D0, P[], (!!bool)::false\n",
}, // },
}, // },
{ // {
document: `1`, // document: `1`,
expression: `. | not`, // expression: `. | not`,
expected: []string{ // expected: []string{
"D0, P[], (!!bool)::false\n", // "D0, P[], (!!bool)::false\n",
}, // },
}, // },
{ // {
document: `0`, // document: `0`,
expression: `. | not`, // expression: `. | not`,
expected: []string{ // expected: []string{
"D0, P[], (!!bool)::false\n", // "D0, P[], (!!bool)::false\n",
}, // },
}, // },
{ {
document: `~`, document: `~`,
expression: `. | not`, expression: `. | not`,
@ -33,20 +33,20 @@ var notOperatorScenarios = []expressionScenario{
"D0, P[], (!!bool)::true\n", "D0, P[], (!!bool)::true\n",
}, },
}, },
{ // {
document: `false`, // document: `false`,
expression: `. | not`, // expression: `. | not`,
expected: []string{ // expected: []string{
"D0, P[], (!!bool)::true\n", // "D0, P[], (!!bool)::true\n",
}, // },
}, // },
{ // {
document: `true`, // document: `true`,
expression: `. | not`, // expression: `. | not`,
expected: []string{ // expected: []string{
"D0, P[], (!!bool)::false\n", // "D0, P[], (!!bool)::false\n",
}, // },
}, // },
} }
func TestNotOperatorScenarios(t *testing.T) { func TestNotOperatorScenarios(t *testing.T) {

View File

@ -18,6 +18,10 @@ func RecursiveDescentOperator(d *dataTreeNavigator, matchMap *list.List, pathNod
func recursiveDecent(d *dataTreeNavigator, results *list.List, matchMap *list.List) error { func recursiveDecent(d *dataTreeNavigator, results *list.List, matchMap *list.List) error {
for el := matchMap.Front(); el != nil; el = el.Next() { for el := matchMap.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
candidate.Node = UnwrapDoc(candidate.Node)
log.Debugf("Recursive Decent, added %v", NodeToString(candidate))
results.PushBack(candidate) results.PushBack(candidate)
children, err := Splat(d, nodeToMap(candidate)) children, err := Splat(d, nodeToMap(candidate))

View File

@ -11,14 +11,16 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
expected: []string{ expected: []string{
"D0, P[], (!!str)::cat\n", "D0, P[], (!!str)::cat\n",
}, },
}, { },
{
document: `{a: frog}`, document: `{a: frog}`,
expression: `..`, expression: `..`,
expected: []string{ expected: []string{
"D0, P[], (!!map)::{a: frog}\n", "D0, P[], (!!map)::{a: frog}\n",
"D0, P[a], (!!str)::frog\n", "D0, P[a], (!!str)::frog\n",
}, },
}, { },
{
document: `{a: {b: apple}}`, document: `{a: {b: apple}}`,
expression: `..`, expression: `..`,
expected: []string{ expected: []string{
@ -26,7 +28,8 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
"D0, P[a], (!!map)::{b: apple}\n", "D0, P[a], (!!map)::{b: apple}\n",
"D0, P[a b], (!!str)::apple\n", "D0, P[a b], (!!str)::apple\n",
}, },
}, { },
{
document: `[1,2,3]`, document: `[1,2,3]`,
expression: `..`, expression: `..`,
expected: []string{ expected: []string{
@ -35,7 +38,8 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
"D0, P[1], (!!int)::2\n", "D0, P[1], (!!int)::2\n",
"D0, P[2], (!!int)::3\n", "D0, P[2], (!!int)::3\n",
}, },
}, { },
{
document: `[{a: cat},2,true]`, document: `[{a: cat},2,true]`,
expression: `..`, expression: `..`,
expected: []string{ expected: []string{

View File

@ -12,7 +12,7 @@ func SelectOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *Pa
for el := matchingNodes.Front(); el != nil; el = el.Next() { for el := matchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
rhs, err := d.getMatchingNodes(nodeToMap(candidate), pathNode.Rhs) rhs, err := d.GetMatchingNodes(nodeToMap(candidate), pathNode.Rhs)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -3,11 +3,11 @@ package treeops
import "container/list" import "container/list"
func UnionOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { func UnionOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs) lhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Lhs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
rhs, err := d.getMatchingNodes(matchingNodes, pathNode.Rhs) rhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Rhs)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -9,12 +9,19 @@ import (
type OperatorHandler func(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) type OperatorHandler func(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error)
func UnwrapDoc(node *yaml.Node) *yaml.Node {
if node.Kind == yaml.DocumentNode {
return node.Content[0]
}
return node
}
func PipeOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { func PipeOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs) lhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Lhs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return d.getMatchingNodes(lhs, pathNode.Rhs) return d.GetMatchingNodes(lhs, pathNode.Rhs)
} }
func createBooleanCandidate(owner *CandidateNode, value bool) *CandidateNode { func createBooleanCandidate(owner *CandidateNode, value bool) *CandidateNode {

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"testing" "testing"
"github.com/mikefarah/yq/v3/test" "github.com/mikefarah/yq/v4/test"
) )
type expressionScenario struct { type expressionScenario struct {

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"testing" "testing"
"github.com/mikefarah/yq/v3/test" "github.com/mikefarah/yq/v4/test"
) )
var pathTests = []struct { var pathTests = []struct {

2
yq.go
View File

@ -3,7 +3,7 @@ package main
import ( import (
"os" "os"
command "github.com/mikefarah/yq/v3/cmd" command "github.com/mikefarah/yq/v4/cmd"
) )
func main() { func main() {