Compare commits

..
Author SHA1 Message Date
Mike Farah ebeb1146ba v4.5.0 2021-02-04 13:04:53 +11:00
Mike Farah 0bb53ec770 Fixed delete bug 2021-02-04 12:48:07 +11:00
Mike Farah a50e154652 Added variable doc 2021-02-04 12:44:03 +11:00
Mike Farah f91d30e46c Fixed variable precedence 2021-02-04 12:39:04 +11:00
Mike Farah 07b053f040 Fixing op precedences 2021-02-04 12:18:54 +11:00
Mike Farah 35c97e832e Fixing op precedences 2021-02-03 17:11:47 +11:00
Mike Farah 8a5c47906e Added variables 2021-02-03 15:51:26 +11:00
Mike Farah a366cacad3 Dont create entries when selecting 2021-02-03 11:56:35 +11:00
Mike Farah 7c8d3b9e70 Pass context through operators
Allows more sophisticated functionality
2021-02-03 11:56:35 +11:00
Mike Farah ca10642e23 Added funding button 2021-02-01 15:50:57 +11:00
zyandMike Farah 73518f3915 change version from master to latest(v4) 2021-01-27 10:09:32 +11:00
zyandMike Farah 348ddb7a59 fix: go install fails
Installation by go will result in an error:
module declares its path as: xxx,
but was required as: xxx.

Go install with master branch will fix it.

fix: #676
2021-01-27 10:09:32 +11:00
Mike Farah f46fe384bd Fixed length of null to be zero 2021-01-26 10:21:16 +11:00
Mike Farah 071ec3c08c fixing docker pipeline for nextime 2021-01-21 21:15:21 +11:00
Mike Farah c2ed3a3e6d attempt to fix dockre 2021-01-21 21:11:04 +11:00
Mike Farah cc4c69bc89 Fixed bad docker version 2021-01-21 21:07:55 +11:00
Mike Farah 917fd0eb6a Bump version 2021-01-18 14:10:35 +11:00
Mike Farah b2056a2056 Can add and merge append to null 2021-01-18 13:58:46 +11:00
evnpandMike Farah ba59201217 Don't escape HTML chars when converting to json
json.Encoder and json.Marshal implicitly use HTMLEscape to convert
>, <, &, with \u003c, \u003e, \u0026. This behavior carries over
to yq, where chars will be escaped when outputting json but not when
outputting yaml.

This changeset disables this behavior via encoder.SetEscapeHTML(false).
Unfortunately there is no equivalent option for json.Marshal, so its
single usage has been replaced with an encoder (with escaping disabled).
2021-01-18 13:33:02 +11:00
Mike Farah cfddee9101 Fixed cross-function combinatorial bug 2021-01-18 13:28:40 +11:00
Mike Farah 1941bb66a5 wip 2021-01-18 10:15:31 +11:00
Mike Farah 29af9e4c63 thoughts 2021-01-16 14:56:52 +11:00
Mike Farah af3a9ae846 cross function fix wip 2021-01-16 14:09:49 +11:00
Mike Farah 5fb88627ca Fixed doker instructions 2021-01-15 09:44:02 +11:00
Mike Farah 4fc3793fcb Fixing multiply doc 2021-01-14 20:28:57 +11:00
Mike Farah d612897e89 Incrementing version 2021-01-14 19:50:37 +11:00
Mike Farah 806f906041 Added keys operator 2021-01-14 15:45:07 +11:00
Mike Farah e8b2f6e383 Added split string operator 2021-01-14 15:05:50 +11:00
Mike Farah e419b5a39d Added join strings operator 2021-01-14 14:46:50 +11:00
Mike Farah a5b6e08282 Split doc operator 2021-01-14 14:25:31 +11:00
Mike Farah 2417c0ee8f Fixing add,multiply,alternative operator precendences 2021-01-14 11:16:04 +11:00
Mike Farah d3b2e37b66 Fixed remove comments example 2021-01-14 09:12:14 +11:00
Mike Farah 54723c1a36 Dont use pointer for env prefs (avoid nil) 2021-01-13 17:00:53 +11:00
Mike Farah 0318c80d33 Dont use pointer for recursive prefs (avoid nil) 2021-01-13 17:00:03 +11:00
Mike Farah 8980846632 Dont use pointer for multiply prefs (avoid nil) 2021-01-13 16:59:01 +11:00
Mike Farah 0fb62d3ae1 Dont use pointer for commment prefs (avoid nil) 2021-01-13 16:56:24 +11:00
Mike Farah 85398727ad Added merge if empty 2021-01-13 16:54:28 +11:00
Mike Farah 0bf3d781f6 Added operator level doc 2021-01-13 15:23:26 +11:00
Mike Farah 5c15936bf3 Incrementing version 2021-01-13 10:29:06 +11:00
Mike Farah b0735d8152 Renaming pathtree to expression 2021-01-13 10:25:26 +11:00
Mike Farah f17cbfd007 Removed global vars 2021-01-13 10:04:52 +11:00
Mike Farah 7b7ab70286 UnwrapDoc now private 2021-01-13 10:00:51 +11:00
Mike Farah 43165fa340 Moved eval function to eval interface 2021-01-13 09:35:57 +11:00
Mikhail KatychevandMike Farah feda9f044d added lib_test.go 2021-01-13 09:22:40 +11:00
Mikhail KatychevandMike Farah 85629af59c added EvaluateNodes and EvaluateCandidateNodes to yqlib 2021-01-13 09:22:40 +11:00
Mike Farah 5fc26e9453 Merge now copies anchor names 2021-01-13 09:21:16 +11:00
Mike Farah 07a6fa4df5 Fixed creation of candidateNode in operators to include file metadata 2021-01-12 19:36:28 +11:00
Mike Farah ec29ee7c14 Cleaning up exposed public api 2021-01-12 09:58:50 +11:00
Mike Farah 24538c0cc7 Fixed tag operator for top level node 2021-01-12 09:45:57 +11:00
Mike Farah 61398bfd3a Fixed equals operator for top level node 2021-01-12 09:40:37 +11:00
Mike Farah f7d95021c1 Fixed has operator for top level node 2021-01-12 09:30:24 +11:00
Mike Farah 6bb8b1fb77 fixing exposed functions and interfaces 2021-01-11 17:13:48 +11:00
Mike Farah 5e2c19cc86 fixing exposed functions and interfaces 2021-01-11 16:46:28 +11:00
Mike Farah 30c269a66c Better add documentation 2021-01-11 15:52:06 +11:00
Mike Farah 70a1c60d7b Added scalar addition 2021-01-11 15:43:50 +11:00
Mike Farah e4d48bbc0d Fixed collect at document level 2021-01-11 14:44:53 +11:00
Mike Farah 369ff94ad0 Better error handling will empty env 2021-01-11 14:38:53 +11:00
Mike Farah 1c7fb14631 Better recursive decent docs 2021-01-10 11:27:18 +11:00
Mike Farah f4de5c4300 Better docIndex docs 2021-01-10 11:18:13 +11:00
Mike Farah a72c14f06b Better env docs 2021-01-10 11:09:59 +11:00
Mike Farah 5ed52aca66 Merged env commands in :eye-roll: 2021-01-10 10:51:23 +11:00
Mike Farah 5a5ac0dfef Merge branch 'env_var' 2021-01-10 10:50:31 +11:00
Mike Farah 5a55869745 Bump version 2021-01-09 12:23:20 +11:00
Mike Farah 15e18bb98b Error when passing files and using null-input flag 2021-01-09 12:23:06 +11:00
Mike Farah 644063646e Env Ops! 2021-01-09 12:06:19 +11:00
Mike Farah aabed1a237 strenv 2021-01-09 11:38:08 +11:00
Mike Farah c12764dba8 wip 2021-01-08 21:09:43 +11:00
Mike Farah 1d5ecb244d wip 2021-01-08 21:07:46 +11:00
Mike Farah 7798a141cf Bumped go yaml for comment hanlding fixes 2021-01-08 20:56:54 +11:00
Mike Farah b7583a538d Added webi 2021-01-08 12:14:36 +11:00
Mike Farah 529e88b630 Added recurse examples 2021-01-08 12:11:29 +11:00
Mike Farah 658006eb93 Added another delete example 2021-01-08 11:59:49 +11:00
Mike Farah 2743a7e058 Can assign-update tag 2021-01-06 20:44:28 +11:00
Mike Farah f95862fba3 Can assign-update style 2021-01-06 20:37:53 +11:00
Mike Farah 3f58c4bc38 Can assign-update aliases and anchors 2021-01-06 20:30:48 +11:00
Mike Farah a7975df7cd Can assign-update comments 2021-01-06 20:22:50 +11:00
Mike Farah 3d5a5e0360 updating readme 2021-01-06 15:33:20 +11:00
Mike Farah 155755ae2f brew v3! 2021-01-06 15:22:29 +11:00
Mike Farah 601c13531c Updated collect objcet doc 2021-01-06 15:20:54 +11:00
Mike Farah 69d00c89df Added shorthand document index selection 2021-01-05 13:28:37 +11:00
Mike Farah 2faff7b05f Unwrap node in get tag to return proper tag at root level 2021-01-05 13:23:03 +11:00
Mike Farah 165949041d Added v3 snap instructions 2021-01-05 13:05:03 +11:00
Mike Farah dbd7ab0f13 Refactored doc generation, add fi fileIndex alias 2021-01-02 10:49:33 +11:00
Mike Farah 6d512ad718 Fixed updating yaml from other files 2021-01-02 10:27:32 +11:00
Mike Farah 4fef4a7ab1 update issue template, instruct questions to be raised in disussion 2020-12-31 13:43:44 +11:00
Mike Farah dcb17b51a9 fixed heading 2020-12-31 09:33:15 +11:00
Mike Farah 385417556d added tar.gz instructions 2020-12-31 09:30:59 +11:00
574 changed files with 5675 additions and 58886 deletions
+1 -1
View File
@@ -1 +1 @@
bin/*
bin
+51
View File
@@ -0,0 +1,51 @@
---
name: Bug report - V3
about: Create a report to help us improve
title: ''
labels: bug, v3
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
Note that any how to questions should be posted in the discussion board and not raised as an issue.
Version of yq: 3.X.X
Operating system: mac/linux/windows/....
Installed via: docker/binary release/homebrew/snap/...
**Input Yaml**
Concise yaml document(s) (as simple as possible to show the bug, please keep it to 10 lines or less)
data1.yml:
```yaml
this: should really work
```
data2.yml:
```yaml
but: it strangely didn't
```
**Command**
The command you ran:
```
yq merge data1.yml data2.yml
```
**Actual behavior**
```yaml
cat: meow
```
**Expected behavior**
```yaml
this: should really work
but: it strangely didn't
```
**Additional context**
Add any other context about the problem here.
+2 -2
View File
@@ -34,13 +34,13 @@ The command you ran:
yq eval-all 'select(fileIndex==0) | .a.b.c' data1.yml data2.yml
```
**Actual behaviour**
**Actual behavior**
```yaml
cat: meow
```
**Expected behaviour**
**Expected behavior**
```yaml
this: should really work
+1 -1
View File
@@ -26,7 +26,7 @@ country: Australia
And we run a command:
```bash
yq 'predictWeatherOf(.country)'
yq eval 'predictWeatherOf(.country)'
```
it could output
-22
View File
@@ -1,22 +0,0 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: docker
directory: /
schedule:
day: thursday
interval: weekly
- package-ecosystem: github-actions
directory: /
schedule:
day: thursday
interval: weekly
- package-ecosystem: gomod
directory: /
schedule:
day: thursday
interval: weekly
-1
View File
@@ -1 +0,0 @@
When you find a bug - make sure to include a new test that exposes the bug, as well as the fix for the bug itself.
-72
View File
@@ -1,72 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '24 3 * * 1'
permissions: {}
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'go' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
# ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
-107
View File
@@ -1,107 +0,0 @@
name: Release Docker GitHub Action
on:
workflow_dispatch:
permissions: {}
jobs:
publishGithubActionDocker:
environment: dockerhub
env:
IMAGE_NAME: mikefarah/yq
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up QEMU
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
with:
platforms: all
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
with:
version: latest
- name: Verify Dockerfile base image digest matches yq:4
run: |
PINNED_DIGEST=$(grep -oE 'sha256:[a-f0-9]{64}' github-action/Dockerfile | head -1)
if [ -z "${PINNED_DIGEST}" ]; then
echo "::error::Could not find a sha256 digest in github-action/Dockerfile"
exit 1
fi
LATEST_DIGEST=$(docker buildx imagetools inspect "${IMAGE_NAME}:4" --format '{{printf "%s" .Manifest.Digest}}')
echo "Dockerfile pins: ${PINNED_DIGEST}"
echo "mikefarah/yq:4 is: ${LATEST_DIGEST}"
if [ "${PINNED_DIGEST}" != "${LATEST_DIGEST}" ]; then
echo "::error::github-action/Dockerfile digest does not match the current mikefarah/yq:4 image"
echo "Update the FROM line in github-action/Dockerfile to:"
echo " FROM mikefarah/yq:4@${LATEST_DIGEST}"
exit 1
fi
- name: Resolve version from yq:4
run: |
IMAGE_VERSION=$(docker run --rm "${IMAGE_NAME}:4" --version | awk '{print $NF}' | sed 's/^v//')
if [ -z "${IMAGE_VERSION}" ]; then
echo "::error::Could not determine yq version from ${IMAGE_NAME}:4"
exit 1
fi
echo "Resolved yq version: ${IMAGE_VERSION}"
echo "IMAGE_VERSION=${IMAGE_VERSION}" >> "${GITHUB_ENV}"
- name: Login to Docker Hub
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to GitHub Container Registry
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push github-action image
working-directory: github-action
run: |
PLATFORMS="linux/amd64,linux/ppc64le,linux/arm64,linux/arm/v7,linux/s390x"
echo "Building and pushing github-action image for version ${IMAGE_VERSION}"
docker buildx build \
--label "org.opencontainers.image.authors=https://github.com/mikefarah/yq/graphs/contributors" \
--label "org.opencontainers.image.created=$(date --rfc-3339=seconds)" \
--label "org.opencontainers.image.description=yq is a portable command-line data file processor" \
--label "org.opencontainers.image.documentation=https://mikefarah.gitbook.io/yq/" \
--label "org.opencontainers.image.licenses=MIT" \
--label "org.opencontainers.image.revision=$(git rev-parse HEAD)" \
--label "org.opencontainers.image.source=https://github.com/mikefarah/yq" \
--label "org.opencontainers.image.title=yq" \
--label "org.opencontainers.image.url=https://mikefarah.gitbook.io/yq/" \
--label "org.opencontainers.image.version=${IMAGE_VERSION}" \
--platform "${PLATFORMS}" \
--pull \
--push \
-t "${IMAGE_NAME}:${IMAGE_VERSION}-githubaction" \
-t "${IMAGE_NAME}:4-githubaction" \
-t "${IMAGE_NAME}:latest-githubaction" \
-t "ghcr.io/${IMAGE_NAME}:${IMAGE_VERSION}-githubaction" \
-t "ghcr.io/${IMAGE_NAME}:4-githubaction" \
-t "ghcr.io/${IMAGE_NAME}:latest-githubaction" \
.
- name: Report action.yml digest to pin
run: |
GITHUBACTION_DIGEST=$(docker buildx imagetools inspect "${IMAGE_NAME}:4-githubaction" --format '{{printf "%s" .Manifest.Digest}}')
echo "Published ${IMAGE_NAME}:4-githubaction at ${GITHUBACTION_DIGEST}"
echo "Update action.yml image to:"
echo " docker://${IMAGE_NAME}:4-githubaction@${GITHUBACTION_DIGEST}"
-82
View File
@@ -1,82 +0,0 @@
name: Release Docker
on:
release:
types: [released]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
permissions: {}
jobs:
publishDocker:
environment: dockerhub
env:
IMAGE_NAME: mikefarah/yq
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up QEMU
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
with:
platforms: all
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
with:
version: latest
- name: Available platforms
run: echo ${{ steps.buildx.outputs.platforms }} && docker version
- name: Login to Docker Hub
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to GitHub Container Registry
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push image
run: |
echo "GithubRef: ${GITHUB_REF}"
VERSION=${GITHUB_REF##*/}
echo "VERSION: ${VERSION}"
IMAGE_VERSION=${VERSION:1}
echo "IMAGE_VERSION: ${IMAGE_VERSION}"
PLATFORMS="linux/amd64,linux/ppc64le,linux/arm64,linux/arm/v7,linux/s390x"
echo "Building and pushing version ${IMAGE_VERSION} of image ${IMAGE_NAME}"
docker buildx build \
--label "org.opencontainers.image.authors=https://github.com/mikefarah/yq/graphs/contributors" \
--label "org.opencontainers.image.created=$(date --rfc-3339=seconds)" \
--label "org.opencontainers.image.description=yq is a portable command-line data file processor" \
--label "org.opencontainers.image.documentation=https://mikefarah.gitbook.io/yq/" \
--label "org.opencontainers.image.licenses=MIT" \
--label "org.opencontainers.image.revision=$(git rev-parse HEAD)" \
--label "org.opencontainers.image.source=https://github.com/mikefarah/yq" \
--label "org.opencontainers.image.title=yq" \
--label "org.opencontainers.image.url=https://mikefarah.gitbook.io/yq/" \
--label "org.opencontainers.image.version=${IMAGE_VERSION}" \
--platform "${PLATFORMS}" \
--pull \
--push \
-t "${IMAGE_NAME}:${IMAGE_VERSION}" \
-t "${IMAGE_NAME}:4" \
-t "${IMAGE_NAME}:latest" \
-t "ghcr.io/${IMAGE_NAME}:${IMAGE_VERSION}" \
-t "ghcr.io/${IMAGE_NAME}:4" \
-t "ghcr.io/${IMAGE_NAME}:latest" \
.
+8 -38
View File
@@ -1,61 +1,31 @@
name: Build
on: [push, pull_request]
permissions:
contents: read
on: [push]
jobs:
verify-action-digest:
name: Verify action.yml image digest
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Verify action.yml digest matches published image
run: |
PINNED_DIGEST=$(grep -oE 'sha256:[a-f0-9]{64}' action.yml | head -1)
if [ -z "${PINNED_DIGEST}" ]; then
echo "::error::action.yml does not pin the runtime image by digest"
exit 1
fi
LATEST_DIGEST=$(docker buildx imagetools inspect docker.io/mikefarah/yq:4-githubaction --format '{{printf "%s" .Manifest.Digest}}')
echo "action.yml pins: ${PINNED_DIGEST}"
echo "mikefarah/yq:4-githubaction: ${LATEST_DIGEST}"
if [ "${PINNED_DIGEST}" != "${LATEST_DIGEST}" ]; then
echo "::error::action.yml digest does not match the current mikefarah/yq:4-githubaction image"
echo "Update the image line in action.yml to:"
echo " docker://mikefarah/yq:4-githubaction@${LATEST_DIGEST}"
exit 1
fi
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
- name: Set up Go 1.15
uses: actions/setup-go@v1
with:
go-version: '^1.20'
go-version: 1.15
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@v2
- name: Get dependencies
run: |
go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
curl -sSfL https://raw.githubusercontent.com/golang/dep/1f7c19e5f52f49ffb9f956f64c010be14683468b/install.sh | env DEP_RELEASE_TAG=v0.5.4 sh
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
- name: Check the build
shell: bash -l {0}
- name: Download deps
run: |
export PATH=${PATH}:`go env GOPATH`/bin
scripts/devtools.sh
make local build
make local build
+53 -47
View File
@@ -2,63 +2,69 @@ name: Release YQ
on:
push:
tags:
- 'v4.*'
- 'draft-*'
permissions: {}
- 'v*'
jobs:
publishGitRelease:
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
go-version: '^1.20'
check-latest: true
- name: Compile man page markup
id: gen-man-page-md
run: |
./scripts/generate-man-page-md.sh
- name: Get the version
id: get_version
run: echo "VERSION=${GITHUB_REF##*/}" >> "${GITHUB_OUTPUT}"
- name: Generate man page
uses: docker://pandoc/core:2.14.2@sha256:04e127c6642a2b9d447c26fe0ac6a5932efa8f508eda9f07da51b6e621dd7c19
id: gen-man-page
with:
args: >-
--standalone
--to man
--variable=title:"YQ"
--variable=section:"1"
--variable=header:"yq (https://github.com/mikefarah/yq/) version ${{ steps.get_version.outputs.VERSION }}"
--variable=author:"Mike Farah"
--output=yq.1
man.md
- name: Install cosign
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
go-version: '^1.15'
- name: Cross compile
run: |
sudo apt-get install rhash -y
go install github.com/goreleaser/goreleaser/v2@v2.16.0
go get github.com/mitchellh/gox
./scripts/xcompile.sh
- name: Sign checksums
run: |
cosign sign-blob --yes --bundle build/checksums.bundle build/checksums
cosign sign-blob --yes --bundle build/checksums-bsd.bundle build/checksums-bsd
- name: Release
uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1
- name: Create Release
id: create_release
uses: actions/create-release@v1.0.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
files: build/*
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
draft: true
fail_on_unmatched_files: true
prerelease: false
- uses: shogo82148/actions-upload-release-asset@v1
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: build/*
publishDocker:
environment: dockerhub
env:
IMAGE_NAME: mikefarah/yq
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
with:
platforms: all
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
with:
version: latest
- name: Available platforms
run: echo ${{ steps.buildx.outputs.platforms }} && docker version
- name: Build and push image
run: |
IMAGE_V_VERSION="$(git describe --tags --abbrev=0)"
IMAGE_VERSION=${IMAGE_V_VERSION:1}
SHORT_SHA1=$(git rev-parse --short HEAD)
PLATFORMS="linux/amd64,linux/ppc64le,linux/arm64"
echo "Building and pushing version ${IMAGE_VERSION} of image ${IMAGE_NAME}"
echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
docker buildx build --platform "${PLATFORMS}" -t "${IMAGE_NAME}:${IMAGE_VERSION}" -t "${IMAGE_NAME}:latest" -t "${IMAGE_NAME}:4" \
--push .
-78
View File
@@ -1,78 +0,0 @@
# This workflow uses actions that are not certified by GitHub. They are provided
# by a third-party and are governed by separate terms of service, privacy
# policy, and support documentation.
name: Scorecard supply-chain security
on:
# For Branch-Protection check. Only the default branch is supported. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
branch_protection_rule:
# To guarantee Maintained check is occasionally updated. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
schedule:
- cron: '39 7 * * 2'
push:
branches: [ "master" ]
# Declare default permissions as read only.
permissions: read-all
jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
# `publish_results: true` only works when run from the default branch. conditional can be removed if disabled.
if: github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request'
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Needed to publish results and get a badge (see publish_results below).
id-token: write
# Uncomment the permissions below if installing in a private repository.
# contents: read
# actions: read
steps:
- name: "Checkout code"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4
with:
results_file: results.sarif
results_format: sarif
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
# - you want to enable the Branch-Protection check on a *public* repository, or
# - you are installing Scorecard on a *private* repository
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional.
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
# Public repositories:
# - Publish results to OpenSSF REST API for easy access by consumers
# - Allows the repository to include the Scorecard badge.
# - See https://github.com/ossf/scorecard-action#publishing-results.
# For private repositories:
# - `publish_results` will always be set to `false`, regardless
# of the value entered here.
publish_results: true
# (Optional) Uncomment file_mode if you have a .gitattributes with files marked export-ignore
# file_mode: git
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard (optional).
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@d1ba80a13dd99fba24a470575428917156a28b43 # v4.37.5
with:
sarif_file: results.sarif
-31
View File
@@ -1,31 +0,0 @@
name: Release Snap
on:
release:
types: [released]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
permissions: {}
jobs:
buildSnap:
environment: snap
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: snapcore/action-build@3bdaa03e1ba6bf59a65f84a751d943d549a54e79 # v1.3.0
id: build
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.STORE_LOGIN }}
with:
snapcraft-args: "remote-build --launchpad-accept-public-upload"
- uses: snapcore/action-publish@214b86e5ca036ead1668c79afb81e550e6c54d40 # v1.2.0
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.STORE_LOGIN }}
with:
snap: ${{ steps.build.outputs.snap }}
release: stable
-28
View File
@@ -1,28 +0,0 @@
name: Test Yq Action
on:
push:
workflow_dispatch:
permissions:
contents: read
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Get test
id: get_value
uses: mikefarah/yq@master
with:
cmd: yq '.test' examples/multiline-text.yaml
- name: Multiline test
run: echo "### It was [${{ steps.get_value.outputs.result }}]" >> $GITHUB_STEP_SUMMARY
- name: Write inplace test
id: lookupSdkVersion
uses: mikefarah/yq@master
with:
cmd: yq -i '.b.c = 5' examples/sample.yaml
-36
View File
@@ -22,10 +22,8 @@ _cgo_gotypes.go
_cgo_export.*
_testmain.go
cover.out
coverage.out
coverage.html
coverage_sorted.txt
*.exe
*.test
*.prof
@@ -40,37 +38,3 @@ parts/
prime/
.snapcraft/
yq*.snap
test.yml
test*.yml
test*.tf
test*.xml
test*.toml
test*.yaml
*.kyaml
test_dir1/
test_dir2/
0.yml
1.yml
2.yml
# man page
man.md
yq.1
# debian pkg
_build
debian/files
# intellij
/.idea
# vscode
.vscode
yq3
# Golang
.gomodcache/
.gocache/
-38
View File
@@ -1,38 +0,0 @@
run:
timeout: 5m
linters:
enable:
- asciicheck
- depguard
- errorlint
- gci
- gochecknoinits
- gofmt
- goimports
- gosec
- gosimple
- staticcheck
- unused
- misspell
- nakedret
- nolintlint
- predeclared
- revive
- unconvert
- unparam
linters-settings:
depguard:
rules:
prevent_unmaintained_packages:
list-mode: lax
files:
- $all
- "!$test"
deny:
- pkg: io/ioutil
desc: "replaced by io and os packages since Go 1.16: https://tip.golang.org/doc/go1.16#ioutil"
issues:
exclude-rules:
- linters:
- revive
text: "var-naming"
-57
View File
@@ -1,57 +0,0 @@
version: "2"
linters:
enable:
- asciicheck
- depguard
- errorlint
- gochecknoinits
- gosec
- misspell
- nakedret
- nolintlint
- predeclared
- revive
- unconvert
- unparam
settings:
misspell:
locale: UK
ignore-rules:
- color
- colors
depguard:
rules:
prevent_unmaintained_packages:
list-mode: lax
files:
- $all
- '!$test'
deny:
- pkg: io/ioutil
desc: 'replaced by io and os packages since Go 1.16: https://tip.golang.org/doc/go1.16#ioutil'
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
rules:
- linters:
- revive
text: var-naming
paths:
- third_party$
- builtin$
- examples$
formatters:
enable:
- gci
- gofmt
- goimports
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
-48
View File
@@ -1,48 +0,0 @@
version: 2
dist: build
builds:
- id: yq
binary: yq_{{ .Os }}_{{ .Arch }}
ldflags:
- -s -w
env:
- CGO_ENABLED=0
targets:
- darwin_amd64
- darwin_arm64
- freebsd_386
- freebsd_amd64
- freebsd_arm
- linux_386
- linux_amd64
- linux_arm
- linux_arm64
- linux_loong64
- linux_mips
- linux_mips64
- linux_mips64le
- linux_mipsle
- linux_ppc64
- linux_ppc64le
- linux_riscv64
- linux_s390x
- netbsd_386
- netbsd_amd64
- netbsd_arm
- openbsd_386
- openbsd_amd64
- windows_386
- windows_amd64
- windows_arm64
no_unique_dist_dir: true
release:
disable: true
skip_upload: true
-500
View File
@@ -1,500 +0,0 @@
# yq — agent instructions
## ⚠️ MANDATORY: GitHub agent disclosure
**Always required. No exceptions.**
Whenever you perform **any** GitHub action on behalf of the user, you **must** disclose that an AI agent (Cursor) wrote the content and is acting on the user's behalf — **not the user personally**. Do this **before** submitting; never post first and add the disclosure later.
Applies to **all** GitHub interactions, including:
- Pull requests (titles, descriptions, and reviews)
- PR comments and inline review comments
- Issues (new issues, comments, and updates)
- Any other post or reply on GitHub
**How to disclose:** Put it prominently at the **top** of every PR description, review body, comment, or issue. Use wording like:
Inline review comments must include a short disclosure too (e.g. `> Generated by Cursor acting on the user's behalf, not the user personally.`).
**Never** submit a GitHub action without this disclosure.
---
Always run the spellcheck before raising a PR:
```bash
bash scripts/spelling.sh
```
This is also included in the full CI pipeline via `make local test`.
## Cursor Cloud specific instructions
### Overview
**yq** is a Go CLI for querying and transforming YAML, JSON, XML, INI, and other structured formats. There are no long-running services — development is build-and-test against a local `./yq` binary.
### Prerequisites
- **Go ≥ 1.25** (see `go.mod`)
- **Bash** (acceptance tests)
- **Docker/Podman** is optional; use `make local <target>` to run natively when containers are unavailable
### PATH
After `scripts/devtools.sh`, add Go tool binaries to PATH:
```bash
export PATH="$HOME/go/bin:$PATH"
```
`golangci-lint` and `typos` install to `$HOME/go/bin`; `gosec` installs to `./bin/gosec` in the repo root.
### Common commands (local, no Docker)
| Task | Command |
|------|---------|
| Install dev tools | `bash scripts/devtools.sh` |
| Vendor dependencies | `make local vendor` |
| Build binary | `go build -o yq .` or `make local build` |
| Format | `make local format` |
| Lint | `make local check` |
| Unit tests | `make local test` or `bash scripts/test.sh` |
| Acceptance (E2E) | `bash scripts/acceptance.sh` (requires `./yq` built first) |
`make local build` runs the full CI chain (format → spelling → gosec → lint → unit tests → build → acceptance). For a faster loop, build with `go build -o yq .` and run `bash scripts/acceptance.sh`.
### Caveats
- **`make` without `local`** tries Docker/Podman (`Dockerfile.dev`). In Cloud Agent VMs without Docker, always prefix with `make local`.
- **Spelling step** uses `typos` (installed by `scripts/devtools.sh`).
- **`make local test` / `scripts/check.sh`** require `golangci-lint` on PATH (`devtools.sh`).
---
# General rules
**DO:**
- You can use ./yq with the `--debug-node-info` flag to get a deeper understanding of the ast.
- run ./scripts/format.sh to format the code; then ./scripts/check.sh lint and finally ./scripts/spelling.sh to check spelling.
- Add comprehensive tests to cover the changes
- Run test suite to ensure there is no regression
- Use UK english spelling
- **Follow the mandatory GitHub agent disclosure rule above** on every GitHub action — no exceptions
**DON'T:**
- Git add or commit
- Add comments to functions that are self-explanatory
- **Post to GitHub without the mandatory agent disclosure** (PRs, reviews, comments, issues, or any other GitHub interaction)
# Adding a New Encoder/Decoder
This guide explains how to add support for a new format (encoder/decoder) to yq without modifying `candidate_node.go`.
## Overview
The encoder/decoder architecture in yq is based on two main interfaces:
- **Encoder**: Converts a `CandidateNode` to output in a specific format
- **Decoder**: Reads input in a specific format and creates a `CandidateNode`
Each format is registered in `pkg/yqlib/format.go` and made available through factory functions.
## Architecture
### Key Files
- `pkg/yqlib/encoder.go` - Defines the `Encoder` interface
- `pkg/yqlib/decoder.go` - Defines the `Decoder` interface
- `pkg/yqlib/format.go` - Format registry and factory functions
- `pkg/yqlib/operator_encoder_decoder.go` - Encode/decode operators
- `pkg/yqlib/encoder_*.go` - Encoder implementations
- `pkg/yqlib/decoder_*.go` - Decoder implementations
### Interfaces
**Encoder Interface:**
```go
type Encoder interface {
Encode(writer io.Writer, node *CandidateNode) error
PrintDocumentSeparator(writer io.Writer) error
PrintLeadingContent(writer io.Writer, content string) error
CanHandleAliases() bool
}
```
**Decoder Interface:**
```go
type Decoder interface {
Init(reader io.Reader) error
Decode() (*CandidateNode, error)
}
```
## Step-by-Step: Adding a New Encoder/Decoder
### Step 1: Create the Encoder File
Create `pkg/yqlib/encoder_<format>.go` implementing the `Encoder` interface:
- `Encode()` - Convert a `CandidateNode` to your format and write to the output writer
- `PrintDocumentSeparator()` - Handle document separators if your format requires them
- `PrintLeadingContent()` - Handle leading content/comments if supported
- `CanHandleAliases()` - Return whether your format supports YAML aliases
See `encoder_json.go` or `encoder_base64.go` for examples.
### Step 2: Create the Decoder File
Create `pkg/yqlib/decoder_<format>.go` implementing the `Decoder` interface:
- `Init()` - Initialize the decoder with the input reader and set up any needed state
- `Decode()` - Decode one document from the input and return a `CandidateNode`, or `io.EOF` when finished
See `decoder_json.go` or `decoder_base64.go` for examples.
### Step 3: Create Tests (Mandatory)
Create a test file `pkg/yqlib/<format>_test.go` using the `formatScenario` pattern:
- Define test scenarios as `formatScenario` structs with fields: `description`, `input`, `expected`, `scenarioType`
- `scenarioType` can be `"decode"` (test decoding to YAML) or `"roundtrip"` (encode/decode preservation)
- Create a helper function `test<Format>Scenario()` that switches on `scenarioType`
- Create main test function `Test<Format>FormatScenarios()` that iterates over scenarios
- The main test function should use `documentScenarios` to ensure testcase documentation is generated.
Test coverage must include:
- Basic data types (scalars, arrays, objects/maps)
- Nested structures
- Edge cases (empty inputs, special characters, escape sequences)
- Format-specific features or syntax
- Round-trip tests: decode → encode → decode should preserve data
See `hcl_test.go` for a complete example.
### Step 4: Register the Format in format.go
Edit `pkg/yqlib/format.go`:
1. Add a new format variable:
- `"<format>"` is the formal name (e.g., "json", "yaml")
- `[]string{...}` contains short aliases (can be empty)
- The first function creates an encoder (can be nil for encode-only formats)
- The second function creates a decoder (can be nil for decode-only formats)
2. Add the format to the `Formats` slice in the same file
See existing formats in `format.go` for the exact structure.
### Step 5: Handle Encoder Configuration (if needed)
If your format has preferences/configuration options:
1. Create a preferences struct with your configuration fields
2. Update the encoder to accept preferences in its factory function
3. Update `format.go` to pass the configured preferences
4. Update `operator_encoder_decoder.go` if special indent handling is needed (see existing formats like JSON and YAML for the pattern)
This pattern is optional and only needed if your format has user-configurable options.
## Build Tags
Use build tags to allow optional compilation of formats:
- Add `//go:build !yq_no<format>` at the top of your encoder and decoder files
- Create a no-build version in `pkg/yqlib/no_<format>.go` that returns nil for encoder/decoder factories
This allows users to compile yq without certain formats using: `go build -tags yq_no<format>`
## Working with CandidateNode
The `CandidateNode` struct represents a YAML node with:
- `Kind`: The node type (ScalarNode, SequenceNode, MappingNode)
- `Tag`: The YAML tag (e.g., "!!str", "!!int", "!!map")
- `Value`: The scalar value (for ScalarNode only)
- `Content`: Child nodes (for SequenceNode and MappingNode)
Key methods:
- `node.guessTagFromCustomType()` - Infer the tag from Go type
- `node.AsList()` - Convert to a list for processing
- `node.CreateReplacement()` - Create a new replacement node
- `NewCandidate()` - Create a new CandidateNode
## Key Points
**DO:**
- Implement only the `Encoder` and `Decoder` interfaces
- Register your format in `format.go` only
- Keep format-specific logic in your encoder/decoder files
- Use the candidate_node style attribute to store style information for round-trip. Ask if this needs to be updated with new styles.
- Use build tags for optional compilation
- Add comprehensive tests
- Run the specific encoder/decoder test (e.g. <format>_test.go) whenever you make ay changes to the encoder_<format> or decoder_<format>
- Handle errors gracefully
- Add the no build directive, like the xml encoder and decoder, that enables a minimal yq builds. e.g. `//go:build !yq_<format>`. Be sure to also update the build_small-yq.sh and build-tinygo-yq.sh to not include the new format.
**DON'T:**
- Modify `candidate_node.go` to add format-specific logic
- Add format-specific fields to `CandidateNode`
- Create special cases in core navigation or evaluation logic
- Bypass the encoder/decoder interfaces
- Use candidate_node tag attribute for anything other than indicate the data type
## Examples
Refer to existing format implementations for patterns:
- **Simple encoder/decoder**: `encoder_json.go`, `decoder_json.go`
- **Complex with preferences**: `encoder_yaml.go`, `decoder_yaml.go`
- **Encoder-only**: `encoder_sh.go` (ShFormat has nil decoder)
- **String-only operations**: `encoder_base64.go`, `decoder_base64.go`
## Testing Your Implementation (Mandatory)
Tests must be implemented in `<format>_test.go` following the `formatScenario` pattern:
1. **Create test scenarios** using the `formatScenario` struct with fields:
- `description`: Brief description of what's being tested
- `input`: Sample input in your format
- `expected`: Expected output (typically in YAML for decode tests)
- `scenarioType`: Either `"decode"` or `"roundtrip"`
2. **Test coverage must include:**
- Basic data types (scalars, arrays, objects/maps)
- Nested structures
- Edge cases (empty inputs, special characters, escape sequences)
- Format-specific features or syntax
- Round-trip tests: decode → encode → decode should preserve data
3. **Test function pattern:**
- `test<Format>Scenario()`: Helper function that switches on `scenarioType`
- `Test<Format>FormatScenarios()`: Main test function that iterates over scenarios
4. **Example from existing formats:**
- See `hcl_test.go` for a complete example
- See `yaml_test.go` for YAML-specific patterns
- See `json_test.go` for more complex scenarios
## Common Patterns
### Format with Indentation
Use preferences to control output formatting:
```go
type <format>Preferences struct {
Indent int
}
func (prefs *<format>Preferences) Copy() <format>Preferences {
return *prefs
}
```
### Multiple Documents
Decoders should support reading multiple documents:
```go
func (dec *<format>Decoder) Decode() (*CandidateNode, error) {
if dec.finished {
return nil, io.EOF
}
// ... decode next document ...
if noMoreDocuments {
dec.finished = true
}
return candidate, nil
}
```
---
# Adding a New Operator
This guide explains how to add a new operator to yq. Operators are the core of yq's expression language and process `CandidateNode` objects without requiring modifications to `candidate_node.go` itself.
## Overview
Operators transform data by implementing a handler function that processes a `Context` containing `CandidateNode` objects. Each operator is:
1. Defined as an `operationType` in `operation.go`
2. Registered in the lexer in `lexer_participle.go`
3. Implemented in its own `operator_<type>.go` file
4. Tested in `operator_<type>_test.go`
5. Documented in `pkg/yqlib/doc/operators/headers/<type>.md`
## Architecture
### Key Files
- `pkg/yqlib/operation.go` - Defines `operationType` and operator registry
- `pkg/yqlib/lexer_participle.go` - Registers operators with their syntax patterns
- `pkg/yqlib/operator_<type>.go` - Operator implementation
- `pkg/yqlib/operator_<type>_test.go` - Operator tests using `expressionScenario`
- `pkg/yqlib/doc/operators/headers/<type>.md` - Documentation header
### Core Types
**operationType:**
```go
type operationType struct {
Type string // Unique operator name (e.g., "REVERSE")
NumArgs uint // Number of arguments (0 for no args)
Precedence uint // Operator precedence (higher = higher precedence)
Handler operatorHandler // The function that executes the operator
CheckForPostTraverse bool // Whether to apply post-traversal logic
ToString func(*Operation) string // Custom string representation
}
```
**operatorHandler signature:**
```go
type operatorHandler func(*dataTreeNavigator, Context, *ExpressionNode) (Context, error)
```
**expressionScenario for tests:**
```go
type expressionScenario struct {
description string
subdescription string
document string
expression string
expected []string
skipDoc bool
expectedError string
}
```
## Step-by-Step: Adding a New Operator
### Step 1: Create the Operator Implementation File
Create `pkg/yqlib/operator_<type>.go` implementing the operator handler function:
- Implement the `operatorHandler` function signature
- Process nodes from `context.MatchingNodes`
- Return a new `Context` with results using `context.ChildContext()`
- Use `candidate.CreateReplacement()` or `candidate.CreateReplacementWithComments()` to create new nodes
- Handle errors gracefully with meaningful error messages
See `operator_reverse.go` or `operator_keys.go` for examples.
### Step 2: Register the Operator in operation.go
Add the operator type definition to `pkg/yqlib/operation.go`:
```go
var <type>OpType = &operationType{
Type: "<TYPE>", // All caps, matches pattern in lexer
NumArgs: 0, // 0 for no args, 1+ for args
Precedence: 50, // Typical range: 40-55
Handler: <type>Operator, // Reference to handler function
}
```
**Precedence guidelines:**
- 10-20: Logical operators (OR, AND, UNION)
- 30: Pipe operator
- 40: Assignment and comparison operators
- 42: Arithmetic operators (ADD, SUBTRACT, MULTIPLY, DIVIDE)
- 50-52: Most other operators
- 55: High precedence (e.g., GET_VARIABLE)
**Optional fields:**
- `CheckForPostTraverse: true` - If your operator can have another directly after it without the pipe character. Most of the time this is false.
- `ToString: customToString` - Custom string representation (rarely needed)
### Step 3: Register the Operator in lexer_participle.go
Edit `pkg/yqlib/lexer_participle.go` to add the operator to the lexer rules:
- Use `simpleOp()` for simple keyword patterns
- Use object syntax for regex patterns or complex syntax
- Support optional characters with `_?` and aliases with `|`
See existing operators in `lexer_participle.go` for pattern examples.
### Step 4: Create Tests (Mandatory)
Create `pkg/yqlib/operator_<type>_test.go` using the `expressionScenario` pattern:
- Define test scenarios with `description`, `document`, `expression`, and `expected` fields
- `expected` is a slice of strings showing output format: `"D<doc>, P[<path>], (<tag>)::<value>\n"`
- Set `skipDoc: true` for edge cases you don't want in generated documentation
- Include `subdescription` for longer test names
- Set `expectedError` if testing error cases
- Create main test function that iterates over scenarios
- The main test function should use `documentScenarios` to ensure testcase documentation is generated.
Test coverage must include:
- Basic data types and nested structures
- Edge cases (empty inputs, special characters, type errors)
- Multiple outputs if applicable
- Format-specific features
See `operator_reverse_test.go` for a simple example and `operator_keys_test.go` for complex cases.
### Step 5: Create Documentation Header
Create `pkg/yqlib/doc/operators/headers/<type>.md`:
- Use the exact operator name as the title
- Include a concise 1-2 sentence summary
- Add additional context or examples if the operator is complex
See existing headers in `doc/operators/headers/` for examples.
## Working with Context and CandidateNode
### Context Management
- `context.ChildContext(results)` - Create child context with results
- `context.GetVariable("varName")` - Get variables stored in context
- `context.SetVariable("varName", value)` - Set variables in context
### CandidateNode Operations
- `candidate.CreateReplacement(ScalarNode, "!!str", stringValue)` - Create a replacement node
- `candidate.CreateReplacementWithComments(SequenceNode, "!!seq", candidate.Style)` - With style preserved
- `candidate.Kind` - The node type (ScalarNode, SequenceNode, MappingNode)
- `candidate.Tag` - The YAML tag (!!str, !!int, etc.)
- `candidate.Value` - The scalar value (for ScalarNode only)
- `candidate.Content` - Child nodes (for SequenceNode and MappingNode)
- `candidate.guessTagFromCustomType()` - Infer the tag from Go type
- `candidate.AsList()` - Convert to a list representation
## Key Points
**DO:**
- Implement the operator handler with the correct signature
- Register in `operation.go` with appropriate precedence
- Add the lexer pattern in `lexer_participle.go`
- Write comprehensive tests covering normal and edge cases
- Create a documentation header in `doc/operators/headers/`
- Use `Context.ChildContext()` for proper context threading
- Handle all node types gracefully
- Return meaningful error messages
**DON'T:**
- Modify `candidate_node.go` (operators shouldn't need this)
- Modify core navigation or evaluation logic
- Bypass the handler function pattern
- Add format-specific or operator-specific fields to `CandidateNode`
- Skip tests or documentation
## Examples
Refer to existing operator implementations for patterns:
- **No-argument operator**: `operator_reverse.go` - Processes arrays/sequences
- **Single-argument operator**: `operator_map.go` - Takes an expression argument
- **Complex multi-output**: `operator_keys.go` - Produces multiple results
- **With preferences**: `operator_to_number.go` - Configuration options
- **Error handling**: `operator_error.go` - Control flow with errors
- **String operations**: `operator_strings.go` - Multiple related operators
## Testing Patterns
Refer to existing test files for specific patterns:
- Basic expression tests in `operator_reverse_test.go`
- Multi-output tests in `operator_keys_test.go`
- Error handling tests in `operator_error_test.go`
- Tests with `skipDoc` flag to exclude from generated documentation
## Common Patterns
Refer to existing operator implementations for these patterns:
- Simple transformation: see `operator_reverse.go`
- Type checking: see `operator_error.go`
- Working with arguments: see `operator_map.go`
- Post-traversal operators: see `operator_with.go`
+6 -6
View File
@@ -11,7 +11,7 @@ appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behaviour that contributes to creating a positive environment
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
@@ -20,7 +20,7 @@ include:
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behaviour by participants include:
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
@@ -34,13 +34,13 @@ Examples of unacceptable behaviour by participants include:
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behaviour and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behaviour.
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviours that they deem inappropriate,
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
@@ -54,7 +54,7 @@ further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behaviour may be
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at mikefarah@gmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
+8 -229
View File
@@ -1,229 +1,8 @@
# Before you begin
Not all new PRs will be merged in
It's recommended to check with the owner first (e.g. raise an issue) to discuss a new feature before developing, to ensure your hard efforts don't go to waste.
PRs to fix bugs and issues are almost always welcome :pray: please ensure you write tests as well.
The following types of PRs will _not_ be accepted:
- **Significant refactors** take a lot of time to understand and can have all sorts of unintended side effects. If you think there's a better way to do things (that requires significant changes) raise an issue for discussion first :)
- **Release pipeline PRs** are a security risk - it's too easy for a serious vulnerability to sneak in (either intended or not). If there is a new cool way of releasing things, raise an issue for discussion first - it will need to be gone over with a fine tooth comb.
- **Version bumps** are handled by dependabot, the bot will auto-raise PRs and they will be regularly merged in.
- **New release platforms** At this stage, yq is not going to maintain any other release platforms other than GitHub and Docker - that said, I'm more than happy to put in other community maintained methods in the README for visibility :heart:
# Development
## Initial Setup
1. Install [Golang](https://golang.org/) (version 1.24.0 or later)
2. Run `scripts/devtools.sh` to install required development tools:
- golangci-lint for code linting
- gosec for security analysis
3. Run `make [local] vendor` to install vendor dependencies
4. Run `make [local] test` to ensure you can run the existing tests
## Development Workflow
1. **Write unit tests first** - Changes will not be accepted without corresponding unit tests (see Testing section below)
2. **Make your code changes**
3. **Run tests and linting**: `make [local] test` (this runs formatting, linting, security checks, and tests)
4. **Create your PR** and get kudos! :)
## Make Commands
- Use `make [local] <command>` for local development (runs in Docker container)
- Use `make <command>` for CI/CD environments
- Common commands:
- `make [local] vendor` - Install dependencies
- `make [local] test` - Run all checks and tests
- `make [local] build` - Build the yq binary
- `make [local] format` - Format code
- `make [local] check` - Run linting and security checks
# Code Quality
## Linting and Formatting
The project uses strict linting rules defined in `.golangci.yml`. All code must pass:
- **Code formatting**: gofmt, goimports, gci
- **Linting**: revive, errorlint, gosec, misspell, and others
- **Security checks**: gosec security analysis
- **Spelling checks**: misspell detection
Run `make [local] check` to verify your code meets all quality standards.
## Code Style Guidelines
- Follow standard Go conventions
- Use meaningful variable names
- Add comments for public functions and complex logic
- Keep functions focused and reasonably sized
- Use the project's existing patterns and conventions
# Testing
## Test Structure
Tests in yq use the `expressionScenario` pattern. Each test scenario includes:
- `expression`: The yq expression to test
- `document`: Input YAML/JSON (optional)
- `expected`: Expected output
- `skipDoc`: Whether to skip documentation generation
## Writing Tests
1. **Find the appropriate test file** (e.g., `operator_add_test.go` for addition operations)
2. **Add your test scenario** to the `*OperatorScenarios` slice
3. **Run the specific test**: `go test -run TestAddOperatorScenarios` (replace with appropriate test name)
4. **Verify documentation generation** (see Documentation section)
## Test Examples
```go
var addOperatorScenarios = []expressionScenario{
{
skipDoc: true,
expression: `"foo" + "bar"`,
expected: []string{
"D0, P[], (!!str)::foobar\n",
},
},
{
document: "apples: 3",
expression: `.apples + 3`,
expected: []string{
"D0, P[apples], (!!int)::6\n",
},
},
}
```
## Running Tests
- **All tests**: `make [local] test`
- **Specific test**: `go test -run TestName`
- **With coverage**: `make [local] cover`
# Documentation
## Documentation Generation
The project uses a documentation system that combines static headers with dynamically generated content from tests.
### How It Works
1. **Static headers** are defined in `pkg/yqlib/doc/operators/headers/*.md`
2. **Dynamic content** is generated from test scenarios in `*_test.go` files
3. **Generated docs** are created in `pkg/yqlib/doc/*.md` by concatenating headers with test-generated content
4. **Documentation is synced** to the gitbook branch for the website
### Updating Operator Documentation
#### For Test-Generated Documentation
Most operator documentation is generated from tests. To update:
1. **Find the test file** (e.g., `operator_add_test.go`)
2. **Update test scenarios** - each `expressionScenario` with `skipDoc: false` becomes documentation
3. **Run the test** to regenerate docs:
```bash
cd pkg/yqlib
go test -run TestAddOperatorScenarios
```
4. **Verify the generated documentation** in `pkg/yqlib/doc/add.md`
5. **Create a PR** with your changes
#### For Header-Only Documentation
If documentation exists only in `headers/*.md` files:
1. **Update the header file directly** (e.g., `pkg/yqlib/doc/operators/headers/add.md`)
2. **Create a PR** with your changes
### Updating Static Documentation
For documentation not in the master branch:
1. **Check the gitbook branch** for additional pages
2. **Update the `*.md` files** directly
3. **Create a PR** to the gitbook branch
### Documentation Best Practices
- **Write clear, concise examples** in test scenarios
- **Use meaningful variable names** in examples
- **Include edge cases** and error conditions
- **Test your documentation changes** by running the specific test
- **Verify generated output** matches expectations
Note: PRs with small changes (e.g. minor typos) may not be merged (see https://joel.net/how-one-guy-ruined-hacktoberfest2020-drama).
# Troubleshooting
## Common Setup Issues
### Docker/Podman Issues
- **Problem**: `make` commands fail with Docker errors
- **Solution**: Ensure Docker or Podman is running and accessible
- **Alternative**: Use `make local <command>` to run in containers
### Go Version Issues
- **Problem**: Build fails with Go version errors
- **Solution**: Ensure you have Go 1.24.0 or later installed
- **Check**: Run `go version` to verify
### Vendor Dependencies
- **Problem**: `make vendor` fails or dependencies are outdated
- **Solution**:
```bash
go mod tidy
make [local] vendor
```
### Linting Failures
- **Problem**: `make check` fails with linting errors
- **Solution**:
```bash
make [local] format # Auto-fix formatting
# Manually fix remaining linting issues
make [local] check # Verify fixes
```
### Test Failures
- **Problem**: Tests fail locally but pass in CI
- **Solution**:
```bash
make [local] test # Run in Docker container
```
- **Problem**: Tests fail with a VCS error:
```bash
error obtaining VCS status: exit status 128
Use -buildvcs=false to disable VCS stamping.
```
- **Solution**:
Git security mechanisms prevent Golang from detecting the Git details inside
the container; either build with the `local` option, or pass GOFLAGS to
disable Golang buildvcs behaviour.
```bash
make local test
# OR
make test GOFLAGS='-buildvcs=true'
```
### Documentation Generation Issues
- **Problem**: Generated docs don't update after test changes
- **Solution**:
```bash
cd pkg/yqlib
go test -run TestSpecificOperatorScenarios
# Check if generated file updated in pkg/yqlib/doc/
```
## Getting Help
- **Check existing issues**: Search GitHub issues for similar problems
- **Create an issue**: If you can't find a solution, create a detailed issue
- **Ask questions**: Use GitHub Discussions for general questions
- **Join the community**: Check the project's community channels
1. Install (golang)[https://golang.org/]
1. Run `scripts/devtools.sh` to install the required devtools
2. Run `make [local] vendor` to install the vendor dependencies
2. Run `make [local] test` to ensure you can run the existing tests
3. Write unit tests - (see existing examples). Changes will not be accepted without corresponding unit tests.
4. Make the code changes.
5. `make [local] test` to lint code and run tests
6. Profit! ok no profit, but raise a PR and get kudos :)
+12 -15
View File
@@ -1,28 +1,25 @@
FROM golang:1.26.5@sha256:079e59808d2d252516e27e3f3a9c003740dee7f75e55aa71528766d52bcfc16a AS builder
FROM golang:1.15 as builder
WORKDIR /go/src/mikefarah/yq
COPY . .
# cache devtools
COPY ./scripts/devtools.sh /go/src/mikefarah/yq/scripts/devtools.sh
RUN ./scripts/devtools.sh
RUN CGO_ENABLED=0 go build -ldflags "-s -w" .
# RUN ./scripts/test.sh -- this too often times out in the github pipeline.
RUN ./scripts/acceptance.sh
COPY . /go/src/mikefarah/yq
RUN CGO_ENABLED=0 make local build
# Choose alpine as a base image to make this useful for CI, as many
# CI tools expect an interactive shell inside the container
FROM alpine:3@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b AS production
LABEL maintainer="Mike Farah <mikefarah@users.noreply.github.com>"
FROM alpine:3.12.3 as production
COPY --from=builder /go/src/mikefarah/yq/yq /usr/bin/yq
RUN chmod +x /usr/bin/yq
ARG VERSION=none
LABEL version=${VERSION}
WORKDIR /workdir
RUN set -eux; \
addgroup -g 1000 yq; \
adduser -u 1000 -G yq -s /bin/sh -h /home/yq -D yq
RUN chown -R yq:yq /workdir
USER yq
ENTRYPOINT ["/usr/bin/yq"]
+24 -4
View File
@@ -1,11 +1,31 @@
FROM golang:1.26.5@sha256:079e59808d2d252516e27e3f3a9c003740dee7f75e55aa71528766d52bcfc16a
FROM golang:1.15
COPY scripts/devtools.sh /opt/devtools.sh
RUN set -e -x && \
/opt/devtools.sh
RUN set -e -x \
&& /opt/devtools.sh
ENV PATH=/go/bin:$PATH
# install mkdocs
RUN set -ex \
&& buildDeps=' \
build-essential \
python3-dev \
' \
&& apt-get update && apt-get install -y --no-install-recommends \
$buildDeps \
python3 \
python3-setuptools \
python3-wheel \
python3-pip \
&& pip3 install --upgrade \
pip \
'Markdown>=2.6.9' \
'mkdocs>=0.16.3' \
'mkdocs-material>=1.10.1' \
'markdown-include>=0.5.1' \
&& apt-get purge -y --auto-remove $buildDeps \
&& rm -rf /var/lib/apt/lists/*
ENV CGO_ENABLED 0
ENV GOPATH /go:/yq
-2
View File
@@ -1,5 +1,3 @@
The MIT License (MIT)
Copyright (c) 2017 Mike Farah
Permission is hereby granted, free of charge, to any person obtaining a copy
+21 -31
View File
@@ -2,7 +2,6 @@ MAKEFLAGS += --warn-undefined-variables
SHELL := /bin/bash
.SHELLFLAGS := -o pipefail -euc
.DEFAULT_GOAL := install
ENGINE := $(shell { (podman version > /dev/null 2>&1 && command -v podman) || command -v docker; } 2>/dev/null)
include Makefile.variables
@@ -18,7 +17,6 @@ help:
@echo ' make vendor Install dependencies to vendor directory.'
@echo ' make format Run code formatter.'
@echo ' make check Run static code analysis (lint).'
@echo ' make secure Run gosec.'
@echo ' make test Run tests on project.'
@echo ' make cover Run tests and capture code coverage metrics on project.'
@echo ' make clean Clean the directory tree of produced artifacts.'
@@ -34,19 +32,18 @@ clean:
## prefix before other make targets to run in your local dev environment
local: | quiet
@$(eval ENGINERUN= )
@$(eval GOFLAGS="$(GOFLAGS)" )
@$(eval DOCKRUN= )
@mkdir -p tmp
@touch tmp/dev_image_id
quiet: # this is silly but shuts up 'Nothing to be done for `local`'
@:
prepare: tmp/dev_image_id
tmp/dev_image_id: Dockerfile.dev scripts/devtools.sh _typos.toml
tmp/dev_image_id: Dockerfile.dev scripts/devtools.sh
@mkdir -p tmp
@${ENGINE} rmi -f ${DEV_IMAGE} > /dev/null 2>&1 || true
@${ENGINE} build -t ${DEV_IMAGE} -f Dockerfile.dev .
@${ENGINE} inspect -f "{{ .ID }}" ${DEV_IMAGE} > tmp/dev_image_id
@docker rmi -f ${DEV_IMAGE} > /dev/null 2>&1 || true
@docker build -t ${DEV_IMAGE} -f Dockerfile.dev .
@docker inspect -f "{{ .ID }}" ${DEV_IMAGE} > tmp/dev_image_id
# ----------------------------------------------
# build
@@ -56,65 +53,58 @@ build: build/dev
.PHONY: build/dev
build/dev: test *.go
@mkdir -p bin/
${ENGINERUN} go build --ldflags "$(LDFLAGS)"
${ENGINERUN} bash ./scripts/acceptance.sh
${DOCKRUN} go build --ldflags "$(LDFLAGS)"
${DOCKRUN} bash ./scripts/acceptance.sh
## Compile the project for multiple OS and Architectures.
xcompile: check
@rm -rf build/
@mkdir -p build
${ENGINERUN} bash ./scripts/xcompile.sh
${DOCKRUN} bash ./scripts/xcompile.sh
@find build -type d -exec chmod 755 {} \; || :
@find build -type f -exec chmod 755 {} \; || :
.PHONY: install
install: build
${ENGINERUN} go install
${DOCKRUN} go install
# Each of the fetch should be an entry within vendor.json; not currently included within project
.PHONY: vendor
vendor: tmp/dev_image_id
@mkdir -p vendor
${ENGINERUN} go mod vendor
${DOCKRUN} go mod vendor
# ----------------------------------------------
# develop and test
.PHONY: format
format: vendor
${ENGINERUN} bash ./scripts/format.sh
.PHONY: spelling
spelling: format
${ENGINERUN} bash ./scripts/spelling.sh
.PHONY: secure
secure: spelling
${ENGINERUN} bash ./scripts/secure.sh
${DOCKRUN} bash ./scripts/format.sh
.PHONY: check
check: secure
${ENGINERUN} bash ./scripts/check.sh
check: format
${DOCKRUN} bash ./scripts/check.sh
.PHONY: test
test: check
${ENGINERUN} bash ./scripts/test.sh
${DOCKRUN} bash ./scripts/test.sh
.PHONY: cover
cover: check
@rm -rf cover/
@mkdir -p cover
${ENGINERUN} bash ./scripts/coverage.sh
${DOCKRUN} bash ./scripts/coverage.sh
@find cover -type d -exec chmod 755 {} \; || :
@find cover -type f -exec chmod 644 {} \; || :
.PHONY: build-docs
build-docs: prepare mkdocs.yml mkdocs/*
${DOCKRUN} mkdocs build
@find docs -type d -exec chmod 755 {} \; || :
@find docs -type f -exec chmod 644 {} \; || :
.PHONY: release
release: xcompile
${ENGINERUN} bash ./scripts/publish.sh
${DOCKRUN} bash ./scripts/publish.sh
# ----------------------------------------------
# utilities
+3 -8
View File
@@ -4,11 +4,9 @@ IMPORT_PATH := github.com/mikefarah/${PROJECT}
export GIT_COMMIT = $(shell git rev-parse --short HEAD)
export GIT_DIRTY = $(shell test -n "$$(git status --porcelain)" && echo "+CHANGES" || true)
export GIT_DESCRIBE = $(shell git describe --tags --always)
GOFLAGS :=
LDFLAGS :=
LDFLAGS += -X main.GitCommit=${GIT_COMMIT}${GIT_DIRTY}
LDFLAGS += -X main.GitDescribe=${GIT_DESCRIBE}
LDFLAGS += -w -s
GITHUB_TOKEN ?=
@@ -27,17 +25,14 @@ ifeq ($(CYG_CHECK),1)
else
# all non-windows environments
ROOT := $(shell pwd)
# Deliberately use `command -v` instead of `which` to be POSIX compliant
SELINUX := $(shell command -v getenforce >/dev/null 2>&1 && echo :z)
endif
DEV_IMAGE := ${PROJECT}_dev
ENGINERUN := ${ENGINE} run --rm \
DOCKRUN := docker run --rm \
-e LDFLAGS="${LDFLAGS}" \
-e GOFLAGS="${GOFLAGS}" \
-e GITHUB_TOKEN="${GITHUB_TOKEN}" \
-v ${ROOT}/vendor:/go/src${SELINUX} \
-v ${ROOT}:/${PROJECT}/src/${IMPORT_PATH}${SELINUX} \
-v ${ROOT}/vendor:/go/src \
-v ${ROOT}:/${PROJECT}/src/${IMPORT_PATH} \
-w /${PROJECT}/src/${IMPORT_PATH} \
${DEV_IMAGE}
+79 -352
View File
@@ -1,107 +1,40 @@
# yq
![Build](https://github.com/mikefarah/yq/workflows/Build/badge.svg) ![Docker Pulls](https://img.shields.io/docker/pulls/mikefarah/yq.svg) ![Github Releases (by Release)](https://img.shields.io/github/downloads/mikefarah/yq/total.svg) ![Go Report](https://goreportcard.com/badge/github.com/mikefarah/yq) ![CodeQL](https://github.com/mikefarah/yq/workflows/CodeQL/badge.svg)
![Build](https://github.com/mikefarah/yq/workflows/Build/badge.svg) ![Docker Pulls](https://img.shields.io/docker/pulls/mikefarah/yq.svg) ![Github Releases (by Release)](https://img.shields.io/github/downloads/mikefarah/yq/total.svg) ![Go Report](https://goreportcard.com/badge/github.com/mikefarah/yq)
A lightweight and portable command-line YAML, JSON, INI and XML processor. `yq` uses [jq](https://github.com/stedolan/jq) (a popular JSON processor) like syntax but works with yaml files as well as json, kyaml, xml, ini, properties, csv and tsv. It doesn't yet support everything `jq` does - but it does support the most common operations and functions, and more is being added continuously.
a lightweight and portable command-line YAML processor. `yq` uses [jq](https://github.com/stedolan/jq) like syntax but works with yaml files as well as json. It doesn't yet support everything `jq` does - but it does support the most common operations and functions, and more is being added continuously.
yq is written in Go - so you can download a dependency free binary for your platform and you are good to go! If you prefer there are a variety of package managers that can be used as well as Docker and Podman, all listed below.
yq is written in go - so you can download a dependency free binary for your platform and you are good to go! If you prefer there are a variety of package managers that can be used as well as docker, all listed below.
## Quick Usage Guide
## V4 released!
V4 is now officially released, it's quite different from V3 (sorry for the migration), however it is much more similar to ```jq```, using a similar expression syntax and therefore support much more complex functionality!
### Basic Operations
If you've been using v3 and want/need to upgrade, checkout the [upgrade guide](https://mikefarah.gitbook.io/yq/v/v4.x/upgrading-from-v3).
**Read a value:**
```bash
yq '.a.b[0].c' file.yaml
```
**Pipe from STDIN:**
```bash
yq '.a.b[0].c' < file.yaml
```
**Update a yaml file in place:**
```bash
yq -i '.a.b[0].c = "cool"' file.yaml
```
**Update using environment variables:**
```bash
NAME=mike yq -i '.a.b[0].c = strenv(NAME)' file.yaml
```
### Advanced Operations
**Merge multiple files:**
```bash
# merge two files
yq -n 'load("file1.yaml") * load("file2.yaml")'
# merge using globs (note: `ea` evaluates all files at once instead of in sequence)
yq ea '. as $item ireduce ({}; . * $item )' path/to/*.yml
```
**Multiple updates to a yaml file:**
```bash
yq -i '
.a.b[0].c = "cool" |
.x.y.z = "foobar" |
.person.name = strenv(NAME)
' file.yaml
```
**Find and update an item in an array:**
```bash
# Note: requires input file - add your file at the end
yq -i '(.[] | select(.name == "foo") | .address) = "12 cat st"' data.yaml
```
**Convert between formats:**
```bash
# Convert JSON to YAML (pretty print)
yq -Poy sample.json
# Convert YAML to JSON
yq -o json file.yaml
# Convert XML to YAML
yq -o yaml file.xml
```
See [recipes](https://mikefarah.gitbook.io/yq/recipes) for more examples and the [documentation](https://mikefarah.gitbook.io/yq/) for more information.
Take a look at the discussions for [common questions](https://github.com/mikefarah/yq/discussions/categories/q-a), and [cool ideas](https://github.com/mikefarah/yq/discussions/categories/show-and-tell)
Support for v3 will cease August 2021, until then, critical bug and security fixes will still get applied if required.
## Install
### [Download the latest binary](https://github.com/mikefarah/yq/releases/latest)
### wget
Use wget to download pre-compiled binaries. Choose your platform and architecture:
Use wget to download the pre-compiled binaries:
**For Linux (example):**
#### Compressed via tar.gz
```bash
# Set your platform variables (adjust as needed)
VERSION=v4.2.0
PLATFORM=linux_amd64
# Download compressed binary
wget https://github.com/mikefarah/yq/releases/download/${VERSION}/yq_${PLATFORM}.tar.gz -O - |\
tar xz && sudo mv yq_${PLATFORM} /usr/local/bin/yq
# Or download plain binary
wget https://github.com/mikefarah/yq/releases/download/${VERSION}/yq_${PLATFORM} -O /usr/local/bin/yq &&\
chmod +x /usr/local/bin/yq
wget https://github.com/mikefarah/yq/releases/download/${VERSION}/${BINARY}.tar.gz -O - |\
tar xz && mv ${BINARY} /usr/bin/yq
```
**Latest version (Linux AMD64):**
#### Plain binary
```bash
wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/local/bin/yq &&\
chmod +x /usr/local/bin/yq
wget https://github.com/mikefarah/yq/releases/download/${VERSION}/${BINARY} -O /usr/bin/yq &&\
chmod +x /usr/bin/yq
```
**Available platforms:** `linux_amd64`, `linux_arm64`, `linux_arm`, `linux_386`, `darwin_amd64`, `darwin_arm64`, `windows_amd64`, `windows_386`, etc.
For instance, VERSION=v4.2.0 and BINARY=yq_linux_amd64
### MacOS / Linux via Homebrew:
Using [Homebrew](https://brew.sh/)
@@ -109,56 +42,49 @@ Using [Homebrew](https://brew.sh/)
brew install yq
```
or, for the (deprecated) v3 version:
```
brew install yq@3
```
Note that for v3, as it is a versioned brew it will not add the `yq` command to your path automatically. Please follow the instructions given by brew upon installation.
### Linux via snap:
```
snap install yq
```
or, for the (deprecated) v3 version:
```
snap install yq --channel=v3/stable
```
#### Snap notes
`yq` installs with [_strict confinement_](https://docs.snapcraft.io/snap-confinement/6233) in snap, this means it doesn't have direct access to root files. To read root files you can:
```
sudo cat /etc/myfile | yq '.a.path'
sudo cat /etc/myfile | yq e '.a.path' -
```
And to write to a root file you can either use [sponge](https://linux.die.net/man/1/sponge):
```
sudo cat /etc/myfile | yq '.a.path = "value"' | sudo sponge /etc/myfile
sudo cat /etc/myfile | yq e '.a.path = "value"' - | sudo sponge /etc/myfile
```
or write to a temporary file:
```
sudo cat /etc/myfile | yq '.a.path = "value"' | sudo tee /etc/myfile.tmp
sudo cat /etc/myfile | yq e '.a.path = "value"' | sudo tee /etc/myfile.tmp
sudo mv /etc/myfile.tmp /etc/myfile
rm /etc/myfile.tmp
```
### Run with Docker or Podman
### Run with Docker
#### One-time use:
```bash
# Docker - process files in current directory
docker run --rm -v "${PWD}":/workdir mikefarah/yq '.a.b[0].c' file.yaml
# Podman - same usage as Docker
podman run --rm -v "${PWD}":/workdir mikefarah/yq '.a.b[0].c' file.yaml
```
**Security note:** You can run `yq` in Docker with restricted privileges:
```bash
docker run --rm --security-opt=no-new-privileges --cap-drop all --network none \
-v "${PWD}":/workdir mikefarah/yq '.a.b[0].c' file.yaml
```
#### Pipe data via STDIN:
You'll need to pass the `-i --interactive` flag to Docker/Podman:
#### Oneshot use:
```bash
# Process piped data
docker run -i --rm mikefarah/yq '.this.thing' < myfile.yml
# Same with Podman
podman run -i --rm mikefarah/yq '.this.thing' < myfile.yml
docker run --rm -v "${PWD}":/workdir mikefarah/yq <command> [flags] [expression ]FILE...
```
#### Run commands interactively:
@@ -167,10 +93,6 @@ podman run -i --rm mikefarah/yq '.this.thing' < myfile.yml
docker run --rm -it -v "${PWD}":/workdir --entrypoint sh mikefarah/yq
```
```bash
podman run --rm -it -v "${PWD}":/workdir --entrypoint sh mikefarah/yq
```
It can be useful to have a bash function to avoid typing the whole docker command:
```bash
@@ -179,99 +101,15 @@ yq() {
}
```
```bash
yq() {
podman run --rm -i -v "${PWD}":/workdir mikefarah/yq "$@"
}
### Go Get:
```
#### Running as root:
`yq`'s container image no longer runs under root (https://github.com/mikefarah/yq/pull/860). If you'd like to install more things in the container image, or you're having permissions issues when attempting to read/write files you'll need to either:
```
docker run --user="root" -it --entrypoint sh mikefarah/yq
```
```
podman run --user="root" -it --entrypoint sh mikefarah/yq
```
Or, in your Dockerfile:
```
FROM mikefarah/yq
USER root
RUN apk add --no-cache bash
USER yq
```
#### Missing timezone data
By default, the alpine image yq uses does not include timezone data. If you'd like to use the `tz` operator, you'll need to include this data:
```
FROM mikefarah/yq
USER root
RUN apk add --no-cache tzdata
USER yq
```
#### Podman with SELinux
If you are using podman with SELinux, you will need to set the shared volume flag `:z` on the volume mount:
```
-v "${PWD}":/workdir:z
```
### GitHub Action
```
- name: Set foobar to cool
uses: mikefarah/yq@master
with:
cmd: yq -i '.foo.bar = "cool"' 'config.yml'
- name: Get an entry with a variable that might contain dots or spaces
id: get_username
uses: mikefarah/yq@master
with:
cmd: yq '.all.children.["${{ matrix.ip_address }}"].username' ops/inventories/production.yml
- name: Reuse a variable obtained in another step
run: echo ${{ steps.get_username.outputs.result }}
```
See https://mikefarah.gitbook.io/yq/usage/github-action for more.
### Go Install:
```
go install github.com/mikefarah/yq/v4@latest
GO111MODULE=on go get github.com/mikefarah/yq/v4
```
## Community Supported Installation methods
As these are supported by the community :heart: - however, they may be out of date with the officially supported releases.
_Please note that the Debian package (previously supported by @rmescandon) is no longer maintained. Please use an alternative installation method._
### X-CMD
Checkout `yq` on x-cmd: https://x-cmd.com/mod/yq
- Instant Results: See the output of your yq filter in real-time.
- Error Handling: Encounter a syntax error? It will display the error message and the results of the closest valid filter
Thanks @edwinjhlee!
### Nix
```
nix profile install nixpkgs#yq-go
```
See [here](https://search.nixos.org/packages?channel=unstable&show=yq-go&from=0&size=50&sort=relevance&type=packages&query=yq-go)
### Webi
# Webi
```
webi yq
@@ -280,34 +118,13 @@ webi yq
See [webi](https://webinstall.dev/)
Supported by @adithyasunil26 (https://github.com/webinstall/webi-installers/tree/master/yq)
### Arch Linux
```
pacman -S go-yq
```
### Windows:
Using [Chocolatey](https://chocolatey.org)
[![Chocolatey](https://img.shields.io/chocolatey/v/yq.svg)](https://chocolatey.org/packages/yq)
[![Chocolatey](https://img.shields.io/chocolatey/dt/yq.svg)](https://chocolatey.org/packages/yq)
```
choco install yq
```
Supported by @chillum (https://chocolatey.org/packages/yq)
Using [scoop](https://scoop.sh/)
```
scoop install main/yq
```
Using [winget](https://learn.microsoft.com/en-us/windows/package-manager/)
```
winget install --id MikeFarah.yq
```
### MacPorts:
### Mac:
Using [MacPorts](https://www.macports.org/)
```
sudo port selfupdate
@@ -316,60 +133,37 @@ sudo port install yq
Supported by @herbygillot (https://ports.macports.org/maintainer/github/herbygillot)
### Alpine Linux
- Enable edge/community repo by adding ```$MIRROR/alpine/edge/community``` to ```/etc/apk/repositories```
- Update database index with ```apk update```
- Install yq with ```apk add yq```
Alpine Linux v3.20+ (and Edge):
```
apk add yq-go
```
Supported by Tuan Hoang
https://pkgs.alpinelinux.org/package/edge/community/x86/yq
Alpine Linux up to v3.19:
```
apk add yq
```
Supported by Tuan Hoang (https://pkgs.alpinelinux.org/packages?name=yq-go)
### Flox:
Flox can be used to install yq on Linux, MacOS, and Windows through WSL.
```
flox install yq
```
### MacOS / Linux via gah:
Using [gah](https://github.com/marverix/gah)
```
gah install yq
### On Ubuntu 16.04 or higher from Debian package:
```sh
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CC86BB64
sudo add-apt-repository ppa:rmescandon/yq
sudo apt update
sudo apt install yq -y
```
Supported by @rmescandon (https://launchpad.net/~rmescandon/+archive/ubuntu/yq)
## Features
- [Detailed documentation with many examples](https://mikefarah.gitbook.io/yq/)
- Written in portable go, so you can download a lovely dependency free binary
- Uses similar syntax as `jq` but works with YAML, INI, [JSON](https://mikefarah.gitbook.io/yq/usage/convert) and [XML](https://mikefarah.gitbook.io/yq/usage/xml) files
- Uses similar syntax as `jq` but works with YAML and JSON files
- Fully supports multi document yaml files
- Supports yaml [front matter](https://mikefarah.gitbook.io/yq/usage/front-matter) blocks (e.g. jekyll/assemble)
- Colorized yaml output
- [Date/Time manipulation and formatting with TZ](https://mikefarah.gitbook.io/yq/operators/datetime)
- [Deep data structures](https://mikefarah.gitbook.io/yq/operators/traverse-read)
- [Sort keys](https://mikefarah.gitbook.io/yq/operators/sort-keys)
- Manipulate yaml [comments](https://mikefarah.gitbook.io/yq/operators/comment-operators), [styling](https://mikefarah.gitbook.io/yq/operators/style), [tags](https://mikefarah.gitbook.io/yq/operators/tag) and [anchors and aliases](https://mikefarah.gitbook.io/yq/operators/anchor-and-alias-operators).
- [Update in place](https://mikefarah.gitbook.io/yq/v/v4.x/commands/evaluate#flags)
- [Complex expressions to select and update](https://mikefarah.gitbook.io/yq/operators/select#select-and-update-matching-values-in-map)
- [Deeply traverse yaml](https://mikefarah.gitbook.io/yq/v/v4.x/traverse)
- [Sort yaml by keys](https://mikefarah.gitbook.io/yq/v/v4.x/sort-keys)
- Manipulate yaml [comments](https://mikefarah.gitbook.io/yq/comment-operators), [styling](https://mikefarah.gitbook.io/yq/style), [tags](https://mikefarah.gitbook.io/yq/tag) and [anchors and aliases](https://mikefarah.gitbook.io/yq/anchor-and-alias-operators).
- [Update yaml inplace](https://mikefarah.gitbook.io/yq/v/v4.x/commands/evaluate#flags)
- [Complex expressions to select and update](https://mikefarah.gitbook.io/yq/v/v4.x/select#select-and-update-matching-values-in-map)
- Keeps yaml formatting and comments when updating (though there are issues with whitespace)
- [Decode/Encode base64 data](https://mikefarah.gitbook.io/yq/operators/encode-decode)
- [Load content from other files](https://mikefarah.gitbook.io/yq/operators/load)
- [Convert to/from json/ndjson](https://mikefarah.gitbook.io/yq/v/v4.x/usage/convert)
- [Convert to/from xml](https://mikefarah.gitbook.io/yq/v/v4.x/usage/xml)
- [Convert to/from hcl (terraform)](https://mikefarah.gitbook.io/yq/v/v4.x/usage/hcl)
- [Convert to/from toml](https://mikefarah.gitbook.io/yq/v/v4.x/usage/toml)
- [Convert to/from properties](https://mikefarah.gitbook.io/yq/v/v4.x/usage/properties)
- [Convert to/from csv/tsv](https://mikefarah.gitbook.io/yq/usage/csv-tsv)
- [Convert to/from json to yaml](https://mikefarah.gitbook.io/yq/v/v4.x/usage/convert)
- [Pipe data in by using '-'](https://mikefarah.gitbook.io/yq/v/v4.x/commands/evaluate)
- [General shell completion scripts (bash/zsh/fish/powershell)](https://mikefarah.gitbook.io/yq/v/v4.x/commands/shell-completion)
- [Reduce](https://mikefarah.gitbook.io/yq/operators/reduce) to merge multiple files or sum an array or other fancy things.
- [Github Action](https://mikefarah.gitbook.io/yq/usage/github-action) to use in your automated pipeline (thanks @devorbitus)
## [Usage](https://mikefarah.gitbook.io/yq/)
@@ -380,101 +174,34 @@ Usage:
yq [flags]
yq [command]
Examples:
# yq tries to auto-detect the file format based off the extension, and defaults to YAML if it's unknown (or piping through STDIN)
# Use the '-p/--input-format' flag to specify a format type.
cat file.xml | yq -p xml
# read the "stuff" node from "myfile.yml"
yq '.stuff' < myfile.yml
# update myfile.yml in place
yq -i '.stuff = "foo"' myfile.yml
# print contents of sample.json as idiomatic YAML
yq -P -oy sample.json
Available Commands:
completion Generate the autocompletion script for the specified shell
eval (default) Apply the expression to each document in each yaml file in sequence
eval-all Loads _all_ yaml documents of _all_ yaml files and runs expression once
help Help about any command
eval Apply expression to each document in each yaml file given in sequence
eval-all Loads _all_ yaml documents of _all_ yaml files and runs expression once
help Help about any command
shell-completion Generate completion script
Flags:
-C, --colors force print with colors
--csv-auto-parse parse CSV YAML/JSON values (default true)
--csv-separator char CSV Separator character (default ,)
--debug-node-info debug node info
-e, --exit-status set exit status if there are no matches or null or false is returned
--expression string forcibly set the expression argument. Useful when yq argument detection thinks your expression is a file.
--from-file string Load expression from specified file.
-f, --front-matter string (extract|process) first input as yaml front-matter. Extract will pull out the yaml content, process will run the expression against the yaml content, leaving the remaining data intact
--header-preprocess Slurp any header comments and separators before processing expression. (default true)
-h, --help help for yq
-I, --indent int sets indent level for output (default 2)
-i, --inplace update the file in place of first file given.
-p, --input-format string [auto|a|yaml|y|json|j|kyaml|ky|props|p|csv|c|tsv|t|xml|x|base64|uri|toml|hcl|h|lua|l|ini|i] parse format for input. (default "auto")
--lua-globals output keys as top-level global variables
--lua-prefix string prefix (default "return ")
--lua-suffix string suffix (default ";\n")
--lua-unquoted output unquoted string keys (e.g. {foo="bar"})
-M, --no-colors force print with no colors
-N, --no-doc Don't print document separators (---)
-0, --nul-output Use NUL char to separate values. If unwrap scalar is also set, fail if unwrapped scalar contains NUL char.
-n, --null-input Don't read input, simply evaluate the expression given. Useful for creating docs from scratch.
-o, --output-format string [auto|a|yaml|y|json|j|kyaml|ky|props|p|csv|c|tsv|t|xml|x|base64|uri|toml|hcl|h|shell|s|lua|l|ini|i] output format type. (default "auto")
-P, --prettyPrint pretty print, shorthand for '... style = ""'
--properties-array-brackets use [x] in array paths (e.g. for SpringBoot)
--properties-separator string separator to use between keys and values (default " = ")
--security-disable-env-ops Disable env related operations.
--security-disable-file-ops Disable file related operations (e.g. load)
--shell-key-separator string separator for shell variable key paths (default "_")
-s, --split-exp string print each result (or doc) into a file named (exp). [exp] argument must return a string. You can use $index in the expression as the result counter. The necessary directories will be created.
--split-exp-file string Use a file to specify the split-exp expression.
--string-interpolation Toggles strings interpolation of \(exp) (default true)
--tsv-auto-parse parse TSV YAML/JSON values (default true)
-r, --unwrapScalar unwrap scalar, print the value with no quotes, colors or comments. Defaults to true for yaml (default true)
-v, --verbose verbose mode
-V, --version Print version information and quit
--xml-attribute-prefix string prefix for xml attributes (default "+@")
--xml-content-name string name for xml content (if no attribute name is present). (default "+content")
--xml-directive-name string name for xml directives (e.g. <!DOCTYPE thing cat>) (default "+directive")
--xml-keep-namespace enables keeping namespace after parsing attributes (default true)
--xml-proc-inst-prefix string prefix for xml processing instructions (e.g. <?xml version="1"?>) (default "+p_")
--xml-raw-token enables using RawToken method instead Token. Commonly disables namespace translations. See https://pkg.go.dev/encoding/xml#Decoder.RawToken for details. (default true)
--xml-skip-directives skip over directives (e.g. <!DOCTYPE thing cat>)
--xml-skip-proc-inst skip over process instructions (e.g. <?xml version="1"?>)
--xml-strict-mode enables strict parsing of XML. See https://pkg.go.dev/encoding/xml for more details.
--yaml-fix-merge-anchor-to-spec Fix merge anchor to match YAML spec. Will default to true in late 2025
-C, --colors force print with colors
-e, --exit-status set exit status if there are no matches or null or false is returned
-h, --help help for yq
-I, --indent int sets indent level for output (default 2)
-i, --inplace update the yaml file inplace of first yaml file given.
-M, --no-colors force print with no colors
-N, --no-doc Don't print document separators (---)
-n, --null-input Don't read input, simply evaluate the expression given. Useful for creating yaml docs from scratch.
-P, --prettyPrint pretty print, shorthand for '... style = ""'
-j, --tojson output as json. Set indent to 0 to print json in one line.
-v, --verbose verbose mode
-V, --version Print version information and quit
Use "yq [command] --help" for more information about a command.
```
## Troubleshooting
Simple Example:
### Common Issues
**PowerShell quoting issues:**
```powershell
# Use single quotes for expressions
yq '.a.b[0].c' file.yaml
# Or escape double quotes
yq ".a.b[0].c = \"value\"" file.yaml
```bash
yq e '.a.b | length' f1.yml f2.yml
```
### Getting Help
- **Check existing issues**: [GitHub Issues](https://github.com/mikefarah/yq/issues)
- **Ask questions**: [GitHub Discussions](https://github.com/mikefarah/yq/discussions)
- **Documentation**: [Complete documentation](https://mikefarah.gitbook.io/yq/)
- **Examples**: [Recipes and examples](https://mikefarah.gitbook.io/yq/recipes)
## Known Issues / Missing Features
- `yq` attempts to preserve comment positions and whitespace as much as possible, but it does not handle all scenarios (see https://github.com/go-yaml/yaml/tree/v3 for details)
- Powershell has its own...[opinions on quoting yq](https://mikefarah.gitbook.io/yq/usage/tips-and-tricks#quotes-in-windows-powershell)
- "yes", "no" were dropped as boolean values in the yaml 1.2 standard - which is the standard yq assumes.
See [tips and tricks](https://mikefarah.gitbook.io/yq/usage/tips-and-tricks) for more common problems and solutions.
-26
View File
@@ -1,26 +0,0 @@
# Security Policy
## Reporting a Vulnerability
Please **do not** report security vulnerabilities through public GitHub issues.
Instead, use GitHub's private vulnerability reporting feature:
👉 https://github.com/mikefarah/yq/security
This allows vulnerabilities to be triaged and addressed confidentially before any public disclosure.
## Scope
### HTTP / TLS / Network vulnerabilities
yq is a command-line YAML/JSON/TOML processor that reads from files or standard input and writes to standard output. **yq does not include any HTTP or network libraries** and makes no network connections at runtime. CVEs related to HTTP, TLS, or networking are therefore **not applicable** to yq.
### Dependency version bumps
yq uses [Dependabot](https://docs.github.com/en/code-security/dependabot) to automatically raise pull requests for:
- Go module dependencies
- Go toolchain version
- Docker base images
Please **do not** raise pull requests or issues solely to bump dependency or Go versions — Dependabot handles this automatically and the maintainers merge those PRs regularly.
-20
View File
@@ -1,20 +0,0 @@
[files]
extend-exclude = ["vendor", "bin"]
[default]
locale = "en"
extend-ignore-identifiers-re = [
"NdJson",
]
[default.extend-identifiers]
AttributeIDSupressMenu = "AttributeIDSupressMenu"
[default.extend-words]
Teh = "Teh"
teh = "teh"
Supress = "Supress"
HashiCorp = "HashiCorp"
Hashi = "Hashi"
fot = "fot"
nd = "nd"
-41
View File
@@ -1,41 +0,0 @@
#!/bin/bash
testWriteInPlacePipeIn() {
result=$(./yq e -i -n '.a' 2>&1)
assertEquals 1 $?
assertEquals "Error: write in place flag only applicable when giving an expression and at least one file" "$result"
}
testWriteInPlacePipeInEvalall() {
result=$(./yq ea -i -n '.a' 2>&1)
assertEquals 1 $?
assertEquals "Error: write in place flag only applicable when giving an expression and at least one file" "$result"
}
testWriteInPlaceWithSplit() {
result=$(./yq e -s "cat" -i '.a = "thing"' test.yml 2>&1)
assertEquals 1 $?
assertEquals "Error: write in place cannot be used with split file" "$result"
}
testWriteInPlaceWithSplitEvalAll() {
result=$(./yq ea -s "cat" -i '.a = "thing"' test.yml 2>&1)
assertEquals 1 $?
assertEquals "Error: write in place cannot be used with split file" "$result"
}
testNullWithFiles() {
result=$(./yq e -n '.a = "thing"' test.yml 2>&1)
assertEquals 1 $?
assertEquals "Error: cannot pass files in when using null-input flag" "$result"
}
testNullWithFilesEvalAll() {
result=$(./yq ea -n '.a = "thing"' test.yml 2>&1)
assertEquals 1 $?
assertEquals "Error: cannot pass files in when using null-input flag" "$result"
}
source ./scripts/shunit2
-373
View File
@@ -1,373 +0,0 @@
#!/bin/bash
setUp() {
rm test*.yml 2>/dev/null || true
rm .xyz 2>/dev/null || true
rm instructions.txt 2>/dev/null || true
}
testBasicEvalRoundTrip() {
./yq -n ".a = 123" > test.yml
X=$(./yq '.a' test.yml)
assertEquals 123 "$X"
}
testBasicTrailingContent() {
cat >test-trailing.yml <<EOL
test:
# this comment will be removed
EOL
read -r -d '' expected << EOM
test:
# this comment will be removed
EOM
X=$(./yq test-trailing.yml -P)
assertEquals "$expected" "$X"
}
testBasicTrailingContent() {
cat >test-trailing.yml <<EOL
test:
# this comment will be removed
EOL
read -r -d '' expected << EOM
test:
# hi
EOM
X=$(./yq '. footComment = "hi"' test-trailing.yml)
assertEquals "$expected" "$X"
}
testBasicTrailingContentEvalAll() {
cat >test-trailing.yml <<EOL
test:
# this comment will be removed
EOL
read -r -d '' expected << EOM
test:
# this comment will be removed
EOM
X=$(./yq ea test-trailing.yml -P)
assertEquals "$expected" "$X"
}
testBasicTrailingContentEvalAll() {
cat >test-trailing.yml <<EOL
test:
# this comment will be removed
EOL
read -r -d '' expected << EOM
test:
# hi
EOM
X=$(./yq ea '. footComment = "hi"' test-trailing.yml)
assertEquals "$expected" "$X"
}
testBasicPipeWithDot() {
./yq -n ".a = 123" > test.yml
X=$(cat test.yml | ./yq '.')
assertEquals "a: 123" "$X"
}
testBasicExpressionMatchesFileName() {
./yq -n ".xyz = 123" > test.yml
touch .xyz
X=$(./yq --expression '.xyz' test.yml)
assertEquals "123" "$X"
X=$(./yq ea --expression '.xyz' test.yml)
assertEquals "123" "$X"
}
testBasicExpressionFromFile() {
./yq -n ".xyz = 123" > test.yml
echo '.xyz = "meow" | .cool = "frog"' > instructions.txt
X=$(./yq --from-file instructions.txt test.yml -o=j -I=0)
assertEquals '{"xyz":"meow","cool":"frog"}' "$X"
X=$(./yq ea --from-file instructions.txt test.yml -o=j -I=0)
assertEquals '{"xyz":"meow","cool":"frog"}' "$X"
}
testBasicExpressionFromFileDos() {
./yq -n ".xyz = 123" > test.yml
echo '.xyz = "meow" | .cool = "frog"' | sed 's/$'"/`echo \\\r`/" > instructions.txt
X=$(./yq --from-file instructions.txt test.yml -o=j -I=0)
assertEquals '{"xyz":"meow","cool":"frog"}' "$X"
X=$(./yq ea --from-file instructions.txt test.yml -o=j -I=0)
assertEquals '{"xyz":"meow","cool":"frog"}' "$X"
}
testBasicGitHubAction() {
./yq -n ".a = 123" > test.yml
X=$(cat /dev/null | ./yq test.yml)
assertEquals "a: 123" "$X"
X=$(cat /dev/null | ./yq e test.yml)
assertEquals "a: 123" "$X"
X=$(cat /dev/null | ./yq ea test.yml)
assertEquals "a: 123" "$X"
}
testBasicGitHubActionWithExpression() {
./yq -n ".a = 123" > test.yml
X=$(cat /dev/null | ./yq '.a' test.yml)
assertEquals "123" "$X"
X=$(cat /dev/null | ./yq e '.a' test.yml)
assertEquals "123" "$X"
X=$(cat /dev/null | ./yq ea '.a' test.yml)
assertEquals "123" "$X"
}
testBasicEvalAllAllFiles() {
./yq -n ".a = 123" > test.yml
./yq -n ".a = 124" > test2.yml
X=$(./yq ea test.yml test2.yml)
Y=$(./yq e '.' test.yml test2.yml)
assertEquals "$Y" "$X"
}
# when given a file, don't read STDIN
# otherwise strange things start happening
# in scripts
# https://github.com/mikefarah/yq/issues/1115
testBasicCatWithFilesNoDash() {
./yq -n ".a = 123" > test.yml
./yq -n ".a = 124" > test2.yml
X=$(cat test.yml | ./yq test2.yml)
Y=$(./yq e '.' test2.yml)
assertEquals "$Y" "$X"
}
# when the nullinput flag is used
# don't automatically read STDIN (this breaks github actions)
testBasicCreateFileGithubAction() {
cat /dev/null | ./yq -n ".a = 123" > test.yml
}
testBasicEvalAllCatWithFilesNoDash() {
./yq -n ".a = 123" > test.yml
./yq -n ".a = 124" > test2.yml
X=$(cat test.yml | ./yq ea test2.yml)
Y=$(./yq e '.' test2.yml)
assertEquals "$Y" "$X"
}
testBasicCatWithFilesNoDashWithExp() {
./yq -n ".a = 123" > test.yml
./yq -n ".a = 124" > test2.yml
X=$(cat test.yml | ./yq '.a' test2.yml)
Y=$(./yq e '.a' test2.yml)
assertEquals "$Y" "$X"
}
testBasicEvalAllCatWithFilesNoDashWithExp() {
./yq -n ".a = 123" > test.yml
./yq -n ".a = 124" > test2.yml
X=$(cat test.yml | ./yq ea '.a' test2.yml)
Y=$(./yq e '.a' test2.yml)
assertEquals "$Y" "$X"
}
testBasicStdInWithFiles() {
./yq -n ".a = 123" > test.yml
./yq -n ".a = 124" > test2.yml
X=$(cat test.yml | ./yq - test2.yml)
Y=$(./yq e '.' test.yml test2.yml)
assertEquals "$Y" "$X"
}
testBasicEvalAllStdInWithFiles() {
./yq -n ".a = 123" > test.yml
./yq -n ".a = 124" > test2.yml
X=$(cat test.yml | ./yq ea - test2.yml)
Y=$(./yq e '.' test.yml test2.yml)
assertEquals "$Y" "$X"
}
testBasicStdInWithFilesReverse() {
./yq -n ".a = 123" > test.yml
./yq -n ".a = 124" > test2.yml
X=$(cat test.yml | ./yq test2.yml -)
Y=$(./yq e '.' test2.yml test.yml)
assertEquals "$Y" "$X"
}
testBasicEvalAllStdInWithFilesReverse() {
./yq -n ".a = 123" > test.yml
./yq -n ".a = 124" > test2.yml
X=$(cat test.yml | ./yq ea test2.yml -)
Y=$(./yq e '.' test2.yml test.yml)
assertEquals "$Y" "$X"
}
testBasicEvalRoundTripNoEval() {
./yq -n ".a = 123" > test.yml
X=$(./yq '.a' test.yml)
assertEquals 123 "$X"
}
testBasicStdInWithOneArg() {
./yq e -n ".a = 123" > test.yml
X=$(cat test.yml | ./yq e ".a")
assertEquals 123 "$X"
X=$(cat test.yml | ./yq ea ".a")
assertEquals 123 "$X"
X=$(cat test.yml | ./yq ".a")
assertEquals 123 "$X"
}
testBasicUpdateInPlaceSequence() {
cat >test.yml <<EOL
a: 0
EOL
./yq e -i ".a = 10" test.yml
X=$(./yq e '.a' test.yml)
assertEquals "10" "$X"
}
testBasicUpdateInPlaceSequenceNoEval() {
cat >test.yml <<EOL
a: 0
EOL
./yq -i ".a = 10" test.yml
X=$(./yq '.a' test.yml)
assertEquals "10" "$X"
}
testBasicUpdateInPlaceSequenceEvalAll() {
cat >test.yml <<EOL
a: 0
EOL
./yq ea -i ".a = 10" test.yml
X=$(./yq e '.a' test.yml)
assertEquals "10" "$X"
}
testBasicUpdateInPlaceMultipleFilesNoExpressionEval() {
cat >test.yml <<EOL
a: 0
EOL
cat >test2.yml <<EOL
a: 1
EOL
read -r -d '' expected << EOM
0
---
1
EOM
./yq -i test.yml test2.yml
X=$(./yq e '.a' test.yml)
assertEquals "$expected" "$X"
}
testBasicUpdateInPlaceMultipleFilesNoExpressionEvalAll() {
cat >test.yml <<EOL
a: 0
EOL
cat >test2.yml <<EOL
a: 1
EOL
read -r -d '' expected << EOM
0
---
1
EOM
./yq -i ea test.yml test2.yml
X=$(./yq e '.a' test.yml)
assertEquals "$expected" "$X"
}
testBasicNoExitStatus() {
echo "a: cat" > test.yml
X=$(./yq e '.z' test.yml)
assertEquals "null" "$X"
}
testBasicExitStatus() {
echo "a: cat" > test.yml
X=$(./yq e -e '.z' test.yml 2&>/dev/null)
assertEquals 1 "$?"
}
testBasicExitStatusNoEval() {
echo "a: cat" > test.yml
X=$(./yq -e '.z' test.yml 2&>/dev/null)
assertEquals 1 "$?"
}
testBasicExtractFieldWithSeparator() {
cat >test.yml <<EOL
---
name: chart-name
version: 1.2.3
EOL
X=$(./yq e '.name' test.yml)
assertEquals "chart-name" "$X"
}
testBasicExtractMultipleFieldWithSeparator() {
cat >test.yml <<EOL
---
name: chart-name
version: 1.2.3
---
name: thing
version: 1.2.3
EOL
read -r -d '' expected << EOM
chart-name
---
thing
EOM
X=$(./yq e '.name' test.yml)
assertEquals "$expected" "$X"
}
testBasicMultiplyAssignMultiDoc() {
cat >test.yml <<EOL
a: 1
---
b: 2
EOL
read -r -d '' expected << EOM
a: 1
c: 3
---
b: 2
c: 3
EOM
X=$(./yq '. *= {"c":3}' test.yml)
assertEquals "$expected" "$X"
}
testBasicClosedStdIn() {
cat >test.yml <<EOL
a: 1
EOL
X=$(./yq e '.a' test.yml <&-)
assertEquals "1" "$X"
}
source ./scripts/shunit2
-9
View File
@@ -1,9 +0,0 @@
#!/bin/bash
testCompletionRuns() {
result=$(./yq __complete "" 2>&1)
assertEquals 0 $?
assertContains "$result" "Completion ended with directive:"
}
source ./scripts/shunit2
-83
View File
@@ -1,83 +0,0 @@
#!/bin/bash
setUp() {
rm test*.yml || true
cat >test.yml <<EOL
# comment
EOL
}
testEmptyEval() {
X=$(./yq e test.yml)
expected="# comment"
assertEquals 0 $?
assertEquals "$expected" "$X"
}
testEmptyEvalNoNewLine() {
echo -n "#comment" >test.yml
X=$(./yq e test.yml)
expected=$(cat test.yml)
assertEquals 0 $?
assertEquals "$expected" "$X"
}
testEmptyEvalNoNewLineWithExpression() {
echo -n "# comment" >test.yml
X=$(./yq e '.apple = "tree"' test.yml)
read -r -d '' expected << EOM
# comment
apple: tree
EOM
assertEquals "$expected" "$X"
}
testEmptyEvalPipe() {
X=$(./yq e - < test.yml)
assertEquals 0 $?
}
testEmptyCommentsWithExpressionEval() {
read -r -d '' expected << EOM
# comment
apple: tree
EOM
X=$(./yq e '.apple="tree"' test.yml)
assertEquals "$expected" "$X"
}
testEmptyCommentsWithExpressionEvalAll() {
read -r -d '' expected << EOM
# comment
apple: tree
EOM
X=$(./yq ea '.apple="tree"' test.yml)
assertEquals "$expected" "$X"
}
testEmptyWithExpressionEval() {
rm test.yml
touch test.yml
expected="apple: tree"
X=$(./yq e '.apple="tree"' test.yml)
assertEquals "$expected" "$X"
}
testEmptyWithExpressionEvalAll() {
rm test.yml
touch test.yml
expected="apple: tree"
X=$(./yq ea '.apple="tree"' test.yml)
assertEquals "$expected" "$X"
}
source ./scripts/shunit2
-21
View File
@@ -1,21 +0,0 @@
#!/bin/bash
setUp() {
rm test*.yml || true
cat >test.yml <<EOL
# comment
EOL
}
testStringInterpolation() {
X=$(./yq -n '"Mike \(3 + 4)"')
assertEquals "Mike 7" "$X"
}
testNoStringInterpolation() {
X=$(./yq --string-interpolation=f -n '"Mike \(3 + 4)"')
assertEquals "Mike \(3 + 4)" "$X"
}
source ./scripts/shunit2
-76
View File
@@ -1,76 +0,0 @@
#!/bin/bash
setUp() {
rm test*.yml || true
cat >test.yml <<EOL
---
a: apple
b: cat
---
not yaml
c: at
EOL
}
testFrontMatterProcessEval() {
read -r -d '' expected << EOM
---
a: apple
b: dog
---
not yaml
c: at
EOM
./yq e --front-matter="process" '.b = "dog"' test.yml -i
assertEquals "$expected" "$(cat test.yml)"
}
testFrontMatterProcessEvalAll() {
read -r -d '' expected << EOM
---
a: apple
b: dog
---
not yaml
c: at
EOM
./yq ea --front-matter="process" '.b = "dog"' test.yml -i
assertEquals "$expected" "$(cat test.yml)"
}
testFrontMatterExtractEval() {
cat >test.yml <<EOL
a: apple
b: cat
---
not yaml
c: at
EOL
read -r -d '' expected << EOM
a: apple
b: dog
EOM
./yq e --front-matter="extract" '.b = "dog"' test.yml -i
assertEquals "$expected" "$(cat test.yml)"
}
testFrontMatterExtractEvalAll() {
cat >test.yml <<EOL
a: apple
b: cat
---
not yaml
c: at
EOL
read -r -d '' expected << EOM
a: apple
b: dog
EOM
./yq ea --front-matter="extract" '.b = "dog"' test.yml -i
assertEquals "$expected" "$(cat test.yml)"
}
source ./scripts/shunit2
-43
View File
@@ -1,43 +0,0 @@
#!/bin/bash
setUp() {
rm test*.yml || true
}
testLineCountFirstLineComment() {
cat >test.yml <<EOL
#test123
abc: 123
test123: 123123
#comment
lalilu: lalilu
EOL
X=$(./yq '.lalilu | line' --header-preprocess=false < test.yml)
assertEquals "5" "$X"
}
testArrayOfDocs() {
cat >test.yml <<EOL
---
# leading comment doc 1
a: 1
---
# leading comment doc 2
a: 2
EOL
read -r -d '' expected << EOM
- # leading comment doc 1
a: 1
- # leading comment doc 2
a: 2
EOM
X=$(./yq ea '[.]' --header-preprocess=false < test.yml)
assertEquals "$expected" "$X"
}
source ./scripts/shunit2
-249
View File
@@ -1,249 +0,0 @@
#!/bin/bash
setUp() {
rm test*.yml 2>/dev/null || true
rm test*.toml 2>/dev/null || true
rm test*.tfstate 2>/dev/null || true
rm test*.json 2>/dev/null || true
rm test*.properties 2>/dev/null || true
rm test*.csv 2>/dev/null || true
rm test*.tsv 2>/dev/null || true
rm test*.xml 2>/dev/null || true
}
testInputJson() {
cat >test.json <<EOL
{ "mike" : { "things": "cool" } }
EOL
read -r -d '' expected << EOM
{
"mike": {
"things": "cool"
}
}
EOM
X=$(./yq test.json)
assertEquals "$expected" "$X"
X=$(./yq ea test.json)
assertEquals "$expected" "$X"
}
testInputToml() {
cat >test.toml <<EOL
[owner]
name = "Tom Preston-Werner"
dob = 1979-05-27T07:32:00-08:00
EOL
read -r -d '' expected << EOM
owner:
name: Tom Preston-Werner
dob: 1979-05-27T07:32:00-08:00
EOM
X=$(./yq -oy test.toml)
assertEquals "$expected" "$X"
X=$(./yq ea -oy test.toml)
assertEquals "$expected" "$X"
}
testInputTfstate() {
cat >test.tfstate <<EOL
{ "mike" : { "things": "cool" } }
EOL
read -r -d '' expected << EOM
{"mike": {"things": "cool"}}
EOM
X=$(./yq test.tfstate)
assertEquals "$expected" "$X"
X=$(./yq ea test.tfstate)
assertEquals "$expected" "$X"
}
testInputJsonOutputYaml() {
cat >test.json <<EOL
{ "mike" : { "things": "cool" } }
EOL
read -r -d '' expected << EOM
mike:
things: cool
EOM
X=$(./yq test.json -oy)
assertEquals "$expected" "$X"
X=$(./yq ea test.json -oy)
assertEquals "$expected" "$X"
}
testInputProperties() {
cat >test.properties <<EOL
mike.things = hello
EOL
read -r -d '' expected << EOM
mike.things = hello
EOM
X=$(./yq e test.properties)
assertEquals "$expected" "$X"
X=$(./yq test.properties)
assertEquals "$expected" "$X"
X=$(./yq ea test.properties)
assertEquals "$expected" "$X"
}
testInputPropertiesGitHubAction() {
cat >test.properties <<EOL
mike.things = hello
EOL
read -r -d '' expected << EOM
mike.things = hello
EOM
X=$(cat /dev/null | ./yq e test.properties)
assertEquals "$expected" "$X"
X=$(cat /dev/null | ./yq ea test.properties)
assertEquals "$expected" "$X"
}
testInputCSV() {
cat >test.csv <<EOL
fruit,yumLevel
apple,5
banana,4
EOL
read -r -d '' expected << EOM
fruit,yumLevel
apple,5
banana,4
EOM
X=$(./yq e test.csv)
assertEquals "$expected" "$X"
X=$(./yq ea test.csv)
assertEquals "$expected" "$X"
}
testInputCSVUTF8() {
read -r -d '' expected << EOM
id,first,last
1,john,smith
1,jane,smith
EOM
X=$(./yq utf8.csv)
assertEquals "$expected" "$X"
}
testInputTSV() {
cat >test.tsv <<EOL
fruit yumLevel
apple 5
banana 4
EOL
read -r -d '' expected << EOM
fruit yumLevel
apple 5
banana 4
EOM
X=$(./yq e test.tsv)
assertEquals "$expected" "$X"
X=$(./yq ea test.tsv)
assertEquals "$expected" "$X"
}
testInputXml() {
cat >test.xml <<EOL
<cat legs="4">BiBi</cat>
EOL
read -r -d '' expected << EOM
<cat legs="4">BiBi</cat>
EOM
X=$(./yq e test.xml)
assertEquals "$expected" "$X"
X=$(./yq ea test.xml)
assertEquals "$expected" "$X"
}
testInputXmlNamespaces() {
cat >test.xml <<EOL
<?xml version="1.0"?>
<map xmlns="some-namespace" xmlns:xsi="some-instance" xsi:schemaLocation="some-url">
</map>
EOL
read -r -d '' expected << EOM
<?xml version="1.0"?>
<map xmlns="some-namespace" xmlns:xsi="some-instance" xsi:schemaLocation="some-url"></map>
EOM
X=$(./yq e test.xml)
assertEquals "$expected" "$X"
X=$(./yq ea test.xml)
assertEquals "$expected" "$X"
}
testInputXmlStrict() {
cat >test.xml <<EOL
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY writer "Catherine.">
<!ENTITY copyright "(r) Great">
]>
<root>
<item>&writer;&copyright;</item>
</root>
EOL
X=$(./yq --xml-strict-mode test.xml 2>&1)
assertEquals 1 $?
assertEquals "Error: bad file 'test.xml': XML syntax error on line 7: invalid character entity &writer;" "$X"
X=$(./yq ea --xml-strict-mode test.xml 2>&1)
assertEquals "Error: bad file 'test.xml': XML syntax error on line 7: invalid character entity &writer;" "$X"
}
testInputXmlGithubAction() {
cat >test.xml <<EOL
<cat legs="4">BiBi</cat>
EOL
read -r -d '' expected << EOM
<cat legs="4">BiBi</cat>
EOM
X=$(cat /dev/null | ./yq e test.xml)
assertEquals "$expected" "$X"
X=$(cat /dev/null | ./yq ea test.xml)
assertEquals "$expected" "$X"
}
source ./scripts/shunit2
-347
View File
@@ -1,347 +0,0 @@
#!/bin/bash
setUp() {
rm test*.yml 2>/dev/null || true
rm test*.properties 2>/dev/null || true
rm test*.csv 2>/dev/null || true
rm test*.tsv 2>/dev/null || true
rm test*.xml 2>/dev/null || true
rm test*.tf 2>/dev/null || true
}
testInputProperties() {
cat >test.properties <<EOL
mike.things = hello
EOL
read -r -d '' expected << EOM
mike:
things: hello
EOM
X=$(./yq e -p=props test.properties)
assertEquals "$expected" "$X"
X=$(./yq ea -p=props test.properties)
assertEquals "$expected" "$X"
}
testInputPropertiesGitHubAction() {
cat >test.properties <<EOL
mike.things = hello
EOL
read -r -d '' expected << EOM
mike:
things: hello
EOM
X=$(cat /dev/null | ./yq e -p=props test.properties)
assertEquals "$expected" "$X"
X=$(cat /dev/null | ./yq ea -p=props test.properties)
assertEquals "$expected" "$X"
}
testInputCSV() {
cat >test.csv <<EOL
fruit,yumLevel
apple,5
banana,4
EOL
read -r -d '' expected << EOM
- fruit: apple
yumLevel: 5
- fruit: banana
yumLevel: 4
EOM
X=$(./yq e -p=csv test.csv)
assertEquals "$expected" "$X"
X=$(./yq ea -p=csv test.csv)
assertEquals "$expected" "$X"
}
testInputCSVCustomSeparator() {
cat >test.csv <<EOL
fruit;yumLevel
apple;5
banana;4
EOL
read -r -d '' expected << EOM
- fruit: apple
yumLevel: 5
- fruit: banana
yumLevel: 4
EOM
X=$(./yq -p=csv --csv-separator ";" test.csv)
assertEquals "$expected" "$X"
X=$(./yq ea -p=csv --csv-separator ";" test.csv)
assertEquals "$expected" "$X"
}
testInputCSVNoAuto() {
cat >test.csv <<EOL
thing1
name: cat
EOL
read -r -d '' expected << EOM
- thing1: 'name: cat'
EOM
X=$(./yq --csv-auto-parse=f test.csv -oy)
assertEquals "$expected" "$X"
X=$(./yq ea --csv-auto-parse=f test.csv -oy)
assertEquals "$expected" "$X"
}
testInputTSVNoAuto() {
cat >test.tsv <<EOL
thing1
name: cat
EOL
read -r -d '' expected << EOM
- thing1: 'name: cat'
EOM
X=$(./yq --tsv-auto-parse=f test.tsv -oy)
assertEquals "$expected" "$X"
X=$(./yq ea --tsv-auto-parse=f test.tsv -oy)
assertEquals "$expected" "$X"
}
testInputCSVUTF8() {
read -r -d '' expected << EOM
- id: 1
first: john
last: smith
- id: 1
first: jane
last: smith
EOM
X=$(./yq -p=csv utf8.csv)
assertEquals "$expected" "$X"
}
testInputTSV() {
cat >test.tsv <<EOL
fruit yumLevel
apple 5
banana 4
EOL
read -r -d '' expected << EOM
- fruit: apple
yumLevel: 5
- fruit: banana
yumLevel: 4
EOM
X=$(./yq e -p=t test.tsv)
assertEquals "$expected" "$X"
X=$(./yq ea -p=t test.tsv)
assertEquals "$expected" "$X"
}
testInputKYaml() {
cat >test.kyaml <<'EOL'
# leading
{
a: 1, # a line
# head b
b: 2,
c: [
# head d
"d", # d line
],
}
EOL
read -r -d '' expected <<'EOM'
# leading
a: 1 # a line
# head b
b: 2
c:
# head d
- d # d line
EOM
X=$(./yq e -p=kyaml -P test.kyaml)
assertEquals "$expected" "$X"
X=$(./yq ea -p=kyaml -P test.kyaml)
assertEquals "$expected" "$X"
}
testInputXml() {
cat >test.yml <<EOL
<cat legs="4">BiBi</cat>
EOL
read -r -d '' expected << EOM
cat:
+content: BiBi
+@legs: "4"
EOM
X=$(./yq e -p=xml test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea -p=xml test.yml)
assertEquals "$expected" "$X"
}
testInputXmlNamespaces() {
cat >test.xml <<EOL
<?xml version="1.0"?>
<map xmlns="some-namespace" xmlns:xsi="some-instance" xsi:schemaLocation="some-url">
</map>
EOL
read -r -d '' expected << EOM
+p_xml: version="1.0"
map:
+@xmlns: some-namespace
+@xmlns:xsi: some-instance
+@xsi:schemaLocation: some-url
EOM
X=$(./yq e -p=xml test.xml)
assertEquals "$expected" "$X"
X=$(./yq ea -p=xml test.xml)
assertEquals "$expected" "$X"
}
testInputXmlRoundtrip() {
cat >test.yml <<EOL
<?xml version="1.0"?>
<!DOCTYPE config SYSTEM "/etc/iwatch/iwatch.dtd" >
<map xmlns="some-namespace" xmlns:xsi="some-instance" xsi:schemaLocation="some-url">Meow</map>
EOL
read -r -d '' expected << EOM
<?xml version="1.0"?>
<!DOCTYPE config SYSTEM "/etc/iwatch/iwatch.dtd" >
<map xmlns="some-namespace" xmlns:xsi="some-instance" xsi:schemaLocation="some-url">Meow</map>
EOM
X=$(./yq -p=xml -o=xml test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea -p=xml -o=xml test.yml)
assertEquals "$expected" "$X"
}
testInputXmlStrict() {
cat >test.yml <<EOL
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY writer "Catherine.">
<!ENTITY copyright "(r) Great">
]>
<root>
<item>&writer;&copyright;</item>
</root>
EOL
X=$(./yq -p=xml --xml-strict-mode test.yml -o=xml 2>&1)
assertEquals 1 $?
assertEquals "Error: bad file 'test.yml': XML syntax error on line 7: invalid character entity &writer;" "$X"
X=$(./yq ea -p=xml --xml-strict-mode test.yml -o=xml 2>&1)
assertEquals "Error: bad file 'test.yml': XML syntax error on line 7: invalid character entity &writer;" "$X"
}
testInputXmlGithubAction() {
cat >test.yml <<EOL
<cat legs="4">BiBi</cat>
EOL
read -r -d '' expected << EOM
cat:
+content: BiBi
+@legs: "4"
EOM
X=$(cat /dev/null | ./yq e -p=xml test.yml)
assertEquals "$expected" "$X"
X=$(cat /dev/null | ./yq ea -p=xml test.yml)
assertEquals "$expected" "$X"
}
testInputTerraform() {
cat >test.tf <<EOL
resource "aws_s3_bucket" "example" {
bucket = "my-bucket"
tags = {
Environment = "Dev"
Project = "Test"
}
}
EOL
read -r -d '' expected << EOM
resource "aws_s3_bucket" "example" {
bucket = "my-bucket"
tags = {
Environment = "Dev"
Project = "Test"
}
}
EOM
X=$(./yq test.tf)
assertEquals "$expected" "$X"
X=$(./yq ea test.tf)
assertEquals "$expected" "$X"
}
testInputTerraformGithubAction() {
cat >test.tf <<EOL
resource "aws_s3_bucket" "example" {
bucket = "my-bucket"
tags = {
Environment = "Dev"
Project = "Test"
}
}
EOL
read -r -d '' expected << EOM
resource "aws_s3_bucket" "example" {
bucket = "my-bucket"
tags = {
Environment = "Dev"
Project = "Test"
}
}
EOM
X=$(cat /dev/null | ./yq test.tf)
assertEquals "$expected" "$X"
X=$(cat /dev/null | ./yq ea test.tf)
assertEquals "$expected" "$X"
}
source ./scripts/shunit2
-529
View File
@@ -1,529 +0,0 @@
#!/bin/bash
# examples where header-preprocess is required
setUp() {
rm test*.yml || true
cat >test.yml <<EOL
---
a: test
EOL
}
testLeadingSeparatorWithDoc() {
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"
}
testLeadingSeparatorWithNewlinesNewDoc() {
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"
}
testLeadingSeparatorWithNewlinesMoreComments() {
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"
}
testLeadingSeparatorWithDirective() {
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"
}
testLeadingSeparatorPipeIntoEvalSeq() {
X=$(./yq e - < test.yml)
expected=$(cat test.yml)
assertEquals "$expected" "$X"
}
testLeadingSeparatorExtractField() {
X=$(./yq e '.a' - < test.yml)
assertEquals "test" "$X"
}
testLeadingSeparatorExtractFieldWithCommentsAfterSep() {
cat >test.yml <<EOL
---
# hi peeps
# cool
a: test
EOL
X=$(./yq e '.a' test.yml)
assertEquals "test" "$X"
}
testLeadingSeparatorExtractFieldWithCommentsBeforeSep() {
cat >test.yml <<EOL
# hi peeps
# cool
---
a: test
EOL
X=$(./yq e '.a' test.yml)
assertEquals "test" "$X"
}
testLeadingSeparatorExtractFieldMultiDoc() {
cat >test.yml <<EOL
---
a: test
---
a: test2
EOL
read -r -d '' expected << EOM
test
---
test2
EOM
X=$(./yq e '.a' test.yml)
assertEquals "$expected" "$X"
}
testLeadingSeparatorExtractFieldMultiDocWithComments() {
cat >test.yml <<EOL
# here
---
# there
a: test
# wherever
---
# you are
a: test2
# woop
EOL
read -r -d '' expected << EOM
test
---
test2
EOM
X=$(./yq e '.a' test.yml)
assertEquals "$expected" "$X"
}
testLeadingSeparatorEvalSeq() {
X=$(./yq e test.yml)
expected=$(cat test.yml)
assertEquals "$expected" "$X"
}
testLeadingSeparatorPipeIntoEvalAll() {
X=$(./yq ea - < test.yml)
expected=$(cat test.yml)
assertEquals "$expected" "$X"
}
testLeadingSeparatorEvalAll() {
X=$(./yq ea test.yml)
expected=$(cat test.yml)
assertEquals "$expected" "$X"
}
testLeadingSeparatorMultiDocEvalSimple() {
read -r -d '' expected << EOM
---
a: test
---
version: 3
application: MyApp
EOM
X=$(./yq e '.' test.yml examples/order.yaml)
assertEquals "$expected" "$X"
}
testLeadingSeparatorMultiDocInOneFile() {
cat >test.yml <<EOL
---
# hi peeps
# cool
a: test
---
b: things
EOL
expected=$(cat test.yml)
X=$(./yq e '.' test.yml)
assertEquals "$expected" "$X"
}
testLeadingSeparatorMultiDocInOneFileEvalAll() {
cat >test.yml <<EOL
---
# hi peeps
# cool
a: test
---
b: things
EOL
expected=$(cat test.yml)
X=$(./yq ea '.' test.yml)
assertEquals "$expected" "$X"
}
testLeadingSeparatorMultiDocEvalComments() {
cat >test.yml <<EOL
# hi peeps
# cool
a: test
EOL
cat >test2.yml <<EOL
# this is another doc
# great
b: sane
EOL
read -r -d '' expected << EOM
# hi peeps
# cool
a: test
---
# this is another doc
# great
b: sane
EOM
X=$(./yq e '.' test.yml test2.yml)
assertEquals "$expected" "$X"
}
testLeadingSeparatorMultiDocEvalCommentsTrailingSep() {
cat >test.yml <<EOL
# hi peeps
# cool
---
a: test
EOL
cat >test2.yml <<EOL
# this is another doc
# great
---
b: sane
EOL
read -r -d '' expected << EOM
# hi peeps
# cool
---
a: test
---
# this is another doc
# great
---
b: sane
EOM
X=$(./yq e '.' test.yml test2.yml)
assertEquals "$expected" "$X"
}
testLeadingSeparatorMultiMultiDocEvalCommentsTrailingSep() {
cat >test.yml <<EOL
# hi peeps
# cool
---
a: test
---
a1: test2
EOL
cat >test2.yml <<EOL
# this is another doc
# great
---
b: sane
---
b2: cool
EOL
read -r -d '' expected << EOM
# hi peeps
# cool
---
a: test
---
a1: test2
---
# this is another doc
# great
---
b: sane
---
b2: cool
EOM
X=$(./yq e '.' test.yml test2.yml)
assertEquals "$expected" "$X"
}
testLeadingSeparatorMultiDocEvalCommentsLeadingSep() {
cat >test.yml <<EOL
---
# hi peeps
# cool
a: test
EOL
cat >test2.yml <<EOL
---
# this is another doc
# great
b: sane
EOL
read -r -d '' expected << EOM
---
# hi peeps
# cool
a: test
---
# this is another doc
# great
b: sane
EOM
X=$(./yq e '.' test.yml test2.yml)
assertEquals "$expected" "$X"
}
# https://github.com/mikefarah/yq/issues/919
testLeadingSeparatorDoesNotBreakCommentsOnOtherFiles() {
cat >test.yml <<EOL
# a1
a: 1
# a2
EOL
cat >test2.yml <<EOL
# b1
b: 2
# b2
EOL
read -r -d '' expected << EOM
# a1
a: 1
# a2
# b1
b: 2
# b2
EOM
X=$(./yq ea 'select(fi == 0) * select(fi == 1)' test.yml test2.yml)
assertEquals "$expected" "$X"
}
testLeadingSeparatorMultiDocEvalCommentsStripComments() {
cat >test.yml <<EOL
---
# hi peeps
# cool
a: test
---
# this is another doc
# great
b: sane
EOL
# it will be hard to remove that top level separator
read -r -d '' expected << EOM
a: test
---
b: sane
EOM
X=$(./yq e '... comments=""' test.yml)
assertEquals "$expected" "$X"
}
testLeadingSeparatorMultiDocEvalCommentsLeadingSepNoDocFlag() {
cat >test.yml <<EOL
---
# hi peeps
# cool
a: test
---
# this is another doc
# great
b: sane
EOL
read -r -d '' expected << EOM
# hi peeps
# cool
a: test
# this is another doc
# great
b: sane
EOM
X=$(./yq e '.' --no-doc test.yml)
assertEquals "$expected" "$X"
}
testLeadingSeparatorMultiDocEvalJsonFlag() {
cat >test.yml <<EOL
---
# hi peeps
# cool
a: test
EOL
cat >test2.yml <<EOL
---
# this is another doc
# great
b: sane
EOL
read -r -d '' expected << EOM
{
"a": "test"
}
{
"b": "sane"
}
EOM
X=$(./yq e '.' -j test.yml test2.yml)
assertEquals "$expected" "$X"
}
testLeadingSeparatorMultiDocEvalAllJsonFlag() {
cat >test.yml <<EOL
---
# hi peeps
# cool
a: test
EOL
cat >test2.yml <<EOL
---
# this is another doc
# great
b: sane
EOL
read -r -d '' expected << EOM
{
"a": "test"
}
{
"b": "sane"
}
EOM
X=$(./yq ea '.' -j test.yml test2.yml)
assertEquals "$expected" "$X"
}
testLeadingSeparatorMultiDocEvalAll() {
read -r -d '' expected << EOM
---
a: test
---
version: 3
application: MyApp
EOM
X=$(./yq ea '.' test.yml examples/order.yaml)
assertEquals "$expected" "$X"
}
source ./scripts/shunit2
-27
View File
@@ -1,27 +0,0 @@
#!/bin/bash
testLoadFileNotExist() {
result=$(./yq e -n 'load("cat.yml")' 2>&1)
assertEquals 1 $?
assertEquals "Error: failed to load cat.yml: open cat.yml: no such file or directory" "$result"
}
testLoadFileExpNotExist() {
result=$(./yq e -n 'load(.a)' 2>&1)
assertEquals 1 $?
assertEquals "Error: filename expression returned nil" "$result"
}
testStrLoadFileNotExist() {
result=$(./yq e -n 'strload("cat.yml")' 2>&1)
assertEquals 1 $?
assertEquals "Error: failed to load cat.yml: open cat.yml: no such file or directory" "$result"
}
testStrLoadFileExpNotExist() {
result=$(./yq e -n 'strload(.a)' 2>&1)
assertEquals 1 $?
assertEquals "Error: filename expression returned nil" "$result"
}
source ./scripts/shunit2
-286
View File
@@ -1,286 +0,0 @@
#!/bin/bash
setUp() {
rm test*.yml || true
}
## Convenient bash shortcut to read records of NUL separated values
## from stdin the safe way. See example usage in the next tests.
read-0() {
local eof="" IFS=''
while [ "$1" ]; do
## - The `-r` avoids bad surprise with '\n' and other interpreted
## sequences that can be read.
## - The `-d ''` is the (strange?) way to refer to NUL delimiter.
## - The `--` is how to avoid unpleasant surprises if your
## "$1" starts with "-" (minus) sign. This protection also
## will produce a readable error if you want to try to start
## your variable names with a "-".
read -r -d '' -- "$1" || eof=1
shift
done
[ -z "$eof" ] ## fail on EOF
}
## Convenient bash shortcut to be used with the next function `p-err`
## to read NUL separated values the safe way AND catch any errors from
## the process creating the stream of NUL separated data. See example
## usage in the tests.
read-0-err() {
local ret="$1" eof="" idx=0 last=
read -r -- "${ret?}" <<<"0"
shift
while [ "$1" ]; do
last=$idx
read -r -d '' -- "$1" || {
## Put this last value in ${!ret}
eof="$1"
read -r -- "$ret" <<<"${!eof}"
break
}
((idx++))
shift
done
[ -z "$eof" ] || {
if [ "$last" != 0 ]; then
## Uhoh, we have no idea if the errorlevel of the internal
## command was properly delimited with a NUL char, and
## anyway something went really wrong at least about the
## number of fields separated by NUL char and the one
## expected.
echo "Error: read-0-err couldn't fill all value $ret = '${!ret}', '$eof', '${!eof}'" >&2
read -r -- "$ret" <<<"not-enough-values"
else
if ! [[ "${!ret}" =~ ^[0-9]+$ && "${!ret}" -ge 0 && "${!ret}" -le 127 ]]; then
## This could happen if you don't use `p-err` wrapper,
## or used stdout in unexpected ways in your inner
## command.
echo "Error: last value is not a number, did you finish with an errorlevel ?" >&2
read -r -- "$ret" <<<"last-value-not-a-number"
fi
fi
false
}
}
## Simply runs command given as argument and adds errorlevel in the
## standard output. Is expected to be used in tandem with
## `read-0-err`.
p-err() {
local exp="$1"
"$@"
printf "%s" "$?"
}
wyq-r() {
local exp="$1"
./yq e -0 -r=false "$1"
printf "%s" "$?"
}
testBasicUsageRaw() {
cat >test.yml <<EOL
a: foo
b: bar
EOL
printf "foo\0bar\0" > expected.out
## We need to compare binary content here. We have to filter the compared
## content through a representation that gets rid of NUL chars but accurately
## transcribe the content.
## Also as it would be nice to have a pretty output in case the test fails,
## we use here 'hd': a widely available shortcut to 'hexdump' that will
## pretty-print any binary to it's hexadecimal representation.
##
## Note that the standard `assertEquals` compare its arguments
## value, but they can't hold NUL characters (this comes from the
## limitation of the C API of `exec*(..)` functions that requires
## `const char *arv[]`). And these are NUL terminated strings. As a
## consequence, the NUL characters gets removed in bash arguments.
assertEquals "$(hd expected.out)" \
"$(./yq e -0 '.a, .b' test.yml | hd)"
rm expected.out
}
testBasicUsage() {
local a b
cat >test.yml <<EOL
a: foo
b: bar
EOL
## We provide 2 values, and ask to fill 2 variables.
read-0 a b < <(./yq e -0 '.a, .b' test.yml)
assertEquals "$?" "0" ## Everything is fine
assertEquals "foo" "$a" ## Values are correctly parsed
assertEquals "bar" "$b"
a=YYY ; b=XXX
## Not enough values provided to fill `a` and `b`.
read-0 a b < <(./yq e -0 '.a' test.yml)
assertEquals "$?" "1" ## An error was emitted
assertEquals "foo" "$a" ## First value was correctly parsed
assertEquals "" "$b" ## Second was still reset
## Error from inner command are not catchable !. Use
## `read-0-err`/`p-err` for that.
read-0 a < <(printf "\0"; ./yq e -0 'xxx' test.yml; )
assertEquals "$?" "0"
}
testBasicUsageJson() {
cat >test.yml <<EOL
a:
x: foo
b: bar
EOL
read-0 a b < <(./yq e -0 -o=json '.a, .b' test.yml)
assertEquals '{
"x": "foo"
}' "$a"
assertEquals '"bar"' "$b"
}
testFailWithValueContainingNUL() {
local a b c
## Note that value of field 'a' actually contains a NUL char !
cat >test.yml <<EOL
a: "foo\u0000bar"
b: 1
c: |
wiz
boom
EOL
## We are looking for trouble with asking to separated fields with NUL
## char and requested value `.a` actually contains itself a NUL char !
read-0 a b c < <(./yq e -0 '.a, .b, .c' test.yml)
assertNotEquals "0" "$?" ## read-0 failed to fill all values
## But here, we can request for one value, even if `./yq` fails
read-0 b < <(./yq e -0 '.b, .a' test.yml)
assertEquals "0" "$?" ## read-0 succeeds at feeding the first value
## Note: to catch the failure of `yq`, see in the next tests the usage
## of `read-0-err`.
## using -r=false solves any NUL containing value issues, but keeps
## all in YAML representation:
read-0 a b c < <(./yq e -0 -r=false '.a, .b, .c' test.yml)
assertEquals "0" "$?" ## All goes well despite asking for `a` value
assertEquals '"foo\0bar"' "$a" ## This is a YAML string representation
assertEquals '1' "$b"
assertEquals '|
wiz
boom' "$c"
}
testStandardLoop() {
local E a b res
## Here everything is normal: 4 values, that will be paired
## in key/values.
cat >test.yml <<EOL
- yay
- wiz
- hop
- pow
EOL
res=""
while read-0-err E a b; do
res+="$a: $b;"
done < <(p-err ./yq -0 '.[]' test.yml)
assertEquals "0" "$E" ## errorlevel of internal command
assertEquals "yay: wiz;hop: pow;" "$res" ## expected result
}
testStandardLoopWithoutEnoughValues() {
local E a b res
## Here 5 values, there will be a missing value when reading
## pairs of value.
cat >test.yml <<EOL
- yay
- wiz
- hop
- pow
- kwak
EOL
res=""
## The loop will succeed 2 times then fail
while read-0-err E a b; do
res+="$a: $b;"
done < <(p-err ./yq -0 '.[]' test.yml)
assertEquals "not-enough-values" "$E" ## Not enough value error
assertEquals "yay: wiz;hop: pow;" "$res" ## the 2 full key/value pairs
}
testStandardLoopWithInternalCmdError() {
local E a b res
## Note the third value contains a NUL char !
cat >test.yml <<EOL
- yay
- wiz
- "foo\0bar"
- hop
- pow
EOL
res=""
## It should be only upon the second pass in the loop that
## read-0-err will catch the fact that there is an error !
while read-0-err E a b; do
res+="$a: $b;"
done < <(p-err ./yq -0 '.[]' test.yml)
assertEquals "1" "$E" ## Internal command errorlevel (from `./yq`)
assertEquals "yay: wiz;" "$res" ## first 2 values were ok at least
}
testStandardLoopNotEnoughErrorEatsCmdError() {
local E a b res
## Because of possible edge cases where the internal errorlevel
## reported by `p-err` in the standard output might be mangled
## with the unfinished record, `read-0-err E ...` will NOT report
## the internal command error in the variable E and instead will
## store the value 'not-enough-values'. In real world, anyway, you
## will want to react the same if the internal command failed
## and/or you didn't get as much values as expected while
## reading. Keep in mind also that standard error is not
## swallowed, so you can read reports from the inner command AND
## from `read-0-err`.
## Here, note that the fourth value contains a NUL char !
cat >test.yml <<EOL
- yay
- wiz
- hop
- "foo\0bar"
- pow
EOL
res=""
## It should be only upon the second loop that read-0-err will catch
## the fact that there are not enough data to fill the requested variables
while read-0-err E a b; do
res+="$a: $b;"
done < <(p-err ./yq -0 '.[]' test.yml)
assertEquals "not-enough-values" "$E" ## Not enough values error eats internal error !
assertEquals "yay: wiz;" "$res" ## first 2 values were ok at least
}
source ./scripts/shunit2
-392
View File
@@ -1,392 +0,0 @@
#!/bin/bash
setUp() {
rm test*.yml || true
}
testOutputJsonDeprecated() {
cat >test.yml <<EOL
a: {b: ["cat"]}
EOL
read -r -d '' expected << EOM
{
"a": {
"b": [
"cat"
]
}
}
EOM
X=$(./yq e -j test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea -j test.yml)
assertEquals "$expected" "$X"
}
testOutputJson() {
cat >test.yml <<EOL
a: {b: ["cat"]}
EOL
read -r -d '' expected << EOM
{
"a": {
"b": [
"cat"
]
}
}
EOM
X=$(./yq e --output-format=json test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea --output-format=json test.yml)
assertEquals "$expected" "$X"
}
testOutputYamlRawDefault() {
cat >test.yml <<EOL
a: "cat"
EOL
X=$(./yq e '.a' test.yml)
assertEquals "cat" "$X"
X=$(./yq ea '.a' test.yml)
assertEquals "cat" "$X"
}
testOutputYamlRawOff() {
cat >test.yml <<EOL
a: "cat"
EOL
X=$(./yq e -r=false '.a' test.yml)
assertEquals "\"cat\"" "$X"
X=$(./yq ea -r=false '.a' test.yml)
assertEquals "\"cat\"" "$X"
}
testOutputYamlRawOnRoot() {
cat >test.yml <<EOL
'a'
EOL
X=$(./yq e -r '.' test.yml)
assertEquals "a" "$X"
}
testOutputJsonRaw() {
cat >test.yml <<EOL
a: cat
EOL
X=$(./yq e -r --output-format=json '.a' test.yml)
assertEquals "cat" "$X"
X=$(./yq ea -r --output-format=json '.a' test.yml)
assertEquals "cat" "$X"
}
testOutputJsonDefault() {
cat >test.yml <<EOL
a: cat
EOL
X=$(./yq e --output-format=json '.a' test.yml)
assertEquals "\"cat\"" "$X"
X=$(./yq ea --output-format=json '.a' test.yml)
assertEquals "\"cat\"" "$X"
}
testOutputJsonShort() {
cat >test.yml <<EOL
a: {b: ["cat"]}
EOL
read -r -d '' expected << EOM
{
"a": {
"b": [
"cat"
]
}
}
EOM
X=$(./yq e -o=j test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea -o=j test.yml)
assertEquals "$expected" "$X"
}
testOutputProperties() {
cat >test.yml <<EOL
a: {b: {c: ["cat cat"]}}
EOL
read -r -d '' expected << EOM
a.b.c.0 = cat cat
EOM
X=$(./yq e --output-format=props test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea --output-format=props test.yml)
assertEquals "$expected" "$X"
}
testOutputPropertiesDontUnwrap() {
cat >test.yml <<EOL
a: {b: {c: ["cat cat"]}}
EOL
read -r -d '' expected << EOM
a.b.c.0 = "cat cat"
EOM
X=$(./yq e -r=false --output-format=props test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea -r=false --output-format=props test.yml)
assertEquals "$expected" "$X"
}
testOutputPropertiesShort() {
cat >test.yml <<EOL
a: {b: {c: ["cat cat"]}}
EOL
read -r -d '' expected << EOM
a.b.c.0 = cat cat
EOM
X=$(./yq e -o=p test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea -o=p test.yml)
assertEquals "$expected" "$X"
}
testOutputCSV() {
cat >test.yml <<EOL
- fruit: apple
yumLevel: 5
- fruit: banana
yumLevel: 4
EOL
read -r -d '' expected << EOM
fruit,yumLevel
apple,5
banana,4
EOM
X=$(./yq -o=c test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea -o=csv test.yml)
assertEquals "$expected" "$X"
}
testOutputCSVCustomSeparator() {
cat >test.yml <<EOL
- fruit: apple
yumLevel: 5
- fruit: banana
yumLevel: 4
EOL
read -r -d '' expected << EOM
fruit;yumLevel
apple;5
banana;4
EOM
X=$(./yq -oc --csv-separator ";" test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea -o=csv --csv-separator ";" test.yml)
assertEquals "$expected" "$X"
}
testOutputTSV() {
cat >test.yml <<EOL
- fruit: apple
yumLevel: 5
- fruit: banana
yumLevel: 4
EOL
read -r -d '' expected << EOM
fruit yumLevel
apple 5
banana 4
EOM
X=$(./yq -o=t test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea -o=tsv test.yml)
assertEquals "$expected" "$X"
}
testOutputXml() {
cat >test.yml <<EOL
a: {b: {c: ["cat"]}}
EOL
read -r -d '' expected << EOM
<a>
<b>
<c>cat</c>
</b>
</a>
EOM
X=$(./yq e --output-format=xml test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea --output-format=xml test.yml)
assertEquals "$expected" "$X"
}
testOutputXmlShort() {
cat >test.yml <<EOL
a: {b: {c: ["cat"]}}
EOL
read -r -d '' expected << EOM
<a>
<b>
<c>cat</c>
</b>
</a>
EOM
X=$(./yq e --output-format=x test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea --output-format=x test.yml)
assertEquals "$expected" "$X"
}
testOutputKYaml() {
cat >test.yml <<'EOL'
# leading
a: 1 # a line
# head b
b: 2
c:
# head d
- d # d line
EOL
read -r -d '' expected <<'EOM'
# leading
{
a: 1, # a line
# head b
b: 2,
c: [
# head d
"d", # d line
],
}
EOM
X=$(./yq e --output-format=kyaml test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea --output-format=kyaml test.yml)
assertEquals "$expected" "$X"
}
testOutputKYamlShort() {
cat >test.yml <<EOL
a: b
EOL
read -r -d '' expected <<'EOM'
{
a: "b",
}
EOM
X=$(./yq e -o=ky test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea -o=ky test.yml)
assertEquals "$expected" "$X"
}
testOutputXmComplex() {
cat >test.yml <<EOL
a: {b: {c: ["cat", "dog"], +@f: meow}}
EOL
read -r -d '' expected << EOM
<a>
<b f="meow">
<c>cat</c>
<c>dog</c>
</b>
</a>
EOM
X=$(./yq e --output-format=x test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea --output-format=x test.yml)
assertEquals "$expected" "$X"
}
testLuaOutputPretty() {
cat >test.yml <<EOL
animals:
cat: meow
EOL
read -r -d '' expected << EOM
return {
["animals"] = {
["cat"] = "meow";
};
};
EOM
X=$(./yq e --output-format=lua test.yml)
assertEquals "$expected" "$X"
X=$(./yq e --output-format=lua --prettyPrint test.yml)
assertEquals "$expected" "$X"
}
testLuaOutputSubset() {
cat >test.yml <<EOL
animals:
cat: meow
EOL
read -r -d '' expected << EOM
return {
["cat"] = "meow";
};
EOM
X=$(./yq e --output-format=lua '.animals' test.yml)
assertEquals "$expected" "$X"
}
source ./scripts/shunit2
-70
View File
@@ -1,70 +0,0 @@
#!/bin/bash
setUp() {
rm test*.yml || true
cat >test.yml <<EOL
a: frog
EOL
}
testPipeViaCatWithParam() {
X=$(cat test.yml | ./yq '.a')
assertEquals "frog" "$X"
}
testPipeViaCatWithParamEval() {
X=$(cat test.yml | ./yq e '.a')
assertEquals "frog" "$X"
}
testPipeViaCatWithParamEvalAll() {
X=$(cat test.yml | ./yq ea '.a')
assertEquals "frog" "$X"
}
testPipeViaCatNoParam() {
X=$(cat test.yml | ./yq)
assertEquals "a: frog" "$X"
}
testPipeViaCatNoParamEval() {
X=$(cat test.yml | ./yq e)
assertEquals "a: frog" "$X"
}
testPipeViaCatNoParamEvalAll() {
X=$(cat test.yml | ./yq ea)
assertEquals "a: frog" "$X"
}
testPipeViaFileishWithParam() {
X=$(./yq '.a' < test.yml)
assertEquals "frog" "$X"
}
testPipeViaFileishWithParamEval() {
X=$(./yq e '.a' < test.yml)
assertEquals "frog" "$X"
}
testPipeViaFileishWithParamEvalAll() {
X=$(./yq ea '.a' < test.yml)
assertEquals "frog" "$X"
}
testPipeViaFileishNoParam() {
X=$(./yq < test.yml)
assertEquals "a: frog" "$X"
}
testPipeViaFileishNoParamEval() {
X=$(./yq e < test.yml)
assertEquals "a: frog" "$X"
}
testPipeViaFileishNoParamEvalAll() {
X=$(./yq ea < test.yml)
assertEquals "a: frog" "$X"
}
source ./scripts/shunit2
-176
View File
@@ -1,176 +0,0 @@
#!/bin/bash
setUp() {
rm test*.yml || true
}
testPrettyPrintWithBooleans() {
cat >test.yml <<EOL
leaveUnquoted: [yes, no, on, off, y, n, true, false]
leaveQuoted: ["yes", "no", "on", "off", "y", "n", "true", "false"]
EOL
read -r -d '' expected << EOM
leaveUnquoted:
- yes
- no
- on
- off
- y
- n
- true
- false
leaveQuoted:
- "yes"
- "no"
- "on"
- "off"
- "y"
- "n"
- "true"
- "false"
EOM
X=$(./yq e --prettyPrint test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea --prettyPrint test.yml)
assertEquals "$expected" "$X"
}
testPrettyPrintWithBooleansCapitals() {
cat >test.yml <<EOL
leaveUnquoted: [YES, NO, ON, OFF, Y, N, TRUE, FALSE]
leaveQuoted: ["YES", "NO", "ON", "OFF", "Y", "N", "TRUE", "FALSE"]
EOL
read -r -d '' expected << EOM
leaveUnquoted:
- YES
- NO
- ON
- OFF
- Y
- N
- TRUE
- FALSE
leaveQuoted:
- "YES"
- "NO"
- "ON"
- "OFF"
- "Y"
- "N"
- "TRUE"
- "FALSE"
EOM
X=$(./yq e --prettyPrint test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea --prettyPrint test.yml)
assertEquals "$expected" "$X"
}
testPrettyPrintOtherStringValues() {
cat >test.yml <<EOL
leaveUnquoted: [yesSir, hellno, bonapite]
makeUnquoted: ["yesSir", "hellno", "bonapite"]
EOL
read -r -d '' expected << EOM
leaveUnquoted:
- yesSir
- hellno
- bonapite
makeUnquoted:
- yesSir
- hellno
- bonapite
EOM
X=$(./yq e --prettyPrint test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea --prettyPrint test.yml)
assertEquals "$expected" "$X"
}
testPrettyPrintKeys() {
cat >test.yml <<EOL
"removeQuotes": "please"
EOL
read -r -d '' expected << EOM
removeQuotes: please
EOM
X=$(./yq e --prettyPrint test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea --prettyPrint test.yml)
assertEquals "$expected" "$X"
}
testPrettyPrintOtherStringValues() {
cat >test.yml <<EOL
leaveUnquoted: [yesSir, hellno, bonapite]
makeUnquoted: ["yesSir", "hellno", "bonapite"]
EOL
read -r -d '' expected << EOM
leaveUnquoted:
- yesSir
- hellno
- bonapite
makeUnquoted:
- yesSir
- hellno
- bonapite
EOM
X=$(./yq e --prettyPrint test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea --prettyPrint test.yml)
assertEquals "$expected" "$X"
}
testPrettyPrintStringBlocks() {
cat >test.yml <<EOL
"removeQuotes": |
"please"
EOL
read -r -d '' expected << EOM
removeQuotes: |
"please"
EOM
X=$(./yq e --prettyPrint test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea --prettyPrint test.yml)
assertEquals "$expected" "$X"
}
testPrettyPrintWithExpression() {
cat >test.yml <<EOL
a: {b: {c: ["cat"]}}
EOL
read -r -d '' expected << EOM
b:
c:
- cat
EOM
X=$(./yq e '.a' --prettyPrint test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea '.a' --prettyPrint test.yml)
assertEquals "$expected" "$X"
}
source ./scripts/shunit2
-26
View File
@@ -1,26 +0,0 @@
#!/bin/bash
setUp() {
rm test*.yq || true
cat >test.yq <<EOL
#!./yq
.a.b
EOL
chmod +x test.yq
rm test*.yml || true
cat >test.yml <<EOL
a: {b: apple}
EOL
}
testCanExecYqFile() {
read -r -d '' expected << EOM
apple
EOM
X=$(./test.yq test.yml)
assertEquals "$expected" "$X"
}
source ./scripts/shunit2
-227
View File
@@ -1,227 +0,0 @@
#!/bin/bash
setUp() {
rm test*.yml || true
rm -rf test_dir* || true
}
testBasicSplitWithName() {
cat >test.yml <<EOL
a: test_doc1
---
a: test_doc2
EOL
./yq e test.yml -s ".a"
doc1=$(cat test_doc1.yml)
assertEquals "a: test_doc1" "$doc1"
doc2=$(cat test_doc2.yml)
read -r -d '' expectedDoc2 << EOM
---
a: test_doc2
EOM
assertEquals "$expectedDoc2" "$doc2"
}
testBasicSplitWithNameCustomExtension() {
rm test*.yaml || true
cat >test.yml <<EOL
a: test_doc1
---
a: test_doc2
EOL
./yq e test.yml -s '.a + ".yaml"'
doc1=$(cat test_doc1.yaml)
assertEquals "a: test_doc1" "$doc1"
doc2=$(cat test_doc2.yaml)
read -r -d '' expectedDoc2 << EOM
---
a: test_doc2
EOM
assertEquals "$expectedDoc2" "$doc2"
}
testSplitFromFile() {
cat >test.yml <<EOL
a: test_doc1
---
a: test_doc2
EOL
cat >test_splitExp.yml <<EOL
.a
EOL
./yq test.yml --split-exp-file test_splitExp.yml
doc1=$(cat test_doc1.yml)
assertEquals "a: test_doc1" "$doc1"
doc2=$(cat test_doc2.yml)
read -r -d '' expectedDoc2 << EOM
---
a: test_doc2
EOM
assertEquals "$expectedDoc2" "$doc2"
}
testBasicSplitWithNameEvalAll() {
cat >test.yml <<EOL
a: test_doc1
---
a: test_doc2
EOL
./yq ea test.yml -s ".a"
doc1=$(cat test_doc1.yml)
assertEquals "a: test_doc1" "$doc1"
doc2=$(cat test_doc2.yml)
read -r -d '' expectedDoc2 << EOM
---
a: test_doc2
EOM
assertEquals "$expectedDoc2" "$doc2"
}
testBasicSplitWithIndex() {
cat >test.yml <<EOL
a: test_doc1
---
a: test_doc2
EOL
./yq e test.yml -s '"test_" + $index'
doc1=$(cat test_0.yml)
assertEquals "a: test_doc1" "$doc1"
doc2=$(cat test_1.yml)
read -r -d '' expectedDoc2 << EOM
---
a: test_doc2
EOM
assertEquals "$expectedDoc2" "$doc2"
}
testBasicSplitWithIndexEvalAll() {
cat >test.yml <<EOL
a: test_doc1
---
a: test_doc2
EOL
./yq ea test.yml -s '"test_" + $index'
doc1=$(cat test_0.yml)
assertEquals "a: test_doc1" "$doc1"
doc2=$(cat test_1.yml)
read -r -d '' expectedDoc2 << EOM
---
a: test_doc2
EOM
assertEquals "$expectedDoc2" "$doc2"
}
testArraySplitWithNameNoSeparators() {
cat >test.yml <<EOL
- name: test_fred
age: 35
- name: test_catherine
age: 37
EOL
./yq e --no-doc -s ".name" ".[]" test.yml
doc1=$(cat test_fred.yml)
read -r -d '' expectedDoc1 << EOM
name: test_fred
age: 35
EOM
assertEquals "$expectedDoc1" "$doc1"
doc2=$(cat test_catherine.yml)
read -r -d '' expectedDoc2 << EOM
name: test_catherine
age: 37
EOM
assertEquals "$expectedDoc2" "$doc2"
}
testArraySplitWithNameNoSeparatorsEvalAll() {
cat >test.yml <<EOL
- name: test_fred
age: 35
- name: test_catherine
age: 37
EOL
cat >test2.yml <<EOL
- name: test_mike
age: 564
EOL
./yq ea --no-doc -s ".name" ".[]" test.yml test2.yml
doc1=$(cat test_fred.yml)
read -r -d '' expectedDoc1 << EOM
name: test_fred
age: 35
EOM
assertEquals "$expectedDoc1" "$doc1"
doc2=$(cat test_catherine.yml)
read -r -d '' expectedDoc2 << EOM
name: test_catherine
age: 37
EOM
assertEquals "$expectedDoc2" "$doc2"
doc3=$(cat test_mike.yml)
read -r -d '' expectedDoc3 << EOM
name: test_mike
age: 564
EOM
assertEquals "$expectedDoc3" "$doc3"
}
testSplitWithDirectories() {
cat >test.yml <<EOL
f: test_dir1/test_file1
---
f: test_dir2/dir22/test_file2
---
f: test_file3
EOL
./yq e --no-doc -s ".f" test.yml
doc1=$(cat test_dir1/test_file1.yml)
assertEquals "f: test_dir1/test_file1" "$doc1"
doc2=$(cat test_dir2/dir22/test_file2.yml)
assertEquals "f: test_dir2/dir22/test_file2" "$doc2"
doc3=$(cat test_file3.yml)
assertEquals "f: test_file3" "$doc3"
}
source ./scripts/shunit2
+3 -7
View File
@@ -1,17 +1,13 @@
name: 'yq - portable yaml processor'
description: 'create, read, update, delete, merge, validate and do more with yaml'
branding:
icon: command
color: gray-dark
icon: command
color: gray-dark
inputs:
cmd:
description: 'The Command which should be run'
required: true
outputs:
result:
description: "The complete result from the yq command being run"
runs:
using: 'docker'
image: 'docker://mikefarah/yq:4-githubaction@sha256:e1b8c865f299ea6b02910a7ddf147d5d431244d4cc116f89c2148c9f53822906'
image: 'github-action/Dockerfile'
args:
- ${{ inputs.cmd }}
+83
View File
@@ -0,0 +1,83 @@
package cmd
// import (
// "strings"
// "testing"
// "github.com/mikefarah/yq/v3/test"
// "github.com/spf13/cobra"
// )
// func getRootCommand() *cobra.Command {
// return New()
// }
// func TestRootCmd(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "")
// if result.Error != nil {
// t.Error(result.Error)
// }
// if !strings.Contains(result.Output, "Usage:") {
// t.Error("Expected usage message to be printed out, but the usage message was not found.")
// }
// }
// func TestRootCmd_Help(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "--help")
// if result.Error != nil {
// t.Error(result.Error)
// }
// if !strings.Contains(result.Output, "yq is a lightweight and portable command-line YAML processor. It aims to be the jq or sed of yaml files.") {
// t.Error("Expected usage message to be printed out, but the usage message was not found.")
// }
// }
// func TestRootCmd_VerboseLong(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "--verbose")
// if result.Error != nil {
// t.Error(result.Error)
// }
// if !verbose {
// t.Error("Expected verbose to be true")
// }
// }
// func TestRootCmd_VerboseShort(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "-v")
// if result.Error != nil {
// t.Error(result.Error)
// }
// if !verbose {
// t.Error("Expected verbose to be true")
// }
// }
// func TestRootCmd_VersionShort(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "-V")
// if result.Error != nil {
// t.Error(result.Error)
// }
// if !strings.Contains(result.Output, "yq version") {
// t.Error("expected version message to be printed out, but the message was not found.")
// }
// }
// func TestRootCmd_VersionLong(t *testing.T) {
// cmd := getRootCommand()
// result := test.RunCmd(cmd, "--version")
// if result.Error != nil {
// t.Error(result.Error)
// }
// if !strings.Contains(result.Output, "yq version") {
// t.Error("expected version message to be printed out, but the message was not found.")
// }
// }
+4 -24
View File
@@ -1,39 +1,19 @@
package cmd
var unwrapScalarFlag = newUnwrapFlag()
var printNodeInfo = false
var unwrapScalar = false
var unwrapScalar = true
var writeInplace = false
var outputToJSON = false
var outputFormat = ""
var inputFormat = ""
var exitStatus = false
var forceColor = false
var forceNoColor = false
var colorsEnabled = false
var indent = 2
var noDocSeparators = false
var nullInput = false
var nulSepOutput = false
var verbose = false
var version = false
var prettyPrint = false
var forceColor = false
var forceNoColor = false
var colorsEnabled = false
// can be either "" (off), "extract" or "process"
var frontMatter = ""
var splitFileExp = ""
var splitFileExpFile = ""
var completedSuccessfully = false
var forceExpression = ""
var expressionFile = ""
+34 -83
View File
@@ -2,6 +2,8 @@ package cmd
import (
"errors"
"fmt"
"os"
"github.com/mikefarah/yq/v4/pkg/yqlib"
"github.com/spf13/cobra"
@@ -12,125 +14,74 @@ func createEvaluateAllCommand() *cobra.Command {
Use: "eval-all [expression] [yaml_file1]...",
Aliases: []string{"ea"},
Short: "Loads _all_ yaml documents of _all_ yaml files and runs expression once",
ValidArgsFunction: func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) {
if len(args) == 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return nil, cobra.ShellCompDirectiveDefault
},
Example: `
# Merge f2.yml into f1.yml (in place)
yq eval-all --inplace 'select(fileIndex == 0) * select(fileIndex == 1)' f1.yml f2.yml
## the same command and expression using shortened names:
yq ea -i 'select(fi == 0) * select(fi == 1)' f1.yml f2.yml
# Merge all given files
yq ea '. as $item ireduce ({}; . * $item )' file1.yml file2.yml ...
# Pipe from STDIN
## use '-' as a filename to pipe from STDIN
cat file2.yml | yq ea '.a.b' file1.yml - file3.yml
`,
Long: `yq is a portable command-line data file processor (https://github.com/mikefarah/yq/)
See https://mikefarah.gitbook.io/yq/ for detailed documentation and examples.
## Evaluate All ##
This command loads _all_ yaml documents of _all_ yaml files and runs expression once
Useful when you need to run an expression across several yaml documents or files (like merge).
Note that it consumes more memory than eval.
# merges f2.yml into f1.yml (inplace)
yq eval-all --inplace 'select(fileIndex == 0) * select(fileIndex == 1)' f1.yml f2.yml
`,
Long: "Evaluate All:\nUseful when you need to run an expression across several yaml documents or files. Consumes more memory than eval",
RunE: evaluateAll,
}
return cmdEvalAll
}
func evaluateAll(cmd *cobra.Command, args []string) (cmdError error) {
func evaluateAll(cmd *cobra.Command, args []string) error {
cmd.SilenceUsage = true
// 0 args, read std in
// 1 arg, null input, process expression
// 1 arg, read file in sequence
// 2+ args, [0] = expression, file the rest
var err error
expression, args, err := initCommand(cmd, args)
if err != nil {
return err
}
stat, _ := os.Stdin.Stat()
pipingStdIn := (stat.Mode() & os.ModeCharDevice) == 0
out := cmd.OutOrStdout()
fileInfo, _ := os.Stdout.Stat()
if forceColor || (!forceNoColor && (fileInfo.Mode()&os.ModeCharDevice) != 0) {
colorsEnabled = true
}
if writeInplace && len(args) < 2 {
return fmt.Errorf("Write inplace flag only applicable when giving an expression and at least one file")
}
if writeInplace {
// only use colours if its forced
// only use colors if its forced
colorsEnabled = forceColor
writeInPlaceHandler := yqlib.NewWriteInPlaceHandler(args[0])
writeInPlaceHandler := yqlib.NewWriteInPlaceHandler(args[1])
out, err = writeInPlaceHandler.CreateTempFile()
if err != nil {
return err
}
// need to indirectly call the function so that completedSuccessfully is
// passed when we finish execution as opposed to now
defer func() {
if cmdError == nil {
cmdError = writeInPlaceHandler.FinishWriteInPlace(completedSuccessfully)
}
}()
defer func() { writeInPlaceHandler.FinishWriteInPlace(completedSuccessfully) }()
}
format, err := yqlib.FormatFromString(outputFormat)
if err != nil {
return err
if nullInput && len(args) > 1 {
return errors.New("Cannot pass files in when using null-input flag")
}
decoder, err := configureDecoder(true)
if err != nil {
return err
}
printerWriter, err := configurePrinterWriter(format, out)
if err != nil {
return err
}
encoder, err := configureEncoder()
if err != nil {
return err
}
printer := yqlib.NewPrinter(encoder, printerWriter)
if nulSepOutput {
printer.SetNulSepOutput(true)
}
if frontMatter != "" {
originalFilename := args[0]
frontMatterHandler := yqlib.NewFrontMatterHandler(args[0])
err = frontMatterHandler.Split()
if err != nil {
return err
}
args[0] = frontMatterHandler.GetYamlFrontMatterFilename()
yqlib.SetFilenameAlias(args[0], originalFilename)
defer yqlib.ClearFilenameAliases()
if frontMatter == "process" {
reader := frontMatterHandler.GetContentReader()
printer.SetAppendix(reader)
defer yqlib.SafelyCloseReader(reader)
}
defer frontMatterHandler.CleanUp()
}
printer := yqlib.NewPrinter(out, outputToJSON, unwrapScalar, colorsEnabled, indent, !noDocSeparators)
allAtOnceEvaluator := yqlib.NewAllAtOnceEvaluator()
switch len(args) {
case 0:
if nullInput {
err = yqlib.NewStreamEvaluator().EvaluateNew(processExpression(expression), printer)
if pipingStdIn {
err = allAtOnceEvaluator.EvaluateFiles(processExpression(""), []string{"-"}, printer)
} else {
cmd.Println(cmd.UsageString())
return nil
}
case 1:
if nullInput {
err = yqlib.NewStreamEvaluator().EvaluateNew(processExpression(args[0]), printer)
} else {
err = allAtOnceEvaluator.EvaluateFiles(processExpression(""), []string{args[0]}, printer)
}
default:
err = allAtOnceEvaluator.EvaluateFiles(processExpression(expression), args, printer, decoder)
err = allAtOnceEvaluator.EvaluateFiles(processExpression(args[0]), args[1:], printer)
}
completedSuccessfully = err == nil
-329
View File
@@ -1,329 +0,0 @@
package cmd
import (
"bytes"
"os"
"path/filepath"
"strings"
"testing"
)
func TestCreateEvaluateAllCommand(t *testing.T) {
cmd := createEvaluateAllCommand()
if cmd == nil {
t.Fatal("createEvaluateAllCommand returned nil")
return
}
// Test basic command properties
if cmd.Use != "eval-all [expression] [yaml_file1]..." {
t.Errorf("Expected Use to be 'eval-all [expression] [yaml_file1]...', got %q", cmd.Use)
}
if cmd.Short == "" {
t.Error("Expected Short description to be non-empty")
}
if cmd.Long == "" {
t.Error("Expected Long description to be non-empty")
}
// Test aliases
expectedAliases := []string{"ea"}
if len(cmd.Aliases) != len(expectedAliases) {
t.Errorf("Expected %d aliases, got %d", len(expectedAliases), len(cmd.Aliases))
}
for i, expected := range expectedAliases {
if i >= len(cmd.Aliases) || cmd.Aliases[i] != expected {
t.Errorf("Expected alias %d to be %q, got %q", i, expected, cmd.Aliases[i])
}
}
}
func TestEvaluateAll_NoArgs(t *testing.T) {
// Create a temporary command
cmd := createEvaluateAllCommand()
// Set up command to capture output
var output bytes.Buffer
cmd.SetOut(&output)
// Test with no arguments and no null input
nullInput = false
defer func() { nullInput = false }()
err := evaluateAll(cmd, []string{})
// Should not error, but should print usage
if err != nil {
t.Errorf("evaluateAll with no args should not error, got: %v", err)
}
// Should have printed usage information
if output.Len() == 0 {
t.Error("Expected usage information to be printed")
}
}
func TestEvaluateAll_NullInput(t *testing.T) {
// Create a temporary command
cmd := createEvaluateAllCommand()
// Set up command to capture output
var output bytes.Buffer
cmd.SetOut(&output)
// Test with null input
nullInput = true
defer func() { nullInput = false }()
err := evaluateAll(cmd, []string{})
// Should not error when using null input
if err != nil {
t.Errorf("evaluateAll with null input should not error, got: %v", err)
}
}
func TestEvaluateAll_WithSingleFile(t *testing.T) {
// Create a temporary YAML file
tempDir := t.TempDir()
yamlFile := filepath.Join(tempDir, "test.yaml")
yamlContent := []byte("name: test\nage: 25\n")
err := os.WriteFile(yamlFile, yamlContent, 0600)
if err != nil {
t.Fatalf("Failed to create test YAML file: %v", err)
}
// Create a temporary command
cmd := createEvaluateAllCommand()
// Set up command to capture output
var output bytes.Buffer
cmd.SetOut(&output)
// Test with a single file
err = evaluateAll(cmd, []string{yamlFile})
// Should not error
if err != nil {
t.Errorf("evaluateAll with single file should not error, got: %v", err)
}
// Should have some output
if output.Len() == 0 {
t.Error("Expected output from evaluateAll with single file")
}
}
func TestEvaluateAll_WithMultipleFiles(t *testing.T) {
// Create temporary YAML files
tempDir := t.TempDir()
yamlFile1 := filepath.Join(tempDir, "test1.yaml")
yamlContent1 := []byte("name: test1\nage: 25\n")
err := os.WriteFile(yamlFile1, yamlContent1, 0600)
if err != nil {
t.Fatalf("Failed to create test YAML file 1: %v", err)
}
yamlFile2 := filepath.Join(tempDir, "test2.yaml")
yamlContent2 := []byte("name: test2\nage: 30\n")
err = os.WriteFile(yamlFile2, yamlContent2, 0600)
if err != nil {
t.Fatalf("Failed to create test YAML file 2: %v", err)
}
// Create a temporary command
cmd := createEvaluateAllCommand()
// Set up command to capture output
var output bytes.Buffer
cmd.SetOut(&output)
// Test with multiple files
err = evaluateAll(cmd, []string{yamlFile1, yamlFile2})
// Should not error
if err != nil {
t.Errorf("evaluateAll with multiple files should not error, got: %v", err)
}
// Should have output
if output.Len() == 0 {
t.Error("Expected output from evaluateAll with multiple files")
}
}
func TestEvaluateAll_WithExpression(t *testing.T) {
// Create a temporary YAML file
tempDir := t.TempDir()
yamlFile := filepath.Join(tempDir, "test.yaml")
yamlContent := []byte("name: test\nage: 25\n")
err := os.WriteFile(yamlFile, yamlContent, 0600)
if err != nil {
t.Fatalf("Failed to create test YAML file: %v", err)
}
// Create a temporary command
cmd := createEvaluateAllCommand()
// Set up command to capture output
var output bytes.Buffer
cmd.SetOut(&output)
// Test with expression
err = evaluateAll(cmd, []string{".name", yamlFile})
// Should not error
if err != nil {
t.Errorf("evaluateAll with expression should not error, got: %v", err)
}
// Should have output
if output.Len() == 0 {
t.Error("Expected output from evaluateAll with expression")
}
}
func TestEvaluateAll_WriteInPlace(t *testing.T) {
// Create a temporary YAML file
tempDir := t.TempDir()
yamlFile := filepath.Join(tempDir, "test.yaml")
yamlContent := []byte("name: test\nage: 25\n")
err := os.WriteFile(yamlFile, yamlContent, 0600)
if err != nil {
t.Fatalf("Failed to create test YAML file: %v", err)
}
// Create a temporary command
cmd := createEvaluateAllCommand()
// Set up command to capture output
var output bytes.Buffer
cmd.SetOut(&output)
// Enable write in place
originalWriteInplace := writeInplace
writeInplace = true
defer func() { writeInplace = originalWriteInplace }()
// Test with write in place
err = evaluateAll(cmd, []string{".name = \"updated\"", yamlFile})
// Should not error
if err != nil {
t.Errorf("evaluateAll with write in place should not error, got: %v", err)
}
// Verify the file was updated
updatedContent, err := os.ReadFile(yamlFile)
if err != nil {
t.Fatalf("Failed to read updated file: %v", err)
}
// Should contain the updated content
if !strings.Contains(string(updatedContent), "updated") {
t.Errorf("Expected file to contain 'updated', got: %s", string(updatedContent))
}
}
func TestEvaluateAll_ExitStatus(t *testing.T) {
// Create a temporary YAML file
tempDir := t.TempDir()
yamlFile := filepath.Join(tempDir, "test.yaml")
yamlContent := []byte("name: test\nage: 25\n")
err := os.WriteFile(yamlFile, yamlContent, 0600)
if err != nil {
t.Fatalf("Failed to create test YAML file: %v", err)
}
// Create a temporary command
cmd := createEvaluateAllCommand()
// Set up command to capture output
var output bytes.Buffer
cmd.SetOut(&output)
// Enable exit status
originalExitStatus := exitStatus
exitStatus = true
defer func() { exitStatus = originalExitStatus }()
// Test with expression that should find no matches
err = evaluateAll(cmd, []string{".nonexistent", yamlFile})
// Should error when no matches found and exit status is enabled
if err == nil {
t.Error("Expected error when no matches found and exit status is enabled")
}
}
func TestEvaluateAll_WithMultipleDocuments(t *testing.T) {
// Create a temporary YAML file with multiple documents
tempDir := t.TempDir()
yamlFile := filepath.Join(tempDir, "test.yaml")
yamlContent := []byte("---\nname: doc1\nage: 25\n---\nname: doc2\nage: 30\n")
err := os.WriteFile(yamlFile, yamlContent, 0600)
if err != nil {
t.Fatalf("Failed to create test YAML file: %v", err)
}
// Create a temporary command
cmd := createEvaluateAllCommand()
// Set up command to capture output
var output bytes.Buffer
cmd.SetOut(&output)
// Test with multiple documents
err = evaluateAll(cmd, []string{".", yamlFile})
// Should not error
if err != nil {
t.Errorf("evaluateAll with multiple documents should not error, got: %v", err)
}
// Should have output
if output.Len() == 0 {
t.Error("Expected output from evaluateAll with multiple documents")
}
}
func TestEvaluateAll_NulSepOutput(t *testing.T) {
// Create a temporary YAML file
tempDir := t.TempDir()
yamlFile := filepath.Join(tempDir, "test.yaml")
yamlContent := []byte("name: test\nage: 25\n")
err := os.WriteFile(yamlFile, yamlContent, 0600)
if err != nil {
t.Fatalf("Failed to create test YAML file: %v", err)
}
// Create a temporary command
cmd := createEvaluateAllCommand()
// Set up command to capture output
var output bytes.Buffer
cmd.SetOut(&output)
// Enable nul separator output
originalNulSepOutput := nulSepOutput
nulSepOutput = true
defer func() { nulSepOutput = originalNulSepOutput }()
// Test with nul separator output
err = evaluateAll(cmd, []string{".name", yamlFile})
// Should not error
if err != nil {
t.Errorf("evaluateAll with nul separator output should not error, got: %v", err)
}
// Should have output
if output.Len() == 0 {
t.Error("Expected output from evaluateAll with nul separator output")
}
}
-161
View File
@@ -1,161 +0,0 @@
package cmd
import (
"errors"
"fmt"
"github.com/mikefarah/yq/v4/pkg/yqlib"
"github.com/spf13/cobra"
)
func createEvaluateSequenceCommand() *cobra.Command {
var cmdEvalSequence = &cobra.Command{
Use: "eval [expression] [yaml_file1]...",
Aliases: []string{"e"},
Short: "(default) Apply the expression to each document in each yaml file in sequence",
ValidArgsFunction: func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) {
if len(args) == 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return nil, cobra.ShellCompDirectiveDefault
},
Example: `
# Reads field under the given path for each file
yq e '.a.b' f1.yml f2.yml
# Prints out the file
yq e sample.yaml
# Pipe from STDIN
## use '-' as a filename to pipe from STDIN
cat file2.yml | yq e '.a.b' file1.yml - file3.yml
# Creates a new yaml document
## Note that editing an empty file does not work.
yq e -n '.a.b.c = "cat"'
# Update a file in place
yq e '.a.b = "cool"' -i file.yaml
`,
Long: `yq is a portable command-line data file processor (https://github.com/mikefarah/yq/)
See https://mikefarah.gitbook.io/yq/ for detailed documentation and examples.
## Evaluate Sequence ##
This command iterates over each yaml document from each given file, applies the
expression and prints the result in sequence.`,
RunE: evaluateSequence,
}
return cmdEvalSequence
}
func processExpression(expression string) string {
if prettyPrint && expression == "" {
return yqlib.PrettyPrintExp
} else if prettyPrint {
return fmt.Sprintf("%v | %v", expression, yqlib.PrettyPrintExp)
}
return expression
}
func evaluateSequence(cmd *cobra.Command, args []string) (cmdError error) {
// 0 args, read std in
// 1 arg, null input, process expression
// 1 arg, read file in sequence
// 2+ args, [0] = expression, file the rest
out := cmd.OutOrStdout()
var err error
expression, args, err := initCommand(cmd, args)
if err != nil {
return err
}
if writeInplace {
// only use colours if its forced
colorsEnabled = forceColor
writeInPlaceHandler := yqlib.NewWriteInPlaceHandler(args[0])
out, err = writeInPlaceHandler.CreateTempFile()
if err != nil {
return err
}
// need to indirectly call the function so that completedSuccessfully is
// passed when we finish execution as opposed to now
defer func() {
if cmdError == nil {
cmdError = writeInPlaceHandler.FinishWriteInPlace(completedSuccessfully)
}
}()
}
format, err := yqlib.FormatFromString(outputFormat)
if err != nil {
return err
}
printerWriter, err := configurePrinterWriter(format, out)
if err != nil {
return err
}
encoder, err := configureEncoder()
if err != nil {
return err
}
printer := yqlib.NewPrinter(encoder, printerWriter)
if printNodeInfo {
printer = yqlib.NewNodeInfoPrinter(printerWriter)
}
if nulSepOutput {
printer.SetNulSepOutput(true)
}
decoder, err := configureDecoder(false)
if err != nil {
return err
}
streamEvaluator := yqlib.NewStreamEvaluator()
if frontMatter != "" {
yqlib.GetLogger().Debug("using front matter handler")
originalFilename := args[0]
frontMatterHandler := yqlib.NewFrontMatterHandler(args[0])
err = frontMatterHandler.Split()
if err != nil {
return err
}
args[0] = frontMatterHandler.GetYamlFrontMatterFilename()
yqlib.SetFilenameAlias(args[0], originalFilename)
defer yqlib.ClearFilenameAliases()
if frontMatter == "process" {
reader := frontMatterHandler.GetContentReader()
printer.SetAppendix(reader)
defer yqlib.SafelyCloseReader(reader)
}
defer frontMatterHandler.CleanUp()
}
switch len(args) {
case 0:
if nullInput {
err = streamEvaluator.EvaluateNew(processExpression(expression), printer)
} else {
cmd.Println(cmd.UsageString())
return nil
}
default:
err = streamEvaluator.EvaluateFiles(processExpression(expression), args, printer, decoder)
}
completedSuccessfully = err == nil
if err == nil && exitStatus && !printer.PrintedAnything() {
return errors.New("no matches found")
}
return err
}
-277
View File
@@ -1,277 +0,0 @@
package cmd
import (
"bytes"
"os"
"path/filepath"
"strings"
"testing"
)
func TestCreateEvaluateSequenceCommand(t *testing.T) {
cmd := createEvaluateSequenceCommand()
if cmd == nil {
t.Fatal("createEvaluateSequenceCommand returned nil")
return
}
// Test basic command properties
if cmd.Use != "eval [expression] [yaml_file1]..." {
t.Errorf("Expected Use to be 'eval [expression] [yaml_file1]...', got %q", cmd.Use)
}
if cmd.Short == "" {
t.Error("Expected Short description to be non-empty")
}
if cmd.Long == "" {
t.Error("Expected Long description to be non-empty")
}
// Test aliases
expectedAliases := []string{"e"}
if len(cmd.Aliases) != len(expectedAliases) {
t.Errorf("Expected %d aliases, got %d", len(expectedAliases), len(cmd.Aliases))
}
for i, expected := range expectedAliases {
if i >= len(cmd.Aliases) || cmd.Aliases[i] != expected {
t.Errorf("Expected alias %d to be %q, got %q", i, expected, cmd.Aliases[i])
}
}
}
func TestProcessExpression(t *testing.T) {
// Reset global variables
originalPrettyPrint := prettyPrint
defer func() { prettyPrint = originalPrettyPrint }()
tests := []struct {
name string
prettyPrint bool
expression string
expected string
}{
{
name: "empty expression without pretty print",
prettyPrint: false,
expression: "",
expected: "",
},
{
name: "empty expression with pretty print",
prettyPrint: true,
expression: "",
expected: `(... | (select(tag != "!!str"), select(tag == "!!str") | select(test("(?i)^(y|yes|n|no|on|off)$") | not)) ) style=""`,
},
{
name: "simple expression without pretty print",
prettyPrint: false,
expression: ".a.b",
expected: ".a.b",
},
{
name: "simple expression with pretty print",
prettyPrint: true,
expression: ".a.b",
expected: `.a.b | (... | (select(tag != "!!str"), select(tag == "!!str") | select(test("(?i)^(y|yes|n|no|on|off)$") | not)) ) style=""`,
},
{
name: "complex expression with pretty print",
prettyPrint: true,
expression: ".items[] | select(.active == true)",
expected: `.items[] | select(.active == true) | (... | (select(tag != "!!str"), select(tag == "!!str") | select(test("(?i)^(y|yes|n|no|on|off)$") | not)) ) style=""`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
prettyPrint = tt.prettyPrint
result := processExpression(tt.expression)
if result != tt.expected {
t.Errorf("processExpression(%q) = %q, want %q", tt.expression, result, tt.expected)
}
})
}
}
func TestEvaluateSequence_NoArgs(t *testing.T) {
// Create a temporary command
cmd := createEvaluateSequenceCommand()
// Set up command to capture output
var output bytes.Buffer
cmd.SetOut(&output)
// Test with no arguments and no null input
nullInput = false
defer func() { nullInput = false }()
err := evaluateSequence(cmd, []string{})
// Should not error, but should print usage
if err != nil {
t.Errorf("evaluateSequence with no args should not error, got: %v", err)
}
// Should have printed usage information
if output.Len() == 0 {
t.Error("Expected usage information to be printed")
}
}
func TestEvaluateSequence_NullInput(t *testing.T) {
// Create a temporary command
cmd := createEvaluateSequenceCommand()
// Set up command to capture output
var output bytes.Buffer
cmd.SetOut(&output)
// Test with null input
nullInput = true
defer func() { nullInput = false }()
err := evaluateSequence(cmd, []string{})
// Should not error when using null input
if err != nil {
t.Errorf("evaluateSequence with null input should not error, got: %v", err)
}
}
func TestEvaluateSequence_WithFile(t *testing.T) {
// Create a temporary YAML file
tempDir := t.TempDir()
yamlFile := filepath.Join(tempDir, "test.yaml")
yamlContent := []byte("name: test\nage: 25\n")
err := os.WriteFile(yamlFile, yamlContent, 0600)
if err != nil {
t.Fatalf("Failed to create test YAML file: %v", err)
}
// Create a temporary command
cmd := createEvaluateSequenceCommand()
// Set up command to capture output
var output bytes.Buffer
cmd.SetOut(&output)
// Test with a file
err = evaluateSequence(cmd, []string{yamlFile})
// Should not error
if err != nil {
t.Errorf("evaluateSequence with file should not error, got: %v", err)
}
// Should have some output
if output.Len() == 0 {
t.Error("Expected output from evaluateSequence with file")
}
}
func TestEvaluateSequence_WithExpressionAndFile(t *testing.T) {
// Create a temporary YAML file
tempDir := t.TempDir()
yamlFile := filepath.Join(tempDir, "test.yaml")
yamlContent := []byte("name: test\nage: 25\n")
err := os.WriteFile(yamlFile, yamlContent, 0600)
if err != nil {
t.Fatalf("Failed to create test YAML file: %v", err)
}
// Create a temporary command
cmd := createEvaluateSequenceCommand()
// Set up command to capture output
var output bytes.Buffer
cmd.SetOut(&output)
// Test with expression and file
err = evaluateSequence(cmd, []string{".name", yamlFile})
// Should not error
if err != nil {
t.Errorf("evaluateSequence with expression and file should not error, got: %v", err)
}
// Should have output
if output.Len() == 0 {
t.Error("Expected output from evaluateSequence with expression and file")
}
}
func TestEvaluateSequence_WriteInPlace(t *testing.T) {
// Create a temporary YAML file
tempDir := t.TempDir()
yamlFile := filepath.Join(tempDir, "test.yaml")
yamlContent := []byte("name: test\nage: 25\n")
err := os.WriteFile(yamlFile, yamlContent, 0600)
if err != nil {
t.Fatalf("Failed to create test YAML file: %v", err)
}
// Create a temporary command
cmd := createEvaluateSequenceCommand()
// Set up command to capture output
var output bytes.Buffer
cmd.SetOut(&output)
// Enable write in place
originalWriteInplace := writeInplace
writeInplace = true
defer func() { writeInplace = originalWriteInplace }()
// Test with write in place
err = evaluateSequence(cmd, []string{".name = \"updated\"", yamlFile})
// Should not error
if err != nil {
t.Errorf("evaluateSequence with write in place should not error, got: %v", err)
}
// Verify the file was updated
updatedContent, err := os.ReadFile(yamlFile)
if err != nil {
t.Fatalf("Failed to read updated file: %v", err)
}
// Should contain the updated content
if !strings.Contains(string(updatedContent), "updated") {
t.Errorf("Expected file to contain 'updated', got: %s", string(updatedContent))
}
}
func TestEvaluateSequence_ExitStatus(t *testing.T) {
// Create a temporary YAML file
tempDir := t.TempDir()
yamlFile := filepath.Join(tempDir, "test.yaml")
yamlContent := []byte("name: test\nage: 25\n")
err := os.WriteFile(yamlFile, yamlContent, 0600)
if err != nil {
t.Fatalf("Failed to create test YAML file: %v", err)
}
// Create a temporary command
cmd := createEvaluateSequenceCommand()
// Set up command to capture output
var output bytes.Buffer
cmd.SetOut(&output)
// Enable exit status
originalExitStatus := exitStatus
exitStatus = true
defer func() { exitStatus = originalExitStatus }()
// Test with expression that should find no matches
err = evaluateSequence(cmd, []string{".nonexistent", yamlFile})
// Should error when no matches found and exit status is enabled
if err == nil {
t.Error("Expected error when no matches found and exit status is enabled")
}
}
+114
View File
@@ -0,0 +1,114 @@
package cmd
import (
"errors"
"fmt"
"os"
"github.com/mikefarah/yq/v4/pkg/yqlib"
"github.com/spf13/cobra"
)
func createEvaluateSequenceCommand() *cobra.Command {
var cmdEvalSequence = &cobra.Command{
Use: "eval [expression] [yaml_file1]...",
Aliases: []string{"e"},
Short: "Apply expression to each document in each yaml file given in sequence",
Example: `
# runs the expression against each file, in series
yq e '.a.b | length' f1.yml f2.yml
# prints out the file
yq e sample.yaml
# prints a new yaml document
yq e -n '.a.b.c = "cat"'
# updates file.yaml directly
yq e '.a.b = "cool"' -i file.yaml
`,
Long: "Evaluate Sequence:\nIterate over each yaml document, apply the expression and print the results, in sequence.",
RunE: evaluateSequence,
}
return cmdEvalSequence
}
func processExpression(expression string) string {
if prettyPrint && expression == "" {
return `... style=""`
} else if prettyPrint {
return fmt.Sprintf("%v | ... style= \"\"", expression)
}
return expression
}
func evaluateSequence(cmd *cobra.Command, args []string) error {
cmd.SilenceUsage = true
// 0 args, read std in
// 1 arg, null input, process expression
// 1 arg, read file in sequence
// 2+ args, [0] = expression, file the rest
var err error
stat, _ := os.Stdin.Stat()
pipingStdIn := (stat.Mode() & os.ModeCharDevice) == 0
out := cmd.OutOrStdout()
fileInfo, _ := os.Stdout.Stat()
if forceColor || (!forceNoColor && (fileInfo.Mode()&os.ModeCharDevice) != 0) {
colorsEnabled = true
}
if writeInplace && len(args) < 2 {
return fmt.Errorf("Write inplace flag only applicable when giving an expression and at least one file")
}
if writeInplace {
// only use colors if its forced
colorsEnabled = forceColor
writeInPlaceHandler := yqlib.NewWriteInPlaceHandler(args[1])
out, err = writeInPlaceHandler.CreateTempFile()
if err != nil {
return err
}
// need to indirectly call the function so that completedSuccessfully is
// passed when we finish execution as opposed to now
defer func() { writeInPlaceHandler.FinishWriteInPlace(completedSuccessfully) }()
}
printer := yqlib.NewPrinter(out, outputToJSON, unwrapScalar, colorsEnabled, indent, !noDocSeparators)
streamEvaluator := yqlib.NewStreamEvaluator()
if nullInput && len(args) > 1 {
return errors.New("Cannot pass files in when using null-input flag")
}
switch len(args) {
case 0:
if pipingStdIn {
err = streamEvaluator.EvaluateFiles(processExpression(""), []string{"-"}, printer)
} else {
cmd.Println(cmd.UsageString())
return nil
}
case 1:
if nullInput {
err = streamEvaluator.EvaluateNew(processExpression(args[0]), printer)
} else {
err = streamEvaluator.EvaluateFiles(processExpression(""), []string{args[0]}, printer)
}
default:
err = streamEvaluator.EvaluateFiles(args[0], args[1:], printer)
}
completedSuccessfully = err == nil
if err == nil && exitStatus && !printer.PrintedAnything() {
return errors.New("no matches found")
}
return err
}
+20 -183
View File
@@ -1,221 +1,58 @@
package cmd
import (
"fmt"
"log/slog"
"os"
"strings"
"github.com/mikefarah/yq/v4/pkg/yqlib"
"github.com/spf13/cobra"
logging "gopkg.in/op/go-logging.v1"
)
type runeValue rune
func newRuneVar(p *rune) *runeValue {
return (*runeValue)(p)
}
func (r *runeValue) String() string {
return string(*r)
}
func (r *runeValue) Set(rawVal string) error {
val := strings.ReplaceAll(rawVal, "\\n", "\n")
val = strings.ReplaceAll(val, "\\t", "\t")
val = strings.ReplaceAll(val, "\\r", "\r")
val = strings.ReplaceAll(val, "\\f", "\f")
val = strings.ReplaceAll(val, "\\v", "\v")
if len(val) != 1 {
return fmt.Errorf("[%v] is not a valid character. Must be length 1 was %v", val, len(val))
}
*r = runeValue(rune(val[0]))
return nil
}
func (r *runeValue) Type() string {
return "char"
}
func New() *cobra.Command {
var rootCmd = &cobra.Command{
Use: "yq",
Short: "yq is a lightweight and portable command-line data file processor.",
Long: `yq is a portable command-line data file processor (https://github.com/mikefarah/yq/)
See https://mikefarah.gitbook.io/yq/ for detailed documentation and examples.`,
Example: `
# yq tries to auto-detect the file format based off the extension, and defaults to YAML if it's unknown (or piping through STDIN)
# Use the '-p/--input-format' flag to specify a format type.
cat file.xml | yq -p xml
# read the "stuff" node from "myfile.yml"
yq '.stuff' < myfile.yml
# update myfile.yml in place
yq -i '.stuff = "foo"' myfile.yml
# print contents of sample.json as idiomatic YAML
yq -P -oy sample.json
`,
Short: "yq is a lightweight and portable command-line YAML processor.",
Long: `yq is a lightweight and portable command-line YAML processor. It aims to be the jq or sed of yaml files.`,
RunE: func(cmd *cobra.Command, args []string) error {
if version {
cmd.Print(GetVersionDisplay())
return nil
}
return evaluateSequence(cmd, args)
cmd.Println(cmd.UsageString())
return nil
},
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
PersistentPreRun: func(cmd *cobra.Command, args []string) {
cmd.SetOut(cmd.OutOrStdout())
var format = logging.MustStringFormatter(
`%{color}%{time:15:04:05} %{shortfunc} [%{level:.4s}]%{color:reset} %{message}`,
)
var backend = logging.AddModuleLevel(
logging.NewBackendFormatter(logging.NewLogBackend(os.Stderr, "", 0), format))
// when NO_COLOR environment variable presents and not an empty string the coloured output should be disabled;
// refer to no-color.org
forceNoColor = forceNoColor || os.Getenv("NO_COLOR") != ""
level := slog.LevelWarn
if verbose {
level = slog.LevelDebug
backend.SetLevel(logging.DEBUG, "")
} else {
backend.SetLevel(logging.ERROR, "")
}
yqlib.GetLogger().SetLevel(level)
opts := &slog.HandlerOptions{Level: level, AddSource: verbose}
handler := slog.NewTextHandler(os.Stderr, opts)
yqlib.GetLogger().SetSlogger(slog.New(handler))
yqlib.InitExpressionParser()
return nil
logging.SetBackend(backend)
},
}
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose mode")
rootCmd.PersistentFlags().BoolVarP(&printNodeInfo, "debug-node-info", "", false, "debug node info")
rootCmd.PersistentFlags().BoolVarP(&outputToJSON, "tojson", "j", false, "(deprecated) output as json. Set indent to 0 to print json in one line.")
err := rootCmd.PersistentFlags().MarkDeprecated("tojson", "please use -o=json instead")
if err != nil {
panic(err)
}
rootCmd.PersistentFlags().StringVarP(&outputFormat, "output-format", "o", "auto", fmt.Sprintf("[auto|a|%v] output format type.", yqlib.GetAvailableOutputFormatString()))
var outputCompletions = []string{"auto"}
for _, formats := range yqlib.GetAvailableOutputFormats() {
outputCompletions = append(outputCompletions, formats.FormalName)
}
if err = rootCmd.RegisterFlagCompletionFunc("output-format", cobra.FixedCompletions(outputCompletions, cobra.ShellCompDirectiveNoFileComp)); err != nil {
panic(err)
}
rootCmd.PersistentFlags().StringVarP(&inputFormat, "input-format", "p", "auto", fmt.Sprintf("[auto|a|%v] parse format for input.", yqlib.GetAvailableInputFormatString()))
var inputCompletions = []string{"auto"}
for _, formats := range yqlib.GetAvailableInputFormats() {
inputCompletions = append(inputCompletions, formats.FormalName)
}
if err = rootCmd.RegisterFlagCompletionFunc("input-format", cobra.FixedCompletions(inputCompletions, cobra.ShellCompDirectiveNoFileComp)); err != nil {
panic(err)
}
rootCmd.PersistentFlags().StringVar(&yqlib.ConfiguredXMLPreferences.AttributePrefix, "xml-attribute-prefix", yqlib.ConfiguredXMLPreferences.AttributePrefix, "prefix for xml attributes")
if err = rootCmd.RegisterFlagCompletionFunc("xml-attribute-prefix", cobra.NoFileCompletions); err != nil {
panic(err)
}
rootCmd.PersistentFlags().StringVar(&yqlib.ConfiguredXMLPreferences.ContentName, "xml-content-name", yqlib.ConfiguredXMLPreferences.ContentName, "name for xml content (if no attribute name is present).")
if err = rootCmd.RegisterFlagCompletionFunc("xml-content-name", cobra.NoFileCompletions); err != nil {
panic(err)
}
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredXMLPreferences.StrictMode, "xml-strict-mode", yqlib.ConfiguredXMLPreferences.StrictMode, "enables strict parsing of XML. See https://pkg.go.dev/encoding/xml for more details.")
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredXMLPreferences.KeepNamespace, "xml-keep-namespace", yqlib.ConfiguredXMLPreferences.KeepNamespace, "enables keeping namespace after parsing attributes")
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredXMLPreferences.UseRawToken, "xml-raw-token", yqlib.ConfiguredXMLPreferences.UseRawToken, "enables using RawToken method instead Token. Commonly disables namespace translations. See https://pkg.go.dev/encoding/xml#Decoder.RawToken for details.")
rootCmd.PersistentFlags().StringVar(&yqlib.ConfiguredXMLPreferences.ProcInstPrefix, "xml-proc-inst-prefix", yqlib.ConfiguredXMLPreferences.ProcInstPrefix, "prefix for xml processing instructions (e.g. <?xml version=\"1\"?>)")
if err = rootCmd.RegisterFlagCompletionFunc("xml-proc-inst-prefix", cobra.NoFileCompletions); err != nil {
panic(err)
}
rootCmd.PersistentFlags().StringVar(&yqlib.ConfiguredXMLPreferences.DirectiveName, "xml-directive-name", yqlib.ConfiguredXMLPreferences.DirectiveName, "name for xml directives (e.g. <!DOCTYPE thing cat>)")
if err = rootCmd.RegisterFlagCompletionFunc("xml-directive-name", cobra.NoFileCompletions); err != nil {
panic(err)
}
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredXMLPreferences.SkipProcInst, "xml-skip-proc-inst", yqlib.ConfiguredXMLPreferences.SkipProcInst, "skip over process instructions (e.g. <?xml version=\"1\"?>)")
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredXMLPreferences.SkipDirectives, "xml-skip-directives", yqlib.ConfiguredXMLPreferences.SkipDirectives, "skip over directives (e.g. <!DOCTYPE thing cat>)")
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredCsvPreferences.AutoParse, "csv-auto-parse", yqlib.ConfiguredCsvPreferences.AutoParse, "parse CSV YAML/JSON values")
rootCmd.PersistentFlags().Var(newRuneVar(&yqlib.ConfiguredCsvPreferences.Separator), "csv-separator", "CSV Separator character")
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredTsvPreferences.AutoParse, "tsv-auto-parse", yqlib.ConfiguredTsvPreferences.AutoParse, "parse TSV YAML/JSON values")
rootCmd.PersistentFlags().StringVar(&yqlib.ConfiguredLuaPreferences.DocPrefix, "lua-prefix", yqlib.ConfiguredLuaPreferences.DocPrefix, "prefix")
if err = rootCmd.RegisterFlagCompletionFunc("lua-prefix", cobra.NoFileCompletions); err != nil {
panic(err)
}
rootCmd.PersistentFlags().StringVar(&yqlib.ConfiguredLuaPreferences.DocSuffix, "lua-suffix", yqlib.ConfiguredLuaPreferences.DocSuffix, "suffix")
if err = rootCmd.RegisterFlagCompletionFunc("lua-suffix", cobra.NoFileCompletions); err != nil {
panic(err)
}
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredLuaPreferences.UnquotedKeys, "lua-unquoted", yqlib.ConfiguredLuaPreferences.UnquotedKeys, "output unquoted string keys (e.g. {foo=\"bar\"})")
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredLuaPreferences.Globals, "lua-globals", yqlib.ConfiguredLuaPreferences.Globals, "output keys as top-level global variables")
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredINIPreferences.PreserveSurroundedQuote, "ini-preserve-quotes", yqlib.ConfiguredINIPreferences.PreserveSurroundedQuote, "preserve surrounding quotes on INI values during round-trip")
rootCmd.PersistentFlags().StringVar(&yqlib.ConfiguredPropertiesPreferences.KeyValueSeparator, "properties-separator", yqlib.ConfiguredPropertiesPreferences.KeyValueSeparator, "separator to use between keys and values")
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredPropertiesPreferences.UseArrayBrackets, "properties-array-brackets", yqlib.ConfiguredPropertiesPreferences.UseArrayBrackets, "use [x] in array paths (e.g. for SpringBoot)")
rootCmd.PersistentFlags().StringVar(&yqlib.ConfiguredShellVariablesPreferences.KeySeparator, "shell-key-separator", yqlib.ConfiguredShellVariablesPreferences.KeySeparator, "separator for shell variable key paths")
if err = rootCmd.RegisterFlagCompletionFunc("shell-key-separator", cobra.NoFileCompletions); err != nil {
panic(err)
}
rootCmd.PersistentFlags().BoolVar(&yqlib.StringInterpolationEnabled, "string-interpolation", yqlib.StringInterpolationEnabled, "Toggles strings interpolation of \\(exp)")
rootCmd.PersistentFlags().BoolVarP(&nullInput, "null-input", "n", false, "Don't read input, simply evaluate the expression given. Useful for creating docs from scratch.")
rootCmd.PersistentFlags().BoolVarP(&outputToJSON, "tojson", "j", false, "output as json. Set indent to 0 to print json in one line.")
rootCmd.PersistentFlags().BoolVarP(&nullInput, "null-input", "n", false, "Don't read input, simply evaluate the expression given. Useful for creating yaml docs from scratch.")
rootCmd.PersistentFlags().BoolVarP(&noDocSeparators, "no-doc", "N", false, "Don't print document separators (---)")
rootCmd.PersistentFlags().IntVarP(&indent, "indent", "I", 2, "sets indent level for output")
if err = rootCmd.RegisterFlagCompletionFunc("indent", cobra.NoFileCompletions); err != nil {
panic(err)
}
rootCmd.Flags().BoolVarP(&version, "version", "V", false, "Print version information and quit")
rootCmd.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the file in place of first file given.")
rootCmd.PersistentFlags().VarP(unwrapScalarFlag, "unwrapScalar", "r", "unwrap scalar, print the value with no quotes, colours or comments. Defaults to true for yaml")
rootCmd.PersistentFlags().Lookup("unwrapScalar").NoOptDefVal = "true"
rootCmd.PersistentFlags().BoolVarP(&nulSepOutput, "nul-output", "0", false, "Use NUL char to separate values. If unwrap scalar is also set, fail if unwrapped scalar contains NUL char.")
rootCmd.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace of first yaml file given.")
rootCmd.PersistentFlags().BoolVarP(&unwrapScalar, "unwrapScalar", "", true, "unwrap scalar, print the value with no quotes, colors or comments")
rootCmd.PersistentFlags().BoolVarP(&prettyPrint, "prettyPrint", "P", false, "pretty print, shorthand for '... style = \"\"'")
rootCmd.PersistentFlags().BoolVarP(&exitStatus, "exit-status", "e", false, "set exit status if there are no matches or null or false is returned")
rootCmd.PersistentFlags().BoolVarP(&forceColor, "colors", "C", false, "force print with colors")
rootCmd.PersistentFlags().BoolVarP(&forceNoColor, "no-colors", "M", forceNoColor, "force print with no colors")
rootCmd.PersistentFlags().StringVarP(&frontMatter, "front-matter", "f", "", "(extract|process) first input as yaml front-matter. Extract will pull out the yaml content, process will run the expression against the yaml content, leaving the remaining data intact")
if err = rootCmd.RegisterFlagCompletionFunc("front-matter", cobra.FixedCompletions([]string{"extract", "process"}, cobra.ShellCompDirectiveNoFileComp)); err != nil {
panic(err)
}
rootCmd.PersistentFlags().StringVarP(&forceExpression, "expression", "", "", "forcibly set the expression argument. Useful when yq argument detection thinks your expression is a file.")
if err = rootCmd.RegisterFlagCompletionFunc("expression", cobra.NoFileCompletions); err != nil {
panic(err)
}
rootCmd.PersistentFlags().BoolVarP(&yqlib.ConfiguredYamlPreferences.LeadingContentPreProcessing, "header-preprocess", "", true, "Slurp any header comments and separators before processing expression.")
rootCmd.PersistentFlags().BoolVarP(&yqlib.ConfiguredYamlPreferences.FixMergeAnchorToSpec, "yaml-fix-merge-anchor-to-spec", "", false, "Fix merge anchor to match YAML spec. Will default to true in late 2025")
rootCmd.PersistentFlags().BoolVarP(&yqlib.ConfiguredYamlPreferences.CompactSequenceIndent, "yaml-compact-seq-indent", "c", false, "Use compact sequence indentation where '- ' is considered part of the indentation.")
rootCmd.PersistentFlags().StringVarP(&splitFileExp, "split-exp", "s", "", "print each result (or doc) into a file named (exp). [exp] argument must return a string. You can use $index in the expression as the result counter. The necessary directories will be created.")
if err = rootCmd.RegisterFlagCompletionFunc("split-exp", cobra.NoFileCompletions); err != nil {
panic(err)
}
rootCmd.PersistentFlags().StringVarP(&splitFileExpFile, "split-exp-file", "", "", "Use a file to specify the split-exp expression.")
if err = rootCmd.MarkPersistentFlagFilename("split-exp-file"); err != nil {
panic(err)
}
rootCmd.PersistentFlags().StringVarP(&expressionFile, "from-file", "", "", "Load expression from specified file.")
if err = rootCmd.MarkPersistentFlagFilename("from-file"); err != nil {
panic(err)
}
rootCmd.PersistentFlags().BoolVarP(&yqlib.ConfiguredSecurityPreferences.DisableEnvOps, "security-disable-env-ops", "", false, "Disable env related operations.")
rootCmd.PersistentFlags().BoolVarP(&yqlib.ConfiguredSecurityPreferences.DisableFileOps, "security-disable-file-ops", "", false, "Disable file related operations (e.g. load)")
rootCmd.PersistentFlags().BoolVarP(&yqlib.ConfiguredSecurityPreferences.EnableSystemOps, "security-enable-system-operator", "", false, "Enable system operator to allow execution of external commands.")
rootCmd.PersistentFlags().BoolVarP(&forceNoColor, "no-colors", "M", false, "force print with no colors")
rootCmd.AddCommand(
createEvaluateSequenceCommand(),
createEvaluateAllCommand(),
-265
View File
@@ -1,265 +0,0 @@
package cmd
import (
"strings"
"testing"
)
func TestNewRuneVar(t *testing.T) {
var r rune
runeVar := newRuneVar(&r)
if runeVar == nil {
t.Fatal("newRuneVar returned nil")
}
}
func TestRuneValue_String(t *testing.T) {
tests := []struct {
name string
runeVal rune
expected string
}{
{
name: "simple character",
runeVal: 'a',
expected: "a",
},
{
name: "special character",
runeVal: '\n',
expected: "\n",
},
{
name: "unicode character",
runeVal: 'ñ',
expected: "ñ",
},
{
name: "zero rune",
runeVal: 0,
expected: string(rune(0)),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
runeVal := runeValue(tt.runeVal)
result := runeVal.String()
if result != tt.expected {
t.Errorf("runeValue.String() = %q, want %q", result, tt.expected)
}
})
}
}
func TestRuneValue_Set(t *testing.T) {
tests := []struct {
name string
input string
expected rune
expectError bool
}{
{
name: "simple character",
input: "a",
expected: 'a',
expectError: false,
},
{
name: "newline escape",
input: "\\n",
expected: '\n',
expectError: false,
},
{
name: "tab escape",
input: "\\t",
expected: '\t',
expectError: false,
},
{
name: "carriage return escape",
input: "\\r",
expected: '\r',
expectError: false,
},
{
name: "form feed escape",
input: "\\f",
expected: '\f',
expectError: false,
},
{
name: "vertical tab escape",
input: "\\v",
expected: '\v',
expectError: false,
},
{
name: "empty string",
input: "",
expected: 0,
expectError: true,
},
{
name: "multiple characters",
input: "ab",
expected: 0,
expectError: true,
},
{
name: "special character",
input: "ñ",
expected: 'ñ',
expectError: true, // This will fail because the Set function checks len(val) != 1
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var r rune
runeVal := newRuneVar(&r)
err := runeVal.Set(tt.input)
if tt.expectError {
if err == nil {
t.Errorf("Expected error for input %q, but got none", tt.input)
}
} else {
if err != nil {
t.Errorf("Unexpected error for input %q: %v", tt.input, err)
}
if r != tt.expected {
t.Errorf("Expected rune %q (%d), got %q (%d)",
string(tt.expected), tt.expected, string(r), r)
}
}
})
}
}
func TestRuneValue_Set_ErrorMessages(t *testing.T) {
tests := []struct {
name string
input string
expectedError string
}{
{
name: "empty string error",
input: "",
expectedError: "[] is not a valid character. Must be length 1 was 0",
},
{
name: "multiple characters error",
input: "abc",
expectedError: "[abc] is not a valid character. Must be length 1 was 3",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var r rune
runeVal := newRuneVar(&r)
err := runeVal.Set(tt.input)
if err == nil {
t.Errorf("Expected error for input %q, but got none", tt.input)
return
}
if !strings.Contains(err.Error(), tt.expectedError) {
t.Errorf("Expected error message to contain %q, got %q",
tt.expectedError, err.Error())
}
})
}
}
func TestRuneValue_Type(t *testing.T) {
var r rune
runeVal := newRuneVar(&r)
result := runeVal.Type()
expected := "char"
if result != expected {
t.Errorf("runeValue.Type() = %q, want %q", result, expected)
}
}
func TestNew(t *testing.T) {
rootCmd := New()
if rootCmd == nil {
t.Fatal("New() returned nil")
return
}
// Test basic command properties
if rootCmd.Use != "yq" {
t.Errorf("Expected Use to be 'yq', got %q", rootCmd.Use)
}
if rootCmd.Short == "" {
t.Error("Expected Short description to be non-empty")
}
if rootCmd.Long == "" {
t.Error("Expected Long description to be non-empty")
}
// Test that the command has the expected subcommands
expectedCommands := []string{"eval", "eval-all", "completion"}
actualCommands := make([]string, 0, len(rootCmd.Commands()))
for _, cmd := range rootCmd.Commands() {
actualCommands = append(actualCommands, cmd.Name())
}
for _, expected := range expectedCommands {
found := false
for _, actual := range actualCommands {
if actual == expected {
found = true
break
}
}
if !found {
t.Errorf("Expected command %q not found in actual commands: %v",
expected, actualCommands)
}
}
}
func TestNew_FlagCompletions(t *testing.T) {
rootCmd := New()
// Test that flag completion functions are registered
// This is a basic smoke test - we can't easily test the actual completion logic
// without more complex setup
flags := []string{
"output-format",
"input-format",
"xml-attribute-prefix",
"xml-content-name",
"xml-proc-inst-prefix",
"xml-directive-name",
"lua-prefix",
"lua-suffix",
"properties-separator",
"indent",
"front-matter",
"expression",
"split-exp",
}
for _, flagName := range flags {
flag := rootCmd.PersistentFlags().Lookup(flagName)
if flag == nil {
t.Errorf("Expected flag %q to exist", flagName)
}
}
}
+10 -11
View File
@@ -7,20 +7,19 @@ import (
)
var completionCmd = &cobra.Command{
Use: "completion [bash|zsh|fish|powershell]",
Aliases: []string{"shell-completion"},
Short: "Generate the autocompletion script for the specified shell",
Use: "shell-completion [bash|zsh|fish|powershell]",
Short: "Generate completion script",
Long: `To load completions:
Bash:
$ source <(yq completion bash)
$ source <(yq shell-completion bash)
# To load completions for each session, execute once:
Linux:
$ yq completion bash > /etc/bash_completion.d/yq
$ yq shell-completion bash > /etc/bash_completion.d/yq
MacOS:
$ yq completion bash > /usr/local/etc/bash_completion.d/yq
$ yq shell-completion bash > /usr/local/etc/bash_completion.d/yq
Zsh:
@@ -30,25 +29,25 @@ Zsh:
$ echo "autoload -U compinit; compinit" >> ~/.zshrc
# To load completions for each session, execute once:
$ yq completion zsh > "${fpath[1]}/_yq"
$ yq shell-completion zsh > "${fpath[1]}/_yq"
# You will need to start a new shell for this setup to take effect.
Fish:
$ yq completion fish | source
$ yq shell-completion fish | source
# To load completions for each session, execute once:
$ yq completion fish > ~/.config/fish/completions/yq.fish
$ yq shell-completion fish > ~/.config/fish/completions/yq.fish
`,
DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
Args: cobra.ExactValidArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
var err error = nil
switch args[0] {
case "bash":
err = cmd.Root().GenBashCompletionV2(os.Stdout, true)
err = cmd.Root().GenBashCompletion(os.Stdout)
case "zsh":
err = cmd.Root().GenZshCompletion(os.Stdout)
case "fish":
-46
View File
@@ -1,46 +0,0 @@
package cmd
import (
"strconv"
"github.com/spf13/pflag"
)
type boolFlag interface {
pflag.Value
IsExplicitlySet() bool
IsSet() bool
}
type unwrapScalarFlagStrc struct {
explicitlySet bool
value bool
}
func newUnwrapFlag() boolFlag {
return &unwrapScalarFlagStrc{value: true}
}
func (f *unwrapScalarFlagStrc) IsExplicitlySet() bool {
return f.explicitlySet
}
func (f *unwrapScalarFlagStrc) IsSet() bool {
return f.value
}
func (f *unwrapScalarFlagStrc) String() string {
return strconv.FormatBool(f.value)
}
func (f *unwrapScalarFlagStrc) Set(value string) error {
v, err := strconv.ParseBool(value)
f.value = v
f.explicitlySet = true
return err
}
func (*unwrapScalarFlagStrc) Type() string {
return "bool"
}
-308
View File
@@ -1,308 +0,0 @@
package cmd
import (
"fmt"
"io"
"log/slog"
"os"
"strings"
"github.com/mikefarah/yq/v4/pkg/yqlib"
"github.com/spf13/cobra"
)
func isAutomaticOutputFormat() bool {
return outputFormat == "" || outputFormat == "auto" || outputFormat == "a"
}
func initCommand(cmd *cobra.Command, args []string) (string, []string, error) {
cmd.SilenceUsage = true
setupColors()
expression, args, err := processArgs(args)
if err != nil {
return "", nil, err
}
if err := loadSplitFileExpression(); err != nil {
return "", nil, err
}
handleBackwardsCompatibility()
if err := validateCommandFlags(args); err != nil {
return "", nil, err
}
if err := configureFormats(args); err != nil {
return "", nil, err
}
configureUnwrapScalar()
return expression, args, nil
}
func setupColors() {
fileInfo, _ := os.Stdout.Stat()
if forceColor || (!forceNoColor && (fileInfo.Mode()&os.ModeCharDevice) != 0) {
colorsEnabled = true
}
}
func loadSplitFileExpression() error {
if splitFileExpFile != "" {
splitExpressionBytes, err := os.ReadFile(splitFileExpFile)
if err != nil {
return err
}
splitFileExp = string(splitExpressionBytes)
}
return nil
}
func handleBackwardsCompatibility() {
// backwards compatibility
if outputToJSON {
outputFormat = "json"
}
}
func validateCommandFlags(args []string) error {
if writeInplace && (len(args) == 0 || args[0] == "-") {
return fmt.Errorf("write in place flag only applicable when giving an expression and at least one file")
}
if frontMatter != "" && len(args) == 0 {
return fmt.Errorf("front matter flag only applicable when giving an expression and at least one file")
}
if writeInplace && splitFileExp != "" {
return fmt.Errorf("write in place cannot be used with split file")
}
if nullInput && len(args) > 0 {
return fmt.Errorf("cannot pass files in when using null-input flag")
}
if indent < 0 {
return fmt.Errorf("indent must not be negative")
}
return nil
}
func configureFormats(args []string) error {
inputFilename := ""
if len(args) > 0 {
inputFilename = args[0]
}
if err := configureInputFormat(inputFilename); err != nil {
return err
}
if err := configureOutputFormat(); err != nil {
return err
}
yqlib.GetLogger().Debugf("Using input format %v", inputFormat)
yqlib.GetLogger().Debugf("Using output format %v", outputFormat)
return nil
}
func configureInputFormat(inputFilename string) error {
if inputFormat == "" || inputFormat == "auto" || inputFormat == "a" {
inputFormat = yqlib.FormatStringFromFilename(inputFilename)
_, err := yqlib.FormatFromString(inputFormat)
if err != nil {
// unknown file type, default to yaml
yqlib.GetLogger().Debugf("Unknown file format extension '%v', defaulting to yaml", inputFormat)
inputFormat = "yaml"
if isAutomaticOutputFormat() {
outputFormat = "yaml"
}
} else if isAutomaticOutputFormat() {
outputFormat = inputFormat
}
} else if isAutomaticOutputFormat() {
// backwards compatibility -
// before this was introduced, `yq -pcsv things.csv`
// would produce *yaml* output.
//
outputFormat = yqlib.FormatStringFromFilename(inputFilename)
if inputFilename != "-" {
yqlib.GetLogger().Warningf("yq default output is now 'auto' (based on the filename extension). Normally yq would output '%v', but for backwards compatibility 'yaml' has been set. Please use -oy to specify yaml, or drop the -p flag.", outputFormat)
}
outputFormat = "yaml"
}
return nil
}
func configureOutputFormat() error {
outputFormatType, err := yqlib.FormatFromString(outputFormat)
if err != nil {
return err
}
if outputFormatType == yqlib.YamlFormat ||
outputFormatType == yqlib.PropertiesFormat {
unwrapScalar = true
}
return nil
}
func configureUnwrapScalar() {
if unwrapScalarFlag.IsExplicitlySet() {
unwrapScalar = unwrapScalarFlag.IsSet()
}
}
func configureDecoder(evaluateTogether bool) (yqlib.Decoder, error) {
format, err := yqlib.FormatFromString(inputFormat)
if err != nil {
return nil, err
}
yqlib.ConfiguredYamlPreferences.EvaluateTogether = evaluateTogether
if format.DecoderFactory == nil {
return nil, fmt.Errorf("no support for %s input format", inputFormat)
}
yqlibDecoder := format.DecoderFactory()
if yqlibDecoder == nil {
return nil, fmt.Errorf("no support for %s input format", inputFormat)
}
return yqlibDecoder, nil
}
func configurePrinterWriter(format *yqlib.Format, out io.Writer) (yqlib.PrinterWriter, error) {
var printerWriter yqlib.PrinterWriter
if splitFileExp != "" {
colorsEnabled = forceColor
splitExp, err := yqlib.ExpressionParser.ParseExpression(splitFileExp)
if err != nil {
return nil, fmt.Errorf("bad split document expression: %w", err)
}
printerWriter = yqlib.NewMultiPrinterWriter(splitExp, format)
} else {
printerWriter = yqlib.NewSinglePrinterWriter(out)
}
return printerWriter, nil
}
func configureEncoder() (yqlib.Encoder, error) {
yqlibOutputFormat, err := yqlib.FormatFromString(outputFormat)
if err != nil {
return nil, err
}
yqlib.ConfiguredXMLPreferences.Indent = indent
yqlib.ConfiguredYamlPreferences.Indent = indent
yqlib.ConfiguredKYamlPreferences.Indent = indent
yqlib.ConfiguredJSONPreferences.Indent = indent
yqlib.ConfiguredYamlPreferences.UnwrapScalar = unwrapScalar
yqlib.ConfiguredKYamlPreferences.UnwrapScalar = unwrapScalar
yqlib.ConfiguredPropertiesPreferences.UnwrapScalar = unwrapScalar
yqlib.ConfiguredJSONPreferences.UnwrapScalar = unwrapScalar
yqlib.ConfiguredShellVariablesPreferences.UnwrapScalar = unwrapScalar
yqlib.ConfiguredYamlPreferences.ColorsEnabled = colorsEnabled
yqlib.ConfiguredKYamlPreferences.ColorsEnabled = colorsEnabled
yqlib.ConfiguredJSONPreferences.ColorsEnabled = colorsEnabled
yqlib.ConfiguredHclPreferences.ColorsEnabled = colorsEnabled
yqlib.ConfiguredTomlPreferences.ColorsEnabled = colorsEnabled
yqlib.ConfiguredYamlPreferences.PrintDocSeparators = !noDocSeparators
yqlib.ConfiguredKYamlPreferences.PrintDocSeparators = !noDocSeparators
encoder := yqlibOutputFormat.EncoderFactory()
if encoder == nil {
return nil, fmt.Errorf("no support for %s output format", outputFormat)
}
return encoder, err
}
// this is a hack to enable backwards compatibility with githubactions (which pipe /dev/null into everything)
// and being able to call yq with the filename as a single parameter
//
// without this - yq detects there is stdin (thanks githubactions),
// then tries to parse the filename as an expression
func maybeFile(str string) bool {
yqlib.GetLogger().Debugf("checking '%v' is a file", str)
stat, err := os.Stat(str) // #nosec
result := err == nil && !stat.IsDir()
if yqlib.GetLogger().IsEnabledFor(slog.LevelDebug) {
if err != nil {
yqlib.GetLogger().Debugf("error: %v", err)
} else {
yqlib.GetLogger().Debugf("error: %v, dir: %v", err, stat.IsDir())
}
yqlib.GetLogger().Debugf("result: %v", result)
}
return result
}
func processStdInArgs(args []string) []string {
stat, err := os.Stdin.Stat()
if err != nil {
yqlib.GetLogger().Debugf("error getting stdin: %v", err)
}
pipingStdin := stat != nil && (stat.Mode()&os.ModeCharDevice) == 0
// if we've been given a file, don't automatically
// read from stdin.
// this happens if there is more than one argument
// or only one argument and its a file
if nullInput || !pipingStdin || len(args) > 1 || (len(args) > 0 && maybeFile(args[0])) {
return args
}
for _, arg := range args {
if arg == "-" {
return args
}
}
yqlib.GetLogger().Debugf("missing '-', adding it to the end")
// we're piping from stdin, but there's no '-' arg
// lets add one to the end
return append(args, "-")
}
func processArgs(originalArgs []string) (string, []string, error) {
expression := forceExpression
args := processStdInArgs(originalArgs)
maybeFirstArgIsAFile := len(args) > 0 && maybeFile(args[0])
if expressionFile == "" && maybeFirstArgIsAFile && strings.HasSuffix(args[0], ".yq") {
// lets check if an expression file was given
yqlib.GetLogger().Debugf("Assuming arg %v is an expression file", args[0])
expressionFile = args[0]
args = args[1:]
}
if expressionFile != "" {
expressionBytes, err := os.ReadFile(expressionFile)
if err != nil {
return "", nil, err
}
//replace \r\n (windows) with good ol' unix file endings.
expression = strings.ReplaceAll(string(expressionBytes), "\r\n", "\n")
}
yqlib.GetLogger().Debugf("processed args: %v", args)
if expression == "" && len(args) > 0 && args[0] != "-" && !maybeFile(args[0]) {
yqlib.GetLogger().Debugf("assuming expression is '%v'", args[0])
expression = args[0]
args = args[1:]
}
return expression, args, nil
}
-1460
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -11,7 +11,7 @@ var (
GitDescribe string
// Version is main version number that is being run at the moment.
Version = "v4.53.4"
Version = "4.5.0"
// VersionPrerelease is a pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
@@ -25,7 +25,7 @@ const ProductName = "yq"
// GetVersionDisplay composes the parts of the version in a way that's suitable
// for displaying to humans.
func GetVersionDisplay() string {
return fmt.Sprintf("yq (https://github.com/mikefarah/yq/) version %s\n", getHumanVersion())
return fmt.Sprintf("%s version %s\n", ProductName, getHumanVersion())
}
func getHumanVersion() string {
@@ -45,5 +45,5 @@ func getHumanVersion() string {
}
// Strip off any single quotes added by the git information.
return strings.ReplaceAll(version, "'", "")
return strings.Replace(version, "'", "", -1)
}
+2 -132
View File
@@ -1,12 +1,9 @@
package cmd
import (
"strings"
"testing"
)
import "testing"
func TestGetVersionDisplay(t *testing.T) {
var expectedVersion = ProductName + " (https://github.com/mikefarah/yq/) version " + Version
var expectedVersion = ProductName + " version " + Version
if VersionPrerelease != "" {
expectedVersion = expectedVersion + "-" + VersionPrerelease
}
@@ -28,18 +25,6 @@ func TestGetVersionDisplay(t *testing.T) {
}
func Test_getHumanVersion(t *testing.T) {
// Save original values
origGitDescribe := GitDescribe
origGitCommit := GitCommit
origVersionPrerelease := VersionPrerelease
// Restore after test
defer func() {
GitDescribe = origGitDescribe
GitCommit = origGitCommit
VersionPrerelease = origVersionPrerelease
}()
GitDescribe = "e42813d"
GitCommit = "e42813d+CHANGES"
var wanted string
@@ -64,118 +49,3 @@ func Test_getHumanVersion(t *testing.T) {
}
}
}
func Test_getHumanVersion_NoGitDescribe(t *testing.T) {
// Save original values
origGitDescribe := GitDescribe
origGitCommit := GitCommit
origVersionPrerelease := VersionPrerelease
// Restore after test
defer func() {
GitDescribe = origGitDescribe
GitCommit = origGitCommit
VersionPrerelease = origVersionPrerelease
}()
GitDescribe = ""
GitCommit = ""
VersionPrerelease = ""
got := getHumanVersion()
if got != Version {
t.Errorf("getHumanVersion() = %v, want %v", got, Version)
}
}
func Test_getHumanVersion_WithPrerelease(t *testing.T) {
// Save original values
origGitDescribe := GitDescribe
origGitCommit := GitCommit
origVersionPrerelease := VersionPrerelease
// Restore after test
defer func() {
GitDescribe = origGitDescribe
GitCommit = origGitCommit
VersionPrerelease = origVersionPrerelease
}()
GitDescribe = ""
GitCommit = "abc123"
VersionPrerelease = "beta"
got := getHumanVersion()
expected := Version + "-beta (abc123)"
if got != expected {
t.Errorf("getHumanVersion() = %v, want %v", got, expected)
}
}
func Test_getHumanVersion_PrereleaseInVersion(t *testing.T) {
// Save original values
origGitDescribe := GitDescribe
origGitCommit := GitCommit
origVersionPrerelease := VersionPrerelease
// Restore after test
defer func() {
GitDescribe = origGitDescribe
GitCommit = origGitCommit
VersionPrerelease = origVersionPrerelease
}()
GitDescribe = "v1.2.3-rc1"
GitCommit = "xyz789"
VersionPrerelease = "rc1"
got := getHumanVersion()
// Should not duplicate "rc1" since it's already in GitDescribe
expected := "v1.2.3-rc1 (xyz789)"
if got != expected {
t.Errorf("getHumanVersion() = %v, want %v", got, expected)
}
}
func Test_getHumanVersion_StripSingleQuotes(t *testing.T) {
// Save original values
origGitDescribe := GitDescribe
origGitCommit := GitCommit
origVersionPrerelease := VersionPrerelease
// Restore after test
defer func() {
GitDescribe = origGitDescribe
GitCommit = origGitCommit
VersionPrerelease = origVersionPrerelease
}()
GitDescribe = "'v1.2.3'"
GitCommit = "'commit123'"
VersionPrerelease = ""
got := getHumanVersion()
// Should strip single quotes
if strings.Contains(got, "'") {
t.Errorf("getHumanVersion() = %v, should not contain single quotes", got)
}
expected := "v1.2.3"
if got != expected {
t.Errorf("getHumanVersion() = %v, want %v", got, expected)
}
}
func TestProductName(t *testing.T) {
if ProductName != "yq" {
t.Errorf("ProductName = %v, want yq", ProductName)
}
}
func TestVersionIsSet(t *testing.T) {
if Version == "" {
t.Error("Version should not be empty")
}
if !strings.HasPrefix(Version, "v") {
t.Errorf("Version %v should start with 'v'", Version)
}
}
+76
View File
@@ -0,0 +1,76 @@
yq (3.3.2) focal; urgency=medium
* Bug fix: existStatus bug (#459)
* Automatically makes a os temp directory if it does not exist (#461)
-- Roberto Mier Escandon <rmescandon@gmail.com> Fri, 07 Aug 2020 18:53:01 +0200
yq (3.3-0) focal; urgency=medium
* You can control string styles (quotes) using the new --style flag
* String values now always have quotes when outputting to json
* Negative array indices now traverse the array backwards
* Added a --stripComments flag to print yaml without any comments
* Bumped go to version 1.14
-- Roberto Mier Escandon <rmescandon@gmail.com> Thu, 30 Apr 2020 20:45:44 +0200
yq (3.1-2) eoan; urgency=medium
* Bug fix: yq 3 was removing empty inline-style objects and arrays (#355)
* Bug fix: Merge option returned different output when switching order of
merging files(#347)
* Bug fix: Add new object to existing array object was failing in 3.1.1 (#361)
* Bug fix: yq 3 empty keys did not allow merging of values (#356)
* Bug fix: keys quoted during merge (#363)
* Bug fix: Correct length with wc -l (#362)
* Bug fix: Write to empty document removed path (#359)
-- Roberto Mier Escandon <rmescandon@gmail.com> Mon, 24 Feb 2020 20:31:58 +0100
yq (3.1-1) eoan; urgency=medium
* Keeps yaml comments and formatting, can specify yaml tags when updating.
* Handles anchors
* Can print out matching paths and values when splatting
* JSON output works for all commands
* Yaml files with multiple documents are printed out as one JSON
document per line.
* Deep splat (**) to match arbitrary paths
* Update scripts file format has changed to be more powerful
* Reading and splatting, matching results are printed once per line
* Bugfixing
-- Roberto Mier Escandon <rmescandon@gmail.com> Tue, 11 Feb 2020 22:18:24 +0100
yq (2.2-1) bionic; urgency=medium
* Added Windows support for the "--inplace" command flag
* Prefix now supports arrays
* Add prefix command
* Bump Alpine version to 3.8
* Improved docker build process
* Lint fixes
* Build support for all linux architectures supported by gox
-- Roberto Mier Escandon <rmescandon@gmail.com> Sat, 19 Jan 2019 15:50:47 +0100
yq (2.1-0) bionic; urgency=medium
* Ability to read multiple documents in a single file
* Ability to append list items instead of overwriting
-- Roberto Mier Escandón <rmescandon@gmail.com> Tue, 10 Jul 2018 14:02:42 +0200
yq (2.0-0) bionic; urgency=medium
* Release 2.0.0
-- Roberto Mier Escandón <rmescandon@gmail.com> Wed, 20 Jun 2018 10:29:53 +0200
yq (1.15-0) bionic; urgency=medium
* Release 1.15
-- Roberto Mier Escandón <rmescandon@gmail.com> Wed, 06 Jun 2018 11:32:03 +0200
+1
View File
@@ -0,0 +1 @@
10
+22
View File
@@ -0,0 +1,22 @@
Source: yq
Section: devel
Priority: optional
Maintainer: Roberto Mier Escandón <rmescandon@gmail.com>
Build-Depends: debhelper (>=10),
dh-golang (>=1.34),
golang-1.13,
rsync
Standards-Version: 4.1.4
Homepage: https://github.com/mikefarah/yq.git
Vcs-Browser: https://github.com/mikefarah/yq.git
Vcs-Git: https://github.com/mikefarah/yq.git
XS-Go-Import-Path: github.com/mikefarah/yq
XSBC-Original-Maintainer: Roberto Mier Escandón <rmescandon@gmail.com>
Package: yq
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: lightweight and portable command-line YAML processor
.
The aim of the project is to be the
[jq](https://github.com/stedolan/jq) or sed of yaml files.
+24
View File
@@ -0,0 +1,24 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: yq
Source: https://github.com/mikefarah/yq.git
Files: *
Copyright: 2017 Mike Farah
License: Expat
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+2
View File
@@ -0,0 +1,2 @@
[DEFAULT]
pristine-tar = True
Vendored Executable
+57
View File
@@ -0,0 +1,57 @@
#!/usr/bin/make -f
#
# Copyright (C) 2018 Roberto Mier Escandón <rmescandon@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
PROJECT := yq
OWNER := mikefarah
REPO := github.com
GOVERSION := 1.13
export DH_OPTIONS
export DH_GOPKG := ${REPO}/${OWNER}/${PROJECT}
export GOROOT := /usr/lib/go-${GOVERSION}
export GOPATH := ${CURDIR}/_build
export GOBIN := ${GOPATH}/bin
export PATH := ${GOROOT}/bin:${GOBIN}:${PATH}
export GOCACHE := /tmp/gocache
export GOFLAGS := -mod=vendor
SRCDIR := ${GOPATH}/src/${DH_GOPKG}
DESTDIR := ${CURDIR}/debian/${PROJECT}
BINDIR := /usr/bin
ASSETSDIR := /usr/share/${PROJECT}
%:
dh $@ --builddirectory=${GOPATH} --buildsystem=golang
override_dh_auto_build:
mkdir -p ${SRCDIR}
mkdir -p ${GOBIN}
# copy project to local srcdir to build from there
rsync -avz --progress --exclude=_build --exclude=debian --exclude=tmp. --exclude=go.mod --exclude=docs . $(SRCDIR)
# build go code
(cd ${SRCDIR} && go install -buildmode=pie ./...)
override_dh_auto_test:
(cd ${SRCDIR} && go test -v ./...)
override_dh_auto_install:
cp ${GOBIN}/yq ${DESTDIR}/${BINDIR}
cp -f ${SRCDIR}/LICENSE ${DESTDIR}/${ASSETSDIR}
chmod a+x ${DESTDIR}/${BINDIR}/yq
override_dh_auto_clean:
dh_clean
rm -rf ${CURDIR}/_build
+1
View File
@@ -0,0 +1 @@
3.0 (native)
+3
View File
@@ -0,0 +1,3 @@
usr/bin
usr/share/yq
usr/share/man/man1
+11 -2
View File
@@ -1,2 +1,11 @@
- [cat, dog, frog, cow]
- [apple, banana, grape, mango]
---
- become: true
gather_facts: false
hosts: lalaland
name: "Apply smth"
roles:
- lala
- land
serial: 1
- become: false
gather_facts: true
-1
View File
@@ -1 +0,0 @@
bXkgc2VjcmV0IGNoaWxsaSByZWNpcGUgaXMuLi4u
-9
View File
@@ -1,9 +0,0 @@
return {
["country"] = "Australia"; -- this place
["cities"] = {
"Sydney",
"Melbourne",
"Brisbane",
"Perth",
};
};
+6 -1
View File
@@ -1 +1,6 @@
a: apple
a:
key1: "value1"
key2: 2.6
ab:
key1: 6
key2: "h"
+7 -5
View File
@@ -1,5 +1,7 @@
# --------------------------------------------------
# It's a test with comment
# --------------------------------------------------
groups:
- name: d
a: other # better than the original
b: [3, 4]
c:
toast: leave
test: 1
tell: 1
tasty.taco: cool
View File
+2 -1
View File
@@ -1 +1,2 @@
# comment
# a: apple
# b: cat
-5
View File
@@ -1,5 +0,0 @@
#! yq
.[] |(
( select(kind == "scalar") | key + "='" + . + "'"),
( select(kind == "seq") | key + "=(" + (map("'" + . + "'") | join(",")) + ")")
)
-6
View File
@@ -1,6 +0,0 @@
# comments on values appear
person.name = Mike
# comments on array values appear
person.pets.0 = cat
person.food.0 = pizza
-6
View File
@@ -1,6 +0,0 @@
---
a: apple
b: banana
---
hello there
apples: great
-10
View File
@@ -1,10 +0,0 @@
# leading
{
a: 1, # a line
# head b
b: 2,
c: [
# head d
"d", # d line
],
}
-7
View File
@@ -1,7 +0,0 @@
# leading
a: 1 # a line
# head b
b: 2
c:
# head d
- d # d line
-2
View File
@@ -1,2 +0,0 @@
---
a: test
-1
View File
@@ -1 +0,0 @@
<zoo><thing><frog>boing</frog></thing></zoo>
-11
View File
@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- osm-->
<osm version="0.6" generator="CGImap 0.0.2">
<!-- bounds-->
<bounds minlat="54.0889580" minlon="12.2487570" maxlat="54.0913900" maxlon="12.2524800">
<!-- great -->
cool
</bounds>
<foo>ba2234r</foo>
<foo>bar2234233</foo>
</osm>
-8
View File
@@ -1,8 +0,0 @@
# Arithmetic with literals and application-provided variables
sum = 1 + addend
# String interpolation and templates
message = "Hello, ${name}!"
# Application-provided functions
shouty_message = upper(message)
-14
View File
@@ -1,14 +0,0 @@
; This is a INI document
[owner]
name = "Tom Preston-Werner"
dob = 1979-05-27T07:32:00-08:00
[database]
db_host = "localhost"
db_port = 5432
db_user = "postgres"
db_password = ""
db_name = "postgres"
-27
View File
@@ -1,27 +0,0 @@
# main.tf
# Define required providers and minimum Terraform version
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
required_version = ">= 1.2"
}
# Configure the AWS provider
provider "aws" {
region = var.aws_region
}
# Define an S3 bucket resource
resource "aws_s3_bucket" "example_bucket" {
bucket = var.bucket_name
tags = {
Environment = "Development"
Project = "TerraformExample"
}
}
-26
View File
@@ -1,26 +0,0 @@
# This is a TOML document
title = "TOML Example"
[owner]
name = "Tom Preston-Werner"
dob = 1979-05-27T07:32:00-08:00
[database]
enabled = true
ports = [ 8000, 8001, 8002 ]
data = [ ["delta", "phi"], [3.14] ]
temp_targets = { cpu = 79.5, case = 72.0 }
[servers]
[servers.alpha]
ip = "10.0.0.1"
role = "frontend"
[servers.beta]
ip = "10.0.0.2"
role = "backend"
+11 -2
View File
@@ -1,2 +1,11 @@
# things
a: apple
# Some doc
a: true
b:
c: 2
d: [3, 4, 5]
e:
- name: fred
value: 3
- name: sam
value: 4
-8
View File
@@ -1,8 +0,0 @@
# Arithmetic with literals and application-provided variables
sum = 1 + addend
# String interpolation and templates
message = "Hello, ${name}!"
# Application-provided functions
shouty_message = upper(message)
-8
View File
@@ -1,8 +0,0 @@
; This is a INI document
name = "Tom Preston-Werner"
dob = 1979-05-27T07:32:00-08:00
enabled = true
ip = "10.0.0.1"
role = "frontend"
treads = 4
-3
View File
@@ -1,3 +0,0 @@
name,numberOfCats,likesApples,height
,1,true,168.8
Samantha's Rabbit,2,false,-188.8
1 name numberOfCats likesApples height
2 1 true 168.8
3 Samantha's Rabbit 2 false -188.8
-1
View File
@@ -1 +0,0 @@
this.is = a properties file
-1
View File
@@ -1 +0,0 @@
<this>is some xml</this>
-4
View File
@@ -1,4 +0,0 @@
---
# comment
# about things
a: cat
-2
View File
@@ -1,2 +0,0 @@
a: apple is included
b: cool.
+1 -5
View File
@@ -1,9 +1,5 @@
FROM mikefarah/yq:4@sha256:11a1f0b604b13dbbdc662260d8db6f644b22d8553122a25c1b5b2e8713ca6977
FROM mikefarah/yq:4.5.0
COPY entrypoint.sh /entrypoint.sh
# github action recommendation is to run as root.
# https://docs.github.com/en/actions/creating-actions/dockerfile-support-for-github-actions#user
USER root
ENTRYPOINT ["/entrypoint.sh"]
+3 -11
View File
@@ -1,12 +1,4 @@
#!/bin/sh -l
set -e
echo "::debug::\$cmd: $1"
RESULT=$(eval "$1")
echo "::debug::\$RESULT: $RESULT"
# updating from
# https://github.com/orgs/community/discussions/26288#discussioncomment-3876281
# https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-output-parameter
delimiter=$(cat /proc/sys/kernel/random/uuid)
echo "result<<${delimiter}" >> "${GITHUB_OUTPUT}"
echo "${RESULT}" >> "${GITHUB_OUTPUT}"
echo "${delimiter}" >> "${GITHUB_OUTPUT}"
echo "$1"
eval $1
+11 -37
View File
@@ -1,42 +1,16 @@
module github.com/mikefarah/yq/v4
require (
github.com/a8m/envsubst v1.4.3
github.com/alecthomas/participle/v2 v2.1.4
github.com/alecthomas/repr v0.5.4
github.com/dimchansky/utfbom v1.1.1
github.com/elliotchance/orderedmap v1.8.0
github.com/fatih/color v1.19.0
github.com/go-ini/ini v1.67.0
github.com/goccy/go-json v0.10.6
github.com/goccy/go-yaml v1.19.2
github.com/hashicorp/hcl/v2 v2.24.0
github.com/jinzhu/copier v0.4.0
github.com/magiconair/properties v1.18.11
github.com/pelletier/go-toml/v2 v2.4.3
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e
github.com/spf13/cobra v1.10.2
github.com/spf13/pflag v1.0.10
github.com/yuin/gopher-lua v1.1.2
github.com/zclconf/go-cty v1.19.0
go.yaml.in/yaml/v4 v4.0.0-rc.6
golang.org/x/mod v0.38.0
golang.org/x/net v0.57.0
golang.org/x/text v0.40.0
github.com/elliotchance/orderedmap v1.3.0
github.com/fatih/color v1.10.0
github.com/goccy/go-yaml v1.8.4
github.com/jinzhu/copier v0.1.0
github.com/spf13/cobra v1.1.1
github.com/timtadh/data-structures v0.5.3 // indirect
github.com/timtadh/lexmachine v0.2.2
golang.org/x/sys v0.0.0-20210105210732-16f7687f5001 // indirect
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
)
require (
github.com/agext/levenshtein v1.2.1 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/apparentlymart/go-textseg/v17 v17.0.1 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
golang.org/x/sync v0.22.0 // indirect
golang.org/x/sys v0.47.0 // indirect
golang.org/x/tools v0.47.0 // indirect
)
go 1.25.0
go 1.15

Some files were not shown because too many files have changed in this diff Show More