Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package dyff for openSUSE:Factory checked in 
at 2026-03-02 17:39:45
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/dyff (Old)
 and      /work/SRC/openSUSE:Factory/.dyff.new.29461 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "dyff"

Mon Mar  2 17:39:45 2026 rev:18 rq:1335705 version:1.11.2

Changes:
--------
--- /work/SRC/openSUSE:Factory/dyff/dyff.changes        2026-02-25 
21:11:26.572328959 +0100
+++ /work/SRC/openSUSE:Factory/.dyff.new.29461/dyff.changes     2026-03-02 
17:39:51.942201330 +0100
@@ -1,0 +2,17 @@
+Mon Mar 02 06:43:50 UTC 2026 - Johannes Kastl 
<[email protected]>
+
+- Update to version 1.11.2:
+  * Fix incorrect diff for mixing types in a list by @HeavyWombat
+    in #613
+- Update to version 1.11.1:
+  * Fix grab issue with non-standard identifier by @HeavyWombat in
+    #612
+- Update to version 1.11.0:
+  * Tidy up go.mod by @HeavyWombat in #608
+  * build(deps): bump goreleaser/goreleaser-action from 6 to 7 by
+    @dependabot[bot] in #609
+  * build(deps): bump golang.org/x/net from 0.50.0 to 0.51.0 by
+    @dependabot[bot] in #610
+  * Introduce format strings feature by @HeavyWombat in #611
+
+-------------------------------------------------------------------

Old:
----
  dyff-1.10.5.obscpio

New:
----
  dyff-1.11.2.obscpio

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ dyff.spec ++++++
--- /var/tmp/diff_new_pack.1uavjC/_old  2026-03-02 17:39:52.922242235 +0100
+++ /var/tmp/diff_new_pack.1uavjC/_new  2026-03-02 17:39:52.926242402 +0100
@@ -17,14 +17,14 @@
 
 
 Name:           dyff
-Version:        1.10.5
+Version:        1.11.2
 Release:        0
 Summary:        Diff tool for YAML files, and sometimes JSON
 License:        MIT
 URL:            https://github.com/homeport/dyff
 Source:         dyff-%{version}.tar.gz
 Source1:        vendor.tar.gz
-BuildRequires:  go >= 1.23
+BuildRequires:  golang(API) >= 1.25
 
 %description
 A diff tool for YAML files, and sometimes JSON.

++++++ _service ++++++
--- /var/tmp/diff_new_pack.1uavjC/_old  2026-03-02 17:39:52.962243905 +0100
+++ /var/tmp/diff_new_pack.1uavjC/_new  2026-03-02 17:39:52.978244572 +0100
@@ -3,7 +3,7 @@
     <param name="url">https://github.com/homeport/dyff</param>
     <param name="scm">git</param>
     <param name="exclude">.git</param>
-    <param name="revision">v1.10.5</param>
+    <param name="revision">v1.11.2</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="versionrewrite-pattern">v(.*)</param>
     <param name="changesgenerate">enable</param>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.1uavjC/_old  2026-03-02 17:39:52.998245407 +0100
+++ /var/tmp/diff_new_pack.1uavjC/_new  2026-03-02 17:39:53.002245574 +0100
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param name="url">https://github.com/homeport/dyff</param>
-              <param 
name="changesrevision">0c7ba9461bf06f54f9527d9945c3f879efc76770</param></service></servicedata>
+              <param 
name="changesrevision">6c36ce0d21d83f307a4e76ee810d9657947e3cf9</param></service></servicedata>
 (No newline at EOF)
 

++++++ dyff-1.10.5.obscpio -> dyff-1.11.2.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dyff-1.10.5/.docs/commands/dyff_between.md 
new/dyff-1.11.2/.docs/commands/dyff_between.md
--- old/dyff-1.10.5/.docs/commands/dyff_between.md      2026-02-22 
13:42:39.000000000 +0100
+++ new/dyff-1.11.2/.docs/commands/dyff_between.md      2026-03-01 
17:08:27.000000000 +0100
@@ -16,30 +16,31 @@
 ### Options
 
 ```
+      --swap                                Swap 'from' and 'to' for comparison
+      --chroot string                       change the root level of the input 
file to another point in the document
+      --chroot-of-from string               only change the root level of the 
from input file
+      --chroot-of-to string                 only change the root level of the 
to input file
+      --chroot-list-to-documents            in case the change root points to 
a list, treat this list as a set of documents and not as the list itself
+  -o, --output string                       specify the output style, 
supported styles: human, brief, github, gitlab, gitea (default "human")
+      --use-indent-lines                    use indent lines in the output
   -i, --ignore-order-changes                ignore order changes in lists
       --ignore-whitespace-changes           ignore leading or trailing 
whitespace changes
+  -v, --ignore-value-changes                exclude changes in values
+      --detect-renames                      enable detection for renames 
(document level for Kubernetes resources) (default true)
+      --format-strings                      format strings (i.e. inline JSON) 
before comparison to avoid formatting differences (default true)
+  -l, --no-table-style                      do not place blocks next to each 
other, always use one row per text block
+  -x, --no-cert-inspection                  disable x509 certificate 
inspection, compare as raw text
+  -g, --use-go-patch-style                  use Go-Patch style paths in outputs
+      --minor-change-threshold float        minor change threshold (default 
0.1)
+      --multi-line-context-lines int        multi-line context lines (default 
10)
       --detect-kubernetes                   detect kubernetes entities 
(default true)
       --additional-identifier stringArray   use additional identifier 
candidates in named entry lists
       --filter strings                      filter reports to a subset of 
differences based on supplied arguments
       --exclude strings                     exclude reports from a set of 
differences based on supplied arguments
       --filter-regexp strings               filter reports to a subset of 
differences based on supplied regular expressions
       --exclude-regexp strings              exclude reports from a set of 
differences based on supplied regular expressions
-  -v, --ignore-value-changes                exclude changes in values
-      --detect-renames                      enable detection for renames 
(document level for Kubernetes resources) (default true)
-  -o, --output string                       specify the output style, 
supported styles: human, brief, github, gitlab, gitea (default "human")
-      --use-indent-lines                    use indent lines in the output 
(default true)
   -b, --omit-header                         omit the dyff summary header
   -s, --set-exit-code                       set program exit code, with 0 
meaning no difference, 1 for differences detected, and 255 for program error
-  -l, --no-table-style                      do not place blocks next to each 
other, always use one row per text block
-  -x, --no-cert-inspection                  disable x509 certificate 
inspection, compare as raw text
-  -g, --use-go-patch-style                  use Go-Patch style paths in outputs
-      --minor-change-threshold float        minor change threshold (default 
0.1)
-      --multi-line-context-lines int        multi-line context lines (default 
4)
-      --swap                                Swap 'from' and 'to' for comparison
-      --chroot string                       change the root level of the input 
file to another point in the document
-      --chroot-of-from string               only change the root level of the 
from input file
-      --chroot-of-to string                 only change the root level of the 
to input file
-      --chroot-list-to-documents            in case the change root points to 
a list, treat this list as a set of documents and not as the list itself
   -h, --help                                help for between
 ```
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dyff-1.10.5/.docs/commands/dyff_last-applied.md 
new/dyff-1.11.2/.docs/commands/dyff_last-applied.md
--- old/dyff-1.10.5/.docs/commands/dyff_last-applied.md 2026-02-22 
13:42:39.000000000 +0100
+++ new/dyff-1.11.2/.docs/commands/dyff_last-applied.md 2026-03-01 
17:08:27.000000000 +0100
@@ -17,25 +17,26 @@
 ### Options
 
 ```
+  -o, --output string                       specify the output style, 
supported styles: human, brief, github, gitlab, gitea (default "human")
+      --use-indent-lines                    use indent lines in the output
   -i, --ignore-order-changes                ignore order changes in lists
       --ignore-whitespace-changes           ignore leading or trailing 
whitespace changes
+  -v, --ignore-value-changes                exclude changes in values
+      --detect-renames                      enable detection for renames 
(document level for Kubernetes resources) (default true)
+      --format-strings                      format strings (i.e. inline JSON) 
before comparison to avoid formatting differences (default true)
+  -l, --no-table-style                      do not place blocks next to each 
other, always use one row per text block
+  -x, --no-cert-inspection                  disable x509 certificate 
inspection, compare as raw text
+  -g, --use-go-patch-style                  use Go-Patch style paths in outputs
+      --minor-change-threshold float        minor change threshold (default 
0.1)
+      --multi-line-context-lines int        multi-line context lines (default 
10)
       --detect-kubernetes                   detect kubernetes entities 
(default true)
       --additional-identifier stringArray   use additional identifier 
candidates in named entry lists
       --filter strings                      filter reports to a subset of 
differences based on supplied arguments
       --exclude strings                     exclude reports from a set of 
differences based on supplied arguments
       --filter-regexp strings               filter reports to a subset of 
differences based on supplied regular expressions
       --exclude-regexp strings              exclude reports from a set of 
differences based on supplied regular expressions
-  -v, --ignore-value-changes                exclude changes in values
-      --detect-renames                      enable detection for renames 
(document level for Kubernetes resources) (default true)
-  -o, --output string                       specify the output style, 
supported styles: human, brief, github, gitlab, gitea (default "human")
-      --use-indent-lines                    use indent lines in the output 
(default true)
   -b, --omit-header                         omit the dyff summary header
   -s, --set-exit-code                       set program exit code, with 0 
meaning no difference, 1 for differences detected, and 255 for program error
-  -l, --no-table-style                      do not place blocks next to each 
other, always use one row per text block
-  -x, --no-cert-inspection                  disable x509 certificate 
inspection, compare as raw text
-  -g, --use-go-patch-style                  use Go-Patch style paths in outputs
-      --minor-change-threshold float        minor change threshold (default 
0.1)
-      --multi-line-context-lines int        multi-line context lines (default 
4)
   -h, --help                                help for last-applied
 ```
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dyff-1.10.5/assets/format-json-strings/from.yml 
new/dyff-1.11.2/assets/format-json-strings/from.yml
--- old/dyff-1.10.5/assets/format-json-strings/from.yml 1970-01-01 
01:00:00.000000000 +0100
+++ new/dyff-1.11.2/assets/format-json-strings/from.yml 2026-03-01 
17:08:27.000000000 +0100
@@ -0,0 +1,11 @@
+---
+foo:
+  bar: |
+    {
+      "foo": "bar",
+      "list": [1, 2, 3],
+      "mapping": {
+        "foo": "bar",
+        "bar": "foo"
+      }
+    }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dyff-1.10.5/assets/format-json-strings/to.yml 
new/dyff-1.11.2/assets/format-json-strings/to.yml
--- old/dyff-1.10.5/assets/format-json-strings/to.yml   1970-01-01 
01:00:00.000000000 +0100
+++ new/dyff-1.11.2/assets/format-json-strings/to.yml   2026-03-01 
17:08:27.000000000 +0100
@@ -0,0 +1,3 @@
+---
+foo:
+  bar: '{"foo":"bar","list":[1,2,3],"mapping":{"foo":"bar","bar":"foo"}}'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dyff-1.10.5/assets/issues/issue-580/from.yml 
new/dyff-1.11.2/assets/issues/issue-580/from.yml
--- old/dyff-1.10.5/assets/issues/issue-580/from.yml    1970-01-01 
01:00:00.000000000 +0100
+++ new/dyff-1.11.2/assets/issues/issue-580/from.yml    2026-03-01 
17:08:27.000000000 +0100
@@ -0,0 +1,3 @@
+list:
+  - 123
+  - 123
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dyff-1.10.5/assets/issues/issue-580/to.yml 
new/dyff-1.11.2/assets/issues/issue-580/to.yml
--- old/dyff-1.10.5/assets/issues/issue-580/to.yml      1970-01-01 
01:00:00.000000000 +0100
+++ new/dyff-1.11.2/assets/issues/issue-580/to.yml      2026-03-01 
17:08:27.000000000 +0100
@@ -0,0 +1,3 @@
+list:
+  - "123"
+  - 123
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dyff-1.10.5/assets/issues/issue-605/from.yml 
new/dyff-1.11.2/assets/issues/issue-605/from.yml
--- old/dyff-1.10.5/assets/issues/issue-605/from.yml    1970-01-01 
01:00:00.000000000 +0100
+++ new/dyff-1.11.2/assets/issues/issue-605/from.yml    2026-03-01 
17:08:27.000000000 +0100
@@ -0,0 +1,13 @@
+# secrets.yaml
+apiVersion: secrets/v1alpha1
+kind: SecretsAccess
+metadata:
+  name: secrets-access
+  namespace: default
+spec:
+  podSelector:
+    - app.kubernetes.io/name: argocd-repo-server
+    - app.kubernetes.io/name: argocd-server
+    - app.kubernetes.io/name: argocd-application-controller
+    - app.kubernetes.io/name: argocd-redis
+    - app.kubernetes.io/name: argocd-dex-server
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dyff-1.10.5/assets/issues/issue-605/to.yml 
new/dyff-1.11.2/assets/issues/issue-605/to.yml
--- old/dyff-1.10.5/assets/issues/issue-605/to.yml      1970-01-01 
01:00:00.000000000 +0100
+++ new/dyff-1.11.2/assets/issues/issue-605/to.yml      2026-03-01 
17:08:27.000000000 +0100
@@ -0,0 +1,13 @@
+# secrets.yaml
+apiVersion: secrets/v1alpha1
+kind: SecretsAccess
+metadata:
+  name: secrets-access
+  namespace: default
+spec:
+  podSelector:
+    - app.kubernetes.io/name: argocd-repo-server
+    - app.kubernetes.io/name: argocd-server
+    - app.kubernetes.io/name: argocd-application-controller
+    - app.kubernetes.io/name: argocd-redis
+    - app.kubernetes.io/name: argocd-dex-server
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dyff-1.10.5/go.mod new/dyff-1.11.2/go.mod
--- old/dyff-1.10.5/go.mod      2026-02-22 13:42:39.000000000 +0100
+++ new/dyff-1.11.2/go.mod      2026-03-01 17:08:27.000000000 +0100
@@ -1,11 +1,12 @@
 module github.com/homeport/dyff
 
-go 1.24.9
+go 1.25.0
 
 require (
+       github.com/caarlos0/env/v11 v11.4.0
        github.com/gonvenience/bunt v1.4.3
        github.com/gonvenience/idem v0.0.3
-       github.com/gonvenience/neat v1.3.17
+       github.com/gonvenience/neat v1.3.18
        github.com/gonvenience/term v1.0.5
        github.com/gonvenience/text v1.0.10
        github.com/gonvenience/ytbx v1.4.8
@@ -13,17 +14,13 @@
        github.com/mitchellh/hashstructure v1.1.0
        github.com/onsi/ginkgo/v2 v2.28.1
        github.com/onsi/gomega v1.39.1
+       github.com/sergi/go-diff v1.4.0
        github.com/spf13/cobra v1.10.2
+       github.com/spf13/pflag v1.0.10
        github.com/texttheater/golang-levenshtein v1.0.1
        go.yaml.in/yaml/v3 v3.0.4
 )
 
-// usage untagged version of go-diff
-// cause https://github.com/sergi/go-diff/issues/123
-// fixed in https://github.com/sergi/go-diff/pull/136
-// but currently not tagged
-require github.com/sergi/go-diff v1.4.0
-
 require (
        github.com/BurntSushi/toml v1.6.0 // indirect
        github.com/Masterminds/semver/v3 v3.4.0 // indirect
@@ -39,11 +36,10 @@
        github.com/mitchellh/go-ps v1.0.0 // indirect
        github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // 
indirect
        github.com/russross/blackfriday/v2 v2.1.0 // indirect
-       github.com/spf13/pflag v1.0.10 // indirect
        github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74 
// indirect
        go.yaml.in/yaml/v2 v2.4.3 // indirect
        golang.org/x/mod v0.33.0 // indirect
-       golang.org/x/net v0.50.0 // indirect
+       golang.org/x/net v0.51.0 // indirect
        golang.org/x/sync v0.19.0 // indirect
        golang.org/x/sys v0.41.0 // indirect
        golang.org/x/term v0.40.0 // indirect
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dyff-1.10.5/go.sum new/dyff-1.11.2/go.sum
--- old/dyff-1.10.5/go.sum      2026-02-22 13:42:39.000000000 +0100
+++ new/dyff-1.11.2/go.sum      2026-03-01 17:08:27.000000000 +0100
@@ -2,6 +2,8 @@
 github.com/BurntSushi/toml v1.6.0/go.mod 
h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
 github.com/Masterminds/semver/v3 v3.4.0 
h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
 github.com/Masterminds/semver/v3 v3.4.0/go.mod 
h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
+github.com/caarlos0/env/v11 v11.4.0 
h1:Kcb6t5kIIr4XkoQC9AF2j+8E1Jsrl3Wz/hhm1LtoGAc=
+github.com/caarlos0/env/v11 v11.4.0/go.mod 
h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U=
 github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod 
h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
 github.com/cpuguy83/go-md2man/v2 v2.0.7 
h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
 github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod 
h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
@@ -27,6 +29,8 @@
 github.com/gonvenience/idem v0.0.3/go.mod 
h1:ChZ+RP8e30+uCBcCIzN/0di6lTO2PucjemgKfzQUQEw=
 github.com/gonvenience/neat v1.3.17 
h1:S/F0XNE4sc/b3APfLDqC9xM476Or55WhXZ7F/Sy07QY=
 github.com/gonvenience/neat v1.3.17/go.mod 
h1:h+b8M0LFDZUKS5D4xaoPd2qTLrVpX+bDRlDJphOAS/s=
+github.com/gonvenience/neat v1.3.18 
h1:WxWoXhsTHA6CStNrGgSEjGTt5MwIm+7Xs+VZmQIuXZA=
+github.com/gonvenience/neat v1.3.18/go.mod 
h1:DTaEyHIOjSkMa066EoZZl3k5KCG/rFGE67n0cjm/9qk=
 github.com/gonvenience/term v1.0.5 
h1:PYfBH7FB1V+tuuJl4KYrqG/tzAOUnvTy8IFa9YqYrJY=
 github.com/gonvenience/term v1.0.5/go.mod 
h1:CYvcU7H3nE6eOP0gvGfYz4BjGJzM1GeNp+fx4IBWKLs=
 github.com/gonvenience/text v1.0.10 
h1:QRqtC/KMk57K7y4jHi4HjLxf8u+tg+/tIRCS5afywNE=
@@ -106,8 +110,8 @@
 go.yaml.in/yaml/v3 v3.0.4/go.mod 
h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
 golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
 golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
-golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
-golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
+golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
+golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
 golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
 golang.org/x/sync v0.19.0/go.mod 
h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dyff-1.10.5/internal/cmd/between-usage-template.txt 
new/dyff-1.11.2/internal/cmd/between-usage-template.txt
--- old/dyff-1.10.5/internal/cmd/between-usage-template.txt     1970-01-01 
01:00:00.000000000 +0100
+++ new/dyff-1.11.2/internal/cmd/between-usage-template.txt     2026-03-01 
17:08:27.000000000 +0100
@@ -0,0 +1,23 @@
+*Usage:*{{if .Runnable}}
+  {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
+  {{.CommandPath}} [command]{{end}}
+  
+{{if gt (len .Aliases) 0 -}}
+*Aliases:*
+  {{.NameAndAliases}}
+{{- end}}
+
+{{if .HasExample -}}
+*Examples:*
+{{.Example}}
+{{end -}}
+
+*Flags:* {{ range $i, $name := flagGroups }}
+_  {{ .Name }}_
+{{ .FlagUsages | trimTrailingWhitespaces }}
+{{ end -}}
+
+{{if .HasAvailableInheritedFlags }}
+*Global Flags:*
+{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}
+{{ end}}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dyff-1.10.5/internal/cmd/between.go 
new/dyff-1.11.2/internal/cmd/between.go
--- old/dyff-1.10.5/internal/cmd/between.go     2026-02-22 13:42:39.000000000 
+0100
+++ new/dyff-1.11.2/internal/cmd/between.go     2026-03-01 17:08:27.000000000 
+0100
@@ -21,14 +21,20 @@
 package cmd
 
 import (
+       _ "embed"
        "fmt"
 
+       "github.com/gonvenience/bunt"
        "github.com/gonvenience/ytbx"
        "github.com/spf13/cobra"
+       "github.com/spf13/pflag"
 
        "github.com/homeport/dyff/pkg/dyff"
 )
 
+//go:embed between-usage-template.txt
+var betweenCmdUsageTemplate string
+
 type betweenCmdOptions struct {
        swap                     bool
        translateListToDocuments bool
@@ -72,47 +78,48 @@
 
                // Change root of 'from' input file if change root flag for 
'from' is set
                if betweenCmdSettings.chrootFrom != "" {
-                       if err = dyff.ChangeRoot(&from, 
betweenCmdSettings.chrootFrom, reportOptions.useGoPatchPaths, 
betweenCmdSettings.translateListToDocuments); err != nil {
+                       if err = dyff.ChangeRoot(&from, 
betweenCmdSettings.chrootFrom, reportOptions.UseGoPatchPaths, 
betweenCmdSettings.translateListToDocuments); err != nil {
                                return fmt.Errorf("failed to change root of %s 
to path %s: %w", from.Location, betweenCmdSettings.chrootFrom, err)
                        }
                }
 
                // Change root of 'to' input file if change root flag for 'to' 
is set
                if betweenCmdSettings.chrootTo != "" {
-                       if err = dyff.ChangeRoot(&to, 
betweenCmdSettings.chrootTo, reportOptions.useGoPatchPaths, 
betweenCmdSettings.translateListToDocuments); err != nil {
+                       if err = dyff.ChangeRoot(&to, 
betweenCmdSettings.chrootTo, reportOptions.UseGoPatchPaths, 
betweenCmdSettings.translateListToDocuments); err != nil {
                                return fmt.Errorf("failed to change root of %s 
to path %s: %w", to.Location, betweenCmdSettings.chrootTo, err)
                        }
                }
 
                report, err := dyff.CompareInputFiles(from, to,
-                       
dyff.IgnoreOrderChanges(reportOptions.ignoreOrderChanges),
-                       
dyff.IgnoreWhitespaceChanges(reportOptions.ignoreWhitespaceChanges),
-                       
dyff.KubernetesEntityDetection(reportOptions.kubernetesEntityDetection),
-                       
dyff.AdditionalIdentifiers(reportOptions.additionalIdentifiers...),
-                       dyff.DetectRenames(reportOptions.detectRenames),
+                       
dyff.IgnoreOrderChanges(reportOptions.IgnoreOrderChanges),
+                       
dyff.IgnoreWhitespaceChanges(reportOptions.IgnoreWhitespaceChanges),
+                       
dyff.KubernetesEntityDetection(reportOptions.KubernetesEntityDetection),
+                       
dyff.AdditionalIdentifiers(reportOptions.AdditionalIdentifiers...),
+                       dyff.DetectRenames(reportOptions.DetectRenames),
+                       dyff.FormatStrings(reportOptions.FormatStrings),
                )
 
                if err != nil {
                        return fmt.Errorf("failed to compare input files: %w", 
err)
                }
 
-               if reportOptions.filters != nil {
-                       report = report.Filter(reportOptions.filters...)
+               if reportOptions.Filters != nil {
+                       report = report.Filter(reportOptions.Filters...)
                }
 
-               if reportOptions.filterRegexps != nil {
-                       report = 
report.FilterRegexp(reportOptions.filterRegexps...)
+               if reportOptions.FilterRegexps != nil {
+                       report = 
report.FilterRegexp(reportOptions.FilterRegexps...)
                }
 
-               if reportOptions.excludes != nil {
-                       report = report.Exclude(reportOptions.excludes...)
+               if reportOptions.Excludes != nil {
+                       report = report.Exclude(reportOptions.Excludes...)
                }
 
-               if reportOptions.excludeRegexps != nil {
-                       report = 
report.ExcludeRegexp(reportOptions.excludeRegexps...)
+               if reportOptions.ExcludeRegexps != nil {
+                       report = 
report.ExcludeRegexp(reportOptions.ExcludeRegexps...)
                }
 
-               if reportOptions.ignoreValueChanges {
+               if reportOptions.IgnoreValueChanges {
                        report = report.IgnoreValueChanges()
                }
 
@@ -123,14 +130,23 @@
 func init() {
        rootCmd.AddCommand(betweenCmd)
 
-       betweenCmd.Flags().SortFlags = false
+       var groups = []*pflag.FlagSet{
+               flagSet("Input Documents Handling", func(fs *pflag.FlagSet) {
+                       fs.BoolVar(&betweenCmdSettings.swap, "swap", false, 
"Swap 'from' and 'to' for comparison")
+                       fs.StringVar(&betweenCmdSettings.chroot, "chroot", "", 
"change the root level of the input file to another point in the document")
+                       fs.StringVar(&betweenCmdSettings.chrootFrom, 
"chroot-of-from", "", "only change the root level of the from input file")
+                       fs.StringVar(&betweenCmdSettings.chrootTo, 
"chroot-of-to", "", "only change the root level of the to input file")
+                       
fs.BoolVar(&betweenCmdSettings.translateListToDocuments, 
"chroot-list-to-documents", false, "in case the change root points to a list, 
treat this list as a set of documents and not as the list itself")
+               }),
+       }
 
-       applyReportOptionsFlags(betweenCmd)
+       groups = append(groups, reportOptionsFlags()...)
+
+       betweenCmd.Flags().SortFlags = false
+       for _, group := range groups {
+               betweenCmd.Flags().AddFlagSet(group)
+       }
 
-       // Input documents modification flags
-       betweenCmd.Flags().BoolVar(&betweenCmdSettings.swap, "swap", false, 
"Swap 'from' and 'to' for comparison")
-       betweenCmd.Flags().StringVar(&betweenCmdSettings.chroot, "chroot", "", 
"change the root level of the input file to another point in the document")
-       betweenCmd.Flags().StringVar(&betweenCmdSettings.chrootFrom, 
"chroot-of-from", "", "only change the root level of the from input file")
-       betweenCmd.Flags().StringVar(&betweenCmdSettings.chrootTo, 
"chroot-of-to", "", "only change the root level of the to input file")
-       
betweenCmd.Flags().BoolVar(&betweenCmdSettings.translateListToDocuments, 
"chroot-list-to-documents", false, "in case the change root points to a list, 
treat this list as a set of documents and not as the list itself")
+       betweenCmd.SetUsageTemplate(bunt.Sprint(betweenCmdUsageTemplate))
+       cobra.AddTemplateFunc("flagGroups", func() []*pflag.FlagSet { return 
groups })
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dyff-1.10.5/internal/cmd/cmds_test.go 
new/dyff-1.11.2/internal/cmd/cmds_test.go
--- old/dyff-1.10.5/internal/cmd/cmds_test.go   2026-02-22 13:42:39.000000000 
+0100
+++ new/dyff-1.11.2/internal/cmd/cmds_test.go   2026-03-01 17:08:27.000000000 
+0100
@@ -326,7 +326,7 @@
 
 type-change-2
   ± type change from string to int
-    - 12
+    - "12"
     + 12
 
 whitespaces
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dyff-1.10.5/internal/cmd/common.go 
new/dyff-1.11.2/internal/cmd/common.go
--- old/dyff-1.10.5/internal/cmd/common.go      2026-02-22 13:42:39.000000000 
+0100
+++ new/dyff-1.11.2/internal/cmd/common.go      2026-03-01 17:08:27.000000000 
+0100
@@ -29,90 +29,105 @@
        "os"
        "strings"
 
+       "github.com/caarlos0/env/v11"
        "github.com/gonvenience/bunt"
        "github.com/gonvenience/neat"
        "github.com/gonvenience/ytbx"
        "github.com/spf13/cobra"
+       "github.com/spf13/pflag"
        yamlv3 "go.yaml.in/yaml/v3"
 
        "github.com/homeport/dyff/pkg/dyff"
 )
 
 type reportConfig struct {
-       style                     string
-       useIndentLines            bool
-       ignoreOrderChanges        bool
-       ignoreWhitespaceChanges   bool
-       kubernetesEntityDetection bool
-       noTableStyle              bool
-       doNotInspectCerts         bool
-       exitWithCode              bool
-       omitHeader                bool
-       useGoPatchPaths           bool
-       ignoreValueChanges        bool
-       detectRenames             bool
-       minorChangeThreshold      float64
-       multilineContextLines     int
-       additionalIdentifiers     []string
-       filters                   []string
-       excludes                  []string
-       filterRegexps             []string
-       excludeRegexps            []string
+       Style          string `envDefault:"human"`
+       UseIndentLines bool   `envDefault:"true"`
+
+       IgnoreOrderChanges      bool `envDefault:"false"`
+       IgnoreWhitespaceChanges bool `envDefault:"false"`
+       IgnoreValueChanges      bool `envDefault:"false"`
+       FormatStrings           bool `envDefault:"true"`
+       DetectRenames           bool `envDefault:"true"`
+
+       NoTableStyle          bool    `envDefault:"false"`
+       DoNotInspectCerts     bool    `envDefault:"false"`
+       UseGoPatchPaths       bool    `envDefault:"false"`
+       MinorChangeThreshold  float64 `envDefault:"0.1"`
+       MultilineContextLines int     `envDefault:"4"`
+
+       KubernetesEntityDetection bool `envDefault:"true"`
+       AdditionalIdentifiers     []string
+       Filters                   []string
+       Excludes                  []string
+       FilterRegexps             []string
+       ExcludeRegexps            []string
+
+       ExitWithCode bool `envDefault:"false"`
+       OmitHeader   bool `envDefault:"false"`
 }
 
-var defaults = reportConfig{
-       style:                     "human",
-       useIndentLines:            true,
-       ignoreOrderChanges:        false,
-       ignoreWhitespaceChanges:   false,
-       kubernetesEntityDetection: true,
-       noTableStyle:              false,
-       doNotInspectCerts:         false,
-       exitWithCode:              false,
-       omitHeader:                false,
-       useGoPatchPaths:           false,
-       ignoreValueChanges:        false,
-       detectRenames:             true,
-       minorChangeThreshold:      0.1,
-       multilineContextLines:     4,
-       additionalIdentifiers:     nil,
-       filters:                   nil,
-       excludes:                  nil,
-       filterRegexps:             nil,
-       excludeRegexps:            nil,
+func initReportConfig() reportConfig {
+       return env.Must(env.ParseAsWithOptions[reportConfig](env.Options{
+               Prefix:                "DYFF_",
+               UseFieldNameByDefault: true,
+       }))
 }
 
-var reportOptions reportConfig
+var reportOptions = initReportConfig()
+
+func flagSet(name string, f ...func(*pflag.FlagSet)) *pflag.FlagSet {
+       var flatSet = pflag.NewFlagSet(name, pflag.ExitOnError)
+       flatSet.SortFlags = false
+
+       for _, fn := range f {
+               fn(flatSet)
+       }
+
+       return flatSet
+}
 
-func applyReportOptionsFlags(cmd *cobra.Command) {
-       // Compare options
-       cmd.Flags().BoolVarP(&reportOptions.ignoreOrderChanges, 
"ignore-order-changes", "i", defaults.ignoreOrderChanges, "ignore order changes 
in lists")
-       cmd.Flags().BoolVar(&reportOptions.ignoreWhitespaceChanges, 
"ignore-whitespace-changes", defaults.ignoreWhitespaceChanges, "ignore leading 
or trailing whitespace changes")
-       cmd.Flags().BoolVarP(&reportOptions.kubernetesEntityDetection, 
"detect-kubernetes", "", defaults.kubernetesEntityDetection, "detect kubernetes 
entities")
-       cmd.Flags().StringArrayVar(&reportOptions.additionalIdentifiers, 
"additional-identifier", defaults.additionalIdentifiers, "use additional 
identifier candidates in named entry lists")
-       cmd.Flags().StringSliceVar(&reportOptions.filters, "filter", 
defaults.filters, "filter reports to a subset of differences based on supplied 
arguments")
-       cmd.Flags().StringSliceVar(&reportOptions.excludes, "exclude", 
defaults.excludes, "exclude reports from a set of differences based on supplied 
arguments")
-       cmd.Flags().StringSliceVar(&reportOptions.filterRegexps, 
"filter-regexp", defaults.filterRegexps, "filter reports to a subset of 
differences based on supplied regular expressions")
-       cmd.Flags().StringSliceVar(&reportOptions.excludeRegexps, 
"exclude-regexp", defaults.excludeRegexps, "exclude reports from a set of 
differences based on supplied regular expressions")
-       cmd.Flags().BoolVarP(&reportOptions.ignoreValueChanges, 
"ignore-value-changes", "v", defaults.ignoreValueChanges, "exclude changes in 
values")
-       cmd.Flags().BoolVar(&reportOptions.detectRenames, "detect-renames", 
defaults.detectRenames, "enable detection for renames (document level for 
Kubernetes resources)")
-
-       // Main output preferences
-       cmd.Flags().StringVarP(&reportOptions.style, "output", "o", 
defaults.style, "specify the output style, supported styles: human, brief, 
github, gitlab, gitea")
-       cmd.Flags().BoolVar(&reportOptions.useIndentLines, "use-indent-lines", 
defaults.useIndentLines, "use indent lines in the output")
-       cmd.Flags().BoolVarP(&reportOptions.omitHeader, "omit-header", "b", 
defaults.omitHeader, "omit the dyff summary header")
-       cmd.Flags().BoolVarP(&reportOptions.exitWithCode, "set-exit-code", "s", 
defaults.exitWithCode, "set program exit code, with 0 meaning no difference, 1 
for differences detected, and 255 for program error")
-
-       // Human/BOSH output related flags
-       cmd.Flags().BoolVarP(&reportOptions.noTableStyle, "no-table-style", 
"l", defaults.noTableStyle, "do not place blocks next to each other, always use 
one row per text block")
-       cmd.Flags().BoolVarP(&reportOptions.doNotInspectCerts, 
"no-cert-inspection", "x", defaults.doNotInspectCerts, "disable x509 
certificate inspection, compare as raw text")
-       cmd.Flags().BoolVarP(&reportOptions.useGoPatchPaths, 
"use-go-patch-style", "g", defaults.useGoPatchPaths, "use Go-Patch style paths 
in outputs")
-       cmd.Flags().Float64VarP(&reportOptions.minorChangeThreshold, 
"minor-change-threshold", "", defaults.minorChangeThreshold, "minor change 
threshold")
-       cmd.Flags().IntVarP(&reportOptions.multilineContextLines, 
"multi-line-context-lines", "", defaults.multilineContextLines, "multi-line 
context lines")
-
-       // Deprecated
-       cmd.Flags().BoolVar(&reportOptions.exitWithCode, "set-exit-status", 
defaults.exitWithCode, "set program exit code, with 0 meaning no difference, 1 
for differences detected, and 255 for program error")
-       _ = cmd.Flags().MarkDeprecated("set-exit-status", "use --set-exit-code 
instead")
+func reportOptionsFlags() []*pflag.FlagSet {
+       return []*pflag.FlagSet{
+               flagSet("Output Preferences", func(fs *pflag.FlagSet) {
+                       fs.StringVarP(&reportOptions.Style, "output", "o", 
reportOptions.Style, "specify the output style, supported styles: human, brief, 
github, gitlab, gitea")
+                       fs.BoolVar(&reportOptions.UseIndentLines, 
"use-indent-lines", reportOptions.UseIndentLines, "use indent lines in the 
output")
+               }),
+
+               flagSet("Compare Options", func(fs *pflag.FlagSet) {
+                       fs.BoolVarP(&reportOptions.IgnoreOrderChanges, 
"ignore-order-changes", "i", reportOptions.IgnoreOrderChanges, "ignore order 
changes in lists")
+                       fs.BoolVar(&reportOptions.IgnoreWhitespaceChanges, 
"ignore-whitespace-changes", reportOptions.IgnoreWhitespaceChanges, "ignore 
leading or trailing whitespace changes")
+                       fs.BoolVarP(&reportOptions.IgnoreValueChanges, 
"ignore-value-changes", "v", reportOptions.IgnoreValueChanges, "exclude changes 
in values")
+                       fs.BoolVar(&reportOptions.DetectRenames, 
"detect-renames", reportOptions.DetectRenames, "enable detection for renames 
(document level for Kubernetes resources)")
+                       fs.BoolVar(&reportOptions.FormatStrings, 
"format-strings", reportOptions.FormatStrings, "format strings (i.e. inline 
JSON) before comparison to avoid formatting differences")
+               }),
+
+               flagSet("Human Output Preferences", func(fs *pflag.FlagSet) {
+                       fs.BoolVarP(&reportOptions.NoTableStyle, 
"no-table-style", "l", reportOptions.NoTableStyle, "do not place blocks next to 
each other, always use one row per text block")
+                       fs.BoolVarP(&reportOptions.DoNotInspectCerts, 
"no-cert-inspection", "x", reportOptions.DoNotInspectCerts, "disable x509 
certificate inspection, compare as raw text")
+                       fs.BoolVarP(&reportOptions.UseGoPatchPaths, 
"use-go-patch-style", "g", reportOptions.UseGoPatchPaths, "use Go-Patch style 
paths in outputs")
+                       fs.Float64VarP(&reportOptions.MinorChangeThreshold, 
"minor-change-threshold", "", reportOptions.MinorChangeThreshold, "minor change 
threshold")
+                       fs.IntVarP(&reportOptions.MultilineContextLines, 
"multi-line-context-lines", "", reportOptions.MultilineContextLines, 
"multi-line context lines")
+               }),
+
+               flagSet("Filter Options", func(fs *pflag.FlagSet) {
+                       fs.BoolVarP(&reportOptions.KubernetesEntityDetection, 
"detect-kubernetes", "", reportOptions.KubernetesEntityDetection, "detect 
kubernetes entities")
+                       fs.StringArrayVar(&reportOptions.AdditionalIdentifiers, 
"additional-identifier", reportOptions.AdditionalIdentifiers, "use additional 
identifier candidates in named entry lists")
+                       fs.StringSliceVar(&reportOptions.Filters, "filter", 
reportOptions.Filters, "filter reports to a subset of differences based on 
supplied arguments")
+                       fs.StringSliceVar(&reportOptions.Excludes, "exclude", 
reportOptions.Excludes, "exclude reports from a set of differences based on 
supplied arguments")
+                       fs.StringSliceVar(&reportOptions.FilterRegexps, 
"filter-regexp", reportOptions.FilterRegexps, "filter reports to a subset of 
differences based on supplied regular expressions")
+                       fs.StringSliceVar(&reportOptions.ExcludeRegexps, 
"exclude-regexp", reportOptions.ExcludeRegexps, "exclude reports from a set of 
differences based on supplied regular expressions")
+               }),
+
+               flagSet("General Options", func(fs *pflag.FlagSet) {
+                       fs.BoolVarP(&reportOptions.OmitHeader, "omit-header", 
"b", reportOptions.OmitHeader, "omit the dyff summary header")
+                       fs.BoolVarP(&reportOptions.ExitWithCode, 
"set-exit-code", "s", reportOptions.ExitWithCode, "set program exit code, with 
0 meaning no difference, 1 for differences detected, and 255 for program error")
+
+                       // Deprecated
+                       fs.BoolVar(&reportOptions.ExitWithCode, 
"set-exit-status", reportOptions.ExitWithCode, "set program exit code, with 0 
meaning no difference, 1 for differences detected, and 255 for program error")
+                       _ = fs.MarkDeprecated("set-exit-status", "use 
--set-exit-code instead")
+               }),
+       }
 }
 
 // OutputWriter encapsulates the required fields to define the look and feel of
@@ -216,18 +231,18 @@
 
 func writeReport(cmd *cobra.Command, report dyff.Report) error {
        var reportWriter dyff.ReportWriter
-       switch strings.ToLower(reportOptions.style) {
+       switch strings.ToLower(reportOptions.Style) {
        case "human", "bosh":
                reportWriter = &dyff.HumanReport{
                        Report:                report,
                        Indent:                2,
-                       UseIndentLines:        reportOptions.useIndentLines,
-                       DoNotInspectCerts:     reportOptions.doNotInspectCerts,
-                       NoTableStyle:          reportOptions.noTableStyle,
-                       OmitHeader:            reportOptions.omitHeader,
-                       UseGoPatchPaths:       reportOptions.useGoPatchPaths,
-                       MinorChangeThreshold:  
reportOptions.minorChangeThreshold,
-                       MultilineContextLines: 
reportOptions.multilineContextLines,
+                       UseIndentLines:        reportOptions.UseIndentLines,
+                       DoNotInspectCerts:     reportOptions.DoNotInspectCerts,
+                       NoTableStyle:          reportOptions.NoTableStyle,
+                       OmitHeader:            reportOptions.OmitHeader,
+                       UseGoPatchPaths:       reportOptions.UseGoPatchPaths,
+                       MinorChangeThreshold:  
reportOptions.MinorChangeThreshold,
+                       MultilineContextLines: 
reportOptions.MultilineContextLines,
                        PrefixMultiline:       false,
                }
 
@@ -239,13 +254,13 @@
                        HumanReport: dyff.HumanReport{
                                Report:                report,
                                Indent:                0,
-                               UseIndentLines:        
reportOptions.useIndentLines,
-                               DoNotInspectCerts:     
reportOptions.doNotInspectCerts,
+                               UseIndentLines:        
reportOptions.UseIndentLines,
+                               DoNotInspectCerts:     
reportOptions.DoNotInspectCerts,
                                NoTableStyle:          true,
                                OmitHeader:            true,
-                               UseGoPatchPaths:       
reportOptions.useGoPatchPaths,
-                               MinorChangeThreshold:  
reportOptions.minorChangeThreshold,
-                               MultilineContextLines: 
reportOptions.multilineContextLines,
+                               UseGoPatchPaths:       
reportOptions.UseGoPatchPaths,
+                               MinorChangeThreshold:  
reportOptions.MinorChangeThreshold,
+                               MultilineContextLines: 
reportOptions.MultilineContextLines,
                                PrefixMultiline:       true,
                        },
                }
@@ -258,13 +273,13 @@
                        HumanReport: dyff.HumanReport{
                                Report:                report,
                                Indent:                0,
-                               UseIndentLines:        
reportOptions.useIndentLines,
-                               DoNotInspectCerts:     
reportOptions.doNotInspectCerts,
+                               UseIndentLines:        
reportOptions.UseIndentLines,
+                               DoNotInspectCerts:     
reportOptions.DoNotInspectCerts,
                                NoTableStyle:          true,
                                OmitHeader:            true,
-                               UseGoPatchPaths:       
reportOptions.useGoPatchPaths,
-                               MinorChangeThreshold:  
reportOptions.minorChangeThreshold,
-                               MultilineContextLines: 
reportOptions.multilineContextLines,
+                               UseGoPatchPaths:       
reportOptions.UseGoPatchPaths,
+                               MinorChangeThreshold:  
reportOptions.MinorChangeThreshold,
+                               MultilineContextLines: 
reportOptions.MultilineContextLines,
                                PrefixMultiline:       true,
                        },
                }
@@ -277,13 +292,13 @@
                        HumanReport: dyff.HumanReport{
                                Report:                report,
                                Indent:                0,
-                               UseIndentLines:        
reportOptions.useIndentLines,
-                               DoNotInspectCerts:     
reportOptions.doNotInspectCerts,
+                               UseIndentLines:        
reportOptions.UseIndentLines,
+                               DoNotInspectCerts:     
reportOptions.DoNotInspectCerts,
                                NoTableStyle:          true,
                                OmitHeader:            true,
-                               UseGoPatchPaths:       
reportOptions.useGoPatchPaths,
-                               MinorChangeThreshold:  
reportOptions.minorChangeThreshold,
-                               MultilineContextLines: 
reportOptions.multilineContextLines,
+                               UseGoPatchPaths:       
reportOptions.UseGoPatchPaths,
+                               MinorChangeThreshold:  
reportOptions.MinorChangeThreshold,
+                               MultilineContextLines: 
reportOptions.MultilineContextLines,
                                PrefixMultiline:       true,
                        },
                }
@@ -294,7 +309,7 @@
                }
 
        default:
-               return fmt.Errorf("unknown output style %s: %w", 
reportOptions.style, errors.New(cmd.UsageString()))
+               return fmt.Errorf("unknown output style %s: %w", 
reportOptions.Style, errors.New(cmd.UsageString()))
        }
 
        if err := reportWriter.WriteReport(os.Stdout); err != nil {
@@ -302,7 +317,7 @@
        }
 
        // If configured, make sure `dyff` exists with an exit status
-       if reportOptions.exitWithCode {
+       if reportOptions.ExitWithCode {
                switch len(report.Diffs) {
                case 0:
                        return errorWithExitCode{value: 0}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dyff-1.10.5/internal/cmd/lastApplied.go 
new/dyff-1.11.2/internal/cmd/lastApplied.go
--- old/dyff-1.10.5/internal/cmd/lastApplied.go 2026-02-22 13:42:39.000000000 
+0100
+++ new/dyff-1.11.2/internal/cmd/lastApplied.go 2026-03-01 17:08:27.000000000 
+0100
@@ -58,7 +58,7 @@
 
                purgeWellKnownMetadataEntries(inputFile.Documents[0])
 
-               report, err := dyff.CompareInputFiles(lastConfiguration, 
inputFile, dyff.IgnoreOrderChanges(reportOptions.ignoreOrderChanges))
+               report, err := dyff.CompareInputFiles(lastConfiguration, 
inputFile, dyff.IgnoreOrderChanges(reportOptions.IgnoreOrderChanges))
                if err != nil {
                        return fmt.Errorf("failed to compare input files: %w", 
err)
                }
@@ -72,7 +72,9 @@
 
        lastAppliedCmd.Flags().SortFlags = false
 
-       applyReportOptionsFlags(lastAppliedCmd)
+       for _, group := range reportOptionsFlags() {
+               lastAppliedCmd.Flags().AddFlagSet(group)
+       }
 }
 
 func lookUpLastAppliedConfiguration(inputFile ytbx.InputFile) (ytbx.InputFile, 
error) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dyff-1.10.5/internal/cmd/root.go 
new/dyff-1.11.2/internal/cmd/root.go
--- old/dyff-1.10.5/internal/cmd/root.go        2026-02-22 13:42:39.000000000 
+0100
+++ new/dyff-1.11.2/internal/cmd/root.go        2026-03-01 17:08:27.000000000 
+0100
@@ -60,7 +60,7 @@
 // ResetSettings resets command settings to default. This is only required by
 // the test suite to make sure that the flag parsing works correctly.
 func ResetSettings() {
-       reportOptions = defaults
+       reportOptions = initReportConfig()
        betweenCmdSettings = betweenCmdOptions{}
        yamlCmdSettings = yamlCmdOptions{}
        jsonCmdSettings = jsonCmdOptions{}
@@ -94,13 +94,13 @@
                os.Args = rearrange()
 
                // Enable Kubernetes specific entity detection implicitly
-               reportOptions.kubernetesEntityDetection = true
+               reportOptions.KubernetesEntityDetection = true
 
                // Add implicit exclude for metadata.managedFields as this 
cannot
                // be configured via a command-line flag using 
KUBECTL_EXTERNAL_DIFF
                // due to an bug/feature in kubectl that ignore command-line 
flags
                // in the diff environment variable with non alphanumeric 
characters
-               reportOptions.excludeRegexps = 
append(reportOptions.excludeRegexps, "^/metadata/managedFields")
+               reportOptions.ExcludeRegexps = 
append(reportOptions.ExcludeRegexps, "^/metadata/managedFields")
        }
 
        if err := rootCmd.Execute(); err != nil {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dyff-1.10.5/pkg/dyff/compare_test.go 
new/dyff-1.11.2/pkg/dyff/compare_test.go
--- old/dyff-1.10.5/pkg/dyff/compare_test.go    2026-02-22 13:42:39.000000000 
+0100
+++ new/dyff-1.11.2/pkg/dyff/compare_test.go    2026-03-01 17:08:27.000000000 
+0100
@@ -844,6 +844,18 @@
                                        singleDiff("/yaml/map/removed", 
dyff.REMOVAL, nil, "removed"),
                                }}))
                        })
+
+                       It("should format strings to rule out formatting 
differences", func() {
+                               report, err := dyff.CompareInputFiles(
+                                       
file(assets("format-json-strings/from.yml")),
+                                       
file(assets("format-json-strings/to.yml")),
+                                       dyff.FormatStrings(true),
+                               )
+
+                               Expect(err).ToNot(HaveOccurred())
+                               Expect(report).NotTo(BeNil())
+                               Expect(report.Diffs).To(HaveLen(0))
+                       })
                })
 
                Context("change root for comparison", func() {
@@ -925,7 +937,7 @@
                        })
                })
 
-               Context("checking known issues of compare", func() {
+               Context("checking known/reported compare issues", func() {
                        It("should not return order change differences in case 
the named-entry list does not have unique identifiers", func() {
                                from, to, err := 
ytbx.LoadFiles("../../assets/issues/issue-38/from.yml", 
"../../assets/issues/issue-38/to.yml")
                                Expect(err).To(BeNil())
@@ -1011,6 +1023,30 @@
                                Expect(results).ToNot(BeNil())
                                Expect(results.Diffs).To(HaveLen(0))
                        })
+
+                       It("should work with non-standard identifier containing 
dots and slashes in named entry lists", func() {
+                               from, to, err := 
ytbx.LoadFiles(assets("issues/issue-605/from.yml"), 
assets("issues/issue-605/to.yml"))
+                               Expect(err).ToNot(HaveOccurred())
+                               Expect(from).ToNot(BeNil())
+                               Expect(to).ToNot(BeNil())
+
+                               results, err := dyff.CompareInputFiles(from, to)
+                               Expect(err).ToNot(HaveOccurred())
+                               Expect(results).ToNot(BeNil())
+                               Expect(results.Diffs).To(HaveLen(0))
+                       })
+
+                       It("should differentiate correctly between number as 
string and number", func() {
+                               from, to, err := 
ytbx.LoadFiles(assets("issues/issue-580/from.yml"), 
assets("issues/issue-580/to.yml"))
+                               Expect(err).ToNot(HaveOccurred())
+                               Expect(from).ToNot(BeNil())
+                               Expect(to).ToNot(BeNil())
+
+                               results, err := dyff.CompareInputFiles(from, to)
+                               Expect(err).ToNot(HaveOccurred())
+                               Expect(results).ToNot(BeNil())
+                               Expect(results.Diffs).To(HaveLen(1))
+                       })
                })
 
                Context("input files containing Kubernetes resources", func() {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dyff-1.10.5/pkg/dyff/core.go 
new/dyff-1.11.2/pkg/dyff/core.go
--- old/dyff-1.10.5/pkg/dyff/core.go    2026-02-22 13:42:39.000000000 +0100
+++ new/dyff-1.11.2/pkg/dyff/core.go    2026-03-01 17:08:27.000000000 +0100
@@ -21,6 +21,8 @@
 package dyff
 
 import (
+       "bytes"
+       "encoding/json"
        "fmt"
        "sort"
        "strings"
@@ -43,6 +45,7 @@
        IgnoreWhitespaceChanges                  bool
        KubernetesEntityDetection                bool
        DetectRenames                            bool
+       FormatStrings                            bool
        AdditionalIdentifiers                    []string
 }
 
@@ -96,6 +99,12 @@
        }
 }
 
+func FormatStrings(value bool) CompareOption {
+       return func(settings *compareSettings) {
+               settings.FormatStrings = value
+       }
+}
+
 // CompareInputFiles is one of the convenience main entry points for comparing
 // objects. In this case the representation of an input file, which might
 // contain multiple documents. It returns a report with the list of 
differences.
@@ -514,11 +523,8 @@
 }
 
 func (compare *compare) simpleLists(path ytbx.Path, from *yamlv3.Node, to 
*yamlv3.Node) ([]Diff, error) {
-       removals := make([]*yamlv3.Node, 0)
-       additions := make([]*yamlv3.Node, 0)
-
-       fromLength := len(from.Content)
-       toLength := len(to.Content)
+       var removals, additions []*yamlv3.Node
+       var fromLength, toLength = len(from.Content), len(to.Content)
 
        // Special case if both lists only contain one entry, then directly 
compare
        // the two entries with each other
@@ -530,12 +536,25 @@
                )
        }
 
-       fromLookup := compare.createLookUpMap(from)
-       toLookup := compare.createLookUpMap(to)
+       var createLookUpMap = func(sequenceNode *yamlv3.Node) map[uint64][]int {
+               var result = make(map[uint64][]int, len(sequenceNode.Content))
+               for idx, entry := range sequenceNode.Content {
+                       var hash = compare.calcNodeHash(entry)
+                       if _, ok := result[hash]; !ok {
+                               result[hash] = []int{}
+                       }
+
+                       result[hash] = append(result[hash], idx)
+               }
+
+               return result
+       }
+
+       fromLookup := createLookUpMap(from)
+       toLookup := createLookUpMap(to)
 
        // Fill two lists with the hashes of the entries of each list
-       fromCommon := make([]*yamlv3.Node, 0, fromLength)
-       toCommon := make([]*yamlv3.Node, 0, toLength)
+       var fromCommon, toCommon []*yamlv3.Node
 
        for idxPos, fromValue := range from.Content {
                hash := compare.calcNodeHash(fromValue)
@@ -592,10 +611,8 @@
 }
 
 func (compare *compare) namedEntryLists(path ytbx.Path, identifier 
listItemIdentifier, from *yamlv3.Node, to *yamlv3.Node) ([]Diff, error) {
-       removals := make([]*yamlv3.Node, 0)
-       additions := make([]*yamlv3.Node, 0)
-
-       result := make([]Diff, 0)
+       var removals, additions []*yamlv3.Node
+       var result []Diff
 
        // Fill two lists with the names of the entries that are common in both 
lists
        fromLength := len(from.Content)
@@ -655,6 +672,32 @@
 }
 
 func (compare *compare) nodeValues(path ytbx.Path, from *yamlv3.Node, to 
*yamlv3.Node) ([]Diff, error) {
+       if compare.settings.FormatStrings {
+               var jsonFormat = func(input string) (string, bool) {
+                       var tmp any
+
+                       if err := json.Unmarshal([]byte(input), &tmp); err != 
nil {
+                               return "", false
+                       }
+
+                       var buf bytes.Buffer
+                       var encoder = json.NewEncoder(&buf)
+                       encoder.SetIndent("", "  ")
+                       if err := encoder.Encode(tmp); err != nil {
+                               return "", false
+                       }
+
+                       return buf.String(), true
+               }
+
+               if fromValue, ok := jsonFormat(from.Value); ok {
+                       if toValue, ok := jsonFormat(to.Value); ok {
+                               from.Value = fromValue
+                               to.Value = toValue
+                       }
+               }
+       }
+
        if strings.Compare(from.Value, to.Value) != 0 {
                // leave and don't report any differences if ignore whitespaces 
changes is
                // configured and it is really only a whitespace only change 
between the strings
@@ -662,14 +705,11 @@
                        return nil, nil
                }
 
-               return []Diff{{
-                       &path,
-                       []Detail{{
-                               Kind: MODIFICATION,
-                               From: from,
-                               To:   to,
-                       }},
-               }}, nil
+               return []Diff{{&path, []Detail{{
+                       Kind: MODIFICATION,
+                       From: from,
+                       To:   to,
+               }}}}, nil
        }
 
        return nil, nil
@@ -1008,20 +1048,6 @@
        return false
 }
 
-func (compare *compare) createLookUpMap(sequenceNode *yamlv3.Node) 
map[uint64][]int {
-       result := make(map[uint64][]int, len(sequenceNode.Content))
-       for idx, entry := range sequenceNode.Content {
-               hash := compare.calcNodeHash(entry)
-               if _, ok := result[hash]; !ok {
-                       result[hash] = []int{}
-               }
-
-               result[hash] = append(result[hash], idx)
-       }
-
-       return result
-}
-
 func (compare *compare) basicType(node *yamlv3.Node) interface{} {
        switch node.Kind {
        case yamlv3.DocumentNode:
@@ -1068,7 +1094,7 @@
                hash, err = hashstructure.Hash(compare.basicType(node), nil)
 
        case yamlv3.ScalarNode:
-               hash, err = hashstructure.Hash(node.Value, nil)
+               hash, err = hashstructure.Hash(node.Tag+"/"+node.Value, nil)
 
        case yamlv3.AliasNode:
                hash = compare.calcNodeHash(followAlias(node))
@@ -1105,22 +1131,6 @@
        })
 }
 
-func min(a, b int) int {
-       if a < b {
-               return a
-       }
-
-       return b
-}
-
-func max(a, b int) int {
-       if a > b {
-               return a
-       }
-
-       return b
-}
-
 func isList(node *yamlv3.Node) bool {
        switch node.Kind {
        case yamlv3.SequenceNode:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dyff-1.10.5/pkg/dyff/core_identifier.go 
new/dyff-1.11.2/pkg/dyff/core_identifier.go
--- old/dyff-1.10.5/pkg/dyff/core_identifier.go 2026-02-22 13:42:39.000000000 
+0100
+++ new/dyff-1.11.2/pkg/dyff/core_identifier.go 2026-03-01 17:08:27.000000000 
+0100
@@ -49,7 +49,7 @@
        IdentifierFieldName string
 }
 
-var _ listItemIdentifier = &singleField{}
+var _ listItemIdentifier = (*singleField)(nil)
 
 func (sf *singleField) FindNodeByName(sequenceNode *yamlv3.Node, name string) 
(*yamlv3.Node, error) {
        for _, mappingNode := range sequenceNode.Content {
@@ -67,12 +67,14 @@
 }
 
 func (sf *singleField) Name(mappingNode *yamlv3.Node) (string, error) {
-       result, err := grab(mappingNode, sf.IdentifierFieldName)
-       if err != nil {
-               return "", err
+       for i := 0; i < len(mappingNode.Content); i += 2 {
+               k, v := mappingNode.Content[i], mappingNode.Content[i+1]
+               if k.Value == sf.IdentifierFieldName {
+                       return followAlias(v).Value, nil
+               }
        }
 
-       return followAlias(result).Value, nil
+       return "", fmt.Errorf("no key %q found in map", sf.IdentifierFieldName)
 }
 
 func (sf *singleField) String() string {
@@ -85,7 +87,7 @@
 // api version, kind, and name field to be used
 type k8sItemIdentifier struct{}
 
-var k8sItem listItemIdentifier = &k8sItemIdentifier{}
+var k8sItem listItemIdentifier = (*k8sItemIdentifier)(nil)
 
 func (i *k8sItemIdentifier) FindNodeByName(sequenceNode *yamlv3.Node, name 
string) (*yamlv3.Node, error) {
        for _, mappingNode := range sequenceNode.Content {

++++++ dyff.obsinfo ++++++
--- /var/tmp/diff_new_pack.1uavjC/_old  2026-03-02 17:39:53.402262271 +0100
+++ /var/tmp/diff_new_pack.1uavjC/_new  2026-03-02 17:39:53.418262938 +0100
@@ -1,5 +1,5 @@
 name: dyff
-version: 1.10.5
-mtime: 1771764159
-commit: 0c7ba9461bf06f54f9527d9945c3f879efc76770
+version: 1.11.2
+mtime: 1772381307
+commit: 6c36ce0d21d83f307a4e76ee810d9657947e3cf9
 

++++++ vendor.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/github.com/caarlos0/env/v11/.editorconfig 
new/vendor/github.com/caarlos0/env/v11/.editorconfig
--- old/vendor/github.com/caarlos0/env/v11/.editorconfig        1970-01-01 
01:00:00.000000000 +0100
+++ new/vendor/github.com/caarlos0/env/v11/.editorconfig        2026-03-01 
17:08:27.000000000 +0100
@@ -0,0 +1,28 @@
+[*]
+charset = utf-8
+end_of_line = lf
+trim_trailing_whitespace = true
+max_line_length = 120
+
+[{go.mod,go.sum,*.go}]
+insert_final_newline = true
+indent_size = tab
+indent_style = tab
+tab_width = 4
+
+[Makefile]
+max_line_length = off
+insert_final_newline = true
+indent_size = tab
+indent_style = tab
+tab_width = 4
+
+[*.md]
+max_line_length = off
+trim_trailing_whitespace = false
+indent_size = tab
+indent_style = space
+tab_width = 2
+
+[.mailmap]
+max_line_length = off
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/github.com/caarlos0/env/v11/.gitignore 
new/vendor/github.com/caarlos0/env/v11/.gitignore
--- old/vendor/github.com/caarlos0/env/v11/.gitignore   1970-01-01 
01:00:00.000000000 +0100
+++ new/vendor/github.com/caarlos0/env/v11/.gitignore   2026-03-01 
17:08:27.000000000 +0100
@@ -0,0 +1,5 @@
+coverage.txt
+bin
+card.png
+dist
+codecov*
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/github.com/caarlos0/env/v11/.golangci.yml 
new/vendor/github.com/caarlos0/env/v11/.golangci.yml
--- old/vendor/github.com/caarlos0/env/v11/.golangci.yml        1970-01-01 
01:00:00.000000000 +0100
+++ new/vendor/github.com/caarlos0/env/v11/.golangci.yml        2026-03-01 
17:08:27.000000000 +0100
@@ -0,0 +1,115 @@
+version: "2"
+run:
+  go: "1.24"
+linters:
+  enable:
+    - bodyclose
+    - copyloopvar
+    - depguard
+    - forbidigo
+    - gocritic
+    - misspell
+    - noctx
+    - nolintlint
+    - perfsprint
+    - revive
+    - testifylint
+    - thelper
+    - tparallel
+    - unconvert
+    - unparam
+    - usetesting
+    - wastedassign
+  settings:
+    depguard:
+      rules:
+        main:
+          deny:
+            - pkg: github.com/pkg/errors
+              desc: use stdlib instead
+            - pkg: math/rand$
+              desc: use math/rand/v2 instead
+            - pkg: github.com/apex/log
+              desc: use caarlos0/log instead
+    forbidigo:
+      forbid:
+        - pattern: ioutil\.*
+    gocritic:
+      disabled-checks:
+        - appendAssign
+    perfsprint:
+      int-conversion: false
+      err-error: false
+      errorf: true
+      sprintf1: false
+      strconcat: false
+    revive:
+      enable-all-rules: false
+      rules:
+        - name: blank-imports
+        - name: context-as-argument
+        - name: context-keys-type
+        - name: comment-spacings
+        - name: dot-imports
+        - name: empty-block
+        - name: empty-lines
+        - name: error-naming
+        - name: error-return
+        - name: error-strings
+        - name: errorf
+        - name: increment-decrement
+        - name: indent-error-flow
+        - name: modifies-value-receiver
+        - name: range
+        - name: receiver-naming
+        - name: redefines-builtin-id
+        - name: superfluous-else
+        - name: time-naming
+        - name: unexported-return
+        - name: unreachable-code
+        - name: unused-parameter
+        - name: var-declaration
+        - name: var-naming
+        # we are not providing an API anyway... maybe someday...
+        # - name: exported
+        # - name: package-comments
+    staticcheck:
+      checks:
+        - all
+        - -SA1019
+    testifylint:
+      enable-all: true
+      disable:
+        - error-is-as
+    usetesting:
+      context-background: true
+      context-todo: true
+      os-chdir: true
+      os-mkdir-temp: true
+      os-setenv: true
+      os-create-temp: true
+      os-temp-dir: true
+  exclusions:
+    generated: lax
+    presets:
+      - common-false-positives
+      - legacy
+      - std-error-handling
+    rules:
+      - linters:
+          - noctx
+          - perfsprint
+        path: _test\.go
+    paths:
+      - third_party$
+      - builtin$
+      - examples$
+formatters:
+  enable:
+    - gofumpt
+  exclusions:
+    generated: lax
+    paths:
+      - third_party$
+      - builtin$
+      - examples$
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/github.com/caarlos0/env/v11/.goreleaser.yml 
new/vendor/github.com/caarlos0/env/v11/.goreleaser.yml
--- old/vendor/github.com/caarlos0/env/v11/.goreleaser.yml      1970-01-01 
01:00:00.000000000 +0100
+++ new/vendor/github.com/caarlos0/env/v11/.goreleaser.yml      2026-03-01 
17:08:27.000000000 +0100
@@ -0,0 +1,5 @@
+# yaml-language-server: $schema=https://goreleaser.com/static/schema-pro.json
+version: 2
+includes:
+  - from_url:
+      url: 
https://raw.githubusercontent.com/caarlos0/.goreleaserfiles/main/lib.yml
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/github.com/caarlos0/env/v11/.mailmap 
new/vendor/github.com/caarlos0/env/v11/.mailmap
--- old/vendor/github.com/caarlos0/env/v11/.mailmap     1970-01-01 
01:00:00.000000000 +0100
+++ new/vendor/github.com/caarlos0/env/v11/.mailmap     2026-03-01 
17:08:27.000000000 +0100
@@ -0,0 +1,7 @@
+Carlos Alexandro Becker <[email protected]> Carlos A Becker 
<[email protected]>
+Carlos Alexandro Becker <[email protected]> Carlos A Becker 
<[email protected]>
+Carlos Alexandro Becker <[email protected]> Carlos Alexandro 
Becker <[email protected]>
+Carlos Alexandro Becker <[email protected]> Carlos Alexandro 
Becker <[email protected]>
+Carlos Alexandro Becker <[email protected]> Carlos Becker 
<[email protected]>
+dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 
dependabot-preview[bot] 
<27856297+dependabot-preview[bot]@users.noreply.github.com>
+actions-user <[email protected]> github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/github.com/caarlos0/env/v11/LICENSE.md 
new/vendor/github.com/caarlos0/env/v11/LICENSE.md
--- old/vendor/github.com/caarlos0/env/v11/LICENSE.md   1970-01-01 
01:00:00.000000000 +0100
+++ new/vendor/github.com/caarlos0/env/v11/LICENSE.md   2026-03-01 
17:08:27.000000000 +0100
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015-2024 Carlos Alexandro Becker
+
+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.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/github.com/caarlos0/env/v11/Makefile 
new/vendor/github.com/caarlos0/env/v11/Makefile
--- old/vendor/github.com/caarlos0/env/v11/Makefile     1970-01-01 
01:00:00.000000000 +0100
+++ new/vendor/github.com/caarlos0/env/v11/Makefile     2026-03-01 
17:08:27.000000000 +0100
@@ -0,0 +1,37 @@
+SOURCE_FILES?=./...
+TEST_PATTERN?=.
+
+export GO111MODULE := on
+
+setup:
+       go mod tidy
+.PHONY: setup
+
+build:
+       go build
+.PHONY: build
+
+test:
+       go test -v -failfast -race -coverpkg=./... -covermode=atomic 
-coverprofile=coverage.txt $(SOURCE_FILES) -run $(TEST_PATTERN) -timeout=2m
+.PHONY: test
+
+cover: test
+       go tool cover -html=coverage.txt
+.PHONY: cover
+
+fmt:
+       gofumpt -w -l .
+.PHONY: fmt
+
+lint:
+       golangci-lint run ./...
+.PHONY: lint
+
+ci: build test
+.PHONY: ci
+
+card:
+       wget -O card.png -c "https://og.caarlos0.dev/**env**: parse envs to 
structs.png?theme=light&md=1&fontSize=100px&images=https://github.com/caarlos0.png";
+.PHONY: card
+
+.DEFAULT_GOAL := ci
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/github.com/caarlos0/env/v11/README.md 
new/vendor/github.com/caarlos0/env/v11/README.md
--- old/vendor/github.com/caarlos0/env/v11/README.md    1970-01-01 
01:00:00.000000000 +0100
+++ new/vendor/github.com/caarlos0/env/v11/README.md    2026-03-01 
17:08:27.000000000 +0100
@@ -0,0 +1,156 @@
+<p align="center">
+  <img alt="GoReleaser Logo" src="https://becker.software/env.png"; 
height="140" />
+  <p align="center">A simple, zero-dependencies library to parse environment 
variables into structs.</p>
+</p>
+
+###### Installation
+
+```bash
+go get github.com/caarlos0/env/v11
+```
+
+###### Getting started
+
+```go
+type config struct {
+  Home string `env:"HOME"`
+}
+
+// parse
+var cfg config
+err := env.Parse(&cfg)
+
+// parse with generics
+cfg, err := env.ParseAs[config]()
+```
+
+You can see the full documentation and list of examples at 
[pkg.go.dev](https://pkg.go.dev/github.com/caarlos0/env/v11).
+
+---
+
+## Used and supported by
+
+<p>
+  <a href="https://encore.dev";>
+    <img 
src="https://user-images.githubusercontent.com/78424526/214602214-52e0483a-b5fc-4d4c-b03e-0b7b23e012df.svg";
 width="120px" alt="encore icon" />
+  </a>
+  <br/>
+  <br/>
+  <b>Encore – the platform for building Go-based cloud backends.</b>
+  <br/>
+</p>
+
+## Usage
+
+### Caveats
+
+> [!CAUTION]
+>
+> _Unexported fields_ will be **ignored** by `env`.
+> This is by design and will not change.
+
+### Functions
+
+- `Parse`: parse the current environment into a type
+- `ParseAs`: parse the current environment into a type using generics
+- `ParseWithOptions`: parse the current environment into a type with custom 
options
+- `ParseAsWithOptions`: parse the current environment into a type with custom 
options and using generics
+- `Must`: can be used to wrap `Parse.*` calls to panic on error
+- `GetFieldParams`: get the `env` parsed options for a type
+- `GetFieldParamsWithOptions`: get the `env` parsed options for a type with 
custom options
+
+### Supported types
+
+Out of the box all built-in types are supported, plus a few others that are 
commonly used.
+
+Complete list:
+
+- `bool`
+- `float32`
+- `float64`
+- `int16`
+- `int32`
+- `int64`
+- `int8`
+- `int`
+- `string`
+- `uint16`
+- `uint32`
+- `uint64`
+- `uint8`
+- `uint`
+- `time.Duration`
+- `time.Location`
+- `encoding.TextUnmarshaler`
+- `url.URL`
+
+Pointers, slices and slices of pointers, and maps of those types are also 
supported.
+
+You may also add custom parsers for your types.
+
+### Tags
+
+The following tags are provided:
+
+- `env`: sets the environment variable name and optionally takes the tag 
options described below
+- `envDefault`: sets the default value for the field
+- `envPrefix`: can be used in a field that is a complex type to set a prefix 
to all environment variables used in it
+- `envSeparator`: sets the character to be used to separate items in slices 
and maps (default: `,`)
+- `envKeyValSeparator`: sets the character to be used to separate keys and 
their values in maps (default: `:`)
+
+### `env` tag options
+
+Here are all the options available for the `env` tag:
+
+- `,expand`: expands environment variables, e.g. `FOO_${BAR}`
+- `,file`: instructs that the content of the variable is a path to a file that 
should be read
+- `,init`: initialize nil pointers
+- `,notEmpty`: make the field errors if the environment variable is empty
+- `,required`: make the field errors if the environment variable is not set
+- `,unset`: unset the environment variable after use
+
+### Parse Options
+
+There are a few options available in the functions that end with `WithOptions`:
+
+- `Environment`: keys and values to be used instead of `os.Environ()`
+- `TagName`: specifies another tag name to use rather than the default `env`
+- `PrefixTagName`: specifies another prefix tag name to use rather than the 
default `envPrefix`
+- `DefaultValueTagName`: specifies another default tag name to use rather than 
the default `envDefault`
+- `RequiredIfNoDef`: set all `env` fields as required if they do not declare 
`envDefault`
+- `OnSet`: allows to hook into the `env` parsing and do something when a value 
is set
+- `Prefix`: prefix to be used in all environment variables
+- `UseFieldNameByDefault`: defines whether or not `env` should use the field 
name by default if the `env` key is missing
+- `FuncMap`: custom parse functions for custom types
+
+### Documentation and examples
+
+Examples are live in 
[pkg.go.dev](https://pkg.go.dev/github.com/caarlos0/env/v11),
+and also in the [example test file](./example_test.go).
+
+## Current state
+
+`env` is considered feature-complete.
+
+I do not intent to add any new features unless they really make sense, and are
+requested by many people.
+
+Eventual bug fixes will keep being merged.
+
+## Badges
+
+[![Release](https://img.shields.io/github/release/caarlos0/env.svg?style=for-the-badge)](https://github.com/goreleaser/goreleaser/releases/latest)
+[![Software 
License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=for-the-badge)](/LICENSE.md)
+[![Build 
status](https://img.shields.io/github/actions/workflow/status/caarlos0/env/build.yml?style=for-the-badge&branch=main)](https://github.com/caarlos0/env/actions?workflow=build)
+[![Codecov 
branch](https://img.shields.io/codecov/c/github/caarlos0/env/main.svg?style=for-the-badge)](https://codecov.io/gh/caarlos0/env)
+[![Go 
docs](https://img.shields.io/badge/godoc-reference-blue.svg?style=for-the-badge)](http://godoc.org/github.com/caarlos0/env/v11)
+[![Powered By: 
GoReleaser](https://img.shields.io/badge/powered%20by-goreleaser-green.svg?style=for-the-badge)](https://github.com/goreleaser)
+[![Conventional 
Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg?style=for-the-badge)](https://conventionalcommits.org)
+
+## Related projects
+
+- [envdoc](https://github.com/g4s8/envdoc) - generate documentation for 
environment variables from `env` tags
+
+## Stargazers over time
+
+[![Stargazers over 
time](https://starchart.cc/caarlos0/env.svg)](https://starchart.cc/caarlos0/env)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/github.com/caarlos0/env/v11/env.go 
new/vendor/github.com/caarlos0/env/v11/env.go
--- old/vendor/github.com/caarlos0/env/v11/env.go       1970-01-01 
01:00:00.000000000 +0100
+++ new/vendor/github.com/caarlos0/env/v11/env.go       2026-03-01 
17:08:27.000000000 +0100
@@ -0,0 +1,853 @@
+// Package env is a simple, zero-dependencies library to parse environment
+// variables into structs.
+//
+// Example:
+//
+//     type config struct {
+//             Home string `env:"HOME"`
+//     }
+//     // parse
+//     var cfg config
+//     err := env.Parse(&cfg)
+//     // or parse with generics
+//     cfg, err := env.ParseAs[config]()
+//
+// Check the examples and README for more detailed usage.
+package env
+
+import (
+       "encoding"
+       "fmt"
+       "net/url"
+       "os"
+       "reflect"
+       "strconv"
+       "strings"
+       "time"
+       "unicode"
+)
+
+var defaultBuiltInParsers = map[reflect.Kind]ParserFunc{ 
//nolint:gochecknoglobals
+       reflect.Bool: func(v string) (interface{}, error) {
+               return strconv.ParseBool(v)
+       },
+       reflect.String: func(v string) (interface{}, error) {
+               return v, nil
+       },
+       reflect.Int: func(v string) (interface{}, error) {
+               i, err := strconv.ParseInt(v, 10, 32)
+               return int(i), err
+       },
+       reflect.Int16: func(v string) (interface{}, error) {
+               i, err := strconv.ParseInt(v, 10, 16)
+               return int16(i), err
+       },
+       reflect.Int32: func(v string) (interface{}, error) {
+               i, err := strconv.ParseInt(v, 10, 32)
+               return int32(i), err
+       },
+       reflect.Int64: func(v string) (interface{}, error) {
+               return strconv.ParseInt(v, 10, 64)
+       },
+       reflect.Int8: func(v string) (interface{}, error) {
+               i, err := strconv.ParseInt(v, 10, 8)
+               return int8(i), err
+       },
+       reflect.Uint: func(v string) (interface{}, error) {
+               i, err := strconv.ParseUint(v, 10, 32)
+               return uint(i), err
+       },
+       reflect.Uint16: func(v string) (interface{}, error) {
+               i, err := strconv.ParseUint(v, 10, 16)
+               return uint16(i), err
+       },
+       reflect.Uint32: func(v string) (interface{}, error) {
+               i, err := strconv.ParseUint(v, 10, 32)
+               return uint32(i), err
+       },
+       reflect.Uint64: func(v string) (interface{}, error) {
+               i, err := strconv.ParseUint(v, 10, 64)
+               return i, err
+       },
+       reflect.Uint8: func(v string) (interface{}, error) {
+               i, err := strconv.ParseUint(v, 10, 8)
+               return uint8(i), err
+       },
+       reflect.Float64: func(v string) (interface{}, error) {
+               return strconv.ParseFloat(v, 64)
+       },
+       reflect.Float32: func(v string) (interface{}, error) {
+               f, err := strconv.ParseFloat(v, 32)
+               return float32(f), err
+       },
+}
+
+func defaultTypeParsers() map[reflect.Type]ParserFunc {
+       return map[reflect.Type]ParserFunc{
+               reflect.TypeOf(url.URL{}):       parseURL,
+               reflect.TypeOf(time.Nanosecond): parseDuration,
+               reflect.TypeOf(time.Location{}): parseLocation,
+       }
+}
+
+func parseURL(v string) (interface{}, error) {
+       u, err := url.Parse(v)
+       if err != nil {
+               return nil, newParseValueError("unable to parse URL", err)
+       }
+       return *u, nil
+}
+
+func parseDuration(v string) (interface{}, error) {
+       d, err := time.ParseDuration(v)
+       if err != nil {
+               return nil, newParseValueError("unable to parse duration", err)
+       }
+       return d, err
+}
+
+func parseLocation(v string) (interface{}, error) {
+       loc, err := time.LoadLocation(v)
+       if err != nil {
+               return nil, newParseValueError("unable to parse location", err)
+       }
+       return *loc, nil
+}
+
+// ParserFunc defines the signature of a function that can be used within
+// `Options`' `FuncMap`.
+type ParserFunc func(v string) (interface{}, error)
+
+// OnSetFn is a hook that can be run when a value is set.
+type OnSetFn func(tag string, value interface{}, isDefault bool)
+
+// processFieldFn is a function which takes all information about a field and 
processes it.
+type processFieldFn func(
+       refField reflect.Value,
+       refTypeField reflect.StructField,
+       opts Options,
+       fieldParams FieldParams,
+) error
+
+// Options for the parser.
+type Options struct {
+       // Environment keys and values that will be accessible for the service.
+       Environment map[string]string
+
+       // TagName specifies another tag name to use rather than the default 
'env'.
+       TagName string
+
+       // PrefixTagName specifies another prefix tag name to use rather than 
the default 'envPrefix'.
+       PrefixTagName string
+
+       // DefaultValueTagName specifies another default tag name to use rather 
than the default 'envDefault'.
+       DefaultValueTagName string
+
+       // RequiredIfNoDef automatically sets all fields as required if they do 
not
+       // declare 'envDefault'.
+       RequiredIfNoDef bool
+
+       // OnSet allows to run a function when a value is set.
+       OnSet OnSetFn
+
+       // Prefix define a prefix for every key.
+       Prefix string
+
+       // UseFieldNameByDefault defines whether or not `env` should use the 
field
+       // name by default if the `env` key is missing.
+       // Note that the field name will be "converted" to conform with 
environment
+       // variable names conventions.
+       UseFieldNameByDefault bool
+
+       // SetDefaultsForZeroValuesOnly defines whether to set defaults for 
zero values
+       // If the `env` variable for the value is not set
+       // and `envDefault` is set
+       // and the value is not a zero value for the the type
+       // and SetDefaultsForZeroValuesOnly=true
+       // the value from `envDefault` will be ignored
+       // Useful for mixing default values from `envDefault` and struct 
initialization
+       SetDefaultsForZeroValuesOnly bool
+
+       // Custom parse functions for different types.
+       FuncMap map[reflect.Type]ParserFunc
+
+       // Used internally. maps the env variable key to its resolved string 
value.
+       // (for env var expansion)
+       rawEnvVars map[string]string
+}
+
+func (opts *Options) getRawEnv(s string) string {
+       val := opts.rawEnvVars[s]
+       if val == "" {
+               val = opts.Environment[s]
+       }
+       return os.Expand(val, opts.getRawEnv)
+}
+
+func defaultOptions() Options {
+       return Options{
+               TagName:             "env",
+               PrefixTagName:       "envPrefix",
+               DefaultValueTagName: "envDefault",
+               Environment:         toMap(os.Environ()),
+               FuncMap:             defaultTypeParsers(),
+               rawEnvVars:          make(map[string]string),
+       }
+}
+
+func mergeOptions[T any](target, source *T) {
+       targetPtr := reflect.ValueOf(target).Elem()
+       sourcePtr := reflect.ValueOf(source).Elem()
+
+       targetType := targetPtr.Type()
+       for i := 0; i < targetPtr.NumField(); i++ {
+               fieldName := targetType.Field(i).Name
+               targetField := targetPtr.Field(i)
+               sourceField := sourcePtr.FieldByName(fieldName)
+
+               if targetField.CanSet() && !isZero(sourceField) {
+                       // FuncMaps are being merged, while Environments must 
be overwritten
+                       if fieldName == "FuncMap" {
+                               if !sourceField.IsZero() {
+                                       iter := sourceField.MapRange()
+                                       for iter.Next() {
+                                               
targetField.SetMapIndex(iter.Key(), iter.Value())
+                                       }
+                               }
+                       } else {
+                               targetField.Set(sourceField)
+                       }
+               }
+       }
+}
+
+func isZero(v reflect.Value) bool {
+       switch v.Kind() {
+       case reflect.Func, reflect.Map, reflect.Slice:
+               return v.IsNil()
+       default:
+               zero := reflect.Zero(v.Type())
+               return v.Interface() == zero.Interface()
+       }
+}
+
+func customOptions(opts Options) Options {
+       defOpts := defaultOptions()
+       mergeOptions(&defOpts, &opts)
+       return defOpts
+}
+
+func optionsWithSliceEnvPrefix(opts Options, index int) Options {
+       return Options{
+               Environment:                  opts.Environment,
+               TagName:                      opts.TagName,
+               PrefixTagName:                opts.PrefixTagName,
+               DefaultValueTagName:          opts.DefaultValueTagName,
+               RequiredIfNoDef:              opts.RequiredIfNoDef,
+               OnSet:                        opts.OnSet,
+               Prefix:                       fmt.Sprintf("%s%d_", opts.Prefix, 
index),
+               UseFieldNameByDefault:        opts.UseFieldNameByDefault,
+               SetDefaultsForZeroValuesOnly: opts.SetDefaultsForZeroValuesOnly,
+               FuncMap:                      opts.FuncMap,
+               rawEnvVars:                   opts.rawEnvVars,
+       }
+}
+
+func optionsWithEnvPrefix(field reflect.StructField, opts Options) Options {
+       return Options{
+               Environment:                  opts.Environment,
+               TagName:                      opts.TagName,
+               PrefixTagName:                opts.PrefixTagName,
+               DefaultValueTagName:          opts.DefaultValueTagName,
+               RequiredIfNoDef:              opts.RequiredIfNoDef,
+               OnSet:                        opts.OnSet,
+               Prefix:                       opts.Prefix + 
field.Tag.Get(opts.PrefixTagName),
+               UseFieldNameByDefault:        opts.UseFieldNameByDefault,
+               SetDefaultsForZeroValuesOnly: opts.SetDefaultsForZeroValuesOnly,
+               FuncMap:                      opts.FuncMap,
+               rawEnvVars:                   opts.rawEnvVars,
+       }
+}
+
+// Parse parses a struct containing `env` tags and loads its values from
+// environment variables.
+func Parse(v interface{}) error {
+       return parseInternal(v, setField, defaultOptions())
+}
+
+// ParseWithOptions parses a struct containing `env` tags and loads its values 
from
+// environment variables.
+func ParseWithOptions(v interface{}, opts Options) error {
+       return parseInternal(v, setField, customOptions(opts))
+}
+
+// ParseAs parses the given struct type containing `env` tags and loads its
+// values from environment variables.
+func ParseAs[T any]() (T, error) {
+       var t T
+       err := Parse(&t)
+       return t, err
+}
+
+// ParseAsWithOptions parses the given struct type containing `env` tags and
+// loads its values from environment variables.
+func ParseAsWithOptions[T any](opts Options) (T, error) {
+       var t T
+       err := ParseWithOptions(&t, opts)
+       return t, err
+}
+
+// Must panic is if err is not nil, and returns t otherwise.
+func Must[T any](t T, err error) T {
+       if err != nil {
+               panic(err)
+       }
+       return t
+}
+
+// GetFieldParams parses a struct containing `env` tags and returns 
information about
+// tags it found.
+func GetFieldParams(v interface{}) ([]FieldParams, error) {
+       return GetFieldParamsWithOptions(v, defaultOptions())
+}
+
+// GetFieldParamsWithOptions parses a struct containing `env` tags and returns 
information about
+// tags it found.
+func GetFieldParamsWithOptions(v interface{}, opts Options) ([]FieldParams, 
error) {
+       var result []FieldParams
+       err := parseInternal(
+               v,
+               func(_ reflect.Value, _ reflect.StructField, _ Options, 
fieldParams FieldParams) error {
+                       if fieldParams.OwnKey != "" {
+                               result = append(result, fieldParams)
+                       }
+                       return nil
+               },
+               customOptions(opts),
+       )
+       if err != nil {
+               return nil, err
+       }
+
+       return result, nil
+}
+
+func parseInternal(v interface{}, processField processFieldFn, opts Options) 
error {
+       ptrRef := reflect.ValueOf(v)
+       if ptrRef.Kind() != reflect.Ptr {
+               return newAggregateError(NotStructPtrError{})
+       }
+       ref := ptrRef.Elem()
+       if ref.Kind() != reflect.Struct {
+               return newAggregateError(NotStructPtrError{})
+       }
+
+       return doParse(ref, processField, opts)
+}
+
+func doParse(ref reflect.Value, processField processFieldFn, opts Options) 
error {
+       refType := ref.Type()
+
+       var agrErr AggregateError
+
+       for i := 0; i < refType.NumField(); i++ {
+               refField := ref.Field(i)
+               refTypeField := refType.Field(i)
+
+               if err := doParseField(refField, refTypeField, processField, 
opts); err != nil {
+                       if val, ok := err.(AggregateError); ok {
+                               agrErr.Errors = append(agrErr.Errors, 
val.Errors...)
+                       } else {
+                               agrErr.Errors = append(agrErr.Errors, err)
+                       }
+               }
+       }
+
+       if len(agrErr.Errors) == 0 {
+               return nil
+       }
+
+       return agrErr
+}
+
+func doParseField(
+       refField reflect.Value,
+       refTypeField reflect.StructField,
+       processField processFieldFn,
+       opts Options,
+) error {
+       if !refField.CanSet() {
+               return nil
+       }
+       if refField.Kind() == reflect.Ptr && refField.Elem().Kind() == 
reflect.Struct && !refField.IsNil() {
+               return parseInternal(refField.Interface(), processField, 
optionsWithEnvPrefix(refTypeField, opts))
+       }
+       if refField.Kind() == reflect.Struct && refField.CanAddr() && 
refField.Type().Name() == "" {
+               return parseInternal(refField.Addr().Interface(), processField, 
optionsWithEnvPrefix(refTypeField, opts))
+       }
+
+       params, err := parseFieldParams(refTypeField, opts)
+       if err != nil {
+               return err
+       }
+
+       if params.Ignored {
+               return nil
+       }
+
+       if err := processField(refField, refTypeField, opts, params); err != 
nil {
+               return err
+       }
+
+       if params.Init && isInvalidPtr(refField) {
+               refField.Set(reflect.New(refField.Type().Elem()))
+               refField = refField.Elem()
+       }
+
+       if refField.Kind() == reflect.Struct {
+               return doParse(refField, processField, 
optionsWithEnvPrefix(refTypeField, opts))
+       }
+
+       if isSliceOfStructs(refTypeField) {
+               return doParseSlice(refField, processField, 
optionsWithEnvPrefix(refTypeField, opts))
+       }
+
+       return nil
+}
+
+func isSliceOfStructs(refTypeField reflect.StructField) bool {
+       field := refTypeField.Type
+
+       // *[]struct
+       if field.Kind() == reflect.Ptr {
+               field = field.Elem()
+               if field.Kind() == reflect.Slice && field.Elem().Kind() == 
reflect.Struct {
+                       return true
+               }
+       }
+
+       // []struct{}
+       if field.Kind() == reflect.Slice && field.Elem().Kind() == 
reflect.Struct {
+               return true
+       }
+
+       return false
+}
+
+func doParseSlice(ref reflect.Value, processField processFieldFn, opts 
Options) error {
+       if opts.Prefix != "" && !strings.HasSuffix(opts.Prefix, 
string(underscore)) {
+               opts.Prefix += string(underscore)
+       }
+
+       var environments []string
+       for environment := range opts.Environment {
+               if strings.HasPrefix(environment, opts.Prefix) {
+                       environments = append(environments, environment)
+               }
+       }
+
+       if len(environments) > 0 {
+               counter := 0
+               for finished := false; !finished; {
+                       finished = true
+                       prefix := fmt.Sprintf("%s%d%c", opts.Prefix, counter, 
underscore)
+                       for _, variable := range environments {
+                               if strings.HasPrefix(variable, prefix) {
+                                       counter++
+                                       finished = false
+                                       break
+                               }
+                       }
+               }
+
+               sliceType := ref.Type()
+               var initialized int
+               if reflect.Ptr == ref.Kind() {
+                       sliceType = sliceType.Elem()
+                       // Due to the rest of code the pre-initialized slice 
has no chance for this situation
+                       initialized = 0
+               } else {
+                       initialized = ref.Len()
+               }
+
+               var capacity int
+               if capacity = initialized; counter > initialized {
+                       capacity = counter
+               }
+               result := reflect.MakeSlice(sliceType, capacity, capacity)
+               for i := 0; i < capacity; i++ {
+                       item := result.Index(i)
+                       if i < initialized {
+                               item.Set(ref.Index(i))
+                       }
+                       if err := doParse(item, processField, 
optionsWithSliceEnvPrefix(opts, i)); err != nil {
+                               return err
+                       }
+               }
+
+               if result.Len() > 0 {
+                       if reflect.Ptr == ref.Kind() {
+                               resultPtr := reflect.New(sliceType)
+                               resultPtr.Elem().Set(result)
+                               result = resultPtr
+                       }
+                       ref.Set(result)
+               }
+       }
+
+       return nil
+}
+
+func setField(refField reflect.Value, refTypeField reflect.StructField, opts 
Options, fieldParams FieldParams) error {
+       value, err := get(fieldParams, opts)
+       if err != nil {
+               return err
+       }
+
+       if value != "" && (!opts.SetDefaultsForZeroValuesOnly || 
refField.IsZero()) {
+               return set(refField, refTypeField, value, opts.FuncMap)
+       }
+
+       return nil
+}
+
+const underscore rune = '_'
+
+func toEnvName(input string) string {
+       var output []rune
+       for i, c := range input {
+               if c == underscore {
+                       continue
+               }
+               if len(output) > 0 && unicode.IsUpper(c) {
+                       if len(input) > i+1 {
+                               peek := rune(input[i+1])
+                               if unicode.IsLower(peek) || 
unicode.IsLower(rune(input[i-1])) {
+                                       output = append(output, underscore)
+                               }
+                       }
+               }
+               output = append(output, unicode.ToUpper(c))
+       }
+       return string(output)
+}
+
+// FieldParams contains information about parsed field tags.
+type FieldParams struct {
+       OwnKey          string
+       Key             string
+       DefaultValue    string
+       HasDefaultValue bool
+       Required        bool
+       LoadFile        bool
+       Unset           bool
+       NotEmpty        bool
+       Expand          bool
+       Init            bool
+       Ignored         bool
+}
+
+func parseFieldParams(field reflect.StructField, opts Options) (FieldParams, 
error) {
+       ownKey, tags := parseKeyForOption(field.Tag.Get(opts.TagName))
+       if ownKey == "" && opts.UseFieldNameByDefault {
+               ownKey = toEnvName(field.Name)
+       }
+
+       defaultValue, hasDefaultValue := 
field.Tag.Lookup(opts.DefaultValueTagName)
+
+       result := FieldParams{
+               OwnKey:          ownKey,
+               Key:             opts.Prefix + ownKey,
+               Required:        opts.RequiredIfNoDef,
+               DefaultValue:    defaultValue,
+               HasDefaultValue: hasDefaultValue,
+               Ignored:         ownKey == "-",
+       }
+
+       for _, tag := range tags {
+               switch tag {
+               case "":
+                       continue
+               case "file":
+                       result.LoadFile = true
+               case "required":
+                       result.Required = true
+               case "unset":
+                       result.Unset = true
+               case "notEmpty":
+                       result.NotEmpty = true
+               case "expand":
+                       result.Expand = true
+               case "init":
+                       result.Init = true
+               case "-":
+                       result.Ignored = true
+               default:
+                       return FieldParams{}, newNoSupportedTagOptionError(tag)
+               }
+       }
+
+       return result, nil
+}
+
+func get(fieldParams FieldParams, opts Options) (val string, err error) {
+       var exists, isDefault bool
+
+       val, exists, isDefault = getOr(
+               fieldParams.Key,
+               fieldParams.DefaultValue,
+               fieldParams.HasDefaultValue,
+               opts.Environment,
+       )
+
+       if fieldParams.Expand {
+               val = os.Expand(val, opts.getRawEnv)
+       }
+
+       opts.rawEnvVars[fieldParams.OwnKey] = val
+
+       if fieldParams.Unset {
+               defer os.Unsetenv(fieldParams.Key)
+       }
+
+       if fieldParams.Required && !exists && fieldParams.OwnKey != "" {
+               return "", newVarIsNotSetError(fieldParams.Key)
+       }
+
+       if fieldParams.NotEmpty && val == "" {
+               return "", newEmptyVarError(fieldParams.Key)
+       }
+
+       if fieldParams.LoadFile && val != "" {
+               filename := val
+               val, err = getFromFile(filename)
+               if err != nil {
+                       return "", newLoadFileContentError(filename, 
fieldParams.Key, err)
+               }
+       }
+
+       if opts.OnSet != nil {
+               if fieldParams.OwnKey != "" {
+                       opts.OnSet(fieldParams.Key, val, isDefault)
+               }
+       }
+       return val, err
+}
+
+// split the env tag's key into the expected key and desired option, if any.
+func parseKeyForOption(key string) (string, []string) {
+       opts := strings.Split(key, ",")
+       return opts[0], opts[1:]
+}
+
+func getFromFile(filename string) (value string, err error) {
+       b, err := os.ReadFile(filename)
+       return string(b), err
+}
+
+func getOr(key, defaultValue string, defExists bool, envs map[string]string) 
(val string, exists, isDefault bool) {
+       value, exists := envs[key]
+       switch {
+       case (!exists || key == "") && defExists:
+               return defaultValue, true, true
+       case exists && value == "" && defExists:
+               return defaultValue, true, true
+       case !exists:
+               return "", false, false
+       }
+
+       return value, true, false
+}
+
+func set(field reflect.Value, sf reflect.StructField, value string, funcMap 
map[reflect.Type]ParserFunc) error {
+       if tm := asTextUnmarshaler(field); tm != nil {
+               if err := tm.UnmarshalText([]byte(value)); err != nil {
+                       return newParseError(sf, err)
+               }
+               return nil
+       }
+
+       typee := sf.Type
+       fieldee := field
+       if typee.Kind() == reflect.Ptr {
+               typee = typee.Elem()
+               fieldee = field.Elem()
+       }
+
+       parserFunc, ok := funcMap[typee]
+       if ok {
+               val, err := parserFunc(value)
+               if err != nil {
+                       return newParseError(sf, err)
+               }
+
+               fieldee.Set(reflect.ValueOf(val))
+               return nil
+       }
+
+       parserFunc, ok = defaultBuiltInParsers[typee.Kind()]
+       if ok {
+               val, err := parserFunc(value)
+               if err != nil {
+                       return newParseError(sf, err)
+               }
+
+               fieldee.Set(reflect.ValueOf(val).Convert(typee))
+               return nil
+       }
+
+       switch field.Kind() {
+       case reflect.Slice:
+               return handleSlice(field, value, sf, funcMap)
+       case reflect.Map:
+               return handleMap(field, value, sf, funcMap)
+       }
+
+       return newNoParserError(sf)
+}
+
+func handleSlice(field reflect.Value, value string, sf reflect.StructField, 
funcMap map[reflect.Type]ParserFunc) error {
+       separator := sf.Tag.Get("envSeparator")
+       if separator == "" {
+               separator = ","
+       }
+       parts := strings.Split(value, separator)
+
+       typee := sf.Type.Elem()
+       if typee.Kind() == reflect.Ptr {
+               typee = typee.Elem()
+       }
+
+       if _, ok := reflect.New(typee).Interface().(encoding.TextUnmarshaler); 
ok {
+               return parseTextUnmarshalers(field, parts, sf)
+       }
+
+       parserFunc, ok := funcMap[typee]
+       if !ok {
+               parserFunc, ok = defaultBuiltInParsers[typee.Kind()]
+               if !ok {
+                       return newNoParserError(sf)
+               }
+       }
+
+       result := reflect.MakeSlice(sf.Type, 0, len(parts))
+       for _, part := range parts {
+               r, err := parserFunc(part)
+               if err != nil {
+                       return newParseError(sf, err)
+               }
+               v := reflect.ValueOf(r).Convert(typee)
+               if sf.Type.Elem().Kind() == reflect.Ptr {
+                       v = reflect.New(typee)
+                       v.Elem().Set(reflect.ValueOf(r).Convert(typee))
+               }
+               result = reflect.Append(result, v)
+       }
+       field.Set(result)
+       return nil
+}
+
+func handleMap(field reflect.Value, value string, sf reflect.StructField, 
funcMap map[reflect.Type]ParserFunc) error {
+       keyType := sf.Type.Key()
+       keyParserFunc, ok := funcMap[keyType]
+       if !ok {
+               keyParserFunc, ok = defaultBuiltInParsers[keyType.Kind()]
+               if !ok {
+                       return newNoParserError(sf)
+               }
+       }
+
+       elemType := sf.Type.Elem()
+       elemParserFunc, ok := funcMap[elemType]
+       if !ok {
+               elemParserFunc, ok = defaultBuiltInParsers[elemType.Kind()]
+               if !ok {
+                       return newNoParserError(sf)
+               }
+       }
+
+       separator := sf.Tag.Get("envSeparator")
+       if separator == "" {
+               separator = ","
+       }
+
+       keyValSeparator := sf.Tag.Get("envKeyValSeparator")
+       if keyValSeparator == "" {
+               keyValSeparator = ":"
+       }
+
+       result := reflect.MakeMap(sf.Type)
+       for _, part := range strings.Split(value, separator) {
+               pairs := strings.SplitN(part, keyValSeparator, 2)
+               if len(pairs) != 2 {
+                       return newParseError(sf, fmt.Errorf(`%q should be in 
"key%svalue" format`, part, keyValSeparator))
+               }
+
+               key, err := keyParserFunc(pairs[0])
+               if err != nil {
+                       return newParseError(sf, err)
+               }
+
+               elem, err := elemParserFunc(pairs[1])
+               if err != nil {
+                       return newParseError(sf, err)
+               }
+
+               result.SetMapIndex(reflect.ValueOf(key).Convert(keyType), 
reflect.ValueOf(elem).Convert(elemType))
+       }
+
+       field.Set(result)
+       return nil
+}
+
+func asTextUnmarshaler(field reflect.Value) encoding.TextUnmarshaler {
+       if field.Kind() == reflect.Ptr {
+               if field.IsNil() {
+                       field.Set(reflect.New(field.Type().Elem()))
+               }
+       } else if field.CanAddr() {
+               field = field.Addr()
+       }
+
+       tm, ok := field.Interface().(encoding.TextUnmarshaler)
+       if !ok {
+               return nil
+       }
+       return tm
+}
+
+func parseTextUnmarshalers(field reflect.Value, data []string, sf 
reflect.StructField) error {
+       s := len(data)
+       elemType := field.Type().Elem()
+       slice := reflect.MakeSlice(reflect.SliceOf(elemType), s, s)
+       for i, v := range data {
+               sv := slice.Index(i)
+               kind := sv.Kind()
+               if kind == reflect.Ptr {
+                       sv = reflect.New(elemType.Elem())
+               } else {
+                       sv = sv.Addr()
+               }
+               tm := sv.Interface().(encoding.TextUnmarshaler)
+               if err := tm.UnmarshalText([]byte(v)); err != nil {
+                       return newParseError(sf, err)
+               }
+               if kind == reflect.Ptr {
+                       slice.Index(i).Set(sv)
+               }
+       }
+
+       field.Set(slice)
+
+       return nil
+}
+
+// ToMap Converts list of env vars as provided by os.Environ() to map you
+// can use as Options.Environment field
+func ToMap(env []string) map[string]string {
+       return toMap(env)
+}
+
+func isInvalidPtr(v reflect.Value) bool {
+       return reflect.Ptr == v.Kind() && v.Elem().Kind() == reflect.Invalid
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/github.com/caarlos0/env/v11/env_tomap.go 
new/vendor/github.com/caarlos0/env/v11/env_tomap.go
--- old/vendor/github.com/caarlos0/env/v11/env_tomap.go 1970-01-01 
01:00:00.000000000 +0100
+++ new/vendor/github.com/caarlos0/env/v11/env_tomap.go 2026-03-01 
17:08:27.000000000 +0100
@@ -0,0 +1,16 @@
+//go:build !windows
+
+package env
+
+import "strings"
+
+func toMap(env []string) map[string]string {
+       r := map[string]string{}
+       for _, e := range env {
+               p := strings.SplitN(e, "=", 2)
+               if len(p) == 2 {
+                       r[p[0]] = p[1]
+               }
+       }
+       return r
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/vendor/github.com/caarlos0/env/v11/env_tomap_windows.go 
new/vendor/github.com/caarlos0/env/v11/env_tomap_windows.go
--- old/vendor/github.com/caarlos0/env/v11/env_tomap_windows.go 1970-01-01 
01:00:00.000000000 +0100
+++ new/vendor/github.com/caarlos0/env/v11/env_tomap_windows.go 2026-03-01 
17:08:27.000000000 +0100
@@ -0,0 +1,29 @@
+//go:build windows
+
+package env
+
+import "strings"
+
+func toMap(env []string) map[string]string {
+       r := map[string]string{}
+       for _, e := range env {
+               p := strings.SplitN(e, "=", 2)
+
+               // On Windows, environment variables can start with '='. If so, 
Split at next character.
+               // See env_windows.go in the Go source: 
https://github.com/golang/go/blob/master/src/syscall/env_windows.go#L58
+               prefixEqualSign := false
+               if len(e) > 0 && e[0] == '=' {
+                       e = e[1:]
+                       prefixEqualSign = true
+               }
+               p = strings.SplitN(e, "=", 2)
+               if prefixEqualSign {
+                       p[0] = "=" + p[0]
+               }
+
+               if len(p) == 2 {
+                       r[p[0]] = p[1]
+               }
+       }
+       return r
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/github.com/caarlos0/env/v11/error.go 
new/vendor/github.com/caarlos0/env/v11/error.go
--- old/vendor/github.com/caarlos0/env/v11/error.go     1970-01-01 
01:00:00.000000000 +0100
+++ new/vendor/github.com/caarlos0/env/v11/error.go     2026-03-01 
17:08:27.000000000 +0100
@@ -0,0 +1,173 @@
+package env
+
+import (
+       "fmt"
+       "reflect"
+       "strings"
+)
+
+// AggregateError is an aggregated error wrapper to combine gathered errors.
+// This allows either to display all errors or convert them individually
+// List of the available errors
+// ParseError
+// NotStructPtrError
+// NoParserError
+// NoSupportedTagOptionError
+// VarIsNotSetError
+// EmptyVarError
+// LoadFileContentError
+// ParseValueError
+type AggregateError struct {
+       Errors []error
+}
+
+func newAggregateError(initErr error) error {
+       return AggregateError{
+               []error{
+                       initErr,
+               },
+       }
+}
+
+func (e AggregateError) Error() string {
+       var sb strings.Builder
+
+       sb.WriteString("env:")
+
+       for _, err := range e.Errors {
+               fmt.Fprintf(&sb, " %v;", err.Error())
+       }
+
+       return strings.TrimRight(sb.String(), ";")
+}
+
+// Unwrap implements std errors.Join go1.20 compatibility
+func (e AggregateError) Unwrap() []error {
+       return e.Errors
+}
+
+// Is conforms with errors.Is.
+func (e AggregateError) Is(err error) bool {
+       for _, ie := range e.Errors {
+               if reflect.TypeOf(ie) == reflect.TypeOf(err) {
+                       return true
+               }
+       }
+       return false
+}
+
+// ParseError occurs when it's impossible to convert the value for given type.
+type ParseError struct {
+       Name string
+       Type reflect.Type
+       Err  error
+}
+
+func newParseError(sf reflect.StructField, err error) error {
+       return ParseError{sf.Name, sf.Type, err}
+}
+
+func (e ParseError) Error() string {
+       return fmt.Sprintf("parse error on field %q of type %q: %v", e.Name, 
e.Type, e.Err)
+}
+
+// NotStructPtrError occurs when pass something that is not a pointer to a 
struct to Parse.
+type NotStructPtrError struct{}
+
+func (e NotStructPtrError) Error() string {
+       return "expected a pointer to a Struct"
+}
+
+// NoParserError occurs when there is no parser provided for given type.
+type NoParserError struct {
+       Name string
+       Type reflect.Type
+}
+
+func newNoParserError(sf reflect.StructField) error {
+       return NoParserError{sf.Name, sf.Type}
+}
+
+func (e NoParserError) Error() string {
+       return fmt.Sprintf("no parser found for field %q of type %q", e.Name, 
e.Type)
+}
+
+// NoSupportedTagOptionError occurs when the given tag is not supported.
+// Built-in supported tags: "", "file", "required", "unset", "notEmpty",
+// "expand", "envDefault", and "envSeparator".
+type NoSupportedTagOptionError struct {
+       Tag string
+}
+
+func newNoSupportedTagOptionError(tag string) error {
+       return NoSupportedTagOptionError{tag}
+}
+
+func (e NoSupportedTagOptionError) Error() string {
+       return fmt.Sprintf("tag option %q not supported", e.Tag)
+}
+
+// EnvVarIsNotSetError occurs when the required variable is not set.
+//
+// Deprecated: use VarIsNotSetError.
+type EnvVarIsNotSetError = VarIsNotSetError
+
+// VarIsNotSetError occurs when the required variable is not set.
+type VarIsNotSetError struct {
+       Key string
+}
+
+func newVarIsNotSetError(key string) error {
+       return VarIsNotSetError{key}
+}
+
+func (e VarIsNotSetError) Error() string {
+       return fmt.Sprintf(`required environment variable %q is not set`, e.Key)
+}
+
+// EmptyEnvVarError occurs when the variable which must be not empty is 
existing but has an empty value
+//
+// Deprecated: use EmptyVarError.
+type EmptyEnvVarError = EmptyVarError
+
+// EmptyVarError occurs when the variable which must be not empty is existing 
but has an empty value
+type EmptyVarError struct {
+       Key string
+}
+
+func newEmptyVarError(key string) error {
+       return EmptyVarError{key}
+}
+
+func (e EmptyVarError) Error() string {
+       return fmt.Sprintf("environment variable %q should not be empty", e.Key)
+}
+
+// LoadFileContentError occurs when it's impossible to load the value from the 
file.
+type LoadFileContentError struct {
+       Filename string
+       Key      string
+       Err      error
+}
+
+func newLoadFileContentError(filename, key string, err error) error {
+       return LoadFileContentError{filename, key, err}
+}
+
+func (e LoadFileContentError) Error() string {
+       return fmt.Sprintf("could not load content of file %q from variable %s: 
%v", e.Filename, e.Key, e.Err)
+}
+
+// ParseValueError occurs when it's impossible to convert value using given 
parser.
+type ParseValueError struct {
+       Msg string
+       Err error
+}
+
+func newParseValueError(message string, err error) error {
+       return ParseValueError{message, err}
+}
+
+func (e ParseValueError) Error() string {
+       return fmt.Sprintf("%s: %v", e.Msg, e.Err)
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/github.com/gonvenience/neat/output_yaml.go 
new/vendor/github.com/gonvenience/neat/output_yaml.go
--- old/vendor/github.com/gonvenience/neat/output_yaml.go       2026-02-22 
13:42:39.000000000 +0100
+++ new/vendor/github.com/gonvenience/neat/output_yaml.go       2026-03-01 
17:08:27.000000000 +0100
@@ -23,6 +23,7 @@
 import (
        "fmt"
        "reflect"
+       "regexp"
        "strings"
        "time"
 
@@ -33,7 +34,7 @@
 )
 
 var (
-       yamlReservedKeywords = []string{"true", "false", "null"}
+       numberRegEx = regexp.MustCompile(`^(-|\+)?[0-9.e+]+$`)
 
        // YAML Spec regarding timestamp: https://yaml.org/type/timestamp.html
        yamlTimeLayouts = [...]string{
@@ -371,7 +372,7 @@
        }
 
        // check if string matches one of the known reserved keywords
-       for _, chk := range yamlReservedKeywords {
+       for _, chk := range []string{"true", "false", "null", ".nan", ".inf", 
"-.inf", "+.inf"} {
                if node.Value == chk {
                        return true
                }
@@ -382,6 +383,11 @@
                return true
        }
 
+       // check if string looks like a number
+       if numberRegEx.MatchString(node.Value) {
+               return true
+       }
+
        // check if string contains special characters
        return strings.ContainsAny(node.Value, " *&:,")
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/golang.org/x/net/html/iter.go 
new/vendor/golang.org/x/net/html/iter.go
--- old/vendor/golang.org/x/net/html/iter.go    2026-02-22 13:42:39.000000000 
+0100
+++ new/vendor/golang.org/x/net/html/iter.go    2026-03-01 17:08:27.000000000 
+0100
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-//go:build go1.23
-
 package html
 
 import "iter"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/modules.txt new/vendor/modules.txt
--- old/vendor/modules.txt      2026-02-22 13:42:39.000000000 +0100
+++ new/vendor/modules.txt      2026-03-01 17:08:27.000000000 +0100
@@ -5,6 +5,9 @@
 # github.com/Masterminds/semver/v3 v3.4.0
 ## explicit; go 1.21
 github.com/Masterminds/semver/v3
+# github.com/caarlos0/env/v11 v11.4.0
+## explicit; go 1.18
+github.com/caarlos0/env/v11
 # github.com/cpuguy83/go-md2man/v2 v2.0.7
 ## explicit; go 1.12
 github.com/cpuguy83/go-md2man/v2/md2man
@@ -23,8 +26,8 @@
 # github.com/gonvenience/idem v0.0.3
 ## explicit; go 1.24.0
 github.com/gonvenience/idem
-# github.com/gonvenience/neat v1.3.17
-## explicit; go 1.24.0
+# github.com/gonvenience/neat v1.3.18
+## explicit; go 1.25.0
 github.com/gonvenience/neat
 # github.com/gonvenience/term v1.0.5
 ## explicit; go 1.24.0
@@ -130,8 +133,8 @@
 # golang.org/x/mod v0.33.0
 ## explicit; go 1.24.0
 golang.org/x/mod/semver
-# golang.org/x/net v0.50.0
-## explicit; go 1.24.0
+# golang.org/x/net v0.51.0
+## explicit; go 1.25.0
 golang.org/x/net/html
 golang.org/x/net/html/atom
 golang.org/x/net/html/charset

Reply via email to