Compare commits

..
78 Commits
Author SHA1 Message Date
Mike Farah eb9181670e Fixed variable operator to work like jq 2023-02-28 16:14:42 +11:00
Mike Farah 0cd15867d0 Variable loop wip 2023-02-28 13:29:47 +11:00
Mike Farah 7ade7a97a5 Variable loop wip 2023-02-23 16:32:06 +11:00
Mike Farah 668e5600c3 Variable loop wip 2023-02-23 16:30:18 +11:00
Mike Farah ae228c4b2a Variable loop wip 2023-02-23 16:14:36 +11:00
Mike Farah 9949a23724 v4.31.1 2023-02-20 16:09:25 +11:00
Mike Farah b86fb0aea0 Bumping version 2023-02-20 16:09:07 +11:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
b369de6662 Bump golang from 1.20.0 to 1.20.1 (#1557)
Bumps golang from 1.20.0 to 1.20.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-20 15:59:50 +11:00
Mike Farah 75483fe908 Fix github action attempt 2023-02-11 23:54:16 +11:00
Mike Farah f4e7203a55 Fix github action attempt 2023-02-11 23:50:47 +11:00
Mike Farah d17fd9424e Added shuffle command #1503 2023-02-11 05:08:20 +11:00
Mike Farah a1698b740a Added ability to sort by multiple fields #1541 2023-02-11 04:46:39 +11:00
Mike Farah 5cb3c876fc bump to go 1.20 2023-02-11 04:06:16 +11:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
0f2a84d270 Bump golang from 1.19.5 to 1.20.0 (#1542)
Bumps golang from 1.19.5 to 1.20.0.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-09 18:20:25 +11:00
Mike Farah bbb149b31e Bumped linter 2023-02-09 18:20:11 +11:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
18a51ca5b8 Bump github.com/a8m/envsubst from 1.3.0 to 1.4.1 (#1551)
Bumps [github.com/a8m/envsubst](https://github.com/a8m/envsubst) from 1.3.0 to 1.4.1.
- [Release notes](https://github.com/a8m/envsubst/releases)
- [Commits](https://github.com/a8m/envsubst/compare/v1.3.0...v1.4.1)

---
updated-dependencies:
- dependency-name: github.com/a8m/envsubst
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-09 18:15:20 +11:00
Vít ZikmundandGitHub 93b7c999be Use a lazy-quoting @sh encoder (#1548)
* Use a lazy-quoting @sh encoder

* Add internal quoting style switch to @sh

* Add test for stray empty quotes in @sh
2023-02-09 18:15:07 +11:00
Mike Farah f64f73a0ce Updated gosec 2023-02-09 18:13:18 +11:00
Mike Farah 88a6b20ba5 Fixed date comparison with string date #1537 2023-02-02 13:30:48 +11:00
Mike Farah 915ab69922 Added to_unix operator 2023-02-02 12:56:16 +11:00
Mike Farah f9f340b6bf Github pipeline not in AU tz :D 2023-02-02 12:47:59 +11:00
Mike Farah 75920481b1 Added from_unix operator #1535 2023-02-02 12:42:36 +11:00
Mike Farah d21bb920d6 Added shell string encoder (@sh) #1526 2023-02-02 12:23:08 +11:00
Arturo FernandezandGitHub d119dbc239 Upgrading golang.org/x/net package (#1540) 2023-02-02 11:56:36 +11:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
7eda4a5100 Bump github.com/fatih/color from 1.13.0 to 1.14.1 (#1531)
Bumps [github.com/fatih/color](https://github.com/fatih/color) from 1.13.0 to 1.14.1.
- [Release notes](https://github.com/fatih/color/releases)
- [Commits](https://github.com/fatih/color/compare/v1.13.0...v1.14.1)

---
updated-dependencies:
- dependency-name: github.com/fatih/color
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-30 10:22:42 +11:00
Mike Farah 3b1bcac5b3 review feed back on URI 2023-01-25 11:18:48 +11:00
Mike Farah 6d7d76a3f1 Adds @uri/@urid #1529 2023-01-23 11:37:18 +11:00
Mike Farah dd6cf3df14 Bumping version 2023-01-15 11:37:07 +11:00
Mike Farah 473be23153 Log info message when unable to chown file #1521 2023-01-15 11:36:52 +11:00
Mike Farah ec40a1a08a Bumping version 2023-01-14 14:05:19 +11:00
Mike Farah 729ac285da updating release notes 2023-01-14 14:04:48 +11:00
Mike Farah 3b84c03131 Fixed bug in splice operator #1511 2023-01-13 13:45:19 +11:00
Mike Farah d7da0cca3c Fixed value operator bug #1515 2023-01-12 15:11:45 +11:00
Mike Farah fcda053d73 Updating tests from lexer version bump 2023-01-12 14:26:58 +11:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
e23c989b78 Bump github.com/alecthomas/repr from 0.1.1 to 0.2.0 (#1517)
Bumps [github.com/alecthomas/repr](https://github.com/alecthomas/repr) from 0.1.1 to 0.2.0.
- [Release notes](https://github.com/alecthomas/repr/releases)
- [Commits](https://github.com/alecthomas/repr/compare/v0.1.1...v0.2.0)

---
updated-dependencies:
- dependency-name: github.com/alecthomas/repr
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-12 14:22:43 +11:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
95d14cb0af Bump golang from 1.19.4 to 1.19.5 (#1516)
Bumps golang from 1.19.4 to 1.19.5.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-12 14:21:28 +11:00
Mike Farah 00c2be541d Added prepend array example 2023-01-11 12:19:46 +11:00
Mike Farah 9af55d555b Fixed handling of merging null #1501 2023-01-10 13:48:57 +11:00
Mike Farah ef2064c753 Updating release notes 2023-01-03 19:25:27 +11:00
18cdea3f88 Build constraint not working for non linux (#1481)
* Build constraint not working for non linux

* Go Build Constraint: Fix Non-Linux Filename (#1494)

Correct the filename pkg/yqlib/chown_not_linux.go to escape the
default OS detection in filename format `*_GOOS[_test].go`.

Add an extra word after `linux` to resolve the issue.
pkg/yqlib/chown_not_linux_os.go

Signed-off-by: Bhargav Ravuri <vaguecoder0to.n@gmail.com>

Signed-off-by: Bhargav Ravuri <vaguecoder0to.n@gmail.com>

Signed-off-by: Bhargav Ravuri <vaguecoder0to.n@gmail.com>
Co-authored-by: Bhargav Ravuri <saibhargavravuri@gmail.com>
2023-01-03 15:52:01 +11:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
36e0194e42 Bump github.com/goccy/go-yaml from 1.9.7 to 1.9.8 (#1487)
Bumps [github.com/goccy/go-yaml](https://github.com/goccy/go-yaml) from 1.9.7 to 1.9.8.
- [Release notes](https://github.com/goccy/go-yaml/releases)
- [Changelog](https://github.com/goccy/go-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/goccy/go-yaml/compare/v1.9.7...v1.9.8)

---
updated-dependencies:
- dependency-name: github.com/goccy/go-yaml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-25 11:45:10 +11:00
Mike Farah 87cba2ecbe Bumping version 2022-12-17 11:16:38 +11:00
Mike Farah 8d8e7c3b2c release notes 2022-12-17 11:15:16 +11:00
Mike Farah 48d00f807e Fixed xml comment in array of scalars #1465 2022-12-17 10:27:54 +11:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
481ea64ccc Bump github.com/magiconair/properties from 1.8.6 to 1.8.7 (#1476)
Bumps [github.com/magiconair/properties](https://github.com/magiconair/properties) from 1.8.6 to 1.8.7.
- [Release notes](https://github.com/magiconair/properties/releases)
- [Changelog](https://github.com/magiconair/properties/blob/main/CHANGELOG.md)
- [Commits](https://github.com/magiconair/properties/compare/v1.8.6...v1.8.7)

---
updated-dependencies:
- dependency-name: github.com/magiconair/properties
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-15 19:35:57 +11:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
f6a48b6899 Bump github.com/goccy/go-yaml from 1.9.6 to 1.9.7 (#1466)
Bumps [github.com/goccy/go-yaml](https://github.com/goccy/go-yaml) from 1.9.6 to 1.9.7.
- [Release notes](https://github.com/goccy/go-yaml/releases)
- [Changelog](https://github.com/goccy/go-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/goccy/go-yaml/compare/v1.9.6...v1.9.7)

---
updated-dependencies:
- dependency-name: github.com/goccy/go-yaml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-09 08:05:37 +11:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
8e5290d7c5 Bump golang from 1.19.3 to 1.19.4 (#1465)
Bumps golang from 1.19.3 to 1.19.4.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-09 08:05:26 +11:00
Mike Farah 83c5e1bc83 Include blank new lines in leading header preprocessing #1462 2022-12-08 13:33:06 +11:00
Bruno BottazziniandGitHub 8df8d89c6d Upgraded golang/x/text and golang/x/net to fix trivy scan vulnerabilities (#1461) 2022-12-06 20:48:47 +11:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
226f1c1a9b Bump github.com/goccy/go-json from 0.9.11 to 0.10.0 (#1455)
Bumps [github.com/goccy/go-json](https://github.com/goccy/go-json) from 0.9.11 to 0.10.0.
- [Release notes](https://github.com/goccy/go-json/releases)
- [Changelog](https://github.com/goccy/go-json/blob/master/CHANGELOG.md)
- [Commits](https://github.com/goccy/go-json/compare/v0.9.11...v0.10.0)

---
updated-dependencies:
- dependency-name: github.com/goccy/go-json
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-01 19:26:28 +11:00
Mike Farah bb3f3e541d updating release notes 2022-11-28 08:39:48 +11:00
Mike Farah 42e7c3cdf0 Bumping version 2022-11-28 08:36:05 +11:00
Mike Farah f9a8ba91e4 Preparing release 2022-11-28 08:34:58 +11:00
Mike Farah 1617e5de19 XML Decoder: Comment parsing tweak 2022-11-27 19:14:41 +11:00
Mike Farah 77998d1bb3 XML Decoder: Fixed processing comments in empty XML #1446 2022-11-27 17:58:37 +11:00
Mike Farah 68f47c02c8 XML Decoder: Checking for invalid content outside of a root node #1448 2022-11-27 17:46:50 +11:00
Mike Farah 02be2b2918 Fixed issue where content surrounding tags are lost #1447 2022-11-27 17:29:27 +11:00
Mike Farah 43233ce62e Fixed xml decode bug when there is content after a comment 2022-11-25 13:21:19 +11:00
Mike Farah 51043770bf Fixed log level, deleted commented code 2022-11-25 12:09:45 +11:00
Mike Farah 4478bd14c9 Fixed loading yaml with header issue #1445 2022-11-25 12:05:56 +11:00
Mike Farah 91dc315fdb updating release notes 2022-11-15 21:20:41 +11:00
Mike Farah a6d1a52e33 Fixing versioning code for release 2022-11-15 20:08:58 +11:00
Mike Farah fd35530f35 Set tags on maps,arrays when decoding json #1422 2022-11-15 11:42:31 +11:00
Mike Farah cb609a1886 Fixed bad line entry for #1422 2022-11-15 11:35:31 +11:00
Mike Farah b202ccc5dc Updating snapcraft release notes 2022-11-15 11:05:56 +11:00
Mike Farah ee226b47fc Preparing release 2022-11-15 10:05:08 +11:00
Mike Farah 9edff1f22c Use new github action syntax 2022-11-14 17:40:12 +11:00
Mike Farah 67864ffdab Use new github action syntax 2022-11-14 17:35:52 +11:00
Mike Farah b55381f34e fixing snap release 2022-11-14 17:28:44 +11:00
Mike Farah b6ad314dbb fixing snap release 2022-11-14 17:28:15 +11:00
Mike Farah 762f46ed54 include yaml directives #1424 2022-11-14 17:22:13 +11:00
Mike Farah 63db5de4e2 (attempt) to automate snap release 2022-11-14 17:00:50 +11:00
Mike Farah 9606957a0e Fixed auto versioning in xcompile.sh 2022-11-14 16:43:06 +11:00
Mike Farah e02bb71948 Fixed parsing of newline character in string expression #1430 2022-11-14 16:40:59 +11:00
Takumi SueandGitHub 1fd96e168e Fix mis-versioning due to hardcoding cmd.Version (#1431) 2022-11-14 16:38:43 +11:00
Mike Farah ffc20f7e2c Length compares to null instead of 0 issue #1427 2022-11-14 16:14:08 +11:00
Mike Farah 3e795d020d Updating docs 2022-11-13 11:16:01 +11:00
Mike Farah 88ce6ffcbe Hotfix: actually update the default xml attribute prefix #1428 2022-11-13 11:13:34 +11:00
88 changed files with 1494 additions and 227 deletions
+2 -2
View File
@@ -10,10 +10,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.19
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.19
go-version: '^1.20'
id: go
- name: Check out code into the Go module directory
+3 -4
View File
@@ -12,17 +12,16 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: '^1.19'
go-version: '^1.20'
check-latest: true
- name: Compile man page markup
id: gen-man-page-md
run: |
./scripts/generate-man-page-md.sh
echo "::set-output name=man-page-md::man.md"
- name: Get the version
id: get_version
run: echo ::set-output name=VERSION::${GITHUB_REF##*/}
run: echo "VERSION=${GITHUB_REF##*/}" >> "${GITHUB_OUTPUT}"
- name: Generate man page
uses: docker://pandoc/core:2.14.2
@@ -36,7 +35,7 @@ jobs:
--variable=header:"yq (https://github.com/mikefarah/yq/) version ${{ steps.get_version.outputs.VERSION }}"
--variable=author:"Mike Farah"
--output=yq.1
${{ steps.gen-man-page-md.outputs.man-page-md }}
man.md
- name: Cross compile
run: |
+23
View File
@@ -0,0 +1,23 @@
name: Release Snap
on:
release:
types: [released]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
buildSnap:
environment: snap
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: snapcore/action-build@v1
id: build
- uses: snapcore/action-publish@v1
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.STORE_LOGIN }}
with:
snap: ${{ steps.build.outputs.snap }}
release: stable
+1 -1
View File
@@ -1,4 +1,4 @@
FROM golang:1.19.3 as builder
FROM golang:1.20.1 as builder
WORKDIR /go/src/mikefarah/yq
+1 -1
View File
@@ -1,4 +1,4 @@
FROM golang:1.19.3
FROM golang:1.20.1
COPY scripts/devtools.sh /opt/devtools.sh
+76
View File
@@ -34,6 +34,82 @@ EOM
assertEquals "$expected" "$X"
}
testLeadingSeperatorWithNewlinesNewDoc() {
cat >test.yml <<EOL
# hi peeps
# cool
---
a: test
---
b: cool
EOL
read -r -d '' expected << EOM
# hi peeps
# cool
---
a: thing
---
b: cool
EOM
X=$(./yq e '(select(di == 0) | .a) = "thing"' - < test.yml)
assertEquals "$expected" "$X"
}
testLeadingSeperatorWithNewlinesMoreComments() {
cat >test.yml <<EOL
# hi peeps
# cool
---
# great
a: test
---
b: cool
EOL
read -r -d '' expected << EOM
# hi peeps
# cool
---
# great
a: thing
---
b: cool
EOM
X=$(./yq e '(select(di == 0) | .a) = "thing"' - < test.yml)
assertEquals "$expected" "$X"
}
testLeadingSeperatorWithDirective() {
cat >test.yml <<EOL
%YAML 1.1
---
this: should really work
EOL
read -r -d '' expected << EOM
%YAML 1.1
---
this: should really work
EOM
X=$(./yq < test.yml)
assertEquals "$expected" "$X"
}
testLeadingSeperatorPipeIntoEvalSeq() {
X=$(./yq e - < test.yml)
expected=$(cat test.yml)
-16
View File
@@ -59,22 +59,6 @@ yq -P sample.json
return err
}
inputFormatType, err := yqlib.InputFormatFromString(inputFormat)
if err != nil {
return err
}
if (inputFormatType == yqlib.XMLInputFormat &&
outputFormatType != yqlib.XMLOutputFormat ||
inputFormatType != yqlib.XMLInputFormat &&
outputFormatType == yqlib.XMLOutputFormat) &&
yqlib.ConfiguredXMLPreferences.AttributePrefix == "+@" {
yqlib.GetLogger().Warning("The default xml-attribute-prefix has changed in the v4.30 to `+@` to avoid " +
"naming conflicts with the default content name, directive name and proc inst prefix. If you need to keep " +
"`+` please set that value explicityly with --xml-attribute-prefix.")
}
if outputFormatType == yqlib.YamlOutputFormat ||
outputFormatType == yqlib.PropsOutputFormat {
unwrapScalar = true
+1 -1
View File
@@ -11,7 +11,7 @@ var (
GitDescribe string
// Version is main version number that is being run at the moment.
Version = "4.30.1"
Version = "v4.31.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
+5 -7
View File
@@ -1,7 +1,5 @@
<?xml version="1.0"?>
<!DOCTYPE config SYSTEM "/etc/iwatch/iwatch.dtd" >
<apple>
<?coolioo version="1.0"?>
<!DOCTYPE config SYSTEM "/etc/iwatch/iwatch.dtd" >
<b>things</b>
</apple>
date: "2022-11-25"
raw:
id: 1
XML: text_outside_1<Tag1 />text_outside_2<Tag2Kling Klong</Tag2>text_outside_3
time: 09:19:00
+4
View File
@@ -0,0 +1,4 @@
---
# comment
# about things
a: cat
+12 -12
View File
@@ -1,31 +1,31 @@
module github.com/mikefarah/yq/v4
require (
github.com/a8m/envsubst v1.3.0
github.com/a8m/envsubst v1.4.1
github.com/alecthomas/participle/v2 v2.0.0-beta.5
github.com/alecthomas/repr v0.1.1
github.com/alecthomas/repr v0.2.0
github.com/dimchansky/utfbom v1.1.1
github.com/elliotchance/orderedmap v1.5.0
github.com/fatih/color v1.13.0
github.com/goccy/go-json v0.9.11
github.com/goccy/go-yaml v1.9.6
github.com/fatih/color v1.14.1
github.com/goccy/go-json v0.10.0
github.com/goccy/go-yaml v1.9.8
github.com/jinzhu/copier v0.3.5
github.com/magiconair/properties v1.8.6
github.com/magiconair/properties v1.8.7
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e
github.com/spf13/cobra v1.6.1
github.com/spf13/pflag v1.0.5
golang.org/x/net v0.0.0-20220708220712-1185a9018129
golang.org/x/net v0.1.1-0.20221104162952-702349b0e862
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e // indirect
golang.org/x/text v0.3.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
golang.org/x/sys v0.3.0 // indirect
golang.org/x/text v0.4.0 // indirect
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
)
go 1.19
go 1.20
+24 -25
View File
@@ -1,10 +1,10 @@
github.com/a8m/envsubst v1.3.0 h1:GmXKmVssap0YtlU3E230W98RWtWCyIZzjtf1apWWyAg=
github.com/a8m/envsubst v1.3.0/go.mod h1:MVUTQNGQ3tsjOOtKCNd+fl8RzhsXcDvvAEzkhGtlsbY=
github.com/a8m/envsubst v1.4.1 h1:RShc2OKbQpIIp5qR2glVsBOJCImTVHFrDkJvmGh0Qi0=
github.com/a8m/envsubst v1.4.1/go.mod h1:MVUTQNGQ3tsjOOtKCNd+fl8RzhsXcDvvAEzkhGtlsbY=
github.com/alecthomas/assert/v2 v2.0.3 h1:WKqJODfOiQG0nEJKFKzDIG3E29CN2/4zR9XGJzKIkbg=
github.com/alecthomas/participle/v2 v2.0.0-beta.5 h1:y6dsSYVb1G5eK6mgmy+BgI3Mw35a3WghArZ/Hbebrjo=
github.com/alecthomas/participle/v2 v2.0.0-beta.5/go.mod h1:RC764t6n4L8D8ITAJv0qdokritYSNR3wV5cVwmIEaMM=
github.com/alecthomas/repr v0.1.1 h1:87P60cSmareLAxMc4Hro0r2RBY4ROm0dYwkJNpS4pPs=
github.com/alecthomas/repr v0.1.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -13,31 +13,31 @@ github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/
github.com/elliotchance/orderedmap v1.5.0 h1:1IsExUsjv5XNBD3ZdC7jkAAqLWOOKdbPTmkHx63OsBg=
github.com/elliotchance/orderedmap v1.5.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-yaml v1.9.6 h1:KhAu1zf9JXnm3vbG49aDE0E5uEBUsM4uwD31/58ZWyI=
github.com/goccy/go-yaml v1.9.6/go.mod h1:JubOolP3gh0HpiBc4BLRD4YmjEjHAmIIB2aaXKkTfoE=
github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA=
github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-yaml v1.9.8 h1:5gMyLUeU1/6zl+WFfR1hN7D2kf+1/eRGa7DFtToiBvQ=
github.com/goccy/go-yaml v1.9.8/go.mod h1:JubOolP3gh0HpiBc4BLRD4YmjEjHAmIIB2aaXKkTfoE=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg=
github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -54,21 +54,20 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20220708220712-1185a9018129 h1:vucSRfWwTsoXro7P+3Cjlr6flUMtzCwzlvkxEQtHHB0=
golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.1-0.20221104162952-702349b0e862 h1:KrLJ+iz8J6j6VVr/OCfULAcK+xozUmWE43fKpMR4MlI=
golang.org/x/net v0.1.1-0.20221104162952-702349b0e862/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220406163625-3f8b81556e12/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e h1:NHvCuwuS43lGnYhten69ZWqi2QOj/CiDNcKbVqwVoew=
golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0=
+25
View File
@@ -0,0 +1,25 @@
//go:build linux
package yqlib
import (
"io/fs"
"os"
"syscall"
)
func changeOwner(info fs.FileInfo, file *os.File) error {
if stat, ok := info.Sys().(*syscall.Stat_t); ok {
uid := int(stat.Uid)
gid := int(stat.Gid)
err := os.Chown(file.Name(), uid, gid)
if err != nil {
// this happens with snap confinement
// not really a big issue as users can chown
// the file themselves if required.
log.Info("Skipping chown: %v", err)
}
}
return nil
}
+12
View File
@@ -0,0 +1,12 @@
//go:build !linux
package yqlib
import (
"io/fs"
"os"
)
func changeOwner(info fs.FileInfo, file *os.File) error {
return nil
}
+1
View File
@@ -15,6 +15,7 @@ const (
JsonInputFormat
CSVObjectInputFormat
TSVObjectInputFormat
UriInputFormat
)
type Decoder interface {
+2 -2
View File
@@ -60,7 +60,7 @@ func (dec *jsonDecoder) convertToYamlNode(data *orderedMap) (*yaml.Node, error)
}
}
var yamlMap = &yaml.Node{Kind: yaml.MappingNode}
var yamlMap = &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map"}
for _, keyValuePair := range data.kv {
yamlValue, err := dec.convertToYamlNode(&keyValuePair.V)
if err != nil {
@@ -74,7 +74,7 @@ func (dec *jsonDecoder) convertToYamlNode(data *orderedMap) (*yaml.Node, error)
func (dec *jsonDecoder) parseArray(dataArray []*orderedMap) (*yaml.Node, error) {
var yamlMap = &yaml.Node{Kind: yaml.SequenceNode}
var yamlMap = &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq"}
for _, value := range dataArray {
yamlValue, err := dec.convertToYamlNode(value)
+2 -2
View File
@@ -63,7 +63,7 @@ func (dec *propertiesDecoder) applyPropertyComments(context Context, path []inte
rhsCandidateNode.Node.Tag = guessTagFromCustomType(rhsCandidateNode.Node)
rhsOp := &Operation{OperationType: valueOpType, CandidateNode: rhsCandidateNode}
rhsOp := &Operation{OperationType: referenceOpType, CandidateNode: rhsCandidateNode}
assignmentOpNode := &ExpressionNode{
Operation: assignmentOp,
@@ -102,7 +102,7 @@ func (dec *propertiesDecoder) applyProperty(context Context, properties *propert
assignmentOp := &Operation{OperationType: assignOpType, Preferences: assignPreferences{}}
rhsOp := &Operation{OperationType: valueOpType, CandidateNode: rhsCandidateNode}
rhsOp := &Operation{OperationType: referenceOpType, CandidateNode: rhsCandidateNode}
assignmentOpNode := &ExpressionNode{
Operation: assignmentOp,
+60
View File
@@ -0,0 +1,60 @@
package yqlib
import (
"bytes"
"io"
"net/url"
yaml "gopkg.in/yaml.v3"
)
type uriDecoder struct {
reader io.Reader
finished bool
readAnything bool
}
func NewUriDecoder() Decoder {
return &uriDecoder{finished: false}
}
func (dec *uriDecoder) Init(reader io.Reader) error {
dec.reader = reader
dec.readAnything = false
dec.finished = false
return nil
}
func (dec *uriDecoder) Decode() (*CandidateNode, error) {
if dec.finished {
return nil, io.EOF
}
buf := new(bytes.Buffer)
if _, err := buf.ReadFrom(dec.reader); err != nil {
return nil, err
}
if buf.Len() == 0 {
dec.finished = true
// if we've read _only_ an empty string, lets return that
// otherwise if we've already read some bytes, and now we get
// an empty string, then we are done.
if dec.readAnything {
return nil, io.EOF
}
}
newValue, err := url.QueryUnescape(buf.String())
if err != nil {
return nil, err
}
dec.readAnything = true
return &CandidateNode{
Node: &yaml.Node{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: newValue,
},
}, nil
}
+72 -23
View File
@@ -3,6 +3,7 @@ package yqlib
import (
"encoding/xml"
"errors"
"fmt"
"io"
"strings"
"unicode"
@@ -53,15 +54,17 @@ func (dec *xmlDecoder) processComment(c string) string {
}
func (dec *xmlDecoder) createMap(n *xmlNode) (*yaml.Node, error) {
log.Debug("createMap: headC: %v, footC: %v", n.HeadComment, n.FootComment)
log.Debug("createMap: headC: %v, lineC: %v, footC: %v", n.HeadComment, n.LineComment, n.FootComment)
yamlNode := &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map"}
if len(n.Data) > 0 {
log.Debugf("creating content node for map: %v", dec.prefs.ContentName)
label := dec.prefs.ContentName
labelNode := createScalarNode(label, label)
labelNode.HeadComment = dec.processComment(n.HeadComment)
labelNode.LineComment = dec.processComment(n.LineComment)
labelNode.FootComment = dec.processComment(n.FootComment)
yamlNode.Content = append(yamlNode.Content, labelNode, createScalarNode(n.Data, n.Data))
yamlNode.Content = append(yamlNode.Content, labelNode, dec.createValueNodeFromData(n.Data))
}
for i, keyValuePair := range n.Children {
@@ -75,7 +78,7 @@ func (dec *xmlDecoder) createMap(n *xmlNode) (*yaml.Node, error) {
labelNode.HeadComment = dec.processComment(n.HeadComment)
}
log.Debugf("label=%v, i=%v, keyValuePair.FootComment: %v", label, i, keyValuePair.FootComment)
labelNode.FootComment = dec.processComment(keyValuePair.FootComment)
log.Debug("len of children in %v is %v", label, len(children))
@@ -89,8 +92,16 @@ func (dec *xmlDecoder) createMap(n *xmlNode) (*yaml.Node, error) {
// if the value is a scalar, the head comment of the scalar needs to go on the key?
// add tests for <z/> as well as multiple <ds> of inputXmlWithComments > yaml
if len(children[0].Children) == 0 && children[0].HeadComment != "" {
labelNode.HeadComment = labelNode.HeadComment + "\n" + strings.TrimSpace(children[0].HeadComment)
children[0].HeadComment = ""
if len(children[0].Data) > 0 {
log.Debug("scalar comment hack, currentlabel [%v]", labelNode.HeadComment)
labelNode.HeadComment = joinComments([]string{labelNode.HeadComment, strings.TrimSpace(children[0].HeadComment)}, "\n")
children[0].HeadComment = ""
} else {
// child is null, put the headComment as a linecomment for reasons
children[0].LineComment = children[0].HeadComment
children[0].HeadComment = ""
}
}
valueNode, err = dec.convertToYamlNode(children[0])
if err != nil {
@@ -103,17 +114,40 @@ func (dec *xmlDecoder) createMap(n *xmlNode) (*yaml.Node, error) {
return yamlNode, nil
}
func (dec *xmlDecoder) createValueNodeFromData(values []string) *yaml.Node {
switch len(values) {
case 0:
return createScalarNode(nil, "")
case 1:
return createScalarNode(values[0], values[0])
default:
content := make([]*yaml.Node, 0)
for _, value := range values {
content = append(content, createScalarNode(value, value))
}
return &yaml.Node{
Kind: yaml.SequenceNode,
Tag: "!!seq",
Content: content,
}
}
}
func (dec *xmlDecoder) convertToYamlNode(n *xmlNode) (*yaml.Node, error) {
if len(n.Children) > 0 {
return dec.createMap(n)
}
scalar := createScalarNode(n.Data, n.Data)
if n.Data == "" {
scalar = createScalarNode(nil, "")
}
log.Debug("scalar headC: %v, footC: %v", n.HeadComment, n.FootComment)
scalar := dec.createValueNodeFromData(n.Data)
log.Debug("scalar (%v), headC: %v, lineC: %v, footC: %v", scalar.Tag, n.HeadComment, n.LineComment, n.FootComment)
scalar.HeadComment = dec.processComment(n.HeadComment)
scalar.LineComment = dec.processComment(n.LineComment)
if scalar.Tag == "!!seq" {
scalar.Content[0].HeadComment = scalar.LineComment
scalar.LineComment = ""
}
scalar.FootComment = dec.processComment(n.FootComment)
return scalar, nil
@@ -156,7 +190,7 @@ type xmlNode struct {
HeadComment string
FootComment string
LineComment string
Data string
Data []string
}
type xmlChildrenKv struct {
@@ -201,6 +235,8 @@ func (dec *xmlDecoder) decodeXML(root *xmlNode) error {
// That will convert the charset if the provided XML is non-UTF-8
xmlDec.CharsetReader = charset.NewReaderLabel
started := false
// Create first element from the root node
elem := &element{
parent: nil,
@@ -241,12 +277,18 @@ func (dec *xmlDecoder) decodeXML(root *xmlNode) error {
a.Name.Local = a.Name.Space + ":" + a.Name.Local
}
}
elem.n.AddChild(dec.prefs.AttributePrefix+a.Name.Local, &xmlNode{Data: a.Value})
elem.n.AddChild(dec.prefs.AttributePrefix+a.Name.Local, &xmlNode{Data: []string{a.Value}})
}
case xml.CharData:
// Extract XML data (if any)
elem.n.Data = trimNonGraphic(string(se))
if elem.n.Data != "" {
newBit := trimNonGraphic(string(se))
if !started && len(newBit) > 0 {
return fmt.Errorf("invalid XML: Encountered chardata [%v] outside of XML node", newBit)
}
if len(newBit) > 0 {
elem.n.Data = append(elem.n.Data, newBit)
elem.state = "chardata"
log.Debug("chardata [%v] for %v", elem.n.Data, elem.label)
}
@@ -268,21 +310,22 @@ func (dec *xmlDecoder) decodeXML(root *xmlNode) error {
} else if elem.state == "chardata" {
log.Debug("got a line comment for (%v) %v: [%v]", elem.state, elem.label, commentStr)
elem.n.LineComment = joinFilter([]string{elem.n.LineComment, commentStr})
elem.n.LineComment = joinComments([]string{elem.n.LineComment, commentStr}, " ")
} else {
log.Debug("got a head comment for (%v) %v: [%v]", elem.state, elem.label, commentStr)
elem.n.HeadComment = joinFilter([]string{elem.n.HeadComment, commentStr})
elem.n.HeadComment = joinComments([]string{elem.n.HeadComment, commentStr}, " ")
}
case xml.ProcInst:
if !dec.prefs.SkipProcInst {
elem.n.AddChild(dec.prefs.ProcInstPrefix+se.Target, &xmlNode{Data: string(se.Inst)})
elem.n.AddChild(dec.prefs.ProcInstPrefix+se.Target, &xmlNode{Data: []string{string(se.Inst)}})
}
case xml.Directive:
if !dec.prefs.SkipDirectives {
elem.n.AddChild(dec.prefs.DirectiveName, &xmlNode{Data: string(se)})
elem.n.AddChild(dec.prefs.DirectiveName, &xmlNode{Data: []string{string(se)}})
}
}
started = true
}
return nil
@@ -294,22 +337,28 @@ func applyFootComment(elem *element, commentStr string) {
if len(elem.n.Children) > 0 {
lastChildIndex := len(elem.n.Children) - 1
childKv := elem.n.Children[lastChildIndex]
log.Debug("got a foot comment for %v: [%v]", childKv.K, commentStr)
childKv.FootComment = joinFilter([]string{elem.n.FootComment, commentStr})
log.Debug("got a foot comment, putting on last child for %v: [%v]", childKv.K, commentStr)
// if it's an array of scalars, put the foot comment on the scalar itself
if len(childKv.V) > 0 && len(childKv.V[0].Children) == 0 {
nodeToUpdate := childKv.V[len(childKv.V)-1]
nodeToUpdate.FootComment = joinComments([]string{nodeToUpdate.FootComment, commentStr}, " ")
} else {
childKv.FootComment = joinComments([]string{elem.n.FootComment, commentStr}, " ")
}
} else {
log.Debug("got a foot comment for %v: [%v]", elem.label, commentStr)
elem.n.FootComment = joinFilter([]string{elem.n.FootComment, commentStr})
elem.n.FootComment = joinComments([]string{elem.n.FootComment, commentStr}, " ")
}
}
func joinFilter(rawStrings []string) string {
func joinComments(rawStrings []string, joinStr string) string {
stringsToJoin := make([]string, 0)
for _, str := range rawStrings {
if str != "" {
stringsToJoin = append(stringsToJoin, str)
}
}
return strings.Join(stringsToJoin, " ")
return strings.Join(stringsToJoin, joinStr)
}
// trimNonGraphic returns a slice of the string s, with all leading and trailing
+35 -6
View File
@@ -2,6 +2,7 @@ package yqlib
import (
"bufio"
"bytes"
"errors"
"io"
"regexp"
@@ -12,11 +13,15 @@ import (
type yamlDecoder struct {
decoder yaml.Decoder
prefs YamlPreferences
// work around of various parsing issues by yaml.v3 with document headers
prefs YamlPreferences
leadingContent string
readAnything bool
firstFile bool
bufferRead bytes.Buffer
readAnything bool
firstFile bool
}
func NewYamlDecoder(prefs YamlPreferences) Decoder {
@@ -25,6 +30,7 @@ func NewYamlDecoder(prefs YamlPreferences) Decoder {
func (dec *yamlDecoder) processReadStream(reader *bufio.Reader) (io.Reader, string, error) {
var commentLineRegEx = regexp.MustCompile(`^\s*#`)
var yamlDirectiveLineRegEx = regexp.MustCompile(`^\s*%YA`)
var sb strings.Builder
for {
peekBytes, err := reader.Peek(3)
@@ -33,6 +39,14 @@ func (dec *yamlDecoder) processReadStream(reader *bufio.Reader) (io.Reader, stri
return reader, sb.String(), nil
} else if err != nil {
return reader, sb.String(), err
} else if string(peekBytes[0]) == "\n" {
_, err := reader.ReadString('\n')
sb.WriteString("\n")
if errors.Is(err, io.EOF) {
return reader, sb.String(), nil
} else if err != nil {
return reader, sb.String(), err
}
} else if string(peekBytes) == "---" {
_, err := reader.ReadString('\n')
sb.WriteString("$yqDocSeperator$\n")
@@ -41,7 +55,7 @@ func (dec *yamlDecoder) processReadStream(reader *bufio.Reader) (io.Reader, stri
} else if err != nil {
return reader, sb.String(), err
}
} else if commentLineRegEx.MatchString(string(peekBytes)) {
} else if commentLineRegEx.MatchString(string(peekBytes)) || yamlDirectiveLineRegEx.MatchString(string(peekBytes)) {
line, err := reader.ReadString('\n')
sb.WriteString(line)
if errors.Is(err, io.EOF) {
@@ -58,6 +72,7 @@ func (dec *yamlDecoder) processReadStream(reader *bufio.Reader) (io.Reader, stri
func (dec *yamlDecoder) Init(reader io.Reader) error {
readerToUse := reader
leadingContent := ""
dec.bufferRead = bytes.Buffer{}
var err error
// if we 'evaluating together' - we only process the leading content
// of the first file - this ensures comments from subsequent files are
@@ -67,6 +82,12 @@ func (dec *yamlDecoder) Init(reader io.Reader) error {
if err != nil {
return err
}
} else if !dec.prefs.LeadingContentPreProcessing {
// if we're not process the leading content
// keep a copy of what we've read. This is incase its a
// doc with only comments - the decoder will return nothing
// then we can read the comments from bufferRead
readerToUse = io.TeeReader(reader, &dec.bufferRead)
}
dec.leadingContent = leadingContent
dec.readAnything = false
@@ -77,13 +98,20 @@ func (dec *yamlDecoder) Init(reader io.Reader) error {
func (dec *yamlDecoder) Decode() (*CandidateNode, error) {
var dataBucket yaml.Node
err := dec.decoder.Decode(&dataBucket)
if errors.Is(err, io.EOF) && dec.leadingContent != "" && !dec.readAnything {
// force returning an empty node with a comment.
dec.readAnything = true
return dec.blankNodeWithComment(), nil
} else if errors.Is(err, io.EOF) && !dec.prefs.LeadingContentPreProcessing && !dec.readAnything {
// didn't find any yaml,
// check the tee buffer, maybe there were comments
dec.readAnything = true
dec.leadingContent = dec.bufferRead.String()
if dec.leadingContent != "" {
return dec.blankNodeWithComment(), nil
}
return nil, err
} else if err != nil {
return nil, err
}
@@ -96,6 +124,7 @@ func (dec *yamlDecoder) Decode() (*CandidateNode, error) {
candidateNode.LeadingContent = dec.leadingContent
dec.leadingContent = ""
}
dec.readAnything = true
// move document comments into candidate node
// otherwise unwrap drops them.
candidateNode.TrailingContent = dataBucket.FootComment
+17
View File
@@ -86,6 +86,23 @@ will output
a: ['dog', 'cat']
```
## Prepend to existing array
Given a sample.yml file of:
```yaml
a:
- dog
```
then
```bash
yq '.a = ["cat"] + .a' sample.yml
```
will output
```yaml
a:
- cat
- dog
```
## Add new object to array
Given a sample.yml file of:
```yaml
+1 -1
View File
@@ -1,6 +1,6 @@
# Array to Map
Use this operator to convert an array to..a map. Skips over null values.
Use this operator to convert an array to..a map. The indices are used as map keys, null values in the array are skipped over.
Behind the scenes, this is implemented using reduce:
@@ -247,6 +247,7 @@ Note the use of `...` to ensure key nodes are included.
Given a sample.yml file of:
```yaml
# hi
a: cat # comment
# great
b: # key comment
@@ -265,6 +266,7 @@ b:
Given a sample.yml file of:
```yaml
# welcome!
a: cat # meow
# have a great day
```
@@ -293,6 +295,7 @@ yq '. | head_comment' sample.yml
will output
```yaml
welcome!
```
## Head comment with document split
+24
View File
@@ -86,6 +86,30 @@ a: cool
updated: 2021-05-19T01:02:03Z
```
## From Unix
Converts from unix time. Note, you don't have to pipe through the tz operator :)
Running
```bash
yq --null-input '1675301929 | from_unix | tz("UTC")'
```
will output
```yaml
2023-02-02T01:38:49Z
```
## To Unix
Converts to unix time
Running
```bash
yq --null-input 'now | to_unix'
```
will output
```yaml
1621386123
```
## Timezone: from standard RFC3339 format
Returns a new datetime in the specified timezone. Specify standard IANA Time Zone format or 'utc', 'local'. When given a single parameter, this assumes the datetime is in RFC3339 format.
+46
View File
@@ -16,6 +16,8 @@ These operators are useful to process yaml documents that have stringified embed
| TSV | from_tsv/@tsvd | to_tsv/@tsv |
| XML | from_xml/@xmld | to_xml(i)/@xml |
| Base64 | @base64d | @base64 |
| URI | @urid | @uri |
| Shell | | @sh |
See CSV and TSV [documentation](https://mikefarah.gitbook.io/yq/usage/csv-tsv) for accepted formats.
@@ -435,6 +437,50 @@ will output
YTogYXBwbGUK
```
## Encode a string to uri
Given a sample.yml file of:
```yaml
coolData: this has & special () characters *
```
then
```bash
yq '.coolData | @uri' sample.yml
```
will output
```yaml
this+has+%26+special+%28%29+characters+%2A
```
## Decode a URI to a string
Given a sample.yml file of:
```yaml
this+has+%26+special+%28%29+characters+%2A
```
then
```bash
yq '@urid' sample.yml
```
will output
```yaml
this has & special () characters *
```
## Encode a string to sh
Sh/Bash friendly string
Given a sample.yml file of:
```yaml
coolData: strings with spaces and a 'quote'
```
then
```bash
yq '.coolData | @sh' sample.yml
```
will output
```yaml
strings' with spaces and a '\'quote\'
```
## Decode a base64 encoded string
Decoded data is assumed to be a string.
@@ -1,6 +1,6 @@
# Array to Map
Use this operator to convert an array to..a map. Skips over null values.
Use this operator to convert an array to..a map. The indices are used as map keys, null values in the array are skipped over.
Behind the scenes, this is implemented using reduce:
@@ -16,6 +16,8 @@ These operators are useful to process yaml documents that have stringified embed
| TSV | from_tsv/@tsvd | to_tsv/@tsv |
| XML | from_xml/@xmld | to_xml(i)/@xml |
| Base64 | @base64d | @base64 |
| URI | @urid | @uri |
| Shell | | @sh |
See CSV and TSV [documentation](https://mikefarah.gitbook.io/yq/usage/csv-tsv) for accepted formats.
@@ -0,0 +1,4 @@
# Shuffle
Shuffles an array. Note that this command does _not_ use a cryptographically secure random number generator to randomise the array order.
@@ -1,4 +1,4 @@
# Slice Array
# Slice/Splice Array
The slice array operator takes an array as input and returns a subarray. Like the `jq` equivalent, `.[10:15]` will return an array of length 5, starting from index 10 inclusive, up to index 15 exclusive. Negative numbers count backwards from the end of the array.
+40
View File
@@ -499,3 +499,43 @@ b: !goat
dog: woof
```
## Merging a null with a map
Running
```bash
yq --null-input 'null * {"some": "thing"}'
```
will output
```yaml
some: thing
```
## Merging a map with null
Running
```bash
yq --null-input '{"some": "thing"} * null'
```
will output
```yaml
some: thing
```
## Merging an null with an array
Running
```bash
yq --null-input 'null * ["some"]'
```
will output
```yaml
- some
```
## Merging an array with null
Running
```bash
yq --null-input '["some"] * null'
```
will output
```yaml
- some
```
+1
View File
@@ -127,6 +127,7 @@ Like pick but recursive. This uses `ireduce` to deeply set the selected paths in
Given a sample.yml file of:
```yaml
parentA: bob
parentB:
child1: i am child1
+51
View File
@@ -0,0 +1,51 @@
# Shuffle
Shuffles an array. Note that this command does _not_ use a cryptographically secure random number generator to randomise the array order.
## Shuffle array
Given a sample.yml file of:
```yaml
- 1
- 2
- 3
- 4
- 5
```
then
```bash
yq 'shuffle' sample.yml
```
will output
```yaml
- 5
- 2
- 4
- 1
- 3
```
## Shuffle array in place
Given a sample.yml file of:
```yaml
cool:
- 1
- 2
- 3
- 4
- 5
```
then
```bash
yq '.cool |= shuffle' sample.yml
```
will output
```yaml
cool:
- 5
- 2
- 4
- 1
- 3
```
+1 -1
View File
@@ -1,4 +1,4 @@
# Slice Array
# Slice/Splice Array
The slice array operator takes an array as input and returns a subarray. Like the `jq` equivalent, `.[10:15]` will return an array of length 5, starting from index 10 inclusive, up to index 15 exclusive. Negative numbers count backwards from the end of the array.
+22
View File
@@ -25,6 +25,28 @@ will output
- a: cat
```
## Sort by multiple fields
Given a sample.yml file of:
```yaml
- a: dog
- a: cat
b: banana
- a: cat
b: apple
```
then
```bash
yq 'sort_by(.a, .b)' sample.yml
```
will output
```yaml
- a: cat
b: apple
- a: cat
b: banana
- a: dog
```
## Sort descending by string field
Use sort with reverse to sort in descending order.
+21
View File
@@ -152,6 +152,27 @@ cat:
+@legs: "4"
```
## Parse xml: content split between comments/children
Multiple content texts are collected into a sequence.
Given a sample.xml file of:
```xml
<root> value <!-- comment-->anotherValue <a>frog</a> cool!</root>
```
then
```bash
yq -p=xml '.' sample.xml
```
will output
```yaml
root:
+content: # comment
- value
- anotherValue
- cool!
a: frog
```
## Parse xml: custom dtd
DTD entities are processed as directives.
+79
View File
@@ -0,0 +1,79 @@
package yqlib
import (
"fmt"
"io"
"regexp"
"strings"
yaml "gopkg.in/yaml.v3"
)
var unsafeChars = regexp.MustCompile(`[^\w@%+=:,./-]`)
type shEncoder struct {
quoteAll bool
}
func NewShEncoder() Encoder {
return &shEncoder{false}
}
func (e *shEncoder) CanHandleAliases() bool {
return false
}
func (e *shEncoder) PrintDocumentSeparator(writer io.Writer) error {
return nil
}
func (e *shEncoder) PrintLeadingContent(writer io.Writer, content string) error {
return nil
}
func (e *shEncoder) Encode(writer io.Writer, originalNode *yaml.Node) error {
node := unwrapDoc(originalNode)
if guessTagFromCustomType(node) != "!!str" {
return fmt.Errorf("cannot encode %v as URI, can only operate on strings. Please first pipe through another encoding operator to convert the value to a string", node.Tag)
}
return writeString(writer, e.encode(originalNode.Value))
}
// put any (shell-unsafe) characters into a single-quoted block, close the block lazily
func (e *shEncoder) encode(input string) string {
const quote = '\''
var inQuoteBlock = false
var encoded strings.Builder
encoded.Grow(len(input))
for _, ir := range input {
// open or close a single-quote block
if ir == quote {
if inQuoteBlock {
// get out of a quote block for an input quote
encoded.WriteRune(quote)
inQuoteBlock = !inQuoteBlock
}
// escape the quote with a backslash
encoded.WriteRune('\\')
} else {
if e.shouldQuote(ir) && !inQuoteBlock {
// start a quote block for any (unsafe) characters
encoded.WriteRune(quote)
inQuoteBlock = !inQuoteBlock
}
}
// pass on the input character
encoded.WriteRune(ir)
}
// close any pending quote block
if inQuoteBlock {
encoded.WriteRune(quote)
}
return encoded.String()
}
func (e *shEncoder) shouldQuote(ir rune) bool {
return e.quoteAll || unsafeChars.MatchString(string(ir))
}
+37
View File
@@ -0,0 +1,37 @@
package yqlib
import (
"fmt"
"io"
"net/url"
yaml "gopkg.in/yaml.v3"
)
type uriEncoder struct {
}
func NewUriEncoder() Encoder {
return &uriEncoder{}
}
func (e *uriEncoder) CanHandleAliases() bool {
return false
}
func (e *uriEncoder) PrintDocumentSeparator(writer io.Writer) error {
return nil
}
func (e *uriEncoder) PrintLeadingContent(writer io.Writer, content string) error {
return nil
}
func (e *uriEncoder) Encode(writer io.Writer, originalNode *yaml.Node) error {
node := unwrapDoc(originalNode)
if guessTagFromCustomType(node) != "!!str" {
return fmt.Errorf("cannot encode %v as URI, can only operate on strings. Please first pipe through another encoding operator to convert the value to a string", node.Tag)
}
_, err := writer.Write([]byte(url.QueryEscape(originalNode.Value)))
return err
}
+8
View File
@@ -78,6 +78,14 @@ const roundTripMultiLineJson = `{
`
var jsonScenarios = []formatScenario{
{
description: "set tags",
skipDoc: true,
input: "[{}]",
expression: `[.. | type]`,
scenarioType: "roundtrip-ndjson",
expected: "[\"!!seq\",\"!!map\"]\n",
},
{
description: "Parse json: simple",
subdescription: "JSON is a subset of yaml, so all you need to do is prettify the output",
+12 -1
View File
@@ -45,8 +45,11 @@ var participleYqRules = []*participleYqRule{
simpleOp("format_datetime", formatDateTimeOpType),
simpleOp("now", nowOpType),
simpleOp("tz", tzOpType),
simpleOp("from_?unix", fromUnixOpType),
simpleOp("to_?unix", toUnixOpType),
simpleOp("with_dtf", withDtFormatOpType),
simpleOp("error", errorOpType),
simpleOp("shuffle", shuffleOpType),
simpleOp("sortKeys", sortKeysOpType),
simpleOp("sort_?keys", sortKeysOpType),
@@ -78,6 +81,10 @@ var participleYqRules = []*participleYqRule{
{"Base64d", `@base64d`, decodeOp(Base64InputFormat), 0},
{"Base64", `@base64`, encodeWithIndent(Base64OutputFormat, 0), 0},
{"Urid", `@urid`, decodeOp(UriInputFormat), 0},
{"Uri", `@uri`, encodeWithIndent(UriOutputFormat, 0), 0},
{"SH", `@sh`, encodeWithIndent(ShOutputFormat, 0), 0},
{"LoadXML", `load_?xml|xml_?load`, loadOp(NewXMLDecoder(ConfiguredXMLPreferences), false), 0},
{"LoadBase64", `load_?base64`, loadOp(NewBase64Decoder(), false), 0},
@@ -86,7 +93,7 @@ var participleYqRules = []*participleYqRule{
{"LoadString", `load_?str|str_?load`, loadOp(nil, true), 0},
{"LoadYaml", `load`, loadOp(NewYamlDecoder(ConfiguredYamlPreferences), false), 0},
{"LoadYaml", `load`, loadOp(NewYamlDecoder(LoadYamlPreferences), false), 0},
{"SplitDocument", `splitDoc|split_?doc`, opToken(splitDocumentOpType), 0},
@@ -352,8 +359,12 @@ func nullValue() yqAction {
func stringValue() yqAction {
return func(rawToken lexer.Token) (*token, error) {
log.Debug("rawTokenvalue: %v", rawToken.Value)
value := unwrap(rawToken.Value)
log.Debug("unwrapped: %v", value)
value = strings.ReplaceAll(value, "\\\"", "\"")
value = strings.ReplaceAll(value, "\\n", "\n")
log.Debug("replaced: %v", value)
return &token{TokenType: operationToken, Operation: createValueOperation(value, value)}, nil
}
}
+23 -2
View File
@@ -61,7 +61,7 @@ var participleLexerScenarios = []participleLexerScenario{
TokenType: operationToken,
Operation: &Operation{
OperationType: valueOpType,
Value: 3,
Value: int64(3),
StringValue: "3",
CandidateNode: &CandidateNode{
Node: &yaml.Node{
@@ -103,7 +103,7 @@ var participleLexerScenarios = []participleLexerScenario{
TokenType: operationToken,
Operation: &Operation{
OperationType: valueOpType,
Value: -2,
Value: int64(-2),
StringValue: "-2",
CandidateNode: &CandidateNode{
Node: &yaml.Node{
@@ -620,6 +620,27 @@ var participleLexerScenarios = []participleLexerScenario{
},
},
},
{
expression: `"string with a\n"`,
tokens: []*token{
{
TokenType: operationToken,
Operation: &Operation{
OperationType: valueOpType,
Value: "string with a\n",
StringValue: "string with a\n",
Preferences: nil,
CandidateNode: &CandidateNode{
Node: &yaml.Node{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "string with a\n",
},
},
},
},
},
},
{
expression: `"string with a \""`,
tokens: []*token{
+12 -4
View File
@@ -53,7 +53,7 @@ var subtractAssignOpType = &operationType{Type: "SUBTRACT_ASSIGN", NumArgs: 2, P
var assignAttributesOpType = &operationType{Type: "ASSIGN_ATTRIBUTES", NumArgs: 2, Precedence: 40, Handler: assignAttributesOperator}
var assignStyleOpType = &operationType{Type: "ASSIGN_STYLE", NumArgs: 2, Precedence: 40, Handler: assignStyleOperator}
var assignVariableOpType = &operationType{Type: "ASSIGN_VARIABLE", NumArgs: 2, Precedence: 40, Handler: assignVariableOperator}
var assignVariableOpType = &operationType{Type: "ASSIGN_VARIABLE", NumArgs: 2, Precedence: 40, Handler: useWithPipe}
var assignTagOpType = &operationType{Type: "ASSIGN_TAG", NumArgs: 2, Precedence: 40, Handler: assignTagOperator}
var assignCommentOpType = &operationType{Type: "ASSIGN_COMMENT", NumArgs: 2, Precedence: 40, Handler: assignCommentsOperator}
var assignAnchorOpType = &operationType{Type: "ASSIGN_ANCHOR", NumArgs: 2, Precedence: 40, Handler: assignAnchorOperator}
@@ -93,6 +93,8 @@ var formatDateTimeOpType = &operationType{Type: "FORMAT_DATE_TIME", NumArgs: 1,
var withDtFormatOpType = &operationType{Type: "WITH_DATE_TIME_FORMAT", NumArgs: 1, Precedence: 50, Handler: withDateTimeFormat}
var nowOpType = &operationType{Type: "NOW", NumArgs: 0, Precedence: 50, Handler: nowOp}
var tzOpType = &operationType{Type: "TIMEZONE", NumArgs: 1, Precedence: 50, Handler: tzOp}
var fromUnixOpType = &operationType{Type: "FROM_UNIX", NumArgs: 0, Precedence: 50, Handler: fromUnixOp}
var toUnixOpType = &operationType{Type: "TO_UNIX", NumArgs: 0, Precedence: 50, Handler: toUnixOp}
var encodeOpType = &operationType{Type: "ENCODE", NumArgs: 0, Precedence: 50, Handler: encodeOperator}
var decodeOpType = &operationType{Type: "DECODE", NumArgs: 0, Precedence: 50, Handler: decodeOperator}
@@ -133,6 +135,7 @@ var explodeOpType = &operationType{Type: "EXPLODE", NumArgs: 1, Precedence: 50,
var sortByOpType = &operationType{Type: "SORT_BY", NumArgs: 1, Precedence: 50, Handler: sortByOperator}
var reverseOpType = &operationType{Type: "REVERSE", NumArgs: 0, Precedence: 50, Handler: reverseOperator}
var sortOpType = &operationType{Type: "SORT", NumArgs: 0, Precedence: 50, Handler: sortOperator}
var shuffleOpType = &operationType{Type: "SHUFFLE", NumArgs: 0, Precedence: 50, Handler: shuffleOperator}
var sortKeysOpType = &operationType{Type: "SORT_KEYS", NumArgs: 1, Precedence: 50, Handler: sortKeysOperator}
@@ -155,6 +158,7 @@ var traverseArrayOpType = &operationType{Type: "TRAVERSE_ARRAY", NumArgs: 2, Pre
var selfReferenceOpType = &operationType{Type: "SELF", NumArgs: 0, Precedence: 55, Handler: selfOperator}
var valueOpType = &operationType{Type: "VALUE", NumArgs: 0, Precedence: 50, Handler: valueOperator}
var referenceOpType = &operationType{Type: "REF", NumArgs: 0, Precedence: 50, Handler: referenceOperator}
var envOpType = &operationType{Type: "ENV", NumArgs: 0, Precedence: 50, Handler: envOperator}
var notOpType = &operationType{Type: "NOT", NumArgs: 0, Precedence: 50, Handler: notOperator}
var emptyOpType = &operationType{Type: "EMPTY", Precedence: 50, Handler: emptyOperator}
@@ -235,13 +239,13 @@ func guessTagFromCustomType(node *yaml.Node) string {
if strings.HasPrefix(node.Tag, "!!") {
return node.Tag
} else if node.Value == "" {
log.Warning("node has no value to guess the type with")
log.Debug("guessTagFromCustomType: node has no value to guess the type with")
return node.Tag
}
dataBucket, errorReading := parseSnippet(node.Value)
if errorReading != nil {
log.Warning("could not guess underlying tag type %v", errorReading)
log.Debug("guessTagFromCustomType: could not guess underlying tag type %v", errorReading)
return node.Tag
}
guessedTag := unwrapDoc(dataBucket).Tag
@@ -268,7 +272,10 @@ func parseSnippet(value string) (*yaml.Node, error) {
if len(parsedNode.Node.Content) == 0 {
return nil, fmt.Errorf("bad data")
}
return unwrapDoc(parsedNode.Node), err
result := unwrapDoc(parsedNode.Node)
result.Line = 0
result.Column = 0
return result, err
}
func recursiveNodeEqual(lhs *yaml.Node, rhs *yaml.Node) bool {
@@ -413,6 +420,7 @@ func footComment(node *yaml.Node) string {
}
func createValueOperation(value interface{}, stringValue string) *Operation {
log.Debug("creating value op for string %v", stringValue)
var node = createScalarNode(value, stringValue)
return &Operation{
+10 -10
View File
@@ -38,8 +38,8 @@ var parseSnippetScenarios = []parseSnippetScenario{
Kind: yaml.ScalarNode,
Tag: "!!null",
Value: "null",
Line: 1,
Column: 1,
Line: 0,
Column: 0,
},
},
{
@@ -48,8 +48,8 @@ var parseSnippetScenarios = []parseSnippetScenario{
Kind: yaml.ScalarNode,
Tag: "!!int",
Value: "3",
Line: 1,
Column: 1,
Line: 0,
Column: 0,
},
},
{
@@ -58,8 +58,8 @@ var parseSnippetScenarios = []parseSnippetScenario{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "cat",
Line: 1,
Column: 1,
Line: 0,
Column: 0,
},
},
{
@@ -68,8 +68,8 @@ var parseSnippetScenarios = []parseSnippetScenario{
Kind: yaml.ScalarNode,
Tag: "!!float",
Value: "3.1",
Line: 1,
Column: 1,
Line: 0,
Column: 0,
},
},
{
@@ -78,8 +78,8 @@ var parseSnippetScenarios = []parseSnippetScenario{
Kind: yaml.ScalarNode,
Tag: "!!bool",
Value: "true",
Line: 1,
Column: 1,
Line: 0,
Column: 0,
},
},
}
+9 -1
View File
@@ -34,7 +34,7 @@ var addOperatorScenarios = []expressionScenario{
{
skipDoc: true,
document: `{}`,
expression: "(.a + .b) as $x",
expression: "(.a + .b) as $x | .",
expected: []string{
"D0, P[], (doc)::{}\n",
},
@@ -101,6 +101,14 @@ var addOperatorScenarios = []expressionScenario{
"D0, P[], (doc)::a: ['dog', 'cat']\n",
},
},
{
description: "Prepend to existing array",
document: `a: [dog]`,
expression: `.a = ["cat"] + .a`,
expected: []string{
"D0, P[], (doc)::a: [cat, dog]\n",
},
},
{
skipDoc: true,
description: "Concatenate to existing array",
+1 -1
View File
@@ -16,7 +16,7 @@ var alternativeOperatorScenarios = []expressionScenario{
},
{
skipDoc: true,
expression: `(.b // "hello") as $x`,
expression: `(.b // "hello") as $x | .`,
document: `a: bridge`,
expected: []string{
"D0, P[], (doc)::a: bridge\n",
+1 -2
View File
@@ -12,8 +12,7 @@ var specDocument = `- &CENTER { x: 1, y: 2 }
var expectedSpecResult = "D0, P[4], (!!map)::x: 1\ny: 2\nr: 10\n"
var simpleArrayRef = `
item_value: &item_value
var simpleArrayRef = `item_value: &item_value
value: true
thingOne:
+13 -5
View File
@@ -102,7 +102,7 @@ var booleanOperatorScenarios = []expressionScenario{
{
skipDoc: true,
document: `[{pet: cat}]`,
expression: `any_c(.name == "harry") as $c`,
expression: `any_c(.name == "harry") as $c | .`,
expected: []string{
"D0, P[], (doc)::[{pet: cat}]\n",
},
@@ -110,9 +110,17 @@ var booleanOperatorScenarios = []expressionScenario{
{
skipDoc: true,
document: `[{pet: cat}]`,
expression: `all_c(.name == "harry") as $c`,
expression: `any_c(.name == "harry") as $c | $c`,
expected: []string{
"D0, P[], (doc)::[{pet: cat}]\n",
"D0, P[], (!!bool)::false\n",
},
},
{
skipDoc: true,
document: `[{pet: cat}]`,
expression: `all_c(.name == "harry") as $c | $c`,
expected: []string{
"D0, P[], (!!bool)::false\n",
},
},
{
@@ -185,7 +193,7 @@ var booleanOperatorScenarios = []expressionScenario{
{
skipDoc: true,
document: `{}`,
expression: `(.a.b or .c) as $x`,
expression: `(.a.b or .c) as $x | .`,
expected: []string{
"D0, P[], (doc)::{}\n",
},
@@ -193,7 +201,7 @@ var booleanOperatorScenarios = []expressionScenario{
{
skipDoc: true,
document: `{}`,
expression: `(.a.b and .c) as $x`,
expression: `(.a.b and .c) as $x | .`,
expected: []string{
"D0, P[], (doc)::{}\n",
},
+1 -1
View File
@@ -221,7 +221,7 @@ var commentOperatorScenarios = []expressionScenario{
document: "# welcome!\n\na: cat # meow\n\n# have a great day",
expression: `. | head_comment`,
expected: []string{
"D0, P[], (!!str)::welcome!\n",
"D0, P[], (!!str)::welcome!\n\n",
},
},
{
@@ -3,7 +3,6 @@ package yqlib
import (
"fmt"
"strconv"
"time"
yaml "gopkg.in/yaml.v3"
)
@@ -16,7 +15,7 @@ type compareTypePref struct {
func compareOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
log.Debugf("-- compareOperator")
prefs := expressionNode.Operation.Preferences.(compareTypePref)
return crossFunction(d, context.ReadOnlyClone(), expressionNode, compare(prefs), true)
return crossFunction(d, context, expressionNode, compare(prefs), true)
}
func compare(prefs compareTypePref) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
@@ -80,7 +79,7 @@ func compareScalars(context Context, prefs compareTypePref, lhs *yaml.Node, rhs
isDateTime := lhs.Tag == "!!timestamp"
// if the lhs is a string, it might be a timestamp in a custom format.
if lhsTag == "!!str" && context.GetDateTimeLayout() != time.RFC3339 {
if lhsTag == "!!str" {
_, err := parseDateTime(context.GetDateTimeLayout(), lhs.Value)
isDateTime = err == nil
}
@@ -127,6 +126,10 @@ func compareScalars(context Context, prefs compareTypePref, lhs *yaml.Node, rhs
return lhs.Value > rhs.Value, nil
}
return lhs.Value < rhs.Value, nil
} else if lhsTag == "!!null" && rhsTag == "!!null" && prefs.OrEqual {
return true, nil
} else if lhsTag == "!!null" || rhsTag == "!!null" {
return false, nil
}
return false, fmt.Errorf("%v not yet supported for comparison", lhs.Tag)
+67
View File
@@ -4,6 +4,7 @@ import (
"container/list"
"errors"
"fmt"
"strconv"
"time"
"gopkg.in/yaml.v3"
@@ -129,3 +130,69 @@ func tzOp(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode)
return context.ChildContext(results), nil
}
func parseUnixTime(unixTime string) (time.Time, error) {
seconds, err := strconv.ParseFloat(unixTime, 64)
if err != nil {
return time.Now(), err
}
return time.UnixMilli(int64(seconds * 1000)), nil
}
func fromUnixOp(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
var results = list.New()
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
actualTag := guessTagFromCustomType(candidate.Node)
if actualTag != "!!int" && guessTagFromCustomType(candidate.Node) != "!!float" {
return Context{}, fmt.Errorf("from_unix only works on numbers, found %v instead", candidate.Node.Tag)
}
parsedTime, err := parseUnixTime(candidate.Node.Value)
if err != nil {
return Context{}, err
}
node := &yaml.Node{
Kind: yaml.ScalarNode,
Tag: "!!timestamp",
Value: parsedTime.Format(time.RFC3339),
}
results.PushBack(candidate.CreateReplacement(node))
}
return context.ChildContext(results), nil
}
func toUnixOp(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
layout := context.GetDateTimeLayout()
var results = list.New()
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
parsedTime, err := parseDateTime(layout, candidate.Node.Value)
if err != nil {
return Context{}, fmt.Errorf("could not parse datetime of [%v] using layout [%v]: %w", candidate.GetNicePath(), layout, err)
}
node := &yaml.Node{
Kind: yaml.ScalarNode,
Tag: "!!int",
Value: fmt.Sprintf("%v", parsedTime.Unix()),
}
results.PushBack(candidate.CreateReplacement(node))
}
return context.ChildContext(results), nil
}
+16
View File
@@ -39,6 +39,22 @@ var dateTimeOperatorScenarios = []expressionScenario{
"D0, P[], (doc)::a: cool\nupdated: 2021-05-19T01:02:03Z\n",
},
},
{
description: "From Unix",
subdescription: "Converts from unix time. Note, you don't have to pipe through the tz operator :)",
expression: `1675301929 | from_unix | tz("UTC")`,
expected: []string{
"D0, P[], (!!timestamp)::2023-02-02T01:38:49Z\n",
},
},
{
description: "To Unix",
subdescription: "Converts to unix time",
expression: `now | to_unix`,
expected: []string{
"D0, P[], (!!int)::1621386123\n",
},
},
{
description: "Timezone: from standard RFC3339 format",
subdescription: "Returns a new datetime in the specified timezone. Specify standard IANA Time Zone format or 'utc', 'local'. When given a single parameter, this assumes the datetime is in RFC3339 format.",
+6
View File
@@ -26,6 +26,10 @@ func configureEncoder(format PrinterOutputFormat, indent int) Encoder {
return NewXMLEncoder(indent, ConfiguredXMLPreferences)
case Base64OutputFormat:
return NewBase64Encoder()
case UriOutputFormat:
return NewUriEncoder()
case ShOutputFormat:
return NewShEncoder()
}
panic("invalid encoder")
}
@@ -113,6 +117,8 @@ func decodeOperator(d *dataTreeNavigator, context Context, expressionNode *Expre
decoder = NewCSVObjectDecoder(',')
case TSVObjectInputFormat:
decoder = NewCSVObjectDecoder('\t')
case UriInputFormat:
decoder = NewUriDecoder()
}
var results = list.New()
@@ -241,6 +241,41 @@ var encoderDecoderOperatorScenarios = []expressionScenario{
"D0, P[], (!!str)::YTogYXBwbGUK\n",
},
},
{
description: "Encode a string to uri",
document: "coolData: this has & special () characters *",
expression: ".coolData | @uri",
expected: []string{
"D0, P[coolData], (!!str)::this+has+%26+special+%28%29+characters+%2A\n",
},
},
{
description: "Decode a URI to a string",
document: "this+has+%26+special+%28%29+characters+%2A",
expression: "@urid",
expected: []string{
"D0, P[], (!!str)::this has & special () characters *\n",
},
},
{
description: "Encode a string to sh",
subdescription: "Sh/Bash friendly string",
document: "coolData: strings with spaces and a 'quote'",
expression: ".coolData | @sh",
expected: []string{
"D0, P[coolData], (!!str)::strings' with spaces and a '\\'quote\\'\n",
},
},
{
description: "Encode a string to sh",
subdescription: "Watch out for stray '' (empty strings)",
document: "coolData: \"'starts, contains more '' and ends with a quote'\"",
expression: ".coolData | @sh",
expected: []string{
"D0, P[coolData], (!!str)::\\'starts,' contains more '\\'\\'' and ends with a quote'\\'\n",
},
skipDoc: true,
},
{
description: "Decode a base64 encoded string",
subdescription: "Decoded data is assumed to be a string.",
+1 -1
View File
@@ -4,7 +4,7 @@ import "gopkg.in/yaml.v3"
func equalsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
log.Debugf("-- equalsOperation")
return crossFunction(d, context.ReadOnlyClone(), expressionNode, isEquals(false), true)
return crossFunction(d, context, expressionNode, isEquals(false), true)
}
func isEquals(flip bool) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
+12 -5
View File
@@ -9,7 +9,14 @@ var equalsOperatorScenarios = []expressionScenario{
skipDoc: true,
expression: ".a == .b",
expected: []string{
"D0, P[], (!!bool)::true\n",
"D0, P[a], (!!bool)::true\n",
},
},
{
expression: `(.k | length) == 0`,
skipDoc: true,
expected: []string{
"D0, P[k], (!!bool)::true\n",
},
},
{
@@ -25,7 +32,7 @@ var equalsOperatorScenarios = []expressionScenario{
document: `a: cat`,
expression: ".b == .a",
expected: []string{
"D0, P[a], (!!bool)::false\n",
"D0, P[b], (!!bool)::false\n",
},
},
{
@@ -40,7 +47,7 @@ var equalsOperatorScenarios = []expressionScenario{
{
skipDoc: true,
document: "{}",
expression: "(.a == .b) as $x",
expression: "(.a == .b) as $x | .",
expected: []string{
"D0, P[], (doc)::{}\n",
},
@@ -50,13 +57,13 @@ var equalsOperatorScenarios = []expressionScenario{
document: "{}",
expression: ".a == .b",
expected: []string{
"D0, P[], (!!bool)::true\n",
"D0, P[a], (!!bool)::true\n",
},
},
{
skipDoc: true,
document: "{}",
expression: "(.a != .b) as $x",
expression: "(.a != .b) as $x | .",
expected: []string{
"D0, P[], (doc)::{}\n",
},
+1 -1
View File
@@ -16,7 +16,7 @@ var hasOperatorScenarios = []expressionScenario{
{
skipDoc: true,
document: `a: hello`,
expression: `has(.b) as $c`,
expression: `has(.b) as $c | .`,
expected: []string{
"D0, P[], (doc)::a: hello\n",
},
+9 -1
View File
@@ -9,6 +9,13 @@ import (
"gopkg.in/yaml.v3"
)
var LoadYamlPreferences = YamlPreferences{
LeadingContentPreProcessing: false,
PrintDocSeparators: true,
UnwrapScalar: true,
EvaluateTogether: false,
}
type loadPrefs struct {
loadAsString bool
decoder Decoder
@@ -43,7 +50,8 @@ func loadYaml(filename string, decoder Decoder) (*CandidateNode, error) {
// return null candidate
return &CandidateNode{Node: &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!null"}}, nil
} else if documents.Len() == 1 {
return documents.Front().Value.(*CandidateNode), nil
candidate := documents.Front().Value.(*CandidateNode)
return candidate, nil
} else {
sequenceNode := &CandidateNode{Node: &yaml.Node{Kind: yaml.SequenceNode}}
+9
View File
@@ -13,6 +13,15 @@ var loadScenarios = []expressionScenario{
"D0, P[], (doc)::# comment\n\n",
},
},
{
skipDoc: true,
description: "Load file with a header comment into an array",
document: `- "../../examples/small.yaml"`,
expression: `.[] |= load(.)`,
expected: []string{
"D0, P[], (doc)::- # comment\n # about things\n a: cat\n",
},
},
{
skipDoc: true,
description: "Load empty file with no comment",
+9 -3
View File
@@ -60,8 +60,14 @@ func multiply(preferences multiplyPreferences) func(d *dataTreeNavigator, contex
log.Debugf("Multiplying LHS: %v", lhs.Node.Tag)
log.Debugf("- RHS: %v", rhs.Node.Tag)
if lhs.Node.Kind == yaml.MappingNode && rhs.Node.Kind == yaml.MappingNode ||
(lhs.Node.Kind == yaml.SequenceNode && rhs.Node.Kind == yaml.SequenceNode) {
if rhs.Node.Tag == "!!null" {
return lhs.Copy()
}
if (lhs.Node.Kind == yaml.MappingNode && rhs.Node.Kind == yaml.MappingNode) ||
(lhs.Node.Tag == "!!null" && rhs.Node.Kind == yaml.MappingNode) ||
(lhs.Node.Kind == yaml.SequenceNode && rhs.Node.Kind == yaml.SequenceNode) ||
(lhs.Node.Tag == "!!null" && rhs.Node.Kind == yaml.SequenceNode) {
var newBlank = CandidateNode{}
err := copier.CopyWithOption(&newBlank, lhs, copier.Option{IgnoreEmpty: true, DeepCopy: true})
if err != nil {
@@ -189,7 +195,7 @@ func applyAssignment(d *dataTreeNavigator, context Context, pathIndexToStartFrom
} else {
log.Debugf("merge - assignmentOp := &Operation{OperationType: assignAttributesOpType}")
}
rhsOp := &Operation{OperationType: valueOpType, CandidateNode: rhs}
rhsOp := &Operation{OperationType: referenceOpType, CandidateNode: rhs}
assignmentOpNode := &ExpressionNode{
Operation: assignmentOp,
+37 -2
View File
@@ -108,7 +108,7 @@ var multiplyOperatorScenarios = []expressionScenario{
document2: docNoComments,
expression: `select(fi == 0) * select(fi == 1)`,
expected: []string{
"D0, P[], (!!map)::# here\na: apple\nb: banana\n",
"D0, P[], (!!map)::# here\n\na: apple\nb: banana\n",
},
},
{
@@ -126,7 +126,7 @@ var multiplyOperatorScenarios = []expressionScenario{
document2: docWithHeader,
expression: `select(fi == 0) * select(fi == 1)`,
expected: []string{
"D0, P[], (!!map)::# here\nb: banana\na: apple\n",
"D0, P[], (!!map)::# here\n\nb: banana\na: apple\n",
},
},
{
@@ -579,6 +579,41 @@ var multiplyOperatorScenarios = []expressionScenario{
"D0, P[], (doc)::a: {a: apple is included, b: cool.}\n",
},
},
{
description: "Merging a null with a map",
expression: `null * {"some": "thing"}`,
expected: []string{
"D0, P[], (!!map)::some: thing\n",
},
},
{
description: "Merging a map with null",
expression: `{"some": "thing"} * null`,
expected: []string{
"D0, P[], (!!map)::some: thing\n",
},
},
{
description: "Merging an null with an array",
expression: `null * ["some"]`,
expected: []string{
"D0, P[], (!!seq)::- some\n",
},
},
{
description: "Merging an array with null",
expression: `["some"] * null`,
expected: []string{
"D0, P[], (!!seq)::- some\n",
},
},
{
skipDoc: true,
expression: `null * null`,
expected: []string{
"D0, P[], (!!null)::null\n",
},
},
}
func TestMultiplyOperatorScenarios(t *testing.T) {
+1 -1
View File
@@ -81,7 +81,7 @@ func setPathOperator(d *dataTreeNavigator, context Context, expressionNode *Expr
return Context{}, fmt.Errorf("SETPATH: expected single value on RHS but found %v", targetContextValue.MatchingNodes.Len())
}
rhsOp := &Operation{OperationType: valueOpType, CandidateNode: targetContextValue.MatchingNodes.Front().Value.(*CandidateNode)}
rhsOp := &Operation{OperationType: referenceOpType, CandidateNode: targetContextValue.MatchingNodes.Front().Value.(*CandidateNode)}
assignmentOpNode := &ExpressionNode{
Operation: assignmentOp,
+3 -3
View File
@@ -2,9 +2,9 @@ package yqlib
func pipeOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
//lhs may update the variable context, we should pass that into the RHS
// BUT we still return the original context back (see jq)
// https://stedolan.github.io/jq/manual/#Variable/SymbolicBindingOperator:...as$identifier|...
if expressionNode.LHS.Operation.OperationType == assignVariableOpType {
return variableLoop(d, context, expressionNode)
}
lhs, err := d.GetMatchingNodes(context, expressionNode.LHS)
if err != nil {
+4 -3
View File
@@ -76,9 +76,10 @@ var selectOperatorScenarios = []expressionScenario{
},
},
{
skipDoc: true,
document: `[{animal: cat, legs: {cool: true}}, {animal: fish}]`,
expression: `(.[] | select(.legs.cool == true).canWalk) = true | (.[] | .alive.things) = "yes"`,
description: "select does not update the map",
skipDoc: true,
document: `[{animal: cat, legs: {cool: true}}, {animal: fish}]`,
expression: `(.[] | select(.legs.cool == true).canWalk) = true | (.[] | .alive.things) = "yes"`,
expected: []string{
"D0, P[], (doc)::[{animal: cat, legs: {cool: true}, canWalk: true, alive: {things: yes}}, {animal: fish, alive: {things: yes}}]\n",
},
+37
View File
@@ -0,0 +1,37 @@
package yqlib
import (
"container/list"
"fmt"
"math/rand"
yaml "gopkg.in/yaml.v3"
)
func shuffleOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
// ignore CWE-338 gosec issue of not using crypto/rand
// this is just to shuffle an array rather generating a
// secret or something that needs proper rand.
myRand := rand.New(rand.NewSource(Now().UnixNano())) // #nosec
results := list.New()
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
candidateNode := unwrapDoc(candidate.Node)
if candidateNode.Kind != yaml.SequenceNode {
return context, fmt.Errorf("node at path [%v] is not an array (it's a %v)", candidate.GetNicePath(), candidate.GetNiceTag())
}
result := deepClone(candidateNode)
a := result.Content
myRand.Shuffle(len(a), func(i, j int) { a[i], a[j] = a[j], a[i] })
results.PushBack(candidate.CreateReplacement(result))
}
return context.ChildContext(results), nil
}
+30
View File
@@ -0,0 +1,30 @@
package yqlib
import "testing"
var shuffleOperatorScenarios = []expressionScenario{
{
description: "Shuffle array",
document: "[1, 2, 3, 4, 5]",
expression: `shuffle`,
expected: []string{
"D0, P[], (!!seq)::[5, 2, 4, 1, 3]\n",
},
},
{
description: "Shuffle array in place",
document: "cool: [1, 2, 3, 4, 5]",
expression: `.cool |= shuffle`,
expected: []string{
"D0, P[], (doc)::cool: [5, 2, 4, 1, 3]\n",
},
},
}
func TestShuffleByOperatorScenarios(t *testing.T) {
for _, tt := range shuffleOperatorScenarios {
testScenario(t, &tt)
}
documentOperatorScenarios(t, "shuffle", shuffleOperatorScenarios)
}
+2
View File
@@ -48,6 +48,8 @@ func sliceArrayOperator(d *dataTreeNavigator, context Context, expressionNode *E
relativeSecondNumber := secondNumber
if relativeSecondNumber < 0 {
relativeSecondNumber = len(original.Content) + secondNumber
} else if relativeSecondNumber > len(original.Content) {
relativeSecondNumber = len(original.Content)
}
log.Debug("calculateIndicesToTraverse: slice from %v to %v", relativeFirstNumber, relativeSecondNumber)
+18
View File
@@ -55,6 +55,24 @@ var sliceArrayScenarios = []expressionScenario{
"D0, P[1], (!!seq)::- banana\n- grape\n",
},
},
{
skipDoc: true,
description: "second index beyond array clamps",
document: `[cat]`,
expression: `.[:3]`,
expected: []string{
"D0, P[], (!!seq)::- cat\n",
},
},
{
skipDoc: true,
description: "first index beyond array returns nothing",
document: `[cat]`,
expression: `.[3:]`,
expected: []string{
"D0, P[], (!!seq)::[]\n",
},
},
{
skipDoc: true,
document: `[[cat, dog, frog, cow], [apple, banana, grape, mango]]`,
+52 -28
View File
@@ -42,18 +42,7 @@ func sortByOperator(d *dataTreeNavigator, context Context, expressionNode *Expre
return Context{}, err
}
nodeToCompare := &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!null"}
if compareContext.MatchingNodes.Len() > 0 {
nodeToCompare = compareContext.MatchingNodes.Front().Value.(*CandidateNode).Node
}
log.Debug("going to compare %v by %v", NodeToString(candidate.CreateReplacement(originalNode)), NodeToString(candidate.CreateReplacement(nodeToCompare)))
sortableArray[i] = sortableNode{Node: originalNode, NodeToCompare: nodeToCompare, dateTimeLayout: context.GetDateTimeLayout()}
if nodeToCompare.Kind != yaml.ScalarNode {
return Context{}, fmt.Errorf("sort only works for scalars, got %v", nodeToCompare.Tag)
}
sortableArray[i] = sortableNode{Node: originalNode, CompareContext: compareContext, dateTimeLayout: context.GetDateTimeLayout()}
}
@@ -72,7 +61,7 @@ func sortByOperator(d *dataTreeNavigator, context Context, expressionNode *Expre
type sortableNode struct {
Node *yaml.Node
NodeToCompare *yaml.Node
CompareContext Context
dateTimeLayout string
}
@@ -82,9 +71,28 @@ func (a sortableNodeArray) Len() int { return len(a) }
func (a sortableNodeArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a sortableNodeArray) Less(i, j int) bool {
lhs := a[i].NodeToCompare
rhs := a[j].NodeToCompare
lhsContext := a[i].CompareContext
rhsContext := a[j].CompareContext
rhsEl := rhsContext.MatchingNodes.Front()
for lhsEl := lhsContext.MatchingNodes.Front(); lhsEl != nil && rhsEl != nil; lhsEl = lhsEl.Next() {
lhs := lhsEl.Value.(*CandidateNode)
rhs := rhsEl.Value.(*CandidateNode)
result := a.compare(lhs.Node, rhs.Node, a[i].dateTimeLayout)
if result < 0 {
return true
} else if result > 0 {
return false
}
rhsEl = rhsEl.Next()
}
return false
}
func (a sortableNodeArray) compare(lhs *yaml.Node, rhs *yaml.Node, dateTimeLayout string) int {
lhsTag := lhs.Tag
rhsTag := rhs.Tag
@@ -99,7 +107,7 @@ func (a sortableNodeArray) Less(i, j int) bool {
}
isDateTime := lhsTag == "!!timestamp" && rhsTag == "!!timestamp"
layout := a[i].dateTimeLayout
layout := dateTimeLayout
// if the lhs is a string, it might be a timestamp in a custom format.
if lhsTag == "!!str" && layout != time.RFC3339 {
_, errLhs := parseDateTime(layout, lhs.Value)
@@ -108,13 +116,13 @@ func (a sortableNodeArray) Less(i, j int) bool {
}
if lhsTag == "!!null" && rhsTag != "!!null" {
return true
return -1
} else if lhsTag != "!!null" && rhsTag == "!!null" {
return false
return 1
} else if lhsTag == "!!bool" && rhsTag != "!!bool" {
return true
return -1
} else if lhsTag != "!!bool" && rhsTag == "!!bool" {
return false
return 1
} else if lhsTag == "!!bool" && rhsTag == "!!bool" {
lhsTruthy, err := isTruthyNode(lhs)
if err != nil {
@@ -125,20 +133,30 @@ func (a sortableNodeArray) Less(i, j int) bool {
if err != nil {
panic(fmt.Errorf("could not parse %v as boolean: %w", rhs.Value, err))
}
return !lhsTruthy && rhsTruthy
if lhsTruthy == rhsTruthy {
return 0
} else if lhsTruthy {
return 1
}
return -1
} else if isDateTime {
lhsTime, err := parseDateTime(layout, lhs.Value)
if err != nil {
log.Warningf("Could not parse time %v with layout %v for sort, sorting by string instead: %w", lhs.Value, layout, err)
return strings.Compare(lhs.Value, rhs.Value) < 0
return strings.Compare(lhs.Value, rhs.Value)
}
rhsTime, err := parseDateTime(layout, rhs.Value)
if err != nil {
log.Warningf("Could not parse time %v with layout %v for sort, sorting by string instead: %w", rhs.Value, layout, err)
return strings.Compare(lhs.Value, rhs.Value) < 0
return strings.Compare(lhs.Value, rhs.Value)
}
return lhsTime.Before(rhsTime)
if lhsTime.Equal(rhsTime) {
return 0
} else if lhsTime.Before(rhsTime) {
return -1
}
return 1
} else if lhsTag == "!!int" && rhsTag == "!!int" {
_, lhsNum, err := parseInt64(lhs.Value)
if err != nil {
@@ -148,7 +166,7 @@ func (a sortableNodeArray) Less(i, j int) bool {
if err != nil {
panic(err)
}
return lhsNum < rhsNum
return int(lhsNum - rhsNum)
} else if (lhsTag == "!!int" || lhsTag == "!!float") && (rhsTag == "!!int" || rhsTag == "!!float") {
lhsNum, err := strconv.ParseFloat(lhs.Value, 64)
if err != nil {
@@ -158,8 +176,14 @@ func (a sortableNodeArray) Less(i, j int) bool {
if err != nil {
panic(err)
}
return lhsNum < rhsNum
if lhsNum == rhsNum {
return 0
} else if lhsNum < rhsNum {
return -1
}
return 1
}
return strings.Compare(lhs.Value, rhs.Value) < 0
return strings.Compare(lhs.Value, rhs.Value)
}
+26
View File
@@ -11,6 +11,32 @@ var sortByOperatorScenarios = []expressionScenario{
"D0, P[], (!!seq)::[{a: apple}, {a: banana}, {a: cat}]\n",
},
},
{
description: "Sort by multiple fields",
document: "[{a: dog},{a: cat, b: banana},{a: cat, b: apple}]",
expression: `sort_by(.a, .b)`,
expected: []string{
"D0, P[], (!!seq)::[{a: cat, b: apple}, {a: cat, b: banana}, {a: dog}]\n",
},
},
{
description: "Sort by multiple fields",
skipDoc: true,
document: "[{a: dog, b: good},{a: cat, c: things},{a: cat, b: apple}]",
expression: `sort_by(.a, .b)`,
expected: []string{
"D0, P[], (!!seq)::[{a: cat, c: things}, {a: cat, b: apple}, {a: dog, b: good}]\n",
},
},
{
description: "Sort by multiple fields",
skipDoc: true,
document: "[{a: dog, b: 0.1},{a: cat, b: 0.01},{a: cat, b: 0.001}]",
expression: `sort_by(.a, .b)`,
expected: []string{
"D0, P[], (!!seq)::[{a: cat, b: 0.001}, {a: cat, b: 0.01}, {a: dog, b: 0.1}]\n",
},
},
{
description: "Sort descending by string field",
subdescription: "Use sort with reverse to sort in descending order.",
+1
View File
@@ -432,6 +432,7 @@ func split(value string, spltStr string) *yaml.Node {
var contents []*yaml.Node
if value != "" {
log.Debug("going to spltStr[%v]", spltStr)
var newStrings = strings.Split(value, spltStr)
contents = make([]*yaml.Node, len(newStrings))
+1 -1
View File
@@ -8,7 +8,7 @@ var subtractOperatorScenarios = []expressionScenario{
{
skipDoc: true,
document: `{}`,
expression: "(.a - .b) as $x",
expression: "(.a - .b) as $x | .",
expected: []string{
"D0, P[], (doc)::{}\n",
},
+1 -1
View File
@@ -39,7 +39,7 @@ func traverse(context Context, matchingNode *CandidateNode, operation *Operation
log.Debug("Traversing %v", NodeToString(matchingNode))
value := matchingNode.Node
if value.Tag == "!!null" && operation.Value != "[]" {
if value.Tag == "!!null" && operation.Value != "[]" && !context.DontAutoCreate {
log.Debugf("Guessing kind")
// we must have added this automatically, lets guess what it should be now
switch operation.Value.(type) {
+2 -3
View File
@@ -4,8 +4,7 @@ import (
"testing"
)
var mergeDocSample = `
foo: &foo
var mergeDocSample = `foo: &foo
a: foo_a
thing: foo_thing
c: foo_c
@@ -152,7 +151,7 @@ var traversePathOperatorScenarios = []expressionScenario{
{
skipDoc: true,
document: `c: dog`,
expression: `.[.a.b] as $x`,
expression: `.[.a.b] as $x | .`,
expected: []string{
"D0, P[], (doc)::c: dog\n",
},
+1 -1
View File
@@ -8,7 +8,7 @@ var unionOperatorScenarios = []expressionScenario{
{
skipDoc: true,
document: "{}",
expression: `(.a, .b.c) as $x`,
expression: `(.a, .b.c) as $x | .`,
expected: []string{
"D0, P[], (doc)::{}\n",
},
+25 -1
View File
@@ -1,6 +1,30 @@
package yqlib
import "container/list"
func referenceOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
return context.SingleChildContext(expressionNode.Operation.CandidateNode), nil
}
func valueOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
log.Debug("value = %v", expressionNode.Operation.CandidateNode.Node.Value)
return context.SingleChildContext(expressionNode.Operation.CandidateNode), nil
if context.MatchingNodes.Len() == 0 {
clone, err := expressionNode.Operation.CandidateNode.Copy()
if err != nil {
return Context{}, err
}
return context.SingleChildContext(clone), nil
}
var results = list.New()
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
clone, err := expressionNode.Operation.CandidateNode.Copy()
if err != nil {
return Context{}, err
}
results.PushBack(clone)
}
return context.ChildContext(results), nil
}
+22
View File
@@ -12,6 +12,28 @@ var valueOperatorScenarios = []expressionScenario{
"D0, P[], (!!int)::1\n",
},
},
{
document: `[1,2,3]`,
expression: `.[] | "foo"`,
expected: []string{
"D0, P[], (!!str)::foo\n",
"D0, P[], (!!str)::foo\n",
"D0, P[], (!!str)::foo\n",
},
},
{
document: `[1,2,3]`,
expression: `[.[] | "foo"] | .[0] = "cat"`,
expected: []string{
"D0, P[], (!!seq)::- cat\n- foo\n- foo\n",
},
},
{
expression: `"foo"`,
expected: []string{
"D0, P[], (!!str)::foo\n",
},
},
{
document: ``,
expression: `0x9f`,
+69 -12
View File
@@ -19,24 +19,81 @@ type assignVarPreferences struct {
IsReference bool
}
func assignVariableOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
lhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.LHS)
func useWithPipe(d *dataTreeNavigator, context Context, originalExp *ExpressionNode) (Context, error) {
return Context{}, fmt.Errorf("must use variable with a pipe, e.g. `exp as $x | ...`")
}
// variables are like loops in jq
// https://stedolan.github.io/jq/manual/#Variable
func variableLoop(d *dataTreeNavigator, context Context, originalExp *ExpressionNode) (Context, error) {
log.Debug("variable loop!")
results := list.New()
var evaluateAllTogether = true
for matchEl := context.MatchingNodes.Front(); matchEl != nil; matchEl = matchEl.Next() {
evaluateAllTogether = evaluateAllTogether && matchEl.Value.(*CandidateNode).EvaluateTogether
if !evaluateAllTogether {
break
}
}
if evaluateAllTogether {
return variableLoopSingleChild(d, context, originalExp)
}
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
result, err := variableLoopSingleChild(d, context.SingleChildContext(el.Value.(*CandidateNode)), originalExp)
if err != nil {
return Context{}, err
}
results.PushBackList(result.MatchingNodes)
}
return context.ChildContext(results), nil
}
func variableLoopSingleChild(d *dataTreeNavigator, context Context, originalExp *ExpressionNode) (Context, error) {
variableExp := originalExp.LHS
lhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), variableExp.LHS)
if err != nil {
return Context{}, err
}
if expressionNode.RHS.Operation.OperationType.Type != "GET_VARIABLE" {
if variableExp.RHS.Operation.OperationType.Type != "GET_VARIABLE" {
return Context{}, fmt.Errorf("RHS of 'as' operator must be a variable name e.g. $foo")
}
variableName := expressionNode.RHS.Operation.StringValue
variableName := variableExp.RHS.Operation.StringValue
prefs := expressionNode.Operation.Preferences.(assignVarPreferences)
prefs := variableExp.Operation.Preferences.(assignVarPreferences)
var variableValue *list.List
if prefs.IsReference {
variableValue = lhs.MatchingNodes
} else {
variableValue = lhs.DeepClone().MatchingNodes
results := list.New()
// now we loop over lhs, set variable to each result and calculate originalExp.Rhs
for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() {
log.Debug("PROCESSING VARIABLE: ", NodeToString(el.Value.(*CandidateNode)))
var variableValue = list.New()
if prefs.IsReference {
variableValue.PushBack(el.Value)
} else {
candidateCopy, err := el.Value.(*CandidateNode).Copy()
if err != nil {
return Context{}, err
}
variableValue.PushBack(candidateCopy)
}
newContext := context.ChildContext(context.MatchingNodes)
newContext.SetVariable(variableName, variableValue)
rhs, err := d.GetMatchingNodes(newContext, originalExp.RHS)
log.Debug("PROCESSING VARIABLE DONE, got back: ", rhs.MatchingNodes.Len())
if err != nil {
return Context{}, err
}
results.PushBackList(rhs.MatchingNodes)
}
context.SetVariable(variableName, variableValue)
return context, nil
// if there is no LHS - then I guess we just calculate originalExp.Rhs
if lhs.MatchingNodes.Len() == 0 {
return d.GetMatchingNodes(context, originalExp.RHS)
}
return context.ChildContext(results), nil
}
+18 -2
View File
@@ -8,7 +8,7 @@ var variableOperatorScenarios = []expressionScenario{
{
skipDoc: true,
document: `{}`,
expression: `.a.b as $foo`,
expression: `.a.b as $foo | .`,
expected: []string{
"D0, P[], (doc)::{}\n",
},
@@ -16,7 +16,7 @@ var variableOperatorScenarios = []expressionScenario{
{
document: "a: [cat]",
skipDoc: true,
expression: "(.[] | {.name: .}) as $item",
expression: "(.[] | {.name: .}) as $item | .",
expectedError: `cannot index array with 'name' (strconv.ParseInt: parsing "name": invalid syntax)`,
},
{
@@ -36,6 +36,22 @@ var variableOperatorScenarios = []expressionScenario{
"D0, P[1], (!!str)::dog\n",
},
},
{
skipDoc: true,
document: `[1, 2]`,
expression: `.[] | . as $f | select($f == 2)`,
expected: []string{
"D0, P[1], (!!int)::2\n",
},
},
{
skipDoc: true,
document: `[1, 2]`,
expression: `[.[] | . as $f | $f + 1]`,
expected: []string{
"D0, P[], (!!seq)::- 2\n- 3\n",
},
},
{
description: "Using variables as a lookup",
subdescription: "Example taken from [jq](https://stedolan.github.io/jq/manual/#Variable/SymbolicBindingOperator:...as$identifier|...)",
+2 -2
View File
@@ -41,9 +41,9 @@ func compoundAssignFunction(d *dataTreeNavigator, context Context, expressionNod
if err != nil {
return Context{}, err
}
valueCopyExp := &ExpressionNode{Operation: &Operation{OperationType: valueOpType, CandidateNode: clone}}
valueCopyExp := &ExpressionNode{Operation: &Operation{OperationType: referenceOpType, CandidateNode: clone}}
valueExpression := &ExpressionNode{Operation: &Operation{OperationType: valueOpType, CandidateNode: candidate}}
valueExpression := &ExpressionNode{Operation: &Operation{OperationType: referenceOpType, CandidateNode: candidate}}
assignmentOpNode := &ExpressionNode{Operation: assignmentOp, LHS: valueExpression, RHS: calculation(valueCopyExp, expressionNode.RHS)}
@@ -14,6 +14,20 @@ var compareOperatorScenarios = []expressionScenario{
"D0, P[a], (!!bool)::true\n",
},
},
{
skipDoc: true,
expression: "(.k | length) >= 0",
expected: []string{
"D0, P[k], (!!bool)::true\n",
},
},
{
skipDoc: true,
expression: `"2022-01-30T15:53:09Z" > "2020-01-30T15:53:09Z"`,
expected: []string{
"D0, P[], (!!bool)::true\n",
},
},
{
skipDoc: true,
document: "a: 5\nb: 4",
@@ -285,28 +299,28 @@ var compareOperatorScenarios = []expressionScenario{
description: "Both sides are null: > is false",
expression: ".a > .b",
expected: []string{
"D0, P[], (!!bool)::false\n",
"D0, P[a], (!!bool)::false\n",
},
},
{
skipDoc: true,
expression: ".a < .b",
expected: []string{
"D0, P[], (!!bool)::false\n",
"D0, P[a], (!!bool)::false\n",
},
},
{
description: "Both sides are null: >= is true",
expression: ".a >= .b",
expected: []string{
"D0, P[], (!!bool)::true\n",
"D0, P[a], (!!bool)::true\n",
},
},
{
skipDoc: true,
expression: ".a <= .b",
expected: []string{
"D0, P[], (!!bool)::true\n",
"D0, P[a], (!!bool)::true\n",
},
},
@@ -350,7 +364,7 @@ var compareOperatorScenarios = []expressionScenario{
document: `a: 5`,
expression: ".b <= .a",
expected: []string{
"D0, P[a], (!!bool)::false\n",
"D0, P[b], (!!bool)::false\n",
},
},
{
@@ -358,7 +372,7 @@ var compareOperatorScenarios = []expressionScenario{
document: `a: 5`,
expression: ".b < .a",
expected: []string{
"D0, P[a], (!!bool)::false\n",
"D0, P[b], (!!bool)::false\n",
},
},
}
+2
View File
@@ -27,6 +27,8 @@ const (
TSVOutputFormat
XMLOutputFormat
Base64OutputFormat
UriOutputFormat
ShOutputFormat
)
func OutputFormatFromString(format string) (PrinterOutputFormat, error) {
+4
View File
@@ -34,6 +34,10 @@ func (w *writeInPlaceHandlerImpl) CreateTempFile() (*os.File, error) {
if err != nil {
return nil, err
}
if err = changeOwner(info, file); err != nil {
return nil, err
}
log.Debug("WriteInPlaceHandler: writing to tempfile: %v", file.Name())
w.tempFile = file
return file, err
+44
View File
@@ -233,6 +233,37 @@ const expectedXmlWithProcInstAndDirectives = `<?xml version="1.0"?>
`
var xmlScenarios = []formatScenario{
{
skipDoc: true,
input: " <root>value<!-- comment--> </root>",
expected: "root: value # comment\n",
},
{
skipDoc: true,
input: "value<root>value</root>",
expectedError: "bad file 'sample.yml': invalid XML: Encountered chardata [value] outside of XML node",
scenarioType: "decode-error",
},
{
skipDoc: true,
input: "<root><!-- comment-->value</root>",
expected: "# comment\nroot: value\n",
},
{
skipDoc: true,
input: "<root> <!-- comment--></root>",
expected: "root: # comment\n",
},
{
skipDoc: true,
input: "<root>value<!-- comment-->anotherValue </root>",
expected: "root:\n # comment\n - value\n - anotherValue\n",
},
{
skipDoc: true,
input: "<root><cats><cat>quick</cat><cat>soft</cat><!-- kitty_comment--><cat>squishy</cat></cats></root>",
expected: "root:\n cats:\n cat:\n - quick\n - soft\n # kitty_comment\n\n - squishy\n",
},
{
description: "Parse xml: simple",
subdescription: "Notice how all the values are strings, see the next example on how you can fix that.",
@@ -264,6 +295,12 @@ var xmlScenarios = []formatScenario{
input: "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<cat legs=\"4\">meow</cat>",
expected: "+p_xml: version=\"1.0\" encoding=\"UTF-8\"\ncat:\n +content: meow\n +@legs: \"4\"\n",
},
{
description: "Parse xml: content split between comments/children",
subdescription: "Multiple content texts are collected into a sequence.",
input: "<root> value <!-- comment-->anotherValue <a>frog</a> cool!</root>",
expected: "root:\n +content: # comment\n - value\n - anotherValue\n - cool!\n a: frog\n",
},
{
description: "Parse xml: custom dtd",
subdescription: "DTD entities are processed as directives.",
@@ -481,6 +518,13 @@ func testXMLScenario(t *testing.T, s formatScenario) {
prefs := NewDefaultXmlPreferences()
prefs.SkipDirectives = true
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewXMLDecoder(prefs), NewXMLEncoder(2, prefs)), s.description)
case "decode-error":
result, err := processFormatScenario(s, NewXMLDecoder(NewDefaultXmlPreferences()), NewYamlEncoder(2, false, ConfiguredYamlPreferences))
if err == nil {
t.Errorf("Expected error '%v' but it worked: %v", s.expectedError, result)
} else {
test.AssertResultComplexWithContext(t, s.expectedError, err.Error(), s.description)
}
case "encode-error":
result, err := processFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewXMLEncoder(2, NewDefaultXmlPreferences()))
if err == nil {
+5 -5
View File
@@ -1,16 +1,16 @@
- update release notes
- increment version in version.go
- increment version in snapcraft.yaml
- make sure local build passes
- run ./scripts/copy-docs.sh (and commit the changed in the yq-book branch)
- commit version update changes
- tag git with same version number
- commit vX tag - this will trigger github actions
- run ./scripts/bump-version.sh
- git push
- git push --tags
- use github actions to publish docker and make github release
- check github updated yq action in marketplace
- snapcraft
- update snapcraft version
- https://snapcraft.io/yq/builds
- will auto create a candidate, test it works then promote
- !! need to update v4/stable as well as latest
+45
View File
@@ -1,3 +1,48 @@
4.31.1:
- Added shuffle command #1503
- Added ability to sort by multiple fields #1541
- Added @sh encoder #1526
- Added @uri/@urid encoder/decoder #1529
- Fixed date comparison with string date #1537
- Added from_unix/to_unix Operators
- Bumped dependency versions
4.30.8:
- Log info message when unable to chown file in linux (e.g. snap confinement) #1521
4.30.7:
- Fixed bug in splice operator #1511
- Fixed value operator bug #1515
- Fixed handling of merging null #1501
- Ownership of file now maintained in linux (thanks @vaguecoder) #1473
- Bumped dependency versions
4.30.6:
- Fixed xml comment in array of scalars #1465
- Include blank new lines in leading header preprocessing #1462
- Added aarch64 build (#1261)
- Bumped dependency versions (#1453)
4.30.5:
- XML Decoder: Comment parsing tweak
- XML Decoder: Fixed processing comments in empty XML #1446
- XML Decoder: Checking for invalid content outside of a root node #1448
- XML Decoder: Fixed issue where content surrounding tags are lost #1447
- XML Decoder: Fixed xml decode bug when there is content after a comment
- Fixed loading yaml with header issue #1445
- guessTagFromCustomType warning log is now a debug.
- Special thanks to @Kopfbremse for reporting XML issues!
4.30.4:
- Fixed bug in automated versioning (snap/brew)
4.30.3:
- Updated release process (automated versioning)
- Fixed handling of yaml directives (#1424)
- Fixed parsing of newline character in string expression #1430
- Fixed length compares to null instead of 0 issue #1427
4.30.2:
- Actually updated the default xml prefix :facepalm:
+43
View File
@@ -0,0 +1,43 @@
#!/bin/bash
set -e
if [ "$1" == "" ]; then
echo "Please specify at a version"
exit 1
fi
version=$1
# validate version is in the right format
echo $version | sed -r '/v4\.[0-9][0-9]\.[0-9][0-9]?$/!{q1}'
previousVersion=$(cat cmd/version.go| sed -n 's/.*Version = "\([^"]*\)"/\1/p')
echo "Updating from $previousVersion to $version"
sed -i "s/\(.*Version =\).*/\1 \"$version\"/" cmd/version.go
go build .
actualVersion=$(./yq --version)
if [ "$actualVersion" != "yq (https://github.com/mikefarah/yq/) version $version" ]; then
echo "Failed to update version.go"
exit 1
else
echo "version.go updated"
fi
version=$version ./yq -i '.version=strenv(version)' snap/snapcraft.yaml
actualSnapVersion=$(./yq '.version' snap/snapcraft.yaml)
if [ "$actualSnapVersion" != "$version" ]; then
echo "Failed to update snapcraft"
exit 1
else
echo "snapcraft updated"
fi
git add cmd/version.go snap/snapcraft.yaml
git commit -m 'Bumping version'
git tag $version
+2 -2
View File
@@ -1,5 +1,5 @@
#!/bin/sh
set -ex
go mod download golang.org/x/tools@latest
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.49.0
wget -O- -nv https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s v2.13.1
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.51.1
wget -O- -nv https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s
+2 -2
View File
@@ -1,7 +1,7 @@
#!/bin/bash
set -e
# you may need to go install github.com/mitchellh/gox@v1.0.1 first
echo $VERSION
CGO_ENABLED=0 gox -ldflags "${LDFLAGS}" -output="build/yq_{{.OS}}_{{.Arch}}" --osarch="darwin/amd64 darwin/arm64 freebsd/386 freebsd/amd64 freebsd/arm linux/386 linux/amd64 linux/arm linux/arm64 linux/mips linux/mips64 linux/mips64le linux/mipsle linux/ppc64 linux/ppc64le linux/s390x netbsd/386 netbsd/amd64 netbsd/arm openbsd/386 openbsd/amd64 windows/386 windows/amd64"
cd build
@@ -21,4 +21,4 @@ rhash -r -a . -o checksums
rhash --list-hashes > checksums_hashes_order
cp ../scripts/extract-checksum.sh .
cp ../scripts/extract-checksum.sh .
+2 -5
View File
@@ -1,22 +1,19 @@
name: yq
version: '4.30.1'
version: 'v4.31.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.
base: core18
grade: stable # devel|stable. must be 'stable' to release into candidate/stable channels
confinement: strict
apps:
yq:
command: yq
plugs: [home]
parts:
yq:
plugin: go
go-channel: 1.19/stable
go-channel: 1.20/stable
source: .
source-type: git
go-importpath: github.com/mikefarah/yq