Skip to content

kubecfg/lensed

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

68 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

lensed

Edit complex files in place

By default edits JSON/YAML files, but supports more formats with "lenses".

Lenses can be nested. You can in-place "edit" a json field encoded as a string in a TOML file that in turn is embedded in a string literal of a YAML file.

Install

Homebrew

brew install kubecfg/lensed/lensed

From sources

go install github.com/kubecfg/lensed@latest

Usage

Let's start with a field replacement in YAML files.

Basics

It can operate in a pipeline:

$ cat >example.yaml <<EOF
foo:
  bar: old
EOF
$ lensed set /foo/bar=new <example.yaml
foo:
  bar: new

or modify files in place (like `sed -i``):

$ cat >example.yaml <<EOF
foo:
  bar: old
EOF
$ lensed set /foo/bar=new -f example.yaml
$ cat example.yaml
foo:
  bar: new

Preserves formatting

It preserves comments:

$ cat >example.yaml <<EOF
foo:
  bar: old # comment preserved
EOF
$ lensed set /foo/bar=new <example.yaml
foo:
  bar: new # comment preserved

and other stylistic choices from the source file:

$ cat >example.yaml <<EOF
foo:
  bar: 'old'
EOF
$ lensed set /foo/bar=new <example.yaml
foo:
  bar: 'new'
$ cat >example.yaml <<EOF
foo:
  bar: "old"
EOF
$ lensed set /foo/bar=new <example.yaml
foo:
  bar: "new"

Lenses (file formats)

Once you reached a string field, you can "dig deeper" and interpret the content of that string as another hierarchical text format, and address other fields inside that inner string:

$ cat >example.yaml <<EOF                  
foo: |
  [bar]
  baz = "old"
EOF
$ lensed set "/foo/~(toml)/bar/baz=new" <example.yaml  
foo: |
  [bar]
  baz = "new"

The edits in the inner text are properly escaped back.

$ cat >example.yaml <<EOF                      
foo: "bar = \"I'm old\""
EOF
$ lensed set "/foo/~(toml)/bar=I'm \"quoted\"" -f example.yaml  
$ cat example.yaml
foo: "bar = \"I'm \\\"quoted\\\"\""
$ lensed get "/foo/~(toml)/bar" < example.yaml
I'm "quoted"

Supported lenses:

  • yaml (also json)
  • yamls (yaml stream)
  • toml
  • base64
  • line
  • regexp
  • oci (alias ociImageRef)
  • jsonnet

Base64

Decoding a base64 field from a YAML file is not hard:

$ cat >example.yaml <<EOF
apiVersion: v1
kind: Secret
data:
  foo: eyJwYXNzd29yZCI6ICAgICAiaHVudGVyMTIifQ==
EOF
$ lensed get "/data/foo/~(base64)" <example.yaml                               
{"password":     "hunter12"}

But editing a field inside of a base64 encoded JSON inside a YAML file, it's a breeze with lensed:

$ lensed set "/data/foo/~(base64)/~(yaml)/password=newpassword" -f example.yaml
$ cat example.yaml
apiVersion: v1
kind: Secret
data:
  foo: eyJwYXNzd29yZCI6ICAgICAibmV3cGFzc3dvcmQifQ==
$ lensed get "/data/foo/~(base64)" <example.yaml                               
{"password":     "newpassword"}

(notice how the original formatting of the embedded value is preserved, despite the various decoding/encoding roundtrips)

Jsonnet

This example also shows that you can edit a top-level format that is not YAML. All you need to do is to start the JSONPointer with ~(<lensname>):

$ cat >example.yaml <<EOF
{
  foo+: {
    bar: "old",
  }
}
EOF
$ lensed get "~(jsonnet)/foo/bar" <example.yaml 
old
$ lensed set "~(jsonnet)/foo/bar=new" <example.yaml
{
  foo+: {
    bar: "new",
  }
}

It can also edit the urls inside imports:

$ cat >example.yaml <<EOF
{
  foo+: importstr "old.yaml",
}
EOF
$ lensed get "~(jsonnet)/foo/~file" <example.yaml 
old
$ lensed set "~(jsonnet)/foo/~file=new.yaml" <example.yaml
{
  foo+: importstr "new.yaml",
}

Limitations

It can only handle leaf nodes. i.e. you can't replace a yaml subtree with another yaml subtree