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

Compare commits

...

8 Commits

Author SHA1 Message Date
Mike Farah
2f9f8665dd bolden contribute disclaimer 2020-01-13 10:21:28 +11:00
Conor Nosal
3dea1efc03 Allow merge commands to specify overwrite and append simultaneously 2020-01-13 10:17:29 +11:00
Ryan SIU
547787f3ae #314 Fix the big int changing into float 2020-01-13 10:17:08 +11:00
Ryan SIU
3e83ff7ac8 #20 Read the top level keys only 2020-01-13 10:16:53 +11:00
Mike Farah
f18c5161e0 Fixed sponge instructions 2020-01-11 16:01:32 +11:00
Mike Farah
eeeeeffd7b Added note about v3 2020-01-11 11:44:02 +11:00
Mike Farah
27604289f4 Log out original error if removing temporary file failed 2019-12-23 12:09:38 +11:00
Mike Farah
3f36a18791 Add readme notice re v3 2019-12-23 10:01:55 +11:00
9 changed files with 125 additions and 12 deletions

View File

@@ -7,7 +7,21 @@ a lightweight and portable command-line YAML processor
The aim of the project is to be the [jq](https://github.com/stedolan/jq) or sed of yaml files. The aim of the project is to be the [jq](https://github.com/stedolan/jq) or sed of yaml files.
## Major upgrade - V3 beta is out!
This addresses a number of features requests and issues that have been raised :)
Currently only available only available as a [binary release here](https://github.com/mikefarah/yq/releases/tag/3.0.0-beta) or via docker mikefarah/yq:3.0.0-beta!
It does have a few breaking changes listed on the [release page](https://github.com/mikefarah/yq/releases/tag/3.0.0-beta)
Looking forward to feedback - once this is out of beta it will be added to the remaining package managers, and be the default version downloaded (and merged into master).
V2 will no longer have any new features added, and will be moved to a branch (v2). It will have limited maintenance for bugs for a few months.
## Install ## Install
### On MacOS: ### On MacOS:
``` ```
brew install yq brew install yq
@@ -21,16 +35,16 @@ snap install yq
`yq` installs with with [_strict confinement_](https://docs.snapcraft.io/snap-confinement/6233) in snap, this means it doesn't have direct access to root files. To read root files you can: `yq` installs with with [_strict confinement_](https://docs.snapcraft.io/snap-confinement/6233) in snap, this means it doesn't have direct access to root files. To read root files you can:
``` ```
sudo cat /etc/myfile | yq -r - somecommand sudo cat /etc/myfile | yq r - a.path
``` ```
And to write to a root file you can either use [sponge](https://linux.die.net/man/1/sponge): And to write to a root file you can either use [sponge](https://linux.die.net/man/1/sponge):
``` ```
sudo cat /etc/myfile | yq -r - somecommand | sudo sponge /etc/myfile sudo cat /etc/myfile | yq w - a.path value | sudo sponge /etc/myfile
``` ```
or write to a temporary file: or write to a temporary file:
``` ```
sudo cat /etc/myfile | yq -r - somecommand | sudo tee /etc/myfile.tmp sudo cat /etc/myfile | yq w - a.path value | sudo tee /etc/myfile.tmp
sudo mv /etc/myfile.tmp /etc/myfile sudo mv /etc/myfile.tmp /etc/myfile
rm /etc/myfile.tmp rm /etc/myfile.tmp
``` ```
@@ -115,6 +129,9 @@ Use "yq [command] --help" for more information about a command.
``` ```
## Contribute ## Contribute
**Note: v3 is currently in progress - for the moment I won't be accepting new feature PRs until v3 is ready :)**
1. `scripts/devtools.sh` 1. `scripts/devtools.sh`
2. `make [local] vendor` 2. `make [local] vendor`
3. add unit tests 3. add unit tests

View File

@@ -1091,6 +1091,25 @@ c:
` `
test.AssertResult(t, expectedOutput, result.Output) test.AssertResult(t, expectedOutput, result.Output)
} }
func TestMergeOverwriteAndAppendCmd(t *testing.T) {
cmd := getRootCommand()
result := test.RunCmd(cmd, "merge --append --overwrite examples/data1.yaml examples/data2.yaml")
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `a: other
b:
- 1
- 2
- 3
- 4
c:
test: 1
`
test.AssertResult(t, expectedOutput, result.Output)
}
func TestMergeArraysCmd(t *testing.T) { func TestMergeArraysCmd(t *testing.T) {
cmd := getRootCommand() cmd := getRootCommand()
result := test.RunCmd(cmd, "merge --append examples/sample_array.yaml examples/sample_array_2.yaml") result := test.RunCmd(cmd, "merge --append examples/sample_array.yaml examples/sample_array_2.yaml")

View File

@@ -58,11 +58,13 @@ func (l *lib) DeletePath(dataBucket interface{}, path string) (interface{}, erro
return l.navigator.DeleteChildValue(dataBucket, paths) return l.navigator.DeleteChildValue(dataBucket, paths)
} }
func (l *lib) Merge(dst interface{}, src interface{}, overwrite bool, append bool) error { func (l *lib) Merge(dst interface{}, src interface{}, overwriteFlag bool, appendFlag bool) error {
if overwrite { opts := []func(*mergo.Config){}
return mergo.Merge(dst, src, mergo.WithOverride) if overwriteFlag {
} else if append { opts = append(opts, mergo.WithOverride)
return mergo.Merge(dst, src, mergo.WithAppendSlice)
} }
return mergo.Merge(dst, src) if appendFlag {
opts = append(opts, mergo.WithAppendSlice)
}
return mergo.Merge(dst, src, opts...)
} }

View File

@@ -156,6 +156,23 @@ b: 2
test.AssertResult(t, `[{a b} {c d} {a 1} {b 2}]`, fmt.Sprintf("%v", mergedData["root"])) test.AssertResult(t, `[{a b} {c d} {a 1} {b 2}]`, fmt.Sprintf("%v", mergedData["root"]))
}) })
t.Run("TestMerge_WithAppendAndOverwrite", func(t *testing.T) {
var dst = map[interface{}]interface{}{
"a": "initial",
"b": []string{"old"},
}
var src = map[interface{}]interface{}{
"a": "replaced",
"b": []string{"new"},
}
err := subject.Merge(&dst, src, true, true)
if err != nil {
t.Fatal("Unexpected error")
}
test.AssertResult(t, `map[a:replaced b:[old new]]`, fmt.Sprintf("%v", dst))
})
t.Run("TestMerge_WithError", func(t *testing.T) { t.Run("TestMerge_WithError", func(t *testing.T) {
err := subject.Merge(nil, nil, false, false) err := subject.Merge(nil, nil, false, false)
if err == nil { if err == nil {

View File

@@ -18,9 +18,16 @@ func (v *valueParser) ParseValue(argument string) interface{} {
var value, err interface{} var value, err interface{}
var inQuotes = len(argument) > 0 && argument[0] == '"' var inQuotes = len(argument) > 0 && argument[0] == '"'
if !inQuotes { if !inQuotes {
value, err = strconv.ParseFloat(argument, 64) intValue, intErr := strconv.ParseInt(argument, 10, 64)
if err == nil { floatValue, floatErr := strconv.ParseFloat(argument, 64)
return value if intErr == nil && floatErr == nil {
if int64(floatValue) == intValue {
return intValue
}
return floatValue
} else if floatErr == nil {
// In case cannot parse the int due to large precision
return floatValue
} }
value, err = strconv.ParseBool(argument) value, err = strconv.ParseBool(argument)
if err == nil { if err == nil {

View File

@@ -16,6 +16,7 @@ var parseValueTests = []struct {
{"3.4", 3.4, "number"}, {"3.4", 3.4, "number"},
{"\"3.4\"", "3.4", "number as string"}, {"\"3.4\"", "3.4", "number as string"},
{"", "", "empty string"}, {"", "", "empty string"},
{"1212121", int64(1212121), "big number"},
} }
func TestParseValue(t *testing.T) { func TestParseValue(t *testing.T) {

View File

@@ -0,0 +1,6 @@
a:
b:
c: 1
d:
e: 2
f:

10
yq.go
View File

@@ -25,6 +25,7 @@ var writeInplace = false
var writeScript = "" var writeScript = ""
var outputToJSON = false var outputToJSON = false
var overwriteFlag = false var overwriteFlag = false
var keyOnlyFlag = false
var allowEmptyFlag = false var allowEmptyFlag = false
var appendFlag = false var appendFlag = false
var verbose = false var verbose = false
@@ -111,6 +112,7 @@ yq r -- things.yaml --key-starting-with-dashes
} }
cmdRead.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") cmdRead.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)")
cmdRead.PersistentFlags().BoolVarP(&outputToJSON, "tojson", "j", false, "output as json") cmdRead.PersistentFlags().BoolVarP(&outputToJSON, "tojson", "j", false, "output as json")
cmdRead.PersistentFlags().BoolVarP(&keyOnlyFlag, "keyonly", "k", false, "output with top level keys only")
return cmdRead return cmdRead
} }
@@ -304,6 +306,13 @@ func readProperty(cmd *cobra.Command, args []string) error {
dataBucket = mappedDocs dataBucket = mappedDocs
} }
if keyOnlyFlag {
for _, value := range dataBucket.(yaml.MapSlice) {
cmd.Println(value.Key)
}
return nil
}
dataStr, err := toString(dataBucket) dataStr, err := toString(dataBucket)
if err != nil { if err != nil {
return err return err
@@ -583,6 +592,7 @@ func safelyRenameFile(from string, to string) {
removeErr := os.Remove(from) removeErr := os.Remove(from)
if removeErr != nil { if removeErr != nil {
log.Errorf("failed removing original file: %s", from) log.Errorf("failed removing original file: %s", from)
log.Error(removeErr.Error())
} }
} }
} }

View File

@@ -1,12 +1,14 @@
package main package main
import ( import (
"bytes"
"fmt" "fmt"
"runtime" "runtime"
"testing" "testing"
"github.com/mikefarah/yq/v2/pkg/marshal" "github.com/mikefarah/yq/v2/pkg/marshal"
"github.com/mikefarah/yq/v2/test" "github.com/mikefarah/yq/v2/test"
"github.com/spf13/cobra"
) )
func TestMultilineString(t *testing.T) { func TestMultilineString(t *testing.T) {
@@ -33,6 +35,14 @@ func TestNewYamlArray(t *testing.T) {
formattedResult) formattedResult)
} }
func TestNewYamlBigInt(t *testing.T) {
result, _ := newYaml([]string{"b", "1212121"})
formattedResult := fmt.Sprintf("%v", result)
test.AssertResult(t,
"[{b 1212121}]",
formattedResult)
}
func TestNewYaml_WithScript(t *testing.T) { func TestNewYaml_WithScript(t *testing.T) {
writeScript = "examples/instruction_sample.yaml" writeScript = "examples/instruction_sample.yaml"
expectedResult := `b: expectedResult := `b:
@@ -58,3 +68,27 @@ func TestNewYaml_WithUnknownScript(t *testing.T) {
} }
test.AssertResult(t, expectedOutput, err.Error()) test.AssertResult(t, expectedOutput, err.Error())
} }
func TestReadWithKeyOnly(t *testing.T) {
readCmd := createReadCmd()
expectedResult := `b
d
f
`
actualResult, err := executeTestCommand(readCmd, "test/fixture/keyonly.yaml", "a", "-k")
if err != nil {
t.Error(err.Error())
}
test.AssertResult(t, expectedResult, actualResult)
}
func executeTestCommand(command *cobra.Command, args ...string) (output string, err error) {
buf := new(bytes.Buffer)
command.SetOutput(buf)
command.SetArgs(args)
_, err = command.ExecuteC()
return buf.String(), err
}