mirror of
https://github.com/taigrr/yq
synced 2025-01-18 04:53:17 -08:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15f51d4bf0 | ||
|
|
219105f999 | ||
|
|
955ecc2547 | ||
|
|
1d5fbd5ad0 | ||
|
|
1605938c2c | ||
|
|
2780903b60 | ||
|
|
81672d4efe | ||
|
|
6a696c81db | ||
|
|
805729e4f7 | ||
|
|
7db31006da | ||
|
|
8409be1cdf | ||
|
|
c03c4813d4 | ||
|
|
5ee6a9b9ca | ||
|
|
8efc46deae | ||
|
|
e4d5769f29 | ||
|
|
fea6e0db3d | ||
|
|
6823d43325 | ||
|
|
da4b1a6449 |
105
README.md
105
README.md
@@ -1,7 +1,7 @@
|
|||||||
# yaml
|
# yaml
|
||||||
yaml command line tool written in go
|
yaml is portable command line tool written in go
|
||||||
|
|
||||||
Allows you to read (and soon update) yaml files given a yaml path. All in a lovely dependency free binary!
|
Allows you to read and update yaml files from bash (or whatever). All in a lovely dependency free binary!
|
||||||
|
|
||||||
[Download latest release](https://github.com/mikefarah/yaml/releases/latest)
|
[Download latest release](https://github.com/mikefarah/yaml/releases/latest)
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ go get github.com/mikefarah/yaml
|
|||||||
|
|
||||||
## Read examples
|
## Read examples
|
||||||
```
|
```
|
||||||
yaml <yaml file> <path>
|
yaml r <yaml file> <path>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Basic
|
### Basic
|
||||||
@@ -23,10 +23,37 @@ b:
|
|||||||
```
|
```
|
||||||
then
|
then
|
||||||
```bash
|
```bash
|
||||||
yaml sample.yaml b.c
|
yaml r sample.yaml b.c
|
||||||
```
|
```
|
||||||
will output the value of '2'.
|
will output the value of '2'.
|
||||||
|
|
||||||
|
### Reading from STDIN
|
||||||
|
Given a sample.yaml file of:
|
||||||
|
```bash
|
||||||
|
cat sample.yaml | yaml r - b.c
|
||||||
|
```
|
||||||
|
will output the value of '2'.
|
||||||
|
|
||||||
|
### Splat
|
||||||
|
Given a sample.yaml file of:
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
bob:
|
||||||
|
item1:
|
||||||
|
cats: bananas
|
||||||
|
item2:
|
||||||
|
cats: apples
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yaml r sample.yaml bob.*.cats
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
- bananas
|
||||||
|
- apples
|
||||||
|
```
|
||||||
|
|
||||||
### Handling '.' in the yaml key
|
### Handling '.' in the yaml key
|
||||||
Given a sample.yaml file of:
|
Given a sample.yaml file of:
|
||||||
```yaml
|
```yaml
|
||||||
@@ -35,7 +62,7 @@ b.x:
|
|||||||
```
|
```
|
||||||
then
|
then
|
||||||
```bash
|
```bash
|
||||||
yaml sample.yaml \"b.x\".c
|
yaml r sample.yaml \"b.x\".c
|
||||||
```
|
```
|
||||||
will output the value of '2'.
|
will output the value of '2'.
|
||||||
|
|
||||||
@@ -52,11 +79,33 @@ b:
|
|||||||
```
|
```
|
||||||
then
|
then
|
||||||
```
|
```
|
||||||
yaml sample.yaml b.e[1].name
|
yaml r sample.yaml b.e[1].name
|
||||||
```
|
```
|
||||||
will output 'sam'
|
will output 'sam'
|
||||||
|
|
||||||
### Updating yaml
|
### Array Splat
|
||||||
|
e.g.: given a sample file of
|
||||||
|
```yaml
|
||||||
|
b:
|
||||||
|
e:
|
||||||
|
- name: fred
|
||||||
|
value: 3
|
||||||
|
- name: sam
|
||||||
|
value: 4
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```
|
||||||
|
yaml r sample.yaml b.e[*].name
|
||||||
|
```
|
||||||
|
will output:
|
||||||
|
```
|
||||||
|
- fred
|
||||||
|
- sam
|
||||||
|
```
|
||||||
|
|
||||||
|
## Update examples
|
||||||
|
|
||||||
|
### Update to stdout
|
||||||
Given a sample.yaml file of:
|
Given a sample.yaml file of:
|
||||||
```yaml
|
```yaml
|
||||||
b:
|
b:
|
||||||
@@ -64,10 +113,50 @@ b:
|
|||||||
```
|
```
|
||||||
then
|
then
|
||||||
```bash
|
```bash
|
||||||
yaml w sample.yaml b.c 'cat'
|
yaml w sample.yaml b.c cat
|
||||||
```
|
```
|
||||||
will output:
|
will output:
|
||||||
```yaml
|
```yaml
|
||||||
b:
|
b:
|
||||||
c: cat
|
c: cat
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Updating yaml in-place
|
||||||
|
Given a sample.yaml file of:
|
||||||
|
```yaml
|
||||||
|
b:
|
||||||
|
c: 2
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yaml w -i sample.yaml b.c cat
|
||||||
|
```
|
||||||
|
will update the sample.yaml file so that the value of 'c' is cat.
|
||||||
|
|
||||||
|
|
||||||
|
### Updating multiple values with a script
|
||||||
|
Given a sample.yaml file of:
|
||||||
|
```yaml
|
||||||
|
b:
|
||||||
|
c: 2
|
||||||
|
e:
|
||||||
|
- name: Billy Bob
|
||||||
|
```
|
||||||
|
and a script update_instructions.yaml of:
|
||||||
|
```yaml
|
||||||
|
b.c: 3
|
||||||
|
b.e[0].name: Howdy Partner
|
||||||
|
```
|
||||||
|
then
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yaml -w -s update_instructions.yaml sample.yaml
|
||||||
|
```
|
||||||
|
will output:
|
||||||
|
```yaml
|
||||||
|
b:
|
||||||
|
c: 3
|
||||||
|
e:
|
||||||
|
- name: Howdy Partner
|
||||||
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -2,38 +2,58 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
// "fmt"
|
// "fmt"
|
||||||
"log"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
func write(context map[interface{}]interface{}, head string, tail []string, value interface{}) {
|
func write(context map[interface{}]interface{}, head string, tail []string, value interface{}) {
|
||||||
|
if len(tail) == 0 {
|
||||||
|
context[head] = value
|
||||||
|
} else {
|
||||||
// e.g. if updating a.b.c, we need to get the 'b' map...
|
// e.g. if updating a.b.c, we need to get the 'b' map...
|
||||||
toUpdate := readMap(context, head, tail[0:len(tail)-1]).(map[interface{}]interface{})
|
toUpdate := readMap(context, head, tail[0:len(tail)-1]).(map[interface{}]interface{})
|
||||||
// and then set the 'c' key.
|
// and then set the 'c' key.
|
||||||
key := (tail[len(tail)-1])
|
key := (tail[len(tail)-1])
|
||||||
toUpdate[key] = value
|
toUpdate[key] = value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func readMap(context map[interface{}]interface{}, head string, tail []string) interface{} {
|
func readMap(context map[interface{}]interface{}, head string, tail []string) interface{} {
|
||||||
value := context[head]
|
if head == "*" {
|
||||||
if len(tail) > 0 {
|
return readMapSplat(context, tail)
|
||||||
return recurse(value, tail[0], tail[1:len(tail)])
|
|
||||||
}
|
}
|
||||||
return value
|
value := context[head]
|
||||||
|
return calculateValue(value, tail)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readMapSplat(context map[interface{}]interface{}, tail []string) interface{} {
|
||||||
|
var newArray = make([]interface{}, len(context))
|
||||||
|
var i = 0
|
||||||
|
for _, value := range context {
|
||||||
|
if len(tail) > 0 {
|
||||||
|
newArray[i] = recurse(value, tail[0], tail[1:len(tail)])
|
||||||
|
} else {
|
||||||
|
newArray[i] = value
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return newArray
|
||||||
}
|
}
|
||||||
|
|
||||||
func recurse(value interface{}, head string, tail []string) interface{} {
|
func recurse(value interface{}, head string, tail []string) interface{} {
|
||||||
switch value.(type) {
|
switch value.(type) {
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
|
if head == "*" {
|
||||||
|
return readArraySplat(value.([]interface{}), tail)
|
||||||
|
}
|
||||||
index, err := strconv.ParseInt(head, 10, 64)
|
index, err := strconv.ParseInt(head, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error accessing array: %v", err)
|
die("Error accessing array: %v", err)
|
||||||
}
|
}
|
||||||
return readArray(value.([]interface{}), index, tail)
|
return readArray(value.([]interface{}), index, tail)
|
||||||
case nil:
|
case map[interface{}]interface{}:
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
return readMap(value.(map[interface{}]interface{}), head, tail)
|
return readMap(value.(map[interface{}]interface{}), head, tail)
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,6 +63,19 @@ func readArray(array []interface{}, head int64, tail []string) interface{} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
value := array[head]
|
value := array[head]
|
||||||
|
|
||||||
|
return calculateValue(value, tail)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readArraySplat(array []interface{}, tail []string) interface{} {
|
||||||
|
var newArray = make([]interface{}, len(array))
|
||||||
|
for index, value := range array {
|
||||||
|
newArray[index] = calculateValue(value, tail)
|
||||||
|
}
|
||||||
|
return newArray
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateValue(value interface{}, tail []string) interface{} {
|
||||||
if len(tail) > 0 {
|
if len(tail) > 0 {
|
||||||
return recurse(value, tail[0], tail[1:len(tail)])
|
return recurse(value, tail[0], tail[1:len(tail)])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,59 +4,141 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
"os"
|
"os"
|
||||||
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var rawData = `
|
func parseData(rawData string) map[interface{}]interface{} {
|
||||||
a: Easy!
|
var parsedData map[interface{}]interface{}
|
||||||
b:
|
|
||||||
c: 2
|
|
||||||
d: [3, 4]
|
|
||||||
`
|
|
||||||
|
|
||||||
var parsedData map[interface{}]interface{}
|
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
|
||||||
err := yaml.Unmarshal([]byte(rawData), &parsedData)
|
err := yaml.Unmarshal([]byte(rawData), &parsedData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error parsing yaml: %v", err)
|
fmt.Println("Error parsing yaml: %v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
return parsedData
|
||||||
os.Exit(m.Run())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadMap_simple(t *testing.T) {
|
func TestReadMap_simple(t *testing.T) {
|
||||||
assertResult(t, 2, readMap(parsedData, "b", []string{"c"}))
|
var data = parseData(`
|
||||||
|
---
|
||||||
|
b:
|
||||||
|
c: 2
|
||||||
|
`)
|
||||||
|
assertResult(t, 2, readMap(data, "b", []string{"c"}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadMap_splat(t *testing.T) {
|
||||||
|
var data = parseData(`
|
||||||
|
---
|
||||||
|
mapSplat:
|
||||||
|
item1: things
|
||||||
|
item2: whatever
|
||||||
|
`)
|
||||||
|
var result = readMap(data, "mapSplat", []string{"*"}).([]interface{})
|
||||||
|
var actual = []string{result[0].(string), result[1].(string)}
|
||||||
|
sort.Strings(actual)
|
||||||
|
assertResult(t, "[things whatever]", fmt.Sprintf("%v", actual))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadMap_deep_splat(t *testing.T) {
|
||||||
|
var data = parseData(`
|
||||||
|
---
|
||||||
|
mapSplatDeep:
|
||||||
|
item1:
|
||||||
|
cats: bananas
|
||||||
|
item2:
|
||||||
|
cats: apples
|
||||||
|
`)
|
||||||
|
|
||||||
|
var result = readMap(data, "mapSplatDeep", []string{"*", "cats"}).([]interface{})
|
||||||
|
var actual = []string{result[0].(string), result[1].(string)}
|
||||||
|
sort.Strings(actual)
|
||||||
|
assertResult(t, "[apples bananas]", fmt.Sprintf("%v", actual))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadMap_key_doesnt_exist(t *testing.T) {
|
func TestReadMap_key_doesnt_exist(t *testing.T) {
|
||||||
assertResult(t, nil, readMap(parsedData, "b.x.f", []string{"c"}))
|
var data = parseData(`
|
||||||
|
---
|
||||||
|
b:
|
||||||
|
c: 2
|
||||||
|
`)
|
||||||
|
assertResult(t, nil, readMap(data, "b.x.f", []string{"c"}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadMap_recurse_against_string(t *testing.T) {
|
||||||
|
var data = parseData(`
|
||||||
|
---
|
||||||
|
a: cat
|
||||||
|
`)
|
||||||
|
assertResult(t, nil, readMap(data, "a", []string{"b"}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadMap_with_array(t *testing.T) {
|
func TestReadMap_with_array(t *testing.T) {
|
||||||
assertResult(t, 4, readMap(parsedData, "b", []string{"d", "1"}))
|
var data = parseData(`
|
||||||
|
---
|
||||||
|
b:
|
||||||
|
d:
|
||||||
|
- 3
|
||||||
|
- 4
|
||||||
|
`)
|
||||||
|
assertResult(t, 4, readMap(data, "b", []string{"d", "1"}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadMap_with_array_out_of_bounds(t *testing.T) {
|
func TestReadMap_with_array_out_of_bounds(t *testing.T) {
|
||||||
assertResult(t, nil, readMap(parsedData, "b", []string{"d", "3"}))
|
var data = parseData(`
|
||||||
|
---
|
||||||
|
b:
|
||||||
|
d:
|
||||||
|
- 3
|
||||||
|
- 4
|
||||||
|
`)
|
||||||
|
assertResult(t, nil, readMap(data, "b", []string{"d", "3"}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadMap_with_array_splat(t *testing.T) {
|
||||||
|
var data = parseData(`
|
||||||
|
e:
|
||||||
|
-
|
||||||
|
name: Fred
|
||||||
|
thing: cat
|
||||||
|
-
|
||||||
|
name: Sam
|
||||||
|
thing: dog
|
||||||
|
`)
|
||||||
|
assertResult(t, "[Fred Sam]", fmt.Sprintf("%v", readMap(data, "e", []string{"*", "name"})))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWrite_simple(t *testing.T) {
|
func TestWrite_simple(t *testing.T) {
|
||||||
|
var data = parseData(`
|
||||||
|
b:
|
||||||
|
c: 2
|
||||||
|
`)
|
||||||
|
|
||||||
write(parsedData, "b", []string{"c"}, "4")
|
write(data, "b", []string{"c"}, "4")
|
||||||
|
|
||||||
b := parsedData["b"].(map[interface{}]interface{})
|
b := data["b"].(map[interface{}]interface{})
|
||||||
assertResult(t, "4", b["c"].(string))
|
assertResult(t, "4", b["c"].(string))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWrite_with_no_tail(t *testing.T) {
|
||||||
|
var data = parseData(`
|
||||||
|
b:
|
||||||
|
c: 2
|
||||||
|
`)
|
||||||
|
write(data, "b", []string{}, "4")
|
||||||
|
|
||||||
|
b := data["b"]
|
||||||
|
assertResult(t, "4", fmt.Sprintf("%v", b))
|
||||||
|
}
|
||||||
|
|
||||||
func assertResult(t *testing.T, expectedValue interface{}, actualValue interface{}) {
|
func assertResult(t *testing.T, expectedValue interface{}, actualValue interface{}) {
|
||||||
if expectedValue != actualValue {
|
if expectedValue != actualValue {
|
||||||
t.Error("Expected <", expectedValue, "> but got <", actualValue, ">")
|
t.Error("Expected <", expectedValue, "> but got <", actualValue, ">", fmt.Sprintf("%T", actualValue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertResultWithContext(t *testing.T, expectedValue interface{}, actualValue interface{}, context interface{}) {
|
func assertResultWithContext(t *testing.T, expectedValue interface{}, actualValue interface{}, context interface{}) {
|
||||||
|
|
||||||
if expectedValue != actualValue {
|
if expectedValue != actualValue {
|
||||||
t.Error(context)
|
t.Error(context)
|
||||||
t.Error(": expected <", expectedValue, "> but got <", actualValue, ">")
|
t.Error(": expected <", expectedValue, "> but got <", actualValue, ">")
|
||||||
|
|||||||
2
instruction_sample.yaml
Normal file
2
instruction_sample.yaml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
b.c: cat
|
||||||
|
b.e[0].name: Mike Farah
|
||||||
@@ -6,7 +6,7 @@ go test
|
|||||||
|
|
||||||
# acceptance test
|
# acceptance test
|
||||||
go build
|
go build
|
||||||
X=$(./yaml sample.yaml b.c)
|
X=$(./yaml r sample.yaml b.c)
|
||||||
|
|
||||||
if [ $X != 2 ]
|
if [ $X != 2 ]
|
||||||
then
|
then
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
a: Easy! as one two three
|
a: Easy! as one two three
|
||||||
b:
|
b:
|
||||||
c: 3
|
c: things
|
||||||
d: [3, 4]
|
d: whatever
|
||||||
|
things:
|
||||||
|
thing1:
|
||||||
|
cat: 'fred'
|
||||||
|
thing2:
|
||||||
|
cat: 'sam'
|
||||||
|
|||||||
141
yaml.go
141
yaml.go
@@ -2,69 +2,96 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/codegangsta/cli"
|
"github.com/spf13/cobra"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var trimOutput = true
|
||||||
|
var writeInplace = false
|
||||||
|
var writeScript = ""
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
app := cli.NewApp()
|
var cmdRead = &cobra.Command{
|
||||||
app.Name = "yaml"
|
Use: "read [yaml_file] [path]",
|
||||||
app.Usage = "command line tool for reading and writing yaml"
|
|
||||||
app.Commands = []cli.Command{
|
|
||||||
{
|
|
||||||
Name: "read",
|
|
||||||
Aliases: []string{"r"},
|
Aliases: []string{"r"},
|
||||||
Usage: "read <filename> <path>\n\te.g.: yaml read sample.json a.b.c\n\t(default) reads a property from a given yaml file\n",
|
Short: "yaml r sample.yaml a.b.c",
|
||||||
Action: readProperty,
|
Example: `
|
||||||
},
|
yaml read things.yaml a.b.c
|
||||||
{
|
yaml r - a.b.c (reads from stdin)
|
||||||
Name: "write",
|
yaml r things.yaml a.*.c
|
||||||
Aliases: []string{"w"},
|
yaml r things.yaml a.array[0].blah
|
||||||
Usage: "write <filename> <path> <value>\n\te.g.: yaml write sample.json a.b.c 5\n\tupdates a property from a given yaml file, outputs to stdout\n",
|
yaml r things.yaml a.array[*].blah
|
||||||
Action: writeProperty,
|
`,
|
||||||
},
|
Long: "Outputs the value of the given path in the yaml file to STDOUT",
|
||||||
|
Run: readProperty,
|
||||||
}
|
}
|
||||||
app.Action = readProperty
|
|
||||||
app.Run(os.Args)
|
var cmdWrite = &cobra.Command{
|
||||||
|
Use: "write [yaml_file] [path] [value]",
|
||||||
|
Aliases: []string{"w"},
|
||||||
|
Short: "yaml w [--inplace/-i] sample.yaml a.b.c newValueForC",
|
||||||
|
Example: `
|
||||||
|
yaml write things.yaml a.b.c cat
|
||||||
|
yaml write --inplace things.yaml a.b.c cat
|
||||||
|
yaml w -i things.yaml a.b.c cat
|
||||||
|
`,
|
||||||
|
Long: `Updates the yaml file w.r.t the given path and value.
|
||||||
|
Outputs to STDOUT unless the inplace flag is used, in which case the file is updated instead.`,
|
||||||
|
Run: writeProperty,
|
||||||
|
}
|
||||||
|
cmdWrite.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace")
|
||||||
|
cmdWrite.PersistentFlags().StringVarP(&writeScript, "script", "s", "", "yaml script for updating yaml")
|
||||||
|
|
||||||
|
var rootCmd = &cobra.Command{Use: "yaml"}
|
||||||
|
rootCmd.PersistentFlags().BoolVarP(&trimOutput, "trim", "t", true, "trim yaml output")
|
||||||
|
rootCmd.AddCommand(cmdRead, cmdWrite)
|
||||||
|
rootCmd.Execute()
|
||||||
}
|
}
|
||||||
|
|
||||||
func readProperty(c *cli.Context) {
|
func readProperty(cmd *cobra.Command, args []string) {
|
||||||
|
|
||||||
var parsedData map[interface{}]interface{}
|
var parsedData map[interface{}]interface{}
|
||||||
|
|
||||||
readYaml(c, &parsedData)
|
readYaml(args[0], &parsedData)
|
||||||
|
|
||||||
if len(c.Args()) == 1 {
|
if len(args) == 1 {
|
||||||
printYaml(parsedData)
|
printYaml(parsedData)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
var paths = parsePath(c.Args()[1])
|
var paths = parsePath(args[1])
|
||||||
|
|
||||||
printYaml(readMap(parsedData, paths[0], paths[1:len(paths)]))
|
printYaml(readMap(parsedData, paths[0], paths[1:len(paths)]))
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeProperty(c *cli.Context) {
|
func writeProperty(cmd *cobra.Command, args []string) {
|
||||||
var parsedData map[interface{}]interface{}
|
var writeCommands map[string]interface{}
|
||||||
readYaml(c, &parsedData)
|
if writeScript != "" {
|
||||||
|
readYaml(writeScript, &writeCommands)
|
||||||
if len(c.Args()) < 3 {
|
} else if len(args) < 3 {
|
||||||
log.Fatalf("Must provide <filename> <path_to_update> <value>")
|
die("Must provide <filename> <path_to_update> <value>")
|
||||||
|
} else {
|
||||||
|
writeCommands[args[1]] = parseValue(args[2])
|
||||||
}
|
}
|
||||||
|
|
||||||
var paths = parsePath(c.Args()[1])
|
var parsedData map[interface{}]interface{}
|
||||||
|
readYaml(args[0], &parsedData)
|
||||||
|
|
||||||
write(parsedData, paths[0], paths[1:len(paths)], getValue(c.Args()[2]))
|
for path, value := range writeCommands {
|
||||||
|
var paths = parsePath(path)
|
||||||
|
write(parsedData, paths[0], paths[1:len(paths)], value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if writeInplace {
|
||||||
|
ioutil.WriteFile(args[0], []byte(yamlToString(parsedData)), 0644)
|
||||||
|
} else {
|
||||||
printYaml(parsedData)
|
printYaml(parsedData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
func parseValue(argument string) interface{} {
|
||||||
func getValue(argument string) interface{} {
|
|
||||||
var value, err interface{}
|
var value, err interface{}
|
||||||
var inQuotes = argument[0] == '"'
|
var inQuotes = argument[0] == '"'
|
||||||
if !inQuotes {
|
if !inQuotes {
|
||||||
@@ -82,32 +109,58 @@ func getValue(argument string) interface{} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func printYaml(context interface{}) {
|
func printYaml(context interface{}) {
|
||||||
|
fmt.Println(yamlToString(context))
|
||||||
|
}
|
||||||
|
|
||||||
|
func yamlToString(context interface{}) string {
|
||||||
out, err := yaml.Marshal(context)
|
out, err := yaml.Marshal(context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("error printing yaml: %v", err)
|
die("error printing yaml: %v", err)
|
||||||
}
|
}
|
||||||
outStr := string(out)
|
outStr := string(out)
|
||||||
// trim the trailing new line as it's easier for a script to add
|
// trim the trailing new line as it's easier for a script to add
|
||||||
// it in if required than to remove it
|
// it in if required than to remove it
|
||||||
fmt.Println(strings.Trim(outStr, "\n "))
|
if trimOutput {
|
||||||
|
return strings.Trim(outStr, "\n ")
|
||||||
|
}
|
||||||
|
return outStr
|
||||||
}
|
}
|
||||||
|
|
||||||
func readYaml(c *cli.Context, parsedData *map[interface{}]interface{}) {
|
func readYaml(filename string, parsedData interface{}) {
|
||||||
if len(c.Args()) == 0 {
|
if filename == "" {
|
||||||
log.Fatalf("Must provide filename")
|
die("Must provide filename")
|
||||||
}
|
}
|
||||||
var rawData = readFile(c.Args()[0])
|
|
||||||
|
|
||||||
err := yaml.Unmarshal([]byte(rawData), &parsedData)
|
var rawData []byte
|
||||||
if err != nil {
|
if filename == "-" {
|
||||||
log.Fatalf("error: %v", err)
|
rawData = readStdin()
|
||||||
|
} else {
|
||||||
|
rawData = readFile(filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := yaml.Unmarshal([]byte(rawData), parsedData)
|
||||||
|
if err != nil {
|
||||||
|
die("error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readStdin() []byte {
|
||||||
|
bytes, err := ioutil.ReadAll(os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
die("error reading stdin", err)
|
||||||
|
}
|
||||||
|
return bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
func readFile(filename string) []byte {
|
func readFile(filename string) []byte {
|
||||||
var rawData, readError = ioutil.ReadFile(filename)
|
var rawData, readError = ioutil.ReadFile(filename)
|
||||||
if readError != nil {
|
if readError != nil {
|
||||||
log.Fatalf("error: %v", readError)
|
die("error: %v", readError)
|
||||||
}
|
}
|
||||||
return rawData
|
return rawData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func die(message ...interface{}) {
|
||||||
|
fmt.Println(message)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getValueTests = []struct {
|
var parseValueTests = []struct {
|
||||||
argument string
|
argument string
|
||||||
expectedResult interface{}
|
expectedResult interface{}
|
||||||
testDescription string
|
testDescription string
|
||||||
@@ -15,8 +15,8 @@ var getValueTests = []struct {
|
|||||||
{"\"3.4\"", "3.4", "number as string"},
|
{"\"3.4\"", "3.4", "number as string"},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetValue(t *testing.T) {
|
func TestParseValue(t *testing.T) {
|
||||||
for _, tt := range getValueTests {
|
for _, tt := range parseValueTests {
|
||||||
assertResultWithContext(t, tt.expectedResult, getValue(tt.argument), tt.testDescription)
|
assertResultWithContext(t, tt.expectedResult, parseValue(tt.argument), tt.testDescription)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user