diff --git a/cmd/version.go b/cmd/version.go index 6f2d6b52..cec80ec2 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -11,7 +11,7 @@ var ( GitDescribe string // Version is main version number that is being run at the moment. - Version = "4.2.0" + Version = "4.2.1" // 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 diff --git a/github-action/Dockerfile b/github-action/Dockerfile index d3839f8b..0b3bcecd 100644 --- a/github-action/Dockerfile +++ b/github-action/Dockerfile @@ -1,4 +1,4 @@ -FROM mikefarah/yq:4.2.0 +FROM mikefarah/yq:4.2.1 COPY entrypoint.sh /entrypoint.sh diff --git a/pkg/yqlib/candidate_node.go b/pkg/yqlib/candidate_node.go index f9f9e0cc..dd34a811 100644 --- a/pkg/yqlib/candidate_node.go +++ b/pkg/yqlib/candidate_node.go @@ -40,6 +40,7 @@ func (n *CandidateNode) Copy() (*CandidateNode, error) { // updates this candidate from the given candidate node func (n *CandidateNode) UpdateFrom(other *CandidateNode) { + n.UpdateAttributesFrom(other) n.Node.Content = other.Node.Content n.Node.Value = other.Node.Value diff --git a/pkg/yqlib/doc/Assign.md b/pkg/yqlib/doc/Assign.md index faba7f67..a0b7ce5f 100644 --- a/pkg/yqlib/doc/Assign.md +++ b/pkg/yqlib/doc/Assign.md @@ -34,6 +34,27 @@ a: g: foof ``` +## Update node from another file +Note this will also work when the second file is a scalar (string/number) + +Given a sample.yml file of: +```yaml +a: apples +``` +And another sample another.yml file of: +```yaml +b: bob +``` +then +```bash +yq eval-all 'select(fileIndex==0).a = select(fileIndex==1) | select(fileIndex==0)' sample.yml another.yml +``` +will output +```yaml +a: + b: bob +``` + ## Update node to be the sibling value Given a sample.yml file of: ```yaml diff --git a/pkg/yqlib/doc/File Operators.md b/pkg/yqlib/doc/File Operators.md index 946dfee9..c7182ee5 100644 --- a/pkg/yqlib/doc/File Operators.md +++ b/pkg/yqlib/doc/File Operators.md @@ -16,7 +16,7 @@ yq eval 'filename' sample.yml ``` will output ```yaml -sample.yaml +sample.yml ``` ## Get file index diff --git a/pkg/yqlib/operator_assign.go b/pkg/yqlib/operator_assign.go index 82c7db04..f98e232f 100644 --- a/pkg/yqlib/operator_assign.go +++ b/pkg/yqlib/operator_assign.go @@ -33,7 +33,9 @@ func AssignUpdateOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNo first := rhs.Front() if first != nil { - candidate.UpdateFrom(first.Value.(*CandidateNode)) + rhsCandidate := first.Value.(*CandidateNode) + rhsCandidate.Node = UnwrapDoc(rhsCandidate.Node) + candidate.UpdateFrom(rhsCandidate) } } // // if there was nothing given, perhaps we are creating a new yaml doc diff --git a/pkg/yqlib/operator_assign_test.go b/pkg/yqlib/operator_assign_test.go index f3acf2f0..dc397c86 100644 --- a/pkg/yqlib/operator_assign_test.go +++ b/pkg/yqlib/operator_assign_test.go @@ -20,6 +20,16 @@ var assignOperatorScenarios = []expressionScenario{ "D0, P[], (doc)::{a: {g: foof}}\n", }, }, + { + description: "Update node from another file", + subdescription: "Note this will also work when the second file is a scalar (string/number)", + document: `{a: apples}`, + document2: "{b: bob}", + expression: `select(fileIndex==0).a = select(fileIndex==1) | select(fileIndex==0)`, + expected: []string{ + "D0, P[], (doc)::{a: {b: bob}}\n", + }, + }, { description: "Update node to be the sibling value", document: `{a: {b: child}, b: sibling}`, diff --git a/pkg/yqlib/operators_test.go b/pkg/yqlib/operators_test.go index 95047a05..46565916 100644 --- a/pkg/yqlib/operators_test.go +++ b/pkg/yqlib/operators_test.go @@ -18,6 +18,7 @@ type expressionScenario struct { description string subdescription string document string + document2 string expression string expected []string skipDoc bool @@ -41,6 +42,14 @@ func testScenario(t *testing.T, s *expressionScenario) { t.Error(err, s.document, s.expression) return } + if s.document2 != "" { + moreInputs, err := readDocuments(strings.NewReader(s.document2), "another.yml", 1) + if err != nil { + t.Error(err, s.document, s.expression) + return + } + inputs.PushBackList(moreInputs) + } } else { candidateNode := &CandidateNode{ Document: 0, @@ -92,7 +101,7 @@ func copyFromHeader(title string, out *os.File) error { return err } -func formatYaml(yaml string) string { +func formatYaml(yaml string, filename string) string { var output bytes.Buffer printer := NewPrinter(bufio.NewWriter(&output), false, true, false, 2, true) @@ -101,7 +110,7 @@ func formatYaml(yaml string) string { panic(err) } streamEvaluator := NewStreamEvaluator() - err = streamEvaluator.Evaluate("sample.yaml", strings.NewReader(yaml), node, printer) + err = streamEvaluator.Evaluate(filename, strings.NewReader(yaml), node, printer) if err != nil { panic(err) } @@ -136,25 +145,42 @@ func documentScenarios(t *testing.T, title string, scenarios []expressionScenari writeOrPanic(w, "\n\n") } formattedDoc := "" + formattedDoc2 := "" + command := "eval" if s.document != "" { if s.dontFormatInputForDoc { formattedDoc = s.document + "\n" } else { - formattedDoc = formatYaml(s.document) + formattedDoc = formatYaml(s.document, "sample.yml") } - //TODO: pretty here - writeOrPanic(w, "Given a sample.yml file of:\n") + writeOrPanic(w, "Given a sample.yml file of:\n") writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n", formattedDoc)) + + files := "sample.yml" + + if s.document2 != "" { + if s.dontFormatInputForDoc { + formattedDoc2 = s.document2 + "\n" + } else { + formattedDoc2 = formatYaml(s.document2, "another.yml") + } + + writeOrPanic(w, "And another sample another.yml file of:\n") + writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n", formattedDoc2)) + files = "sample.yml another.yml" + command = "eval-all" + } + writeOrPanic(w, "then\n") if s.expression != "" { - writeOrPanic(w, fmt.Sprintf("```bash\nyq eval '%v' sample.yml\n```\n", s.expression)) + writeOrPanic(w, fmt.Sprintf("```bash\nyq %v '%v' %v\n```\n", command, s.expression, files)) } else { - writeOrPanic(w, "```bash\nyq eval sample.yml\n```\n") + writeOrPanic(w, fmt.Sprintf("```bash\nyq %v %v\n```\n", command, files)) } } else { writeOrPanic(w, "Running\n") - writeOrPanic(w, fmt.Sprintf("```bash\nyq eval --null-input '%v'\n```\n", s.expression)) + writeOrPanic(w, fmt.Sprintf("```bash\nyq %v --null-input '%v'\n```\n", command, s.expression)) } writeOrPanic(w, "will output\n") @@ -162,23 +188,48 @@ func documentScenarios(t *testing.T, title string, scenarios []expressionScenari var output bytes.Buffer var err error printer := NewPrinter(bufio.NewWriter(&output), false, true, false, 2, true) - streamEvaluator := NewStreamEvaluator() + + node, err := treeCreator.ParsePath(s.expression) + if err != nil { + t.Error(fmt.Errorf("Error parsing expression %v of %v: %v", s.expression, s.description, err)) + return + } + + inputs := list.New() if s.document != "" { - node, err := treeCreator.ParsePath(s.expression) + inputs, err = readDocuments(strings.NewReader(formattedDoc), "sample.yml", 0) if err != nil { - t.Error(err, s.expression) + t.Error(err, s.document, s.expression) + return } - err = streamEvaluator.Evaluate("sample.yaml", strings.NewReader(formattedDoc), node, printer) - - if err != nil { - t.Error(err, s.expression) + if s.document2 != "" { + moreInputs, err := readDocuments(strings.NewReader(formattedDoc2), "another.yml", 1) + if err != nil { + t.Error(err, s.document, s.expression) + return + } + inputs.PushBackList(moreInputs) } } else { - err = streamEvaluator.EvaluateNew(s.expression, printer) - if err != nil { - t.Error(err, s.expression) + candidateNode := &CandidateNode{ + Document: 0, + Filename: "", + Node: &yaml.Node{Tag: "!!null"}, + FileIndex: 0, } + inputs.PushBack(candidateNode) + + } + + results, err := treeNavigator.GetMatchingNodes(inputs, node) + if err != nil { + t.Error(err, s.expression) + } + + err = printer.PrintResults(results) + if err != nil { + t.Error(err, s.expression) } writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n", output.String())) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 569bfb76..e0f36c92 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,5 +1,5 @@ name: yq -version: '4.2.0' +version: '4.2.1' summary: A lightweight and portable command-line YAML processor description: | The aim of the project is to be the jq or sed of yaml files.