Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package updatecli for openSUSE:Factory checked in at 2025-09-18 21:10:54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/updatecli (Old) and /work/SRC/openSUSE:Factory/.updatecli.new.27445 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "updatecli" Thu Sep 18 21:10:54 2025 rev:28 rq:1305654 version:0.107.0 Changes: -------- --- /work/SRC/openSUSE:Factory/updatecli/updatecli.changes 2025-08-28 17:19:39.197691771 +0200 +++ /work/SRC/openSUSE:Factory/.updatecli.new.27445/updatecli.changes 2025-09-18 21:11:34.737719189 +0200 @@ -1,0 +2,27 @@ +Thu Sep 18 05:43:16 UTC 2025 - Johannes Kastl <[email protected]> + +- Update to version 0.107.0: + * Changes + - chore: test project telemetry using Scarf.io @olblak (#5972) + * Features + - feat: Allow to use regex in Golang module matchingRule + @olblak (#5986) + - feat(file,golang): improve changelog generation with capture + groups @ryancurrah (#5987) + - feat(golang): support replace instruction in Go mod file + @olblak (#5963) + * Bug Fixes + - fix: helm changelog capitalize function @olblak (#5955) + * Maintenance + - deps(go): bump module github.com/spf13/cobra to v1.10.1 + @updateclibot[bot] (#5988) + - deps(github/action): bump all dependencies @updateclibot[bot] + (#5995) + - deps: Bump Golang version to 1.25.1 @updateclibot[bot] + (#5964) + - deps(go): bump module github.com/moby/buildkit to v0.24.0 + @updateclibot[bot] (#5962) + - deps(github/action): bump all dependencies @updateclibot[bot] + (#5952) + +------------------------------------------------------------------- Old: ---- updatecli-0.106.0.obscpio New: ---- updatecli-0.107.0.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ updatecli.spec ++++++ --- /var/tmp/diff_new_pack.YWDx3W/_old 2025-09-18 21:11:36.245782582 +0200 +++ /var/tmp/diff_new_pack.YWDx3W/_new 2025-09-18 21:11:36.253782918 +0200 @@ -17,7 +17,7 @@ Name: updatecli -Version: 0.106.0 +Version: 0.107.0 Release: 0 Summary: A Declarative Dependency Management tool License: Apache-2.0 @@ -26,8 +26,8 @@ Source1: vendor.tar.gz BuildRequires: bash-completion BuildRequires: fish +BuildRequires: go1.25 >= 1.25.1 BuildRequires: zsh -BuildRequires: golang(API) >= 1.25 %description Updatecli is a tool used to apply file update strategies. Designed to be used ++++++ _service ++++++ --- /var/tmp/diff_new_pack.YWDx3W/_old 2025-09-18 21:11:36.441790822 +0200 +++ /var/tmp/diff_new_pack.YWDx3W/_new 2025-09-18 21:11:36.457791494 +0200 @@ -3,7 +3,7 @@ <param name="url">https://github.com/updatecli/updatecli</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">v0.106.0</param> + <param name="revision">v0.107.0</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> <param name="changesgenerate">enable</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.YWDx3W/_old 2025-09-18 21:11:36.541795025 +0200 +++ /var/tmp/diff_new_pack.YWDx3W/_new 2025-09-18 21:11:36.549795362 +0200 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/updatecli/updatecli</param> - <param name="changesrevision">755b5706f716b4929c1db506d9025b907cc95728</param></service></servicedata> + <param name="changesrevision">dd0b9f59f90eeffd3baa5281ff9a1656741a22c9</param></service></servicedata> (No newline at EOF) ++++++ updatecli-0.106.0.obscpio -> updatecli-0.107.0.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/updatecli-0.106.0/README.adoc new/updatecli-0.107.0/README.adoc --- old/updatecli-0.106.0/README.adoc 2025-08-27 15:10:58.000000000 +0200 +++ new/updatecli-0.107.0/README.adoc 2025-09-05 13:54:55.000000000 +0200 @@ -52,7 +52,7 @@ Once you have the three files locally, you can execute the following command ``` -cosign verify-blob --certificate-identity-regexp "https://github.com/updatecli/updatecli" --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' --cert https://github.com/updatecli/updatecli/releases/download/v0.105.1/checksums.txt.pem --signature https://github.com/updatecli/updatecli/releases/download/v0.105.1/checksums.txt.sig checksums.txt +cosign verify-blob --certificate-identity-regexp "https://github.com/updatecli/updatecli" --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' --cert https://github.com/updatecli/updatecli/releases/download/v0.106.0/checksums.txt.pem --signature https://github.com/updatecli/updatecli/releases/download/v0.106.0/checksums.txt.sig checksums.txt ``` A successful output looks like @@ -76,7 +76,7 @@ **Verify Container signature** ``` -cosign verify --certificate-identity-regexp "https://github.com/updatecli/updatecli" --certificate-oidc-issuer "https://token.actions.githubusercontent.com" ghcr.io/updatecli/updatecli:v0.105.1 +cosign verify --certificate-identity-regexp "https://github.com/updatecli/updatecli" --certificate-oidc-issuer "https://token.actions.githubusercontent.com" ghcr.io/updatecli/updatecli:v0.106.0 ``` == Documentation @@ -219,3 +219,5 @@ == Thanks to the sponsors ❤️ link:https://www.prefect.io/[image:https://avatars.githubusercontent.com/u/41086007?v=4[alt=PrefectHQ,height=200, width=200]] + +<img referrerpolicy="no-referrer-when-downgrade" src="https://static.scarf.sh/a.png?x-pxid=bbb8ed0c-c2db-44e1-a072-dfc098e79bea" /> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/updatecli-0.106.0/go.mod new/updatecli-0.107.0/go.mod --- old/updatecli-0.106.0/go.mod 2025-08-27 15:10:58.000000000 +0200 +++ new/updatecli-0.107.0/go.mod 2025-09-05 13:54:55.000000000 +0200 @@ -1,6 +1,6 @@ module github.com/updatecli/updatecli -go 1.25.0 +go 1.25.1 require ( dario.cat/mergo v1.0.2 @@ -15,13 +15,13 @@ github.com/hexops/gotextdiff v1.0.3 github.com/lithammer/dedent v1.1.0 github.com/mitchellh/hashstructure v1.1.0 - github.com/moby/buildkit v0.23.2 + github.com/moby/buildkit v0.24.0 github.com/opencontainers/image-spec v1.1.1 github.com/pkg/errors v0.9.1 // indirect github.com/shurcooL/githubv4 v0.0.0-20230215024106-420ad0987b9b github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29 //indirect github.com/sirupsen/logrus v1.9.3 - github.com/spf13/cobra v1.9.1 + github.com/spf13/cobra v1.10.1 github.com/stretchr/testify v1.11.1 golang.org/x/oauth2 v0.30.0 gopkg.in/yaml.v3 v3.0.1 @@ -170,7 +170,7 @@ github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect - github.com/urfave/cli v1.22.16 // indirect + github.com/urfave/cli v1.22.17 // indirect github.com/vbatts/tar-split v0.12.1 // indirect github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect github.com/x448/float16 v0.8.4 // indirect @@ -216,7 +216,7 @@ github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dlclark/regexp2 v1.11.0 // indirect - github.com/docker/cli v28.2.2+incompatible // indirect + github.com/docker/cli v28.3.3+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker v28.3.3+incompatible // indirect github.com/docker/docker-credential-helpers v0.9.3 // indirect @@ -299,7 +299,7 @@ github.com/shopspring/decimal v1.4.0 // indirect github.com/skeema/knownhosts v1.3.1 // indirect github.com/spf13/cast v1.7.0 // indirect - github.com/spf13/pflag v1.0.7 // indirect + github.com/spf13/pflag v1.0.9 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xlab/treeprint v1.2.0 // indirect gitlab.com/gitlab-org/api/client-go v0.129.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/updatecli-0.106.0/go.sum new/updatecli-0.107.0/go.sum --- old/updatecli-0.106.0/go.sum 2025-08-27 15:10:58.000000000 +0200 +++ new/updatecli-0.107.0/go.sum 2025-09-05 13:54:55.000000000 +0200 @@ -54,7 +54,6 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs= github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= -github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= @@ -203,7 +202,6 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= -github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 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= @@ -226,8 +224,8 @@ github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/docker/cli v28.2.2+incompatible h1:qzx5BNUDFqlvyq4AHzdNB7gSyVTmU4cgsyN9SdInc1A= -github.com/docker/cli v28.2.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v28.3.3+incompatible h1:fp9ZHAr1WWPGdIWBM1b3zLtgCF+83gRdVMTJsUeiyAo= +github.com/docker/cli v28.3.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI= @@ -533,8 +531,8 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/moby/buildkit v0.23.2 h1:gt/dkfcpgTXKx+B9I310kV767hhVqTvEyxGgI3mqsGQ= -github.com/moby/buildkit v0.23.2/go.mod h1:iEjAfPQKIuO+8y6OcInInvzqTMiKMbb2RdJz1K/95a0= +github.com/moby/buildkit v0.24.0 h1:qYfTl7W1SIJzWDIDCcPT8FboHIZCYfi++wvySi3eyFE= +github.com/moby/buildkit v0.24.0/go.mod h1:4qovICAdR2H4C7+EGMRva5zgHW1gyhT4/flHI7F5F9k= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= @@ -679,11 +677,10 @@ github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= +github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= +github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE= github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -700,7 +697,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/testcontainers/testcontainers-go v0.38.0 h1:d7uEapLcv2P8AvH8ahLqDMMxda2W9gQN1nRbHS28HBw= @@ -713,8 +710,8 @@ github.com/tomwright/dasel v1.27.3/go.mod h1:/rESPoTvQxRkrtEH8lhSU8CB2UWPh/bM0kDrKVGf1kQ= github.com/tomwright/dasel/v2 v2.8.1 h1:mo5SlL0V2d3a0uPsD9Rrndn0cHWpbNDheB4+Fm++z8k= github.com/tomwright/dasel/v2 v2.8.1/go.mod h1:6bNDNAnmGEtGpuIvksuQwiNcAgQ87pmzndynsqTNglc= -github.com/urfave/cli v1.22.16 h1:MH0k6uJxdwdeWQTwhSO42Pwr4YLrNLwBtg1MRgTqPdQ= -github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZMn5Po= +github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ= +github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo= github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo= github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= github.com/vmware-labs/yaml-jsonpath v0.3.2 h1:/5QKeCBGdsInyDCyVNLbXyilb61MXGi9NP674f9Hobk= @@ -767,10 +764,10 @@ go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0/go.mod h1:hKvJwTzJdp90Vh7p6q/9PAOd55dI6WA6sWj62a/JvSs= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 h1:S+LdBGiQXtJdowoJoQPEtI52syEP/JYBUpjO49EQhV8= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0/go.mod h1:5KXybFvPGds3QinJWQT7pmXf+TN5YIa7CNYObWRkj50= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 h1:j7ZSD+5yn+lo3sGV69nW04rRR0jhYnBwjuX3r0HvnK0= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0/go.mod h1:WXbYJTUaZXAbYd8lbgGuvih0yuCfOFC5RJoYnoLcGz8= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0 h1:t/Qur3vKSkUCcDVaSumWF2PKHt85pc7fRvFuoVT8qFU= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0/go.mod h1:Rl61tySSdcOJWoEgYZVtmnKdA0GeKrSqkHC1t+91CH8= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 h1:QcFwRrZLc82r8wODjvyCbP7Ifp3UANaBSmhDSFjnqSc= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0/go.mod h1:CXIWhUomyWBG/oY2/r/kLp6K/cmx9e/7DLpBuuGdLCA= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.35.0 h1:0NIXxOCFx+SKbhCVxwl3ETG8ClLPAa0KuKV6p3yhxP8= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.35.0/go.mod h1:ChZSJbbfbl/DcRZNc9Gqh6DYGlfjw4PvO1pEOZH1ZsE= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/updatecli-0.106.0/pkg/plugins/autodiscovery/golang/dependency.go new/updatecli-0.107.0/pkg/plugins/autodiscovery/golang/dependency.go --- old/updatecli-0.106.0/pkg/plugins/autodiscovery/golang/dependency.go 2025-08-27 15:10:58.000000000 +0200 +++ new/updatecli-0.107.0/pkg/plugins/autodiscovery/golang/dependency.go 2025-09-05 13:54:55.000000000 +0200 @@ -65,63 +65,120 @@ } } - goVersion, goModules, err := getGoModContent(foundFile) + goVersion, goModules, goModulesToReplace, err := getGoModContent(foundFile) if err != nil { logrus.Debugln(err) continue } - for goModule, goModuleVersion := range goModules { - // Skip golang module manifest if there is only one rule on the go version - if g.spec.Only.isGoVersionOnly() || g.onlygoVersion { - break - } - // Test if the ignore rule based on path is respected - if len(g.spec.Ignore) > 0 { - if g.spec.Ignore.isMatchingRules(g.rootDir, relativeFoundFile, goVersion, goModule, goModuleVersion) { - logrus.Debugf("Ignoring module %q from file %q, as matching ignore rule(s)\n", goModule, relativeFoundFile) + generateModuleManifests := func(modules map[string]string) { + + for goModule, goModuleVersion := range modules { + // Skip golang module manifest if there is only one rule on the go version + if g.spec.Only.isGoVersionOnly() || g.onlygoVersion { + break + } + // Test if the ignore rule based on path is respected + if len(g.spec.Ignore) > 0 { + if g.spec.Ignore.isMatchingRules(g.rootDir, relativeFoundFile, goVersion, goModule, goModuleVersion, false) { + logrus.Debugf("Ignoring module %q from file %q, as matching ignore rule(s)\n", goModule, relativeFoundFile) + continue + } + } + + // Test if the only rule based on path is respected + if len(g.spec.Only) > 0 { + if !g.spec.Only.isMatchingRules(g.rootDir, relativeFoundFile, goVersion, goModule, goModuleVersion, false) { + logrus.Debugf("Ignoring module %q from %q, as not matching only rule(s)\n", goModule, relativeFoundFile) + continue + } + } + + goModuleVersionPattern, err := g.versionFilter.GreaterThanPattern(goModuleVersion) + if err != nil { + logrus.Debugf("skipping golang module %q due to: %s", goModule, err) continue } - } - // Test if the only rule based on path is respected - if len(g.spec.Only) > 0 { - if !g.spec.Only.isMatchingRules(g.rootDir, relativeFoundFile, goVersion, goModule, goModuleVersion) { - logrus.Debugf("Ignoring module %q from %q, as not matching only rule(s)\n", goModule, relativeFoundFile) + moduleManifest, err := getGolangModuleManifest( + relativeFoundFile, + goModule, + g.versionFilter.Kind, + goModuleVersionPattern, + g.scmID, + g.actionID, + relativeWorkDir, + goModTidyEnabled, + ) + if err != nil { + logrus.Debugf("skipping golang module %q module due to: %s", goModule, err) continue } - } - goModuleVersionPattern, err := g.versionFilter.GreaterThanPattern(goModuleVersion) - if err != nil { - logrus.Debugf("skipping golang module %q due to: %s", goModule, err) - continue + manifests = append(manifests, moduleManifest) } + } - moduleManifest, err := getGolangModuleManifest( - relativeFoundFile, - goModule, - g.versionFilter.Kind, - goModuleVersionPattern, - g.scmID, - g.actionID, - relativeWorkDir, - goModTidyEnabled) - if err != nil { - logrus.Debugf("skipping golang module %q module due to: %s", goModule, err) - continue - } + generateReplaceModuleManifests := func(modules []Replace) { + + for _, replace := range modules { + // Skip golang module manifest if there is only one rule on the go version + if g.spec.Only.isGoVersionOnly() || g.onlygoVersion { + break + } + // Test if the ignore rule based on path is respected + if len(g.spec.Ignore) > 0 { + if g.spec.Ignore.isMatchingRules(g.rootDir, relativeFoundFile, goVersion, replace.NewPath, replace.NewVersion, true) { + logrus.Debugf("Ignoring module %q from file %q, as matching ignore rule(s)\n", replace.NewPath, relativeFoundFile) + continue + } + } + + // Test if the only rule based on path is respected + if len(g.spec.Only) > 0 { + if !g.spec.Only.isMatchingRules(g.rootDir, relativeFoundFile, goVersion, replace.NewPath, replace.NewVersion, true) { + logrus.Debugf("Ignoring module %q from %q, as not matching only rule(s)\n", replace.NewPath, relativeFoundFile) + continue + } + } + + goModuleVersionPattern, err := g.versionFilter.GreaterThanPattern(replace.NewVersion) + if err != nil { + logrus.Debugf("skipping golang module %q due to: %s", replace.NewPath, err) + continue + } + + moduleManifest, err := getGolangReplaceModuleManifest( + relativeFoundFile, + replace.OldPath, + replace.OldVersion, + replace.NewPath, + g.versionFilter.Kind, + goModuleVersionPattern, + g.scmID, + g.actionID, + relativeWorkDir, + goModTidyEnabled, + ) + if err != nil { + logrus.Debugf("skipping golang module %q module due to: %s", replace.NewPath, err) + continue + } - manifests = append(manifests, moduleManifest) + manifests = append(manifests, moduleManifest) + } } + generateModuleManifests(goModules) + generateReplaceModuleManifests(goModulesToReplace) + if g.spec.Only.isGoModuleOnly() || g.onlyGoModule { - return manifests, nil + continue } // Test if the ignore rule based on path is respected if len(g.spec.Ignore) > 0 { - if g.spec.Ignore.isMatchingRules(g.rootDir, relativeFoundFile, goVersion, "", "") { + if g.spec.Ignore.isMatchingRules(g.rootDir, relativeFoundFile, goVersion, "", "", false) { logrus.Debugf("Ignoring golang version update from file %q, as matching ignore rule(s)\n", relativeFoundFile) continue } @@ -129,7 +186,7 @@ // Test if the only rule based on path is respected if len(g.spec.Only) > 0 { - if !g.spec.Only.isMatchingRules(g.rootDir, relativeFoundFile, goVersion, "", "") { + if !g.spec.Only.isMatchingRules(g.rootDir, relativeFoundFile, goVersion, "", "", false) { logrus.Debugf("Ignoring golang version update from %q, as not matching only rule(s)\n", relativeFoundFile) continue } @@ -152,6 +209,7 @@ } } manifests = append(manifests, golangVersionManifest) + } logrus.Printf("%v manifests identified", len(manifests)) @@ -212,6 +270,56 @@ VersionFilterKind: versionFilterKind, VersionFilterPattern: versionFilterPattern, GoModTidyEnabled: goModTidy, + ScmID: scmID, + WorkDir: workdir, + } + + manifest := bytes.Buffer{} + if err := tmpl.Execute(&manifest, params); err != nil { + logrus.Debugln(err) + return nil, err + } + return manifest.Bytes(), nil +} + +func getGolangReplaceModuleManifest(filename, + oldPathModule, + oldVersionModule, + newPathModule, + versionFilterKind, + versionFilterPattern, + scmID, + actionID, + workdir string, + goModTidy bool, +) ([]byte, error) { + + tmpl, err := template.New("manifest").Parse(goReplaceModuleManifestTemplate) + if err != nil { + logrus.Debugln(err) + return nil, err + } + + params := struct { + ActionID string + GoModFile string + OldPathModule string + OldVersionModule string + NewPathModule string + VersionFilterKind string + VersionFilterPattern string + GoModTidyEnabled bool + ScmID string + WorkDir string + }{ + ActionID: actionID, + GoModFile: filename, + OldPathModule: oldPathModule, + OldVersionModule: oldVersionModule, + NewPathModule: newPathModule, + VersionFilterKind: versionFilterKind, + VersionFilterPattern: versionFilterPattern, + GoModTidyEnabled: goModTidy, ScmID: scmID, WorkDir: workdir, } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/updatecli-0.106.0/pkg/plugins/autodiscovery/golang/main_test.go new/updatecli-0.107.0/pkg/plugins/autodiscovery/golang/main_test.go --- old/updatecli-0.106.0/pkg/plugins/autodiscovery/golang/main_test.go 2025-08-27 15:10:58.000000000 +0200 +++ new/updatecli-0.107.0/pkg/plugins/autodiscovery/golang/main_test.go 2025-09-05 13:54:55.000000000 +0200 @@ -1,6 +1,7 @@ package golang import ( + "sort" "testing" "github.com/stretchr/testify/assert" @@ -15,6 +16,126 @@ expectedPipelines []string }{ { + name: "Golang Replace module", + rootDir: "testdata/replace", + expectedPipelines: []string{`name: 'deps(go): bump module github.com/crewjam/saml' +sources: + module: + name: 'Get latest golang module github.com/crewjam/saml version' + kind: 'golang/module' + spec: + module: 'github.com/crewjam/saml' + versionfilter: + kind: 'semver' + pattern: '>=0.6.0' +targets: + module: + name: 'deps(go): bump module github.com/crewjam/saml to {{ source "module" }}' + kind: 'golang/gomod' + sourceid: 'module' + spec: + file: 'go.mod' + module: 'github.com/crewjam/saml' +`, + `name: 'deps(go): bump module github.com/rancher/saml' +sources: + module: + name: 'Get latest golang module github.com/rancher/saml version' + kind: 'golang/module' + spec: + module: 'github.com/rancher/saml' + versionfilter: + kind: 'semver' + pattern: '>=0.3.0' +targets: + module: + name: 'deps(go): bump module github.com/rancher/saml to {{ source "module" }}' + kind: 'golang/gomod' + sourceid: 'module' + spec: + file: 'go.mod' + module: 'github.com/rancher/saml' +`, + `name: 'deps(go): bump module github.com/stretchr/testify' +sources: + module: + name: 'Get latest golang module github.com/stretchr/testify version' + kind: 'golang/module' + spec: + module: 'github.com/stretchr/testify' + versionfilter: + kind: 'semver' + pattern: '>=1.8.4' +targets: + module: + name: 'deps(go): bump module github.com/stretchr/testify to {{ source "module" }}' + kind: 'golang/gomod' + sourceid: 'module' + spec: + file: 'go.mod' + module: 'github.com/stretchr/testify' +`, + `name: 'deps(go): bump replaced module github.com/crewjam/saml' +sources: + module: + name: 'Get latest golang module github.com/crewjam/saml version' + kind: 'golang/module' + spec: + module: 'github.com/crewjam/saml' + versionfilter: + kind: 'semver' + pattern: '>=0.5.0' +targets: + module: + name: 'deps(go): bump module github.com/crewjam/saml to {{ source "module" }}' + kind: 'golang/gomod' + sourceid: 'module' + spec: + file: 'go.mod' + module: 'github.com/crewjam/saml' + replace: true + replaceVersion: 'v0.6.0' + +`, + `name: 'deps(go): bump replaced module github.com/rancher/saml' +sources: + module: + name: 'Get latest golang module github.com/rancher/saml version' + kind: 'golang/module' + spec: + module: 'github.com/rancher/saml' + versionfilter: + kind: 'semver' + pattern: '>=0.2.0' +targets: + module: + name: 'deps(go): bump module github.com/rancher/saml to {{ source "module" }}' + kind: 'golang/gomod' + sourceid: 'module' + spec: + file: 'go.mod' + module: 'github.com/rancher/saml' + replace: true +`, + `name: 'deps(golang): bump Go version' +sources: + go: + name: 'Get latest Go version' + kind: 'golang' + spec: + versionfilter: + kind: 'semver' + pattern: '>=1.25.0' +targets: + go: + name: 'deps(golang): bump Go version to {{ source "go" }}' + kind: 'golang/gomod' + sourceid: 'go' + spec: + file: 'go.mod' +`}, + }, + { name: "Golang Version", rootDir: "testdata/noModule", expectedPipelines: []string{`name: 'deps(go): bump module gopkg.in/yaml.v3' @@ -132,6 +253,12 @@ stringPipelines = append(stringPipelines, string(bytesPipelines[i])) } + // sort stringsPipelines to ensure that the test result + // is always the same + sort.Slice(stringPipelines, func(i, j int) bool { + return stringPipelines[i] < stringPipelines[j] + }) + for i := range stringPipelines { pipelines = append(pipelines, stringPipelines...) assert.Equal(t, tt.expectedPipelines[i], pipelines[i]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/updatecli-0.106.0/pkg/plugins/autodiscovery/golang/manifestTemplate.go new/updatecli-0.107.0/pkg/plugins/autodiscovery/golang/manifestTemplate.go --- old/updatecli-0.106.0/pkg/plugins/autodiscovery/golang/manifestTemplate.go 2025-08-27 15:10:58.000000000 +0200 +++ new/updatecli-0.107.0/pkg/plugins/autodiscovery/golang/manifestTemplate.go 2025-09-05 13:54:55.000000000 +0200 @@ -80,4 +80,61 @@ {{ end }} {{- end }} ` + + // goReplaceModuleManifestTemplate is the Go template used to generate Golang manifest update + goReplaceModuleManifestTemplate string = `name: 'deps(go): bump replaced module {{ .NewPathModule }}' +{{- if .ActionID }} +actions: + {{ .ActionID }}: + title: 'deps(go): bump replaced module {{ .NewPathModule }} to {{ "{{" }} source "module" {{ "}}" }}' +{{ end }} +sources: + module: + name: 'Get latest golang module {{ .NewPathModule }} version' + kind: 'golang/module' + spec: + module: '{{ .NewPathModule }}' + versionfilter: + kind: '{{ .VersionFilterKind }}' + pattern: '{{ .VersionFilterPattern }}' +targets: + module: + name: 'deps(go): bump module {{ .NewPathModule }} to {{ "{{" }} source "module" {{ "}}" }}' + kind: 'golang/gomod' + sourceid: 'module' + spec: + file: '{{ .GoModFile }}' + module: '{{ .OldPathModule }}' + replace: true +{{- if .OldVersionModule }} + replaceVersion: '{{ .OldVersionModule }}' +{{ end }} +{{- if .ScmID }} + scmid: '{{ .ScmID }}' +{{ end }} +{{- if .GoModTidyEnabled }} + tidy: + name: 'clean: go mod tidy' + disablesourceinput: true + dependsonchange: true + dependson: + - 'module' + kind: 'shell' + spec: + command: 'go mod tidy' + environments: + - name: HOME + - name: PATH + workdir: {{ .WorkDir }} + changedif: + kind: 'file/checksum' + spec: + files: + - 'go.mod' + - 'go.sum' +{{- if .ScmID }} + scmid: '{{ .ScmID }}' +{{ end }} +{{- end }} +` ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/updatecli-0.106.0/pkg/plugins/autodiscovery/golang/matchingRule.go new/updatecli-0.107.0/pkg/plugins/autodiscovery/golang/matchingRule.go --- old/updatecli-0.106.0/pkg/plugins/autodiscovery/golang/matchingRule.go 2025-08-27 15:10:58.000000000 +0200 +++ new/updatecli-0.107.0/pkg/plugins/autodiscovery/golang/matchingRule.go 2025-09-05 13:54:55.000000000 +0200 @@ -2,29 +2,73 @@ import ( "path/filepath" + "regexp" "github.com/Masterminds/semver/v3" "github.com/sirupsen/logrus" ) // MatchingRule allows to specifies rules to identify manifest +// to update based on file path, module name, module version, go version and replace directive. type MatchingRule struct { - // Path specifies a go.mod path pattern, the pattern requires to match all of name, not just a substring. + // Path specifies a go.mod path pattern + // The pattern syntax is: + // pattern: + // { term } + // term: + // '*' matches any sequence of non-Separator characters + // '?' matches any single non-Separator character + // '[' [ '^' ] { character-range } ']' + // character class (must be non-empty) + // c matches character c (c != '*', '?', '\\', '[') + // '\\' c matches character c + // example: + // * 'go.mod' matches 'go.mod' in the current directory + // * '*/go.mod' matches 'go.mod' in any first level subdirectory Path string // Modules specifies a list of module pattern. + // The module accepts regular expression for module name and semantic versioning constraint for module version. + // If module version is empty then any version is matching. + // Example: + // * 'github.com/updatecli/updatecli': '' matches any version of the module github.com/updatecli/updatecli + // * 'github.com/updatecli/updatecli': '1.0.0' matches only version 1.0.0 of the module github.com/updatecli/updatecli + // * 'github.com/updatecli/updatecli': '>=1.0.0' matches any version greater than or equal to 1.0.0 of the module github.com/updatecli/updatecli + // * 'github.com/.*': '>=1.0.0' matches any version greater than or equal to 1.0.0 of any module hosted under github.com + // * 'github\.com\/updatecli\/updatecli': '>=1.0.0' matches any version greater than or equal to 1. Modules map[string]string // GoVersions specifies a list of version pattern. + // The version constraint must be a valid semantic version constraint. + // If GoVersion is empty then any version is matching. + // Example: + // * '1.19.*' matches any 1.19.x version + // * '>=1.20.0' matches any version greater than or equal to 1.20.0 + // * '<1.20.0' matches any version strictly less than 1.20.0 + // * '*' matches any version GoVersion string + // Replace indicates if the module is a replace directive. + // If Replace is nil then any module is matching. + // If Replace is true then only module with a replace directive is matching. + // If Replace is false then only module without a replace directive is matching. + Replace *bool } type MatchingRules []MatchingRule // isMatchingRules checks for each matchingRule if parameters are matching rules and then return true or false. -func (m MatchingRules) isMatchingRules(rootDir, filePath, goVersion, moduleName, moduleVersion string) bool { +func (m MatchingRules) isMatchingRules(rootDir, filePath, goVersion, moduleName, moduleVersion string, isReplaced bool) bool { var ruleResults []bool if len(m) > 0 { for _, rule := range m { + + if rule.Replace != nil { + match := false + if *rule.Replace == isReplaced { + match = true + } + ruleResults = append(ruleResults, match) + } + /* Check if rule.Path is matching. Path accepts wildcard path */ @@ -60,7 +104,14 @@ match := false outModule: for ruleModuleName, ruleModuleVersion := range rule.Modules { - if moduleName == ruleModuleName { + + moduleMatch, err := regexp.MatchString(ruleModuleName, moduleName) + if err != nil { + logrus.Debugf("%q - %s", ruleModuleName, err) + break outModule + } + + if moduleMatch { if ruleModuleVersion == "" { match = true break outModule diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/updatecli-0.106.0/pkg/plugins/autodiscovery/golang/matchingRule_test.go new/updatecli-0.107.0/pkg/plugins/autodiscovery/golang/matchingRule_test.go --- old/updatecli-0.106.0/pkg/plugins/autodiscovery/golang/matchingRule_test.go 2025-08-27 15:10:58.000000000 +0200 +++ new/updatecli-0.107.0/pkg/plugins/autodiscovery/golang/matchingRule_test.go 2025-09-05 13:54:55.000000000 +0200 @@ -98,6 +98,28 @@ rules: MatchingRules{ MatchingRule{ Modules: map[string]string{ + "github.com/updatecli/*": "", + }, + }, + }, + moduleName: "github.com/updatecli/updatecli", + expectedResult: true, + }, + { + rules: MatchingRules{ + MatchingRule{ + Modules: map[string]string{ + "github.com/*": "", + }, + }, + }, + moduleName: "github.com/updatecli/updatecli", + expectedResult: true, + }, + { + rules: MatchingRules{ + MatchingRule{ + Modules: map[string]string{ "github.com/updatecli/updatecli": ">=0", }, }, @@ -151,7 +173,9 @@ d.filePath, d.goVersion, d.moduleName, - d.moduleVersion) + d.moduleVersion, + false, + ) assert.Equal(t, d.expectedResult, gotResult) }) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/updatecli-0.106.0/pkg/plugins/autodiscovery/golang/testdata/replace/go.mod new/updatecli-0.107.0/pkg/plugins/autodiscovery/golang/testdata/replace/go.mod --- old/updatecli-0.106.0/pkg/plugins/autodiscovery/golang/testdata/replace/go.mod 1970-01-01 01:00:00.000000000 +0100 +++ new/updatecli-0.107.0/pkg/plugins/autodiscovery/golang/testdata/replace/go.mod 2025-09-05 13:54:55.000000000 +0200 @@ -0,0 +1,17 @@ +module example.com/updatecli-replace-test + +go 1.25.0 + +require ( + github.com/rancher/saml v0.3.0 + github.com/crewjam/saml v0.6.0 + github.com/stretchr/testify v1.8.4 +) + +replace ( + github.com/rancher/saml => github.com/rancher/saml v0.2.0 + + github.com/crewjam/saml v0.6.0 => github.com/crewjam/saml v0.5.0 + + github.com/stretchr/testify => ../local/testify +) \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/updatecli-0.106.0/pkg/plugins/autodiscovery/golang/utils.go new/updatecli-0.107.0/pkg/plugins/autodiscovery/golang/utils.go --- old/updatecli-0.106.0/pkg/plugins/autodiscovery/golang/utils.go 2025-08-27 15:10:58.000000000 +0200 +++ new/updatecli-0.107.0/pkg/plugins/autodiscovery/golang/utils.go 2025-09-05 13:54:55.000000000 +0200 @@ -6,6 +6,7 @@ "os" "os/exec" "path/filepath" + "strings" "github.com/sirupsen/logrus" "golang.org/x/mod/modfile" @@ -15,6 +16,13 @@ GoModFile string = "go.mod" ) +type Replace struct { + OldPath string + OldVersion string + NewPath string + NewVersion string +} + // searchGoModFiles looks, recursively, for every files named go.mod from a root directory. func searchGoModFiles(rootDir string) ([]string, error) { @@ -49,27 +57,42 @@ return err == nil } -func getGoModContent(filename string) (goVersion string, goModules map[string]string, err error) { +func getGoModContent(filename string) (goVersion string, goModules map[string]string, replaceGoModules []Replace, err error) { data, err := os.ReadFile(filename) if err != nil { - return goVersion, goModules, err + return "", nil, nil, err } modfile, err := modfile.Parse(filename, data, nil) if err != nil { - return goVersion, goModules, err + return "", nil, nil, err } goVersion = modfile.Go.Version - goModules = make(map[string]string) for _, r := range modfile.Require { if !r.Indirect { + if goModules == nil { + goModules = make(map[string]string) + } goModules[r.Mod.Path] = r.Mod.Version } } - return goVersion, goModules, nil + for _, r := range modfile.Replace { + // Ignore replace directives with local path + if strings.HasPrefix(r.New.Path, ".") || strings.HasPrefix(r.New.Path, "/") { + continue + } + replaceGoModules = append(replaceGoModules, Replace{ + OldPath: r.Old.Path, + OldVersion: r.Old.Version, + NewPath: r.New.Path, + NewVersion: r.New.Version, + }) + } + + return goVersion, goModules, replaceGoModules, nil } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/updatecli-0.106.0/pkg/plugins/autodiscovery/golang/utils_test.go new/updatecli-0.107.0/pkg/plugins/autodiscovery/golang/utils_test.go --- old/updatecli-0.106.0/pkg/plugins/autodiscovery/golang/utils_test.go 2025-08-27 15:10:58.000000000 +0200 +++ new/updatecli-0.107.0/pkg/plugins/autodiscovery/golang/utils_test.go 2025-09-05 13:54:55.000000000 +0200 @@ -20,6 +20,7 @@ expectedFoundFiles: []string{ "testdata/noModule/go.mod", "testdata/noSumFile/go.mod", + "testdata/replace/go.mod", }, }, } @@ -33,3 +34,57 @@ }) } } + +func TestGetGoModContent(t *testing.T) { + dataset := []struct { + name string + goModFile string + expectedModules map[string]string + expectedReplaceModules []Replace + expectedGoVersion string + }{ + { + name: "Replace go module", + goModFile: "testdata/replace/go.mod", + expectedReplaceModules: []Replace{ + { + OldPath: "github.com/rancher/saml", + OldVersion: "", + NewPath: "github.com/rancher/saml", + NewVersion: "v0.2.0", + }, + { + OldPath: "github.com/crewjam/saml", + OldVersion: "v0.6.0", + NewPath: "github.com/crewjam/saml", + NewVersion: "v0.5.0", + }, + }, + expectedModules: map[string]string{ + "github.com/rancher/saml": "v0.3.0", + "github.com/crewjam/saml": "v0.6.0", + "github.com/stretchr/testify": "v1.8.4", + }, + expectedGoVersion: "1.25.0", + }, + { + name: "Default go modules", + goModFile: "testdata/noModule/go.mod", + expectedModules: map[string]string{ + "gopkg.in/yaml.v3": "v3.0.1", + }, + expectedGoVersion: "1.20", + }, + } + + for _, d := range dataset { + t.Run(d.name, func(t *testing.T) { + foundGoVersion, foundGoModules, foundReplaceGoModules, err := getGoModContent(d.goModFile) + require.NoError(t, err) + + assert.Equal(t, d.expectedModules, foundGoModules) + assert.Equal(t, d.expectedReplaceModules, foundReplaceGoModules) + assert.Equal(t, d.expectedGoVersion, foundGoVersion) + }) + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/updatecli-0.106.0/pkg/plugins/resources/file/main.go new/updatecli-0.107.0/pkg/plugins/resources/file/main.go --- old/updatecli-0.106.0/pkg/plugins/resources/file/main.go 2025-08-27 15:10:58.000000000 +0200 +++ new/updatecli-0.107.0/pkg/plugins/resources/file/main.go 2025-09-05 13:54:55.000000000 +0200 @@ -83,6 +83,14 @@ * condition * target + remarks: + * For targets: Capture groups (parentheses) in the pattern automatically extract + the current value for changelog generation + * Without capture groups, changelogs show generic "unknown" version changes + * With capture groups, changelogs show actual version changes (e.g., "1.24.5" → "1.25.1") + * Example: `"version":\s*"([\d\.]+)"` captures version numbers for changelogs + * Supports full Go regexp syntax + */ MatchPattern string `yaml:",omitempty"` /* diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/updatecli-0.106.0/pkg/plugins/resources/file/target.go new/updatecli-0.107.0/pkg/plugins/resources/file/target.go --- old/updatecli-0.106.0/pkg/plugins/resources/file/target.go 2025-08-27 15:10:58.000000000 +0200 +++ new/updatecli-0.107.0/pkg/plugins/resources/file/target.go 2025-09-05 13:54:55.000000000 +0200 @@ -58,16 +58,6 @@ } resultTarget.NewInformation = inputContent - /* - At the moment, we don't have an easy to identify that precise information - that would be updated without considering the new file content. - - It's doable but out of the current scope of the effort. - - With a valid usecase we can improve the situation. - - Especially considering that we may have multiple files to update - */ resultTarget.Information = "unknown" // If we're using a regexp for the target @@ -77,12 +67,24 @@ inputContent = f.spec.ReplacePattern } + // Update NewInformation to reflect what will actually be written + resultTarget.NewInformation = inputContent + reg, err := regexp.Compile(f.spec.MatchPattern) if err != nil { logrus.Errorf("Validation error in target of type 'file': Unable to parse the regexp specified at f.spec.MatchPattern (%q)", f.spec.MatchPattern) return err } + // Extract current version from first capture group if available + for filePath, file := range f.files { + if matches := reg.FindStringSubmatch(file.content); len(matches) > 1 { + resultTarget.Information = matches[1] + logrus.Debugf("Extracted current version %q from capture group in file %q", matches[1], filePath) + break // Use first match found + } + } + for filePath, file := range f.files { // Check if there is any match in the file if !reg.MatchString(file.content) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/updatecli-0.106.0/pkg/plugins/resources/file/target_test.go new/updatecli-0.107.0/pkg/plugins/resources/file/target_test.go --- old/updatecli-0.106.0/pkg/plugins/resources/file/target_test.go 2025-08-27 15:10:58.000000000 +0200 +++ new/updatecli-0.107.0/pkg/plugins/resources/file/target_test.go 2025-09-05 13:54:55.000000000 +0200 @@ -619,9 +619,138 @@ assert.Equal(t, tt.wantedResult, gotResultTarget.Changed) assert.Equal(t, tt.wantedFiles, gotResultTarget.Files) - for filePath := range f.files { - assert.Equal(t, tt.wantedContents[filePath], mockedText.Contents[filePath]) + }) + } +} + +func TestFile_Target_CaptureGroupExtraction(t *testing.T) { + tests := []struct { + name string + spec Spec + files map[string]fileMetadata + inputSourceValue string + mockedContents map[string]string + expectedInformation string + expectedNewInformation string + wantedResult bool + description string + }{ + { + name: "Extract version from capture group in JSON format", + inputSourceValue: "1.25.1", + spec: Spec{ + File: "go_version.json", + MatchPattern: `"GO_VERSION"\s*:\s*"(1\.24\.\d+)"`, + ReplacePattern: `"GO_VERSION": "1.25.1"`, + }, + files: map[string]fileMetadata{ + "go_version.json": { + originalPath: "go_version.json", + path: "go_version.json", + }, + }, + mockedContents: map[string]string{ + "go_version.json": `{ + "GO_VERSION": "1.24.5", + "OTHER_VERSION": "2.1.0" +}`, + }, + expectedInformation: "1.24.5", + expectedNewInformation: `"GO_VERSION": "1.25.1"`, // ReplacePattern used when specified + wantedResult: true, + description: "Should extract '1.24.5' from capture group instead of 'unknown'", + }, + { + name: "Extract version from capture group in properties format", + inputSourceValue: "3.9.0", + spec: Spec{ + File: "config.properties", + MatchPattern: `maven_version\s*=\s*"(3\.8\.\d+)"`, + ReplacePattern: `maven_version = "3.9.0"`, + }, + files: map[string]fileMetadata{ + "config.properties": { + originalPath: "config.properties", + path: "config.properties", + }, + }, + mockedContents: map[string]string{ + "config.properties": `maven_version = "3.8.2" +git_version = "2.33.1" +jdk_version = "11.0.12"`, + }, + expectedInformation: "3.8.2", + expectedNewInformation: `maven_version = "3.9.0"`, // ReplacePattern used when specified + wantedResult: true, + description: "Should extract '3.8.2' from capture group", + }, + { + name: "No capture group - should remain unknown", + inputSourceValue: "1.25.1", + spec: Spec{ + File: "version.txt", + MatchPattern: `GO_VERSION.*1\.24\.\d+`, // No parentheses = no capture group + ReplacePattern: `GO_VERSION: 1.25.1`, + }, + files: map[string]fileMetadata{ + "version.txt": { + originalPath: "version.txt", + path: "version.txt", + }, + }, + mockedContents: map[string]string{ + "version.txt": `GO_VERSION: 1.24.5`, + }, + expectedInformation: "unknown", + expectedNewInformation: `GO_VERSION: 1.25.1`, // ReplacePattern used when specified + wantedResult: true, + description: "Should remain 'unknown' when no capture group present", + }, + { + name: "Multiple capture groups - should use first one", + inputSourceValue: "2.0.0", + spec: Spec{ + File: "multi_version.txt", + MatchPattern: `version:\s*"(1\.\d+)\.(\d+)"`, + ReplacePattern: `version: "2.0.0"`, + }, + files: map[string]fileMetadata{ + "multi_version.txt": { + originalPath: "multi_version.txt", + path: "multi_version.txt", + }, + }, + mockedContents: map[string]string{ + "multi_version.txt": `version: "1.24.5"`, + }, + expectedInformation: "1.24", // First capture group + expectedNewInformation: `version: "2.0.0"`, // ReplacePattern used when specified + wantedResult: true, + description: "Should extract first capture group when multiple groups present", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockedText := text.MockTextRetriever{ + Contents: tt.mockedContents, + } + f := &File{ + spec: tt.spec, + contentRetriever: &mockedText, + files: tt.files, } + + gotResultTarget := result.Target{} + err := f.Target(tt.inputSourceValue, nil, true, &gotResultTarget) // dry run + require.NoError(t, err, tt.description) + + assert.Equal(t, tt.expectedInformation, gotResultTarget.Information, + "Information field should be %q - %s", tt.expectedInformation, tt.description) + assert.Equal(t, tt.expectedNewInformation, gotResultTarget.NewInformation, + "NewInformation field should be %q", tt.expectedNewInformation) + assert.Equal(t, tt.wantedResult, gotResultTarget.Changed, + "Changed field should be %t", tt.wantedResult) }) } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/updatecli-0.106.0/pkg/plugins/resources/go/gomod/condition_test.go new/updatecli-0.107.0/pkg/plugins/resources/go/gomod/condition_test.go --- old/updatecli-0.106.0/pkg/plugins/resources/go/gomod/condition_test.go 2025-08-27 15:10:58.000000000 +0200 +++ new/updatecli-0.107.0/pkg/plugins/resources/go/gomod/condition_test.go 2025-09-05 13:54:55.000000000 +0200 @@ -54,6 +54,36 @@ }, expectedResult: true, }, + { + name: "Test retrieving module from replace", + spec: Spec{ + File: "testdata/replace.go.mod", + Module: "github.com/gin-gonic/gin", + Replace: true, + Version: "v1.7.0", + }, + expectedResult: true, + }, + { + name: "Test version downgrade", + spec: Spec{ + File: "testdata/replace.2.go.mod", + Module: "github.com/crewjam/saml", + Replace: true, + Version: "v0.5.0", + }, + expectedResult: true, + }, + { + name: "Test module replacement", + spec: Spec{ + File: "testdata/replace.2.go.mod", + Module: "github.com/rancher/saml", + Replace: true, + Version: "v0.2.0", + }, + expectedResult: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/updatecli-0.106.0/pkg/plugins/resources/go/gomod/source_test.go new/updatecli-0.107.0/pkg/plugins/resources/go/gomod/source_test.go --- old/updatecli-0.106.0/pkg/plugins/resources/go/gomod/source_test.go 2025-08-27 15:10:58.000000000 +0200 +++ new/updatecli-0.107.0/pkg/plugins/resources/go/gomod/source_test.go 2025-09-05 13:54:55.000000000 +0200 @@ -57,6 +57,42 @@ }, expectedResult: "v3.2.3", }, + { + name: "Test retrieving module from replace", + spec: Spec{ + File: "testdata/replace.go.mod", + Module: "github.com/gin-gonic/gin", + Replace: true, + }, + expectedResult: "v1.7.0", + }, + { + name: "Test version downgrade", + spec: Spec{ + File: "testdata/replace.2.go.mod", + Module: "github.com/crewjam/saml", + Replace: true, + }, + expectedResult: "v0.5.0", + }, + { + name: "Test module replacement", + spec: Spec{ + File: "testdata/replace.2.go.mod", + Module: "github.com/rancher/saml", + Replace: true, + }, + expectedResult: "v0.2.0", + }, + { + name: "Test dev module", + spec: Spec{ + File: "testdata/replace.2.go.mod", + Module: "github.com/stretchr/testify", + Replace: true, + }, + expectedError: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/updatecli-0.106.0/pkg/plugins/resources/go/gomod/spec.go new/updatecli-0.107.0/pkg/plugins/resources/go/gomod/spec.go --- old/updatecli-0.106.0/pkg/plugins/resources/go/gomod/spec.go 2025-08-27 15:10:58.000000000 +0200 +++ new/updatecli-0.107.0/pkg/plugins/resources/go/gomod/spec.go 2025-09-05 13:54:55.000000000 +0200 @@ -45,4 +45,28 @@ // * condition // Version string `yaml:",omitempty"` + // Replace specifies if we manipulate a replaced dependency + // + // compatible: + // * source + // * condition + // * target + // + Replace bool `yaml:",omitempty"` + // ReplaceVersion specifies the specific Go module version to replace + // + // compatible: + // * source + // * condition + // * target + // + // default: unset, which will match any version of the module being replaced. + // + // Example: + // For the following Go replace instruction: + // moduleA v1.2.3 => moduleB v1.0.0 + // - The 'module' field should be set to 'moduleA' (the module being replaced, left-hand side). + // - The value of ReplaceVersion should be 'v1.2.3', corresponding to the version of moduleA + // (the module being replaced, left-hand side). + ReplaceVersion string `yaml:",omitempty"` } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/updatecli-0.106.0/pkg/plugins/resources/go/gomod/target_test.go new/updatecli-0.107.0/pkg/plugins/resources/go/gomod/target_test.go --- old/updatecli-0.106.0/pkg/plugins/resources/go/gomod/target_test.go 2025-08-27 15:10:58.000000000 +0200 +++ new/updatecli-0.107.0/pkg/plugins/resources/go/gomod/target_test.go 2025-09-05 13:54:55.000000000 +0200 @@ -66,6 +66,46 @@ }, expectedChanged: false, }, + { + name: "Test updating module from replace", + spec: Spec{ + File: "testdata/replace.go.mod", + Module: "github.com/gin-gonic/gin", + Version: "v1.9.1", + Replace: true, + }, + expectedChanged: true, + }, + { + name: "Test retrieving module from replace", + spec: Spec{ + File: "testdata/replace.go.mod", + Module: "github.com/gin-gonic/gin", + Replace: true, + Version: "v1.6.0", + }, + expectedChanged: true, + }, + { + name: "Test version downgrade", + spec: Spec{ + File: "testdata/replace.2.go.mod", + Module: "github.com/crewjam/saml", + Replace: true, + Version: "v0.4.0", + }, + expectedChanged: true, + }, + { + name: "Test module replacement", + spec: Spec{ + File: "testdata/replace.2.go.mod", + Module: "github.com/rancher/saml", + Replace: true, + Version: "v0.1.0", + }, + expectedChanged: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/updatecli-0.106.0/pkg/plugins/resources/go/gomod/testdata/replace.2.go.mod new/updatecli-0.107.0/pkg/plugins/resources/go/gomod/testdata/replace.2.go.mod --- old/updatecli-0.106.0/pkg/plugins/resources/go/gomod/testdata/replace.2.go.mod 1970-01-01 01:00:00.000000000 +0100 +++ new/updatecli-0.107.0/pkg/plugins/resources/go/gomod/testdata/replace.2.go.mod 2025-09-05 13:54:55.000000000 +0200 @@ -0,0 +1,17 @@ +module example.com/updatecli-replace-test + +go 1.22 + +require ( + github.com/rancher/saml v0.3.0 + github.com/crewjam/saml v0.6.0 + github.com/stretchr/testify v1.8.4 +) + +replace ( + github.com/rancher/saml => github.com/rancher/saml v0.2.0 + + github.com/crewjam/saml v0.6.0 => github.com/crewjam/saml v0.5.0 + + github.com/stretchr/testify => ../local/testify +) \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/updatecli-0.106.0/pkg/plugins/resources/go/gomod/testdata/replace.go.mod new/updatecli-0.107.0/pkg/plugins/resources/go/gomod/testdata/replace.go.mod --- old/updatecli-0.106.0/pkg/plugins/resources/go/gomod/testdata/replace.go.mod 1970-01-01 01:00:00.000000000 +0100 +++ new/updatecli-0.107.0/pkg/plugins/resources/go/gomod/testdata/replace.go.mod 2025-09-05 13:54:55.000000000 +0200 @@ -0,0 +1,10 @@ +module example.com/testmodule + +go 1.22 + +require ( + github.com/gin-gonic/gin v1.9.1 // what you'd expect to use + github.com/sirupsen/logrus v1.8.1 +) + +replace github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.7.0 // force older version \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/updatecli-0.106.0/pkg/plugins/resources/go/gomod/version.go new/updatecli-0.107.0/pkg/plugins/resources/go/gomod/version.go --- old/updatecli-0.106.0/pkg/plugins/resources/go/gomod/version.go 2025-08-27 15:10:58.000000000 +0200 +++ new/updatecli-0.107.0/pkg/plugins/resources/go/gomod/version.go 2025-09-05 13:54:55.000000000 +0200 @@ -46,14 +46,26 @@ return modfile.Go.Version, nil case kindModule: - for _, r := range modfile.Require { - if r.Indirect != g.spec.Indirect { - continue + if g.spec.Replace { + for _, r := range modfile.Replace { + if g.spec.ReplaceVersion != "" && r.Old.Version != g.spec.ReplaceVersion { + continue + } + if r.Old.Path == g.spec.Module { + return r.New.Version, nil + } } - if r.Mod.Path == g.spec.Module { - return r.Mod.Version, nil + } else { + for _, r := range modfile.Require { + if r.Indirect != g.spec.Indirect { + continue + } + if r.Mod.Path == g.spec.Module { + return r.Mod.Version, nil + } } } + logrus.Errorf("GO module %q not found in %q", g.spec.Module, filename) return "", ErrModuleNotFound } @@ -99,33 +111,60 @@ case kindModule: moduleFound := false - out: - for _, r := range modFile.Require { - if r.Indirect != g.spec.Indirect { - continue - } - if r.Mod.Path == g.spec.Module { - moduleFound = true - oldVersion = r.Mod.Version - newVersion = version - if newVersion != oldVersion { - - err = modFile.AddRequire(r.Mod.Path, version) - if err != nil { - logrus.Errorln(err) - return "", "", false, fmt.Errorf("failed updating go module %q to %q\n%w", g.spec.Module, version, err) + if g.spec.Replace { + outReplace: + for _, r := range modFile.Replace { + if r.Old.Path == g.spec.Module { + + if g.spec.ReplaceVersion != "" && r.Old.Version != g.spec.ReplaceVersion { + continue + } + + moduleFound = true + oldVersion = r.New.Version + newVersion = version + + if newVersion != oldVersion { + err = modFile.AddReplace(r.Old.Path, r.Old.Version, r.New.Path, version) + if err != nil { + logrus.Errorln(err) + return "", "", false, fmt.Errorf("failed updating go module replacer %q to %q\n%w", g.spec.Module, version, err) + } + changed = true + break outReplace } + } + } + } else { + outRequire: + for _, r := range modFile.Require { + if r.Indirect != g.spec.Indirect { + continue + } + if r.Mod.Path == g.spec.Module { + moduleFound = true + oldVersion = r.Mod.Version + newVersion = version + if newVersion != oldVersion { + + err = modFile.AddRequire(r.Mod.Path, version) + if err != nil { + logrus.Errorln(err) + return "", "", false, fmt.Errorf("failed updating go module %q to %q\n%w", g.spec.Module, version, err) + } - changed = true - break out + changed = true + break outRequire + } } } } + if !moduleFound { err := fmt.Errorf("module %q not found in file %q", g.spec.Module, filename) return "", "", false, err - } + default: logrus.Errorf("kind %q is not supported", g.kind) return "", "", false, fmt.Errorf("something unexpected happened, kind %q not supported", g.kind) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/updatecli-0.106.0/pkg/plugins/resources/go/language/changelog.go new/updatecli-0.107.0/pkg/plugins/resources/go/language/changelog.go --- old/updatecli-0.106.0/pkg/plugins/resources/go/language/changelog.go 2025-08-27 15:10:58.000000000 +0200 +++ new/updatecli-0.107.0/pkg/plugins/resources/go/language/changelog.go 2025-09-05 13:54:55.000000000 +0200 @@ -13,53 +13,29 @@ func (l *Language) Changelog(from, to string) *result.Changelogs { var err error - var froundVersion *semver.Version if from == "" && to == "" { return nil } - versions, err := l.versions() + // Parse the target version (to) for changelog generation + toVersion, err := semver.NewVersion(to) if err != nil { - logrus.Errorf("failed to retrieve golang version: %s", err) + logrus.Errorf("failing parsing to version %q - %q", + to, err) return nil } - if len(versions) == 0 { - logrus.Errorf("no Golang version found") - return nil - } - - fromVersion, err := semver.NewVersion(from) - if err != nil { - logrus.Errorf("failing parsing from version %q - %q", - from, err) - return nil - } - - for i := range versions { - froundVersion, err = semver.NewVersion(versions[i]) - if err != nil { - logrus.Debugf("failing parsing from version %q - %q", - versions[i], err) - return nil - } - - if fromVersion.Equal(froundVersion) { - break - } - } - - title := froundVersion.String() - url := fmt.Sprintf("https://go.dev/doc/devel/release#go%d.%d.minor", froundVersion.Major(), froundVersion.Minor()) - body := fmt.Sprintf("Golang changelog for version %q is available on %q", froundVersion.String(), redact.URL(url)) - - if froundVersion.Patch() == 0 { - title = fmt.Sprintf("%d.%d", froundVersion.Major(), froundVersion.Minor()) - url = fmt.Sprintf("https://go.dev/doc/go%d.%d", froundVersion.Major(), froundVersion.Minor()) + title := toVersion.String() + url := fmt.Sprintf("https://go.dev/doc/devel/release#go%d.%d.minor", toVersion.Major(), toVersion.Minor()) + body := fmt.Sprintf("Golang changelog for version %q is available on %q", toVersion.String(), redact.URL(url)) + + if toVersion.Patch() == 0 { + title = fmt.Sprintf("%d.%d", toVersion.Major(), toVersion.Minor()) + url = fmt.Sprintf("https://go.dev/doc/go%d.%d", toVersion.Major(), toVersion.Minor()) body = fmt.Sprintf(`Golang changelog for version "%d.%d" is available on %q`, - froundVersion.Major(), - froundVersion.Minor(), + toVersion.Major(), + toVersion.Minor(), redact.URL(url)) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/updatecli-0.106.0/pkg/plugins/resources/helm/changelog.go new/updatecli-0.107.0/pkg/plugins/resources/helm/changelog.go --- old/updatecli-0.106.0/pkg/plugins/resources/helm/changelog.go 2025-08-27 15:10:58.000000000 +0200 +++ new/updatecli-0.107.0/pkg/plugins/resources/helm/changelog.go 2025-09-05 13:54:55.000000000 +0200 @@ -12,6 +12,8 @@ "github.com/sirupsen/logrus" "github.com/updatecli/updatecli/pkg/core/result" githubChangelog "github.com/updatecli/updatecli/pkg/plugins/changelog/github/v3" + "golang.org/x/text/cases" + "golang.org/x/text/language" "gopkg.in/yaml.v2" "helm.sh/helm/v3/pkg/repo" ) @@ -286,5 +288,14 @@ } func capitalize(s string) string { - return strings.ToUpper(s[:1]) + s[1:] + if len(s) == 0 { + return "" + } + + capitalizer := cases.Title(language.English) + first := []rune(s)[0] + tail := []rune(s)[1:] + + return capitalizer.String(string(first)) + string(tail) + } ++++++ updatecli.obsinfo ++++++ --- /var/tmp/diff_new_pack.YWDx3W/_old 2025-09-18 21:11:37.241824451 +0200 +++ /var/tmp/diff_new_pack.YWDx3W/_new 2025-09-18 21:11:37.293826637 +0200 @@ -1,5 +1,5 @@ name: updatecli -version: 0.106.0 -mtime: 1756300258 -commit: 755b5706f716b4929c1db506d9025b907cc95728 +version: 0.107.0 +mtime: 1757073295 +commit: dd0b9f59f90eeffd3baa5281ff9a1656741a22c9 ++++++ vendor.tar.gz ++++++ /work/SRC/openSUSE:Factory/updatecli/vendor.tar.gz /work/SRC/openSUSE:Factory/.updatecli.new.27445/vendor.tar.gz differ: char 133, line 1
