mirror of
https://github.com/taigrr/yq
synced 2025-01-18 04:53:17 -08:00
Compare commits
9 Commits
traverse_a
...
cswarth-do
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b887e23b3 | ||
|
|
a76b72e691 | ||
|
|
9509831cff | ||
|
|
e92180e89d | ||
|
|
a6ae33c3f1 | ||
|
|
94a563dfd8 | ||
|
|
0328cfd619 | ||
|
|
88663a6ce3 | ||
|
|
b10a9ccfc6 |
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -68,7 +68,9 @@ jobs:
|
||||
|
||||
- name: Build and push image
|
||||
run: |
|
||||
IMAGE_VERSION="$(git describe --tags --abbrev=0)"
|
||||
IMAGE_V_VERSION="$(git describe --tags --abbrev=0)"
|
||||
IMAGE_VERSION=${IMAGE_V_VERSION:1}
|
||||
|
||||
SHORT_SHA1=$(git rev-parse --short HEAD)
|
||||
PLATFORMS="linux/amd64,linux/ppc64le,linux/arm64"
|
||||
echo "Building and pushing version ${IMAGE_VERSION} of image ${IMAGE_NAME}"
|
||||
|
||||
@@ -4,6 +4,7 @@ COPY scripts/devtools.sh /opt/devtools.sh
|
||||
|
||||
RUN set -e -x \
|
||||
&& /opt/devtools.sh
|
||||
ENV PATH=/go/bin:$PATH
|
||||
|
||||
# install mkdocs
|
||||
RUN set -ex \
|
||||
|
||||
@@ -158,6 +158,7 @@ Flags:
|
||||
-M, --no-colors force print with no colors
|
||||
-N, --no-doc Don't print document separators (---)
|
||||
-n, --null-input Don't read input, simply evaluate the expression given. Useful for creating yaml docs from scratch.
|
||||
-P, --prettyPrint pretty print, shorthand for '... style = ""'
|
||||
-j, --tojson output as json. Set indent to 0 to print json in one line.
|
||||
-v, --verbose verbose mode
|
||||
-V, --version Print version information and quit
|
||||
|
||||
@@ -14,5 +14,6 @@ var noDocSeparators = false
|
||||
var nullInput = false
|
||||
var verbose = false
|
||||
var version = false
|
||||
var prettyPrint = false
|
||||
|
||||
var completedSuccessfully = false
|
||||
|
||||
@@ -65,19 +65,19 @@ func evaluateAll(cmd *cobra.Command, args []string) error {
|
||||
switch len(args) {
|
||||
case 0:
|
||||
if pipingStdIn {
|
||||
err = allAtOnceEvaluator.EvaluateFiles("", []string{"-"}, printer)
|
||||
err = allAtOnceEvaluator.EvaluateFiles(processExpression(""), []string{"-"}, printer)
|
||||
} else {
|
||||
cmd.Println(cmd.UsageString())
|
||||
return nil
|
||||
}
|
||||
case 1:
|
||||
if nullInput {
|
||||
err = yqlib.NewStreamEvaluator().EvaluateNew(args[0], printer)
|
||||
err = yqlib.NewStreamEvaluator().EvaluateNew(processExpression(args[0]), printer)
|
||||
} else {
|
||||
err = allAtOnceEvaluator.EvaluateFiles("", []string{args[0]}, printer)
|
||||
err = allAtOnceEvaluator.EvaluateFiles(processExpression(""), []string{args[0]}, printer)
|
||||
}
|
||||
default:
|
||||
err = allAtOnceEvaluator.EvaluateFiles(args[0], args[1:], printer)
|
||||
err = allAtOnceEvaluator.EvaluateFiles(processExpression(args[0]), args[1:], printer)
|
||||
}
|
||||
|
||||
completedSuccessfully = err == nil
|
||||
|
||||
@@ -33,6 +33,16 @@ yq e '.a.b = "cool"' -i file.yaml
|
||||
}
|
||||
return cmdEvalSequence
|
||||
}
|
||||
|
||||
func processExpression(expression string) string {
|
||||
if prettyPrint && expression == "" {
|
||||
return `... style=""`
|
||||
} else if prettyPrint {
|
||||
return fmt.Sprintf("%v | ... style= \"\"", expression)
|
||||
}
|
||||
return expression
|
||||
}
|
||||
|
||||
func evaluateSequence(cmd *cobra.Command, args []string) error {
|
||||
cmd.SilenceUsage = true
|
||||
// 0 args, read std in
|
||||
@@ -76,16 +86,16 @@ func evaluateSequence(cmd *cobra.Command, args []string) error {
|
||||
switch len(args) {
|
||||
case 0:
|
||||
if pipingStdIn {
|
||||
err = streamEvaluator.EvaluateFiles("", []string{"-"}, printer)
|
||||
err = streamEvaluator.EvaluateFiles(processExpression(""), []string{"-"}, printer)
|
||||
} else {
|
||||
cmd.Println(cmd.UsageString())
|
||||
return nil
|
||||
}
|
||||
case 1:
|
||||
if nullInput {
|
||||
err = streamEvaluator.EvaluateNew(args[0], printer)
|
||||
err = streamEvaluator.EvaluateNew(processExpression(args[0]), printer)
|
||||
} else {
|
||||
err = streamEvaluator.EvaluateFiles("", []string{args[0]}, printer)
|
||||
err = streamEvaluator.EvaluateFiles(processExpression(""), []string{args[0]}, printer)
|
||||
}
|
||||
default:
|
||||
err = streamEvaluator.EvaluateFiles(args[0], args[1:], printer)
|
||||
|
||||
@@ -48,6 +48,7 @@ func New() *cobra.Command {
|
||||
rootCmd.Flags().BoolVarP(&version, "version", "V", false, "Print version information and quit")
|
||||
rootCmd.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace of first yaml file given.")
|
||||
rootCmd.PersistentFlags().BoolVarP(&unwrapScalar, "unwrapScalar", "", true, "unwrap scalar, print the value with no quotes, colors or comments")
|
||||
rootCmd.PersistentFlags().BoolVarP(&prettyPrint, "prettyPrint", "P", false, "pretty print, shorthand for '... style = \"\"'")
|
||||
rootCmd.PersistentFlags().BoolVarP(&exitStatus, "exit-status", "e", false, "set exit status if there are no matches or null or false is returned")
|
||||
|
||||
rootCmd.PersistentFlags().BoolVarP(&forceColor, "colors", "C", false, "force print with colors")
|
||||
|
||||
@@ -11,7 +11,7 @@ var (
|
||||
GitDescribe string
|
||||
|
||||
// Version is main version number that is being run at the moment.
|
||||
Version = "4.1.0"
|
||||
Version = "4.2.0"
|
||||
|
||||
// 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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM mikefarah/yq:3
|
||||
FROM mikefarah/yq:4.2.0
|
||||
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ a: frog
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval 'select(. | documentIndex == 1)' sample.yml
|
||||
yq eval 'select(documentIndex == 1)' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
@@ -42,7 +42,7 @@ a: frog
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a | ({"match": ., "doc": (. | documentIndex)})' sample.yml
|
||||
yq eval '.a | ({"match": ., "doc": documentIndex})' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
|
||||
@@ -1,8 +1,55 @@
|
||||
This operator recursively matches all children nodes given of a particular element, including that node itself. This is most often used to apply a filter recursively against all matches, for instance to set the `style` of all nodes in a yaml doc:
|
||||
This operator recursively matches all children nodes given of a particular element, including that node itself. This is most often used to apply a filter recursively against all matches. It can be used in either the
|
||||
|
||||
## match values form `..`
|
||||
This will, like the `jq` equivalent, recursively match all _value_ nodes. Use it to find/manipulate particular values.
|
||||
|
||||
For instance to set the `style` of all _value_ nodes in a yaml doc, excluding map keys:
|
||||
|
||||
```bash
|
||||
yq eval '.. style= "flow"' file.yaml
|
||||
```
|
||||
|
||||
## match values and map keys form `...`
|
||||
The also includes map keys in the results set. This is particularly useful in YAML as unlike JSON, map keys can have their own styling, tags and use anchors and aliases.
|
||||
|
||||
For instance to set the `style` of all nodes in a yaml doc, including the map keys:
|
||||
|
||||
```bash
|
||||
yq eval '... style= "flow"' file.yaml
|
||||
```
|
||||
## Recurse map (values only)
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: frog
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '..' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a: frog
|
||||
frog
|
||||
```
|
||||
|
||||
## Recurse map (values and keys)
|
||||
Note that the map key appears in the results
|
||||
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: frog
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '...' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a: frog
|
||||
a
|
||||
frog
|
||||
```
|
||||
|
||||
## Aliases are not traversed
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
|
||||
@@ -40,6 +40,26 @@ c: "3.2"
|
||||
e: "true"
|
||||
```
|
||||
|
||||
## Set double quote style on map keys too
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat
|
||||
b: 5
|
||||
c: 3.2
|
||||
e: true
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '... style="double"' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
"a": "cat"
|
||||
"b": "5"
|
||||
"c": "3.2"
|
||||
"e": "true"
|
||||
```
|
||||
|
||||
## Set single quote style
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
@@ -126,18 +146,18 @@ will output
|
||||
```
|
||||
|
||||
## Pretty print
|
||||
Set empty (default) quote style
|
||||
Set empty (default) quote style, note the usage of `...` to match keys too. Note that there is a `--prettyPrint/-P` short flag for this.
|
||||
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat
|
||||
b: 5
|
||||
c: 3.2
|
||||
e: true
|
||||
"b": 5
|
||||
'c': 3.2
|
||||
"e": true
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.. style=""' sample.yml
|
||||
yq eval '... style=""' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
This operator recursively matches all children nodes given of a particular element, including that node itself. This is most often used to apply a filter recursively against all matches, for instance to set the `style` of all nodes in a yaml doc:
|
||||
This operator recursively matches all children nodes given of a particular element, including that node itself. This is most often used to apply a filter recursively against all matches. It can be used in either the
|
||||
|
||||
## match values form `..`
|
||||
This will, like the `jq` equivalent, recursively match all _value_ nodes. Use it to find/manipulate particular values.
|
||||
|
||||
For instance to set the `style` of all _value_ nodes in a yaml doc, excluding map keys:
|
||||
|
||||
```bash
|
||||
yq eval '.. style= "flow"' file.yaml
|
||||
```
|
||||
|
||||
## match values and map keys form `...`
|
||||
The also includes map keys in the results set. This is particularly useful in YAML as unlike JSON, map keys can have their own styling, tags and use anchors and aliases.
|
||||
|
||||
For instance to set the `style` of all nodes in a yaml doc, including the map keys:
|
||||
|
||||
```bash
|
||||
yq eval '... style= "flow"' file.yaml
|
||||
```
|
||||
@@ -19,7 +19,7 @@ type OperationType struct {
|
||||
}
|
||||
|
||||
// operators TODO:
|
||||
// - cookbook doc for common things
|
||||
// - keys operator for controlling key metadata (particularly anchors/aliases)
|
||||
// - mergeEmpty (sets only if the document is empty, do I do that now?)
|
||||
|
||||
var Or = &OperationType{Type: "OR", NumArgs: 2, Precedence: 20, Handler: OrOperator}
|
||||
|
||||
@@ -67,7 +67,9 @@ func collect(d *dataTreeNavigator, aggregate *list.List, remainingMatches *list.
|
||||
}
|
||||
|
||||
candidate := remainingMatches.Remove(remainingMatches.Front()).(*CandidateNode)
|
||||
splatted, err := Splat(d, nodeToMap(candidate))
|
||||
|
||||
splatted, err := Splat(d, nodeToMap(candidate),
|
||||
&TraversePreferences{FollowAlias: false, IncludeMapKeys: false})
|
||||
|
||||
for splatEl := splatted.Front(); splatEl != nil; splatEl = splatEl.Next() {
|
||||
splatEl.Value.(*CandidateNode).Path = nil
|
||||
|
||||
@@ -17,7 +17,7 @@ var documentIndexScenarios = []expressionScenario{
|
||||
{
|
||||
description: "Filter by document index",
|
||||
document: "a: cat\n---\na: frog\n",
|
||||
expression: `select(. | documentIndex == 1)`,
|
||||
expression: `select(documentIndex == 1)`,
|
||||
expected: []string{
|
||||
"D1, P[], (doc)::a: frog\n",
|
||||
},
|
||||
@@ -25,7 +25,7 @@ var documentIndexScenarios = []expressionScenario{
|
||||
{
|
||||
description: "Print Document Index with matches",
|
||||
document: "a: cat\n---\na: frog\n",
|
||||
expression: `.a | ({"match": ., "doc": (. | documentIndex)})`,
|
||||
expression: `.a | ({"match": ., "doc": documentIndex})`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::match: cat\ndoc: 0\n",
|
||||
"D0, P[], (!!map)::match: frog\ndoc: 1\n",
|
||||
|
||||
@@ -85,7 +85,9 @@ func mergeObjects(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode,
|
||||
var results = list.New()
|
||||
|
||||
// shouldn't recurse arrays if appending
|
||||
err := recursiveDecent(d, results, nodeToMap(rhs), !shouldAppendArrays)
|
||||
prefs := &RecursiveDescentPreferences{RecurseArray: !shouldAppendArrays,
|
||||
TraversePreferences: &TraversePreferences{FollowAlias: false}}
|
||||
err := recursiveDecent(d, results, nodeToMap(rhs), prefs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -6,10 +6,16 @@ import (
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type RecursiveDescentPreferences struct {
|
||||
TraversePreferences *TraversePreferences
|
||||
RecurseArray bool
|
||||
}
|
||||
|
||||
func RecursiveDescentOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNode) (*list.List, error) {
|
||||
var results = list.New()
|
||||
|
||||
err := recursiveDecent(d, results, matchMap, true)
|
||||
preferences := pathNode.Operation.Preferences.(*RecursiveDescentPreferences)
|
||||
err := recursiveDecent(d, results, matchMap, preferences)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -17,7 +23,7 @@ func RecursiveDescentOperator(d *dataTreeNavigator, matchMap *list.List, pathNod
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func recursiveDecent(d *dataTreeNavigator, results *list.List, matchMap *list.List, recurseArray bool) error {
|
||||
func recursiveDecent(d *dataTreeNavigator, results *list.List, matchMap *list.List, preferences *RecursiveDescentPreferences) error {
|
||||
for el := matchMap.Front(); el != nil; el = el.Next() {
|
||||
candidate := el.Value.(*CandidateNode)
|
||||
|
||||
@@ -27,14 +33,14 @@ func recursiveDecent(d *dataTreeNavigator, results *list.List, matchMap *list.Li
|
||||
results.PushBack(candidate)
|
||||
|
||||
if candidate.Node.Kind != yaml.AliasNode && len(candidate.Node.Content) > 0 &&
|
||||
(recurseArray || candidate.Node.Kind != yaml.SequenceNode) {
|
||||
(preferences.RecurseArray || candidate.Node.Kind != yaml.SequenceNode) {
|
||||
|
||||
children, err := Splat(d, nodeToMap(candidate))
|
||||
children, err := Splat(d, nodeToMap(candidate), preferences.TraversePreferences)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = recursiveDecent(d, results, children, recurseArray)
|
||||
err = recursiveDecent(d, results, children, preferences)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -13,6 +13,14 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
|
||||
"D0, P[], (!!map)::{}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `{}`,
|
||||
expression: `...`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::{}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `[]`,
|
||||
@@ -21,6 +29,14 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
|
||||
"D0, P[], (!!seq)::[]\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `[]`,
|
||||
expression: `...`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!seq)::[]\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `cat`,
|
||||
@@ -31,13 +47,32 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `{a: frog}`,
|
||||
expression: `..`,
|
||||
document: `cat`,
|
||||
expression: `...`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!str)::cat\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Recurse map (values only)",
|
||||
document: `{a: frog}`,
|
||||
expression: `..`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::{a: frog}\n",
|
||||
"D0, P[a], (!!str)::frog\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Recurse map (values and keys)",
|
||||
subdescription: "Note that the map key appears in the results",
|
||||
document: `{a: frog}`,
|
||||
expression: `...`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::{a: frog}\n",
|
||||
"D0, P[a], (!!str)::a\n",
|
||||
"D0, P[a], (!!str)::frog\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `{a: {b: apple}}`,
|
||||
@@ -48,6 +83,18 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
|
||||
"D0, P[a b], (!!str)::apple\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `{a: {b: apple}}`,
|
||||
expression: `...`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::{a: {b: apple}}\n",
|
||||
"D0, P[a], (!!str)::a\n",
|
||||
"D0, P[a], (!!map)::{b: apple}\n",
|
||||
"D0, P[a b], (!!str)::b\n",
|
||||
"D0, P[a b], (!!str)::apple\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `[1,2,3]`,
|
||||
@@ -59,6 +106,17 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
|
||||
"D0, P[2], (!!int)::3\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `[1,2,3]`,
|
||||
expression: `...`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!seq)::[1, 2, 3]\n",
|
||||
"D0, P[0], (!!int)::1\n",
|
||||
"D0, P[1], (!!int)::2\n",
|
||||
"D0, P[2], (!!int)::3\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `[{a: cat},2,true]`,
|
||||
@@ -71,6 +129,19 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
|
||||
"D0, P[2], (!!bool)::true\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `[{a: cat},2,true]`,
|
||||
expression: `...`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!seq)::[{a: cat}, 2, true]\n",
|
||||
"D0, P[0], (!!map)::{a: cat}\n",
|
||||
"D0, P[0 a], (!!str)::a\n",
|
||||
"D0, P[0 a], (!!str)::cat\n",
|
||||
"D0, P[1], (!!int)::2\n",
|
||||
"D0, P[2], (!!bool)::true\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Aliases are not traversed",
|
||||
document: `{a: &cat {c: frog}, b: *cat}`,
|
||||
@@ -79,6 +150,20 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
|
||||
"D0, P[a], (!!seq)::- {a: &cat {c: frog}, b: *cat}\n- &cat {c: frog}\n- frog\n- *cat\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `{a: &cat {c: frog}, b: *cat}`,
|
||||
expression: `...`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::{a: &cat {c: frog}, b: *cat}\n",
|
||||
"D0, P[a], (!!str)::a\n",
|
||||
"D0, P[a], (!!map)::&cat {c: frog}\n",
|
||||
"D0, P[a c], (!!str)::c\n",
|
||||
"D0, P[a c], (!!str)::frog\n",
|
||||
"D0, P[b], (!!str)::b\n",
|
||||
"D0, P[b], (alias)::*cat\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Merge docs are not traversed",
|
||||
document: mergeDocSample,
|
||||
@@ -87,6 +172,14 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
|
||||
"D0, P[foobar], (!!seq)::- c: foobar_c\n !!merge <<: *foo\n thing: foobar_thing\n- foobar_c\n- *foo\n- foobar_thing\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: mergeDocSample,
|
||||
expression: `.foobar | [...]`,
|
||||
expected: []string{
|
||||
"D0, P[foobar], (!!seq)::- c: foobar_c\n !!merge <<: *foo\n thing: foobar_thing\n- c\n- foobar_c\n- !!merge <<\n- *foo\n- thing\n- foobar_thing\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: mergeDocSample,
|
||||
@@ -100,6 +193,22 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
|
||||
"D0, P[foobarList c], (!!str)::foobarList_c\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: mergeDocSample,
|
||||
expression: `.foobarList | ...`,
|
||||
expected: []string{
|
||||
"D0, P[foobarList], (!!map)::b: foobarList_b\n!!merge <<: [*foo, *bar]\nc: foobarList_c\n",
|
||||
"D0, P[foobarList b], (!!str)::b\n",
|
||||
"D0, P[foobarList b], (!!str)::foobarList_b\n",
|
||||
"D0, P[foobarList <<], (!!merge)::!!merge <<\n",
|
||||
"D0, P[foobarList <<], (!!seq)::[*foo, *bar]\n",
|
||||
"D0, P[foobarList << 0], (alias)::*foo\n",
|
||||
"D0, P[foobarList << 1], (alias)::*bar\n",
|
||||
"D0, P[foobarList c], (!!str)::c\n",
|
||||
"D0, P[foobarList c], (!!str)::foobarList_c\n",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestRecursiveDescentOperatorScenarios(t *testing.T) {
|
||||
|
||||
@@ -21,6 +21,22 @@ var styleOperatorScenarios = []expressionScenario{
|
||||
"D0, P[], (!!map)::a: \"cat\"\nb: \"5\"\nc: \"3.2\"\ne: \"true\"\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Set double quote style on map keys too",
|
||||
document: `{a: cat, b: 5, c: 3.2, e: true}`,
|
||||
expression: `... style="double"`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::\"a\": \"cat\"\n\"b\": \"5\"\n\"c\": \"3.2\"\n\"e\": \"true\"\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: "bing: &foo frog\na:\n c: cat\n <<: [*foo]",
|
||||
expression: `(... | select(tag=="!!str")) style="single"`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::'bing': &foo 'frog'\n'a':\n 'c': 'cat'\n !!merge <<: [*foo]\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Set single quote style",
|
||||
document: `{a: cat, b: 5, c: 3.2, e: true}`,
|
||||
@@ -71,9 +87,9 @@ e: >-
|
||||
},
|
||||
{
|
||||
description: "Pretty print",
|
||||
subdescription: "Set empty (default) quote style",
|
||||
document: `{a: cat, b: 5, c: 3.2, e: true}`,
|
||||
expression: `.. style=""`,
|
||||
subdescription: "Set empty (default) quote style, note the usage of `...` to match keys too. Note that there is a `--prettyPrint/-P` short flag for this.",
|
||||
document: `{a: cat, "b": 5, 'c': 3.2, "e": true}`,
|
||||
expression: `... style=""`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::a: cat\nb: 5\nc: 3.2\ne: true\n",
|
||||
},
|
||||
|
||||
@@ -10,11 +10,12 @@ import (
|
||||
)
|
||||
|
||||
type TraversePreferences struct {
|
||||
DontFollowAlias bool
|
||||
FollowAlias bool
|
||||
IncludeMapKeys bool
|
||||
}
|
||||
|
||||
func Splat(d *dataTreeNavigator, matches *list.List) (*list.List, error) {
|
||||
return traverseNodesWithArrayIndices(matches, make([]*yaml.Node, 0), false)
|
||||
func Splat(d *dataTreeNavigator, matches *list.List, prefs *TraversePreferences) (*list.List, error) {
|
||||
return traverseNodesWithArrayIndices(matches, make([]*yaml.Node, 0), prefs)
|
||||
}
|
||||
|
||||
func TraversePathOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNode) (*list.List, error) {
|
||||
@@ -53,12 +54,8 @@ func traverse(d *dataTreeNavigator, matchingNode *CandidateNode, operation *Oper
|
||||
switch value.Kind {
|
||||
case yaml.MappingNode:
|
||||
log.Debug("its a map with %v entries", len(value.Content)/2)
|
||||
followAlias := true
|
||||
|
||||
if operation.Preferences != nil {
|
||||
followAlias = !operation.Preferences.(*TraversePreferences).DontFollowAlias
|
||||
}
|
||||
return traverseMap(matchingNode, operation.StringValue, followAlias, false)
|
||||
prefs := &TraversePreferences{FollowAlias: true}
|
||||
return traverseMap(matchingNode, operation.StringValue, prefs, false)
|
||||
|
||||
case yaml.SequenceNode:
|
||||
log.Debug("its a sequence of %v things!", len(value.Content))
|
||||
@@ -89,15 +86,15 @@ func TraverseArrayOperator(d *dataTreeNavigator, matchingNodes *list.List, pathN
|
||||
}
|
||||
|
||||
var indicesToTraverse = rhs.Front().Value.(*CandidateNode).Node.Content
|
||||
|
||||
return traverseNodesWithArrayIndices(matchingNodes, indicesToTraverse, true)
|
||||
prefs := &TraversePreferences{FollowAlias: true}
|
||||
return traverseNodesWithArrayIndices(matchingNodes, indicesToTraverse, prefs)
|
||||
}
|
||||
|
||||
func traverseNodesWithArrayIndices(matchingNodes *list.List, indicesToTraverse []*yaml.Node, followAlias bool) (*list.List, error) {
|
||||
func traverseNodesWithArrayIndices(matchingNodes *list.List, indicesToTraverse []*yaml.Node, prefs *TraversePreferences) (*list.List, error) {
|
||||
var matchingNodeMap = list.New()
|
||||
for el := matchingNodes.Front(); el != nil; el = el.Next() {
|
||||
candidate := el.Value.(*CandidateNode)
|
||||
newNodes, err := traverseArrayIndices(candidate, indicesToTraverse, followAlias)
|
||||
newNodes, err := traverseArrayIndices(candidate, indicesToTraverse, prefs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -107,7 +104,7 @@ func traverseNodesWithArrayIndices(matchingNodes *list.List, indicesToTraverse [
|
||||
return matchingNodeMap, nil
|
||||
}
|
||||
|
||||
func traverseArrayIndices(matchingNode *CandidateNode, indicesToTraverse []*yaml.Node, followAlias bool) (*list.List, error) { // call this if doc / alias like the other traverse
|
||||
func traverseArrayIndices(matchingNode *CandidateNode, indicesToTraverse []*yaml.Node, prefs *TraversePreferences) (*list.List, error) { // call this if doc / alias like the other traverse
|
||||
node := matchingNode.Node
|
||||
if node.Tag == "!!null" {
|
||||
log.Debugf("OperatorArrayTraverse got a null - turning it into an empty array")
|
||||
@@ -118,32 +115,32 @@ func traverseArrayIndices(matchingNode *CandidateNode, indicesToTraverse []*yaml
|
||||
|
||||
if node.Kind == yaml.AliasNode {
|
||||
matchingNode.Node = node.Alias
|
||||
return traverseArrayIndices(matchingNode, indicesToTraverse, followAlias)
|
||||
return traverseArrayIndices(matchingNode, indicesToTraverse, prefs)
|
||||
} else if node.Kind == yaml.SequenceNode {
|
||||
return traverseArrayWithIndices(matchingNode, indicesToTraverse)
|
||||
} else if node.Kind == yaml.MappingNode {
|
||||
return traverseMapWithIndices(matchingNode, indicesToTraverse, followAlias)
|
||||
return traverseMapWithIndices(matchingNode, indicesToTraverse, prefs)
|
||||
} else if node.Kind == yaml.DocumentNode {
|
||||
return traverseArrayIndices(&CandidateNode{
|
||||
Node: matchingNode.Node.Content[0],
|
||||
Filename: matchingNode.Filename,
|
||||
FileIndex: matchingNode.FileIndex,
|
||||
Document: matchingNode.Document}, indicesToTraverse, followAlias)
|
||||
Document: matchingNode.Document}, indicesToTraverse, prefs)
|
||||
}
|
||||
log.Debugf("OperatorArrayTraverse skipping %v as its a %v", matchingNode, node.Tag)
|
||||
return list.New(), nil
|
||||
}
|
||||
|
||||
func traverseMapWithIndices(candidate *CandidateNode, indices []*yaml.Node, followAlias bool) (*list.List, error) {
|
||||
func traverseMapWithIndices(candidate *CandidateNode, indices []*yaml.Node, prefs *TraversePreferences) (*list.List, error) {
|
||||
if len(indices) == 0 {
|
||||
return traverseMap(candidate, "", followAlias, true)
|
||||
return traverseMap(candidate, "", prefs, true)
|
||||
}
|
||||
|
||||
var matchingNodeMap = list.New()
|
||||
|
||||
for _, indexNode := range indices {
|
||||
log.Debug("traverseMapWithIndices: %v", indexNode.Value)
|
||||
newNodes, err := traverseMap(candidate, indexNode.Value, followAlias, false)
|
||||
newNodes, err := traverseMap(candidate, indexNode.Value, prefs, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -206,9 +203,9 @@ func keyMatches(key *yaml.Node, wantedKey string) bool {
|
||||
return Match(key.Value, wantedKey)
|
||||
}
|
||||
|
||||
func traverseMap(matchingNode *CandidateNode, key string, followAlias bool, splat bool) (*list.List, error) {
|
||||
func traverseMap(matchingNode *CandidateNode, key string, prefs *TraversePreferences, splat bool) (*list.List, error) {
|
||||
var newMatches = orderedmap.NewOrderedMap()
|
||||
err := doTraverseMap(newMatches, matchingNode, key, followAlias, splat)
|
||||
err := doTraverseMap(newMatches, matchingNode, key, prefs, splat)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -237,7 +234,7 @@ func traverseMap(matchingNode *CandidateNode, key string, followAlias bool, spla
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func doTraverseMap(newMatches *orderedmap.OrderedMap, candidate *CandidateNode, wantedKey string, followAlias bool, splat bool) error {
|
||||
func doTraverseMap(newMatches *orderedmap.OrderedMap, candidate *CandidateNode, wantedKey string, prefs *TraversePreferences, splat bool) error {
|
||||
// value.Content is a concatenated array of key, value,
|
||||
// so keys are in the even indexes, values in odd.
|
||||
// merge aliases are defined first, but we only want to traverse them
|
||||
@@ -252,14 +249,22 @@ func doTraverseMap(newMatches *orderedmap.OrderedMap, candidate *CandidateNode,
|
||||
|
||||
log.Debug("checking %v (%v)", key.Value, key.Tag)
|
||||
//skip the 'merge' tag, find a direct match first
|
||||
if key.Tag == "!!merge" && followAlias {
|
||||
if key.Tag == "!!merge" && prefs.FollowAlias {
|
||||
log.Debug("Merge anchor")
|
||||
err := traverseMergeAnchor(newMatches, candidate, value, wantedKey, splat)
|
||||
err := traverseMergeAnchor(newMatches, candidate, value, wantedKey, prefs, splat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if splat || keyMatches(key, wantedKey) {
|
||||
log.Debug("MATCHED")
|
||||
if prefs.IncludeMapKeys {
|
||||
candidateNode := &CandidateNode{
|
||||
Node: key,
|
||||
Path: candidate.CreateChildPath(key.Value),
|
||||
Document: candidate.Document,
|
||||
}
|
||||
newMatches.Set(fmt.Sprintf("keyOf-%v", candidateNode.GetKey()), candidateNode)
|
||||
}
|
||||
candidateNode := &CandidateNode{
|
||||
Node: value,
|
||||
Path: candidate.CreateChildPath(key.Value),
|
||||
@@ -272,7 +277,7 @@ func doTraverseMap(newMatches *orderedmap.OrderedMap, candidate *CandidateNode,
|
||||
return nil
|
||||
}
|
||||
|
||||
func traverseMergeAnchor(newMatches *orderedmap.OrderedMap, originalCandidate *CandidateNode, value *yaml.Node, wantedKey string, splat bool) error {
|
||||
func traverseMergeAnchor(newMatches *orderedmap.OrderedMap, originalCandidate *CandidateNode, value *yaml.Node, wantedKey string, prefs *TraversePreferences, splat bool) error {
|
||||
switch value.Kind {
|
||||
case yaml.AliasNode:
|
||||
candidateNode := &CandidateNode{
|
||||
@@ -280,10 +285,10 @@ func traverseMergeAnchor(newMatches *orderedmap.OrderedMap, originalCandidate *C
|
||||
Path: originalCandidate.Path,
|
||||
Document: originalCandidate.Document,
|
||||
}
|
||||
return doTraverseMap(newMatches, candidateNode, wantedKey, true, splat)
|
||||
return doTraverseMap(newMatches, candidateNode, wantedKey, prefs, splat)
|
||||
case yaml.SequenceNode:
|
||||
for _, childValue := range value.Content {
|
||||
err := traverseMergeAnchor(newMatches, originalCandidate, childValue, wantedKey, splat)
|
||||
err := traverseMergeAnchor(newMatches, originalCandidate, childValue, wantedKey, prefs, splat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -173,7 +173,11 @@ func initLexer() (*lex.Lexer, error) {
|
||||
lexer.Add([]byte(`\)`), literalToken(CloseBracket, true))
|
||||
|
||||
lexer.Add([]byte(`\.\[`), literalToken(TraverseArrayCollect, false))
|
||||
lexer.Add([]byte(`\.\.`), opToken(RecursiveDescent))
|
||||
lexer.Add([]byte(`\.\.`), opTokenWithPrefs(RecursiveDescent, nil, &RecursiveDescentPreferences{RecurseArray: true,
|
||||
TraversePreferences: &TraversePreferences{FollowAlias: false, IncludeMapKeys: false}}))
|
||||
|
||||
lexer.Add([]byte(`\.\.\.`), opTokenWithPrefs(RecursiveDescent, nil, &RecursiveDescentPreferences{RecurseArray: true,
|
||||
TraversePreferences: &TraversePreferences{FollowAlias: false, IncludeMapKeys: true}}))
|
||||
|
||||
lexer.Add([]byte(`,`), opToken(Union))
|
||||
lexer.Add([]byte(`:\s*`), opToken(CreateMap))
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
- increment version in version.go
|
||||
- increment version in snapcraft.yaml
|
||||
- increment version in github-action/Dockerfile
|
||||
- make sure local build passes
|
||||
- tag git with same version number
|
||||
- commit vX tag - this will trigger github actions
|
||||
- use github actions to publish docker and make github release
|
||||
- check github updated yq action in marketplace
|
||||
|
||||
- snapcraft
|
||||
- will auto create a candidate, test it works then promote
|
||||
|
||||
@@ -3,7 +3,12 @@
|
||||
set -o errexit
|
||||
set -o pipefail
|
||||
|
||||
./bin/golangci-lint run --timeout=5m
|
||||
if command -v golangci-lint &> /dev/null
|
||||
then
|
||||
golangci-lint run --timeout=5m
|
||||
else
|
||||
./bin/golangci-lint run --timeout=5m
|
||||
fi
|
||||
|
||||
# ./bin/golangci-lint \
|
||||
# --tests \
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -ex
|
||||
VERSION="$(git describe --tags --abbrev=0)"
|
||||
docker build \
|
||||
--target production \
|
||||
--build-arg VERSION=${VERSION} \
|
||||
-t mikefarah/yq:latest \
|
||||
-t mikefarah/yq:${VERSION} \
|
||||
-t mikefarah/yq:4 \
|
||||
.
|
||||
|
||||
trivy image mikefarah/yq:${VERSION}
|
||||
@@ -1,21 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -ex
|
||||
GITHUB_TOKEN="${GITHUB_TOKEN:?missing required input \'GITHUB_TOKEN\'}"
|
||||
|
||||
CURRENT="$(git describe --tags --abbrev=0)"
|
||||
PREVIOUS="$(git describe --tags --abbrev=0 --always "${CURRENT}"^)"
|
||||
OWNER="mikefarah"
|
||||
REPO="yq"
|
||||
|
||||
release() {
|
||||
github-release release \
|
||||
--user "$OWNER" \
|
||||
--draft \
|
||||
--repo "$REPO" \
|
||||
--tag "$CURRENT"
|
||||
}
|
||||
|
||||
|
||||
|
||||
release
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -ex
|
||||
GITHUB_TOKEN="${GITHUB_TOKEN:?missing required input \'GITHUB_TOKEN\'}"
|
||||
|
||||
CURRENT="$(git describe --tags --abbrev=0)"
|
||||
PREVIOUS="$(git describe --tags --abbrev=0 --always "${CURRENT}"^)"
|
||||
OWNER="mikefarah"
|
||||
REPO="yq"
|
||||
|
||||
upload() {
|
||||
mkdir -p ./build-done
|
||||
while IFS= read -r -d $'\0'; do
|
||||
file=$REPLY
|
||||
BINARY=$(basename "${file}")
|
||||
echo "--> ${BINARY}"
|
||||
github-release upload \
|
||||
--replace \
|
||||
--user "$OWNER" \
|
||||
--repo "$REPO" \
|
||||
--tag "$CURRENT" \
|
||||
--name "${BINARY}" \
|
||||
--file "$file"
|
||||
mv "$file" "./build-done/${BINARY}"
|
||||
done < <(find ./build -mindepth 1 -maxdepth 1 -print0)
|
||||
}
|
||||
|
||||
|
||||
upload
|
||||
@@ -1,5 +1,5 @@
|
||||
name: yq
|
||||
version: '4.1.0'
|
||||
version: '4.2.0'
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user