Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package melange for openSUSE:Factory checked 
in at 2026-08-28 19:49:50
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/melange (Old)
 and      /work/SRC/openSUSE:Factory/.melange.new.1265 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "melange"

Fri Aug 28 19:49:50 2026 rev:177 rq:1374194 version:0.59.2

Changes:
--------
--- /work/SRC/openSUSE:Factory/melange/melange.changes  2026-08-14 
22:09:04.608518805 +0200
+++ /work/SRC/openSUSE:Factory/.melange.new.1265/melange.changes        
2026-08-28 19:50:41.534918113 +0200
@@ -1,0 +2,17 @@
+Fri Aug 28 06:07:15 UTC 2026 - Johannes Kastl 
<[email protected]>
+
+- Update to version 0.59.2:
+  * build(deps): bump the gomod group with 4 updates (#2621)
+  * build(deps): bump step-security/harden-runner in the actions
+    group (#2623)
+  * build(deps): bump google.golang.org/protobuf from
+    1.36.12-0.20260120151049-f2248ac996af to 1.36.12 (#2622)
+  * Remove stale SHA-1 signature references (#2620)
+  * build(deps): bump chainguard-dev/actions/setup-gitsign from
+    1.6.30 to 1.6.31 in the actions group (#2617)
+  * build(deps): bump the gomod group across 1 directory with 5
+    updates (#2618)
+- Update to version 0.59.1:
+  * Fetch license-check sources with native handlers (#2619)
+
+-------------------------------------------------------------------

Old:
----
  melange-0.59.0.obscpio

New:
----
  melange-0.59.2.obscpio

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

Other differences:
------------------
++++++ melange.spec ++++++
--- /var/tmp/diff_new_pack.A3ISQw/_old  2026-08-28 19:50:46.609095475 +0200
+++ /var/tmp/diff_new_pack.A3ISQw/_new  2026-08-28 19:50:46.611095545 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           melange
-Version:        0.59.0
+Version:        0.59.2
 Release:        0
 Summary:        Build APKs from source code
 License:        Apache-2.0

++++++ _service ++++++
--- /var/tmp/diff_new_pack.A3ISQw/_old  2026-08-28 19:50:46.704098796 +0200
+++ /var/tmp/diff_new_pack.A3ISQw/_new  2026-08-28 19:50:46.713099110 +0200
@@ -3,7 +3,7 @@
     <param name="url">https://github.com/chainguard-dev/melange.git</param>
     <param name="scm">git</param>
     <param name="exclude">.git</param>
-    <param name="revision">refs/tags/v0.59.0</param>
+    <param name="revision">refs/tags/v0.59.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.A3ISQw/_old  2026-08-28 19:50:46.755100578 +0200
+++ /var/tmp/diff_new_pack.A3ISQw/_new  2026-08-28 19:50:46.764100893 +0200
@@ -3,6 +3,6 @@
                 <param 
name="url">https://github.com/chainguard-dev/melange</param>
               <param 
name="changesrevision">3f6115b820985d70ca3c93cdf8519c1b3b4cfe81</param></service><service
 name="tar_scm">
                 <param 
name="url">https://github.com/chainguard-dev/melange.git</param>
-              <param 
name="changesrevision">4ec09c011312941753169b4089e69b5828d71c8e</param></service></servicedata>
+              <param 
name="changesrevision">2043d30b549e6f03c43e90341fb77c3532c4b790</param></service></servicedata>
 (No newline at EOF)
 

++++++ melange-0.59.0.obscpio -> melange-0.59.2.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/melange-0.59.0/go.mod new/melange-0.59.2/go.mod
--- old/melange-0.59.0/go.mod   2026-08-13 17:23:43.000000000 +0200
+++ new/melange-0.59.2/go.mod   2026-08-18 14:48:42.000000000 +0200
@@ -3,24 +3,24 @@
 go 1.26.2
 
 require (
-       chainguard.dev/apko v1.2.33
+       chainguard.dev/apko v1.2.37
        github.com/chainguard-dev/clog v1.8.1
        github.com/chainguard-dev/go-pkgconfig 
v0.0.0-20240404163941-6351b37b2a10
        github.com/chainguard-dev/yam v0.2.65
        github.com/charmbracelet/log v1.0.0
-       github.com/docker/cli v29.7.1+incompatible
+       github.com/docker/cli v29.7.2+incompatible
        github.com/docker/docker v28.5.2+incompatible
        github.com/dprotaso/go-yit v0.0.0-20250513224043-18a80f8f6df4
        github.com/github/go-spdx/v2 v2.7.0
        github.com/go-git/go-git/v5 v5.19.2
        github.com/google/go-cmp v0.7.0
-       github.com/google/go-containerregistry v0.21.8
+       github.com/google/go-containerregistry v0.21.9
        github.com/google/licenseclassifier/v2 v2.0.0
        github.com/in-toto/attestation v1.2.0
        github.com/invopop/jsonschema v0.14.0
        github.com/joho/godotenv v1.5.1
        github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
-       github.com/klauspost/compress v1.19.1
+       github.com/klauspost/compress v1.19.2
        github.com/klauspost/pgzip v1.2.6
        github.com/opencontainers/image-spec v1.1.1
        github.com/package-url/packageurl-go v0.1.6
@@ -28,7 +28,7 @@
        github.com/psanford/memfs v0.0.0-20241019191636-4ef911798f9b
        github.com/spdx/tools-golang v0.5.7
        github.com/spf13/cobra v1.10.2
-       github.com/stretchr/testify v1.11.1
+       github.com/stretchr/testify v1.12.0
        github.com/ulikunitz/xz v0.5.16
        github.com/yookoala/realpath v1.0.0
        github.com/zealic/xignore v0.3.3
@@ -36,12 +36,12 @@
        go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.45.0
        go.opentelemetry.io/otel/sdk v1.45.0
        go.yaml.in/yaml/v2 v2.4.4
-       golang.org/x/crypto v0.54.0
-       golang.org/x/exp v0.0.0-20260709172345-9ea1abe57597
+       golang.org/x/crypto v0.55.0
+       golang.org/x/exp v0.0.0-20260727155853-b88d891fe743
        golang.org/x/sync v0.22.0
        golang.org/x/sys v0.47.0
        golang.org/x/term v0.45.0
-       golang.org/x/text v0.40.0
+       golang.org/x/text v0.41.0
        golang.org/x/time v0.15.0
        gopkg.in/ini.v1 v1.67.3
        gopkg.in/yaml.v3 v3.0.1
@@ -69,7 +69,7 @@
        github.com/pb33f/ordered-map/v2 v2.3.1 // indirect
        go.opencensus.io v0.24.0 // indirect
        go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 
// indirect
-       go.yaml.in/yaml/v3 v3.0.4 // indirect
+       go.yaml.in/yaml/v3 v3.0.5 // indirect
        go.yaml.in/yaml/v4 v4.0.0-rc.6 // indirect
        golang.org/x/tools v0.48.0 // indirect
        k8s.io/klog/v2 v2.140.0 // indirect
@@ -81,8 +81,8 @@
 )
 
 require (
-       chainguard.dev/go-grpc-kit v0.17.17 // indirect
-       chainguard.dev/sdk v0.1.164 // indirect
+       chainguard.dev/go-grpc-kit v0.18.0 // indirect
+       chainguard.dev/sdk v0.1.184 // indirect
        cloud.google.com/go/auth v0.22.0 // indirect
        cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
        cloud.google.com/go/compute/metadata v0.9.0 // indirect
@@ -113,7 +113,7 @@
        github.com/emirpasic/gods v1.18.1 // indirect
        github.com/felixge/httpsnoop v1.1.0 // indirect
        github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
-       github.com/go-git/go-billy/v5 v5.9.0 // indirect
+       github.com/go-git/go-billy/v5 v5.9.1 // indirect
        github.com/go-jose/go-jose/v3 v3.0.5 // indirect
        github.com/go-logfmt/logfmt v0.6.1 // indirect
        github.com/go-logr/logr v1.4.4 // indirect
@@ -142,10 +142,9 @@
        github.com/opencontainers/go-digest v1.0.0 // indirect
        github.com/pierrec/lz4/v4 v4.1.27 // indirect
        github.com/pjbgf/sha1cd v0.6.0
-       github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // 
indirect
-       github.com/prometheus/client_golang v1.23.2 // indirect
+       github.com/prometheus/client_golang v1.24.1 // indirect
        github.com/prometheus/client_model v0.6.2 // indirect
-       github.com/prometheus/common v0.70.0 // indirect
+       github.com/prometheus/common v0.70.1 // indirect
        github.com/prometheus/procfs v0.21.1 // indirect
        github.com/rivo/uniseg v0.4.7 // indirect
        github.com/russross/blackfriday/v2 v2.1.0 // indirect
@@ -168,11 +167,11 @@
        golang.org/x/mod v0.38.0 // indirect
        golang.org/x/net v0.57.0 // indirect
        golang.org/x/oauth2 v0.36.0 // indirect
-       google.golang.org/api v0.291.0 // indirect
-       google.golang.org/genproto/googleapis/api 
v0.0.0-20260706201446-f0a921348800 // indirect
-       google.golang.org/genproto/googleapis/rpc 
v0.0.0-20260724162435-b2f20204f0df // indirect
-       google.golang.org/grpc v1.82.1 // indirect
-       google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af
+       google.golang.org/api v0.292.0 // indirect
+       google.golang.org/genproto/googleapis/api 
v0.0.0-20260715232425-e75dac1f907d // indirect
+       google.golang.org/genproto/googleapis/rpc 
v0.0.0-20260803160001-6ac0973c030d // indirect
+       google.golang.org/grpc v1.83.0 // indirect
+       google.golang.org/protobuf v1.36.12
        gopkg.in/warnings.v0 v0.1.2 // indirect
        k8s.io/apimachinery v0.36.3 // indirect
 )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/melange-0.59.0/go.sum new/melange-0.59.2/go.sum
--- old/melange-0.59.0/go.sum   2026-08-13 17:23:43.000000000 +0200
+++ new/melange-0.59.2/go.sum   2026-08-18 14:48:42.000000000 +0200
@@ -1,9 +1,9 @@
-chainguard.dev/apko v1.2.33 h1:KdMVBNOeCbq/e1pA/F+kquuBObgXQL9qm32JnKCtyA0=
-chainguard.dev/apko v1.2.33/go.mod 
h1:rZDihDy6oDIV9q+/AJ5ne4/OCS0nDOPOD4xxkNvsTjg=
-chainguard.dev/go-grpc-kit v0.17.17 
h1:Jwhc0zyUwQbC2hNcsi+YMeUX/JUnM+dXVCkTw6wtPzs=
-chainguard.dev/go-grpc-kit v0.17.17/go.mod 
h1:qn0meP6RtrbLicE1bgBZnnVU9dvX95eLs0x0T6kZ+b4=
-chainguard.dev/sdk v0.1.164 h1:k2Ir/l1cuHDS/vg29sLNjJYm2kInnCfR5w0uFD0Y1Hg=
-chainguard.dev/sdk v0.1.164/go.mod 
h1:y5AGo04erWjKtEyGX7fO9zZA4vtj9cLGXZjgIYxeMdo=
+chainguard.dev/apko v1.2.37 h1:xiLmeSQKIgrLfVE4YvbFV0FsQoU9x8dpQYoRNsVOVa8=
+chainguard.dev/apko v1.2.37/go.mod 
h1:inQA3zWjDP9dLbnxZzmv0KP6F1NYL+sDOWgvB5gl8oE=
+chainguard.dev/go-grpc-kit v0.18.0 
h1:kr451ml4eBgNcc0ytqcSSPZ3KGtyfHRrrBcYxW/oZwI=
+chainguard.dev/go-grpc-kit v0.18.0/go.mod 
h1:N1ZZiV3KkNHFlIS6RPhLT9ppfObC7aa1hqWMPmNiTA0=
+chainguard.dev/sdk v0.1.184 h1:L9SLup6giGc+qGUDZ7iuMKL5wC0rg6US23L2k5tbg5k=
+chainguard.dev/sdk v0.1.184/go.mod 
h1:EdDABW102LAdDIbUXoTokZlRlyTa6D2ml0tC9Agnpbs=
 cloud.google.com/go v0.26.0/go.mod 
h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go/auth v0.22.0 
h1:Xp9wAKkLoeaYb5pYZZoQGz4E9sdPxIbzS3gywZE3ciQ=
 cloud.google.com/go/auth v0.22.0/go.mod 
h1:M9o2Oz+YI2jAfxewJgb1vyI3vceHF+eohmxyzmrl+9s=
@@ -87,8 +87,8 @@
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod 
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/distribution/reference v0.6.0 
h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
 github.com/distribution/reference v0.6.0/go.mod 
h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/cli v29.7.1+incompatible 
h1:ILZpP6B7fedIr6ANy824QkDp1WMJuouIq0O2SrBkB2w=
-github.com/docker/cli v29.7.1+incompatible/go.mod 
h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
+github.com/docker/cli v29.7.2+incompatible 
h1:dlkwallR8XqfeVnA2ELEhdwvb4lsSwuB4IgsG8Q9cLY=
+github.com/docker/cli v29.7.2+incompatible/go.mod 
h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 github.com/docker/docker v28.5.2+incompatible 
h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
 github.com/docker/docker v28.5.2+incompatible/go.mod 
h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/docker-credential-helpers v0.9.8 
h1:bIREROb7So6PRlq6KTtdS9MPEjC29OQRkFNlvK2OX8Q=
@@ -121,8 +121,8 @@
 github.com/gliderlabs/ssh v0.3.8/go.mod 
h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
 github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 
h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
 github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod 
h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
-github.com/go-git/go-billy/v5 v5.9.0 
h1:jItGXszUDRtR/AlferWPTMN4j38BQ88XnXKbilmmBPA=
-github.com/go-git/go-billy/v5 v5.9.0/go.mod 
h1:jCnQMLj9eUgGU7+ludSTYoZL/GGmii14RxKFj7ROgHw=
+github.com/go-git/go-billy/v5 v5.9.1 
h1:8U73XiOTfINdItHVa6z4Gv7ToObcZ6grkqQbLryLCdA=
+github.com/go-git/go-billy/v5 v5.9.1/go.mod 
h1:ExsU+jcGwXTBOnyilvAnEM1wug1IxHr4yP2ZXsNRtV0=
 github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 
h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
 github.com/go-git/go-git-fixtures/v4 
v4.3.2-0.20231010084843-55a94097c399/go.mod 
h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
 github.com/go-git/go-git/v5 v5.19.2 
h1:wkfn7vOlUBu8ivAWKBWisTiwJK4jYHzTF8Ndv1LyGqY=
@@ -165,8 +165,8 @@
 github.com/google/go-cmp v0.5.9/go.mod 
h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
 github.com/google/go-cmp v0.7.0/go.mod 
h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
-github.com/google/go-containerregistry v0.21.8 
h1:Ig/zIsnztdCUNaiNNczE+MoP5xcyUMfvpvfOr1xyMLE=
-github.com/google/go-containerregistry v0.21.8/go.mod 
h1:dP5XNKcL7kMFF/TB3LfvWmVhAcv7iqkHb3oDK8aauTo=
+github.com/google/go-containerregistry v0.21.9 
h1:F+D4uZ3iA3DLMJLfhaqMdHJbzeqm/216WGQq2dokuLs=
+github.com/google/go-containerregistry v0.21.9/go.mod 
h1:dP5XNKcL7kMFF/TB3LfvWmVhAcv7iqkHb3oDK8aauTo=
 github.com/google/go-licenses/v2 v2.0.1 
h1:ti+9bi5o7DKbeeg5eBb/uZTgsaPNoJaLCh93cRcXsW8=
 github.com/google/go-licenses/v2 v2.0.1/go.mod 
h1:efibo0EDNGkau6AIMOViGW+rTNPudhxX9rCxtfw5zKE=
 github.com/google/go-replayers/httpreplay v1.2.0 
h1:VM1wEyyjaoU53BwrOnaf9VhAyQQEEioJvFYxYcLRKzk=
@@ -216,8 +216,8 @@
 github.com/kelseyhightower/envconfig v1.4.0/go.mod 
h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
 github.com/kevinburke/ssh_config v1.6.0 
h1:J1FBfmuVosPHf5GRdltRLhPJtJpTlMdKTBjRgTaQBFY=
 github.com/kevinburke/ssh_config v1.6.0/go.mod 
h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=
-github.com/klauspost/compress v1.19.1 
h1:VsB4HPswih7mmZ8WleSFQ75c/Ui1M4trX5oAsJnhSlk=
-github.com/klauspost/compress v1.19.1/go.mod 
h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
+github.com/klauspost/compress v1.19.2 
h1:hMRETovs/pu/dVWN7zIT1PGG8t509MwT6bO7XSi26R8=
+github.com/klauspost/compress v1.19.2/go.mod 
h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
 github.com/klauspost/cpuid/v2 v2.4.0 
h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw=
 github.com/klauspost/cpuid/v2 v2.4.0/go.mod 
h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU=
 github.com/klauspost/pgzip v1.2.6 
h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
@@ -280,15 +280,13 @@
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod 
h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0/go.mod 
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 
h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
-github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod 
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v1.23.2 
h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
-github.com/prometheus/client_golang v1.23.2/go.mod 
h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
+github.com/prometheus/client_golang v1.24.1 
h1:JnJkREXzWxUdCuPFpIWZiPispT9xVV59uiuyR2bPlnU=
+github.com/prometheus/client_golang v1.24.1/go.mod 
h1:F+oSRECHg4sse5ucfYpYDeIv/hu68Zo0uoHKetWnzcE=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod 
h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.6.2 
h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
 github.com/prometheus/client_model v0.6.2/go.mod 
h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
-github.com/prometheus/common v0.70.0 
h1:bcpru3tWPVnxGnETLgOV5jbp/JRXgYEyv65CuBLAMMI=
-github.com/prometheus/common v0.70.0/go.mod 
h1:S/SFasQmgGiYH6C81LKCtYa8QACgthGg5zxL2udV7SY=
+github.com/prometheus/common v0.70.1 
h1:1HvjP4D5oL3t8RsPlwxA9onvvStjtIHYE5XuuwOi/PY=
+github.com/prometheus/common v0.70.1/go.mod 
h1:VdFUQDMZK3VLkurFUVhia6uys/0suUp86TJz5qbJRhc=
 github.com/prometheus/procfs v0.21.1 
h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI=
 github.com/prometheus/procfs v0.21.1/go.mod 
h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY=
 github.com/psanford/memfs v0.0.0-20241019191636-4ef911798f9b 
h1:xzjEJAHum+mV5Dd5KyohRlCyP03o4yq6vNpEUtAJQzI=
@@ -329,8 +327,9 @@
 github.com/stretchr/testify v1.8.0/go.mod 
h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.1/go.mod 
h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/stretchr/testify v1.8.4/go.mod 
h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-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/stretchr/testify v1.12.0 
h1:K6Mr6jO9JICuend/5xzTM03ydSV3vdNRYAdPSukj8uI=
+github.com/stretchr/testify v1.12.0/go.mod 
h1:bOYBZb5qJ00vPzWfIqBUZPaxK8jWiXc6d3ErP4Ca9Gw=
 github.com/u-root/u-root v0.16.0 
h1:wY40O83MBVks97+Is0WlFlOPSwKQMIrWP9R1IsrExg8=
 github.com/u-root/u-root v0.16.0/go.mod 
h1:yL/XdSSW27PdGLgUh4MNRBy54mKM+TBLzpwiB4nwj90=
 github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 
h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM=
@@ -358,8 +357,8 @@
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0/go.mod 
h1:z9+yiacE0IHRqM4qFfkbt/JYlmYXgss8GY/jXoNuPJI=
 go.opentelemetry.io/otel v1.45.0 
h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU=
 go.opentelemetry.io/otel v1.45.0/go.mod 
h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 
h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod 
h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 
h1:4YsVu3B8+3qtWYYrsUYgn0OG78pN0rnNPRGX4SbokQI=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0/go.mod 
h1:+wnlSn0mD1ADVMe3v9Z/WIaiz6q6gL2J/ejaAmdmv80=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 
h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod 
h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak=
 go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.45.0 
h1:lsA/S1bxgdbyFGkTj+3meEdJ6ADVU7QoFstV6MXgE68=
@@ -380,8 +379,9 @@
 go.uber.org/goleak v1.3.0/go.mod 
h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
 go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
 go.yaml.in/yaml/v2 v2.4.4/go.mod 
h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
-go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
 go.yaml.in/yaml/v3 v3.0.4/go.mod 
h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
+go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
+go.yaml.in/yaml/v3 v3.0.5/go.mod 
h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
 go.yaml.in/yaml/v4 v4.0.0-rc.6 h1:1h7H1ohdUh93/FyE4YaDa1Zh64K6VVbjF4K6WUxMtH4=
 go.yaml.in/yaml/v4 v4.0.0-rc.6/go.mod 
h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod 
h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -389,11 +389,11 @@
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod 
h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod 
h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.19.0/go.mod 
h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
-golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
-golang.org/x/crypto v0.54.0/go.mod 
h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
+golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M=
+golang.org/x/crypto v0.55.0/go.mod 
h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod 
h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20260709172345-9ea1abe57597 
h1:qLvzZeaANDgyVOA8pyHCOStGlXn0rseXma+GQjeuv2g=
-golang.org/x/exp v0.0.0-20260709172345-9ea1abe57597/go.mod 
h1:EdfpwwqSu+0Li0mzskwHU6FWDV3t9Q+RZDo3QMUtL3Q=
+golang.org/x/exp v0.0.0-20260727155853-b88d891fe743 
h1:ex206bKw+v3K0dm3andkrIF+ijyQKJG1pLgwQ2PYdQM=
+golang.org/x/exp v0.0.0-20260727155853-b88d891fe743/go.mod 
h1:EdfpwwqSu+0Li0mzskwHU6FWDV3t9Q+RZDo3QMUtL3Q=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod 
h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod 
h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod 
h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
@@ -457,8 +457,8 @@
 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/text v0.14.0/go.mod 
h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
-golang.org/x/text v0.40.0/go.mod 
h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
+golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
+golang.org/x/text v0.41.0/go.mod 
h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
 golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
 golang.org/x/time v0.15.0/go.mod 
h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod 
h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -475,8 +475,8 @@
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
 gonum.org/v1/gonum v0.17.0/go.mod 
h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
-google.golang.org/api v0.291.0 h1:wfPbbY+mr9c7wZLqqzrHJLft/q8iFKREd6IgTBUene0=
-google.golang.org/api v0.291.0/go.mod 
h1:at7kwWbuonglBFEBoeMDAV1bguHqL3qf0BHFsv3coa0=
+google.golang.org/api v0.292.0 h1:Ewiwo/GTtiaPZSNAZQUcWLh8AYDEoPmIXyJfeoTSMHU=
+google.golang.org/api v0.292.0/go.mod 
h1:07kjmMnFGm2RQuCza2EZM/5N68G/fVvFb1xKjWqoFA0=
 google.golang.org/appengine v1.1.0/go.mod 
h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod 
h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod 
h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
@@ -484,17 +484,17 @@
 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod 
h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
 google.golang.org/genproto v0.0.0-20260406210006-6f92a3bedf2d 
h1:N1Ec54vZnIPd7MnxRiYLW+oY4fDR4BOS/LrssdD9+ek=
 google.golang.org/genproto v0.0.0-20260406210006-6f92a3bedf2d/go.mod 
h1:c2hJ1grtnH0xUiEKGDGkjGNTJ1Hy2LrblyKOHF0sqRM=
-google.golang.org/genproto/googleapis/api v0.0.0-20260706201446-f0a921348800 
h1:admdQBe8jR3VWhBsUrAOaF2Qw6K/+p5pSm1GN8+6Fw4=
-google.golang.org/genproto/googleapis/api 
v0.0.0-20260706201446-f0a921348800/go.mod 
h1:FPk7EXUKMtImne7AmknoYjT4QXqKIzzRbeQIXzLk6fQ=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20260724162435-b2f20204f0df 
h1:O3ig1i5WDDzsVzRp+cCdgelT9vXnlnOFdlEeFtL4HCc=
-google.golang.org/genproto/googleapis/rpc 
v0.0.0-20260724162435-b2f20204f0df/go.mod 
h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
+google.golang.org/genproto/googleapis/api v0.0.0-20260715232425-e75dac1f907d 
h1:QwnJwPte4XXAkhPu26LTDIahnsMSUV0kK8HkxbC+Pc4=
+google.golang.org/genproto/googleapis/api 
v0.0.0-20260715232425-e75dac1f907d/go.mod 
h1:WRrQ7/7N19PypuT0fxLOL5Lq0waoiRri4FbtHDEKrGE=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d 
h1:IL4hdHzcUv2l/gcg98/Rj3FbtE6axwqslOW8SW0C+S0=
+google.golang.org/genproto/googleapis/rpc 
v0.0.0-20260803160001-6ac0973c030d/go.mod 
h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
 google.golang.org/grpc v1.19.0/go.mod 
h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.23.0/go.mod 
h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 google.golang.org/grpc v1.25.1/go.mod 
h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
 google.golang.org/grpc v1.27.0/go.mod 
h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.33.2/go.mod 
h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
-google.golang.org/grpc v1.82.1 h1:NnAxzGRA0677vCa4BUkOAnO5+FfQqVl9iUXeD0IqcGE=
-google.golang.org/grpc v1.82.1/go.mod 
h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA=
+google.golang.org/grpc v1.83.0 h1:JeNZEKJFbQxArAMl+hiytHauacDNqJUllNfmIMmpqnQ=
+google.golang.org/grpc v1.83.0/go.mod 
h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod 
h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod 
h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod 
h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -504,8 +504,8 @@
 google.golang.org/protobuf v1.23.0/go.mod 
h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod 
h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 google.golang.org/protobuf v1.25.0/go.mod 
h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
-google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af 
h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI=
-google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod 
h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
+google.golang.org/protobuf v1.36.12 
h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
+google.golang.org/protobuf v1.36.12/go.mod 
h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c 
h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/melange-0.59.0/pkg/build/package.go 
new/melange-0.59.2/pkg/build/package.go
--- old/melange-0.59.0/pkg/build/package.go     2026-08-13 17:23:43.000000000 
+0200
+++ new/melange-0.59.2/pkg/build/package.go     2026-08-18 14:48:42.000000000 
+0200
@@ -296,10 +296,6 @@
        return buf.Bytes(), nil
 }
 
-func (pc *PackageBuild) SignatureName() string {
-       return fmt.Sprintf(".SIGN.RSA.%s.pub", 
filepath.Base(pc.Build.SigningKey))
-}
-
 // removeSelfProvidedDeps removes dependencies which are provided by the 
package itself.
 func removeSelfProvidedDeps(runtimeDeps, providedDeps []string) []string {
        providedDepsMap := map[string]bool{}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/melange-0.59.0/pkg/sign/apk.go 
new/melange-0.59.2/pkg/sign/apk.go
--- old/melange-0.59.0/pkg/sign/apk.go  2026-08-13 17:23:43.000000000 +0200
+++ new/melange-0.59.2/pkg/sign/apk.go  2026-08-18 14:48:42.000000000 +0200
@@ -155,7 +155,7 @@
        return sigbuf.Bytes(), nil
 }
 
-// Key base signature (normal) uses a SHA-1 hash on the control digest.
+// Key base signature (normal) uses a SHA-256 hash on the control digest.
 type KeyApkSigner struct {
        KeyFile       string
        KeyPassphrase string
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/melange-0.59.0/pkg/source/fetch.go 
new/melange-0.59.2/pkg/source/fetch.go
--- old/melange-0.59.0/pkg/source/fetch.go      1970-01-01 01:00:00.000000000 
+0100
+++ new/melange-0.59.2/pkg/source/fetch.go      2026-08-18 14:48:42.000000000 
+0200
@@ -0,0 +1,209 @@
+// Copyright 2026 Chainguard, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package source
+
+import (
+       "context"
+       "crypto/sha256"
+       "crypto/sha512"
+       "encoding/hex"
+       "fmt"
+       "hash"
+       "io"
+       "net/http"
+       "net/url"
+       "os"
+       "os/exec"
+       "path"
+       "path/filepath"
+       "strings"
+
+       "github.com/chainguard-dev/clog"
+)
+
+// FetchOptions describes a native equivalent of the built-in `fetch` pipeline.
+// Values come from the melange config and are used only as data: a URL, a
+// filename, and argv elements.
+type FetchOptions struct {
+       URI             string
+       Directory       string // where to extract; relative to the current 
working directory
+       StripComponents int
+       Extract         bool
+       ExpectedSHA256  string
+       ExpectedSHA512  string
+       Delete          bool
+}
+
+// Fetch downloads the artifact named by opts.URI into the current working
+// directory, optionally verifies its checksum, and optionally extracts it. The
+// URI is parsed and used as data (never passed to a shell).
+func Fetch(ctx context.Context, opts *FetchOptions) error {
+       log := clog.FromContext(ctx)
+
+       u, err := url.Parse(opts.URI)
+       if err != nil {
+               return fmt.Errorf("invalid fetch uri %q: %w", opts.URI, err)
+       }
+       if u.Scheme != "http" && u.Scheme != "https" {
+               return fmt.Errorf("invalid fetch uri %q: only http and https 
schemes are supported", opts.URI)
+       }
+       if u.Host == "" {
+               return fmt.Errorf("invalid fetch uri %q: missing host", 
opts.URI)
+       }
+
+       // Derive the local filename from the URL path only (not the raw 
string), so
+       // query strings or crafted values can't influence the on-disk name.
+       base := path.Base(u.Path)
+       if base == "" || base == "." || base == ".." || base == "/" {
+               return fmt.Errorf("could not determine a filename from uri %q", 
opts.URI)
+       }
+
+       log.Infof("Fetching %s", opts.URI)
+       if err := download(ctx, opts.URI, base); err != nil {
+               return err
+       }
+
+       if err := verifyChecksum(base, opts.ExpectedSHA256, 
opts.ExpectedSHA512); err != nil {
+               return err
+       }
+
+       if opts.Extract {
+               dir := opts.Directory
+               if dir == "" {
+                       dir = "."
+               }
+               // tar is invoked with an explicit argv, so the filename and 
directory
+               // are passed as plain arguments. tar auto-detects the 
compression
+               // format, matching the built-in pipeline.
+               // #nosec G204 - argv-only invocation; args are validated data, 
not shell
+               cmd := exec.CommandContext(ctx, "tar", "-x",
+                       fmt.Sprintf("--strip-components=%d", 
opts.StripComponents),
+                       "--no-same-owner", "-C", dir, "-f", base)
+               cmd.Stdout = os.Stdout
+               cmd.Stderr = os.Stderr
+               if err := cmd.Run(); err != nil {
+                       return fmt.Errorf("extracting %q: %w", base, err)
+               }
+       }
+
+       if opts.Delete {
+               if err := os.Remove(base); err != nil {
+                       return fmt.Errorf("deleting %q: %w", base, err)
+               }
+       }
+
+       return nil
+}
+
+// download streams uri to the file named dest in the current working 
directory.
+func download(ctx context.Context, uri, dest string) error {
+       req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
+       if err != nil {
+               return fmt.Errorf("creating request for %s: %w", uri, err)
+       }
+
+       resp, err := http.DefaultClient.Do(req) // #nosec G107 - uri scheme 
validated to http(s) by caller
+       if err != nil {
+               return fmt.Errorf("fetching %s: %w", uri, err)
+       }
+       defer resp.Body.Close()
+
+       if resp.StatusCode != http.StatusOK {
+               return fmt.Errorf("fetching %s: unexpected status %d", uri, 
resp.StatusCode)
+       }
+
+       // #nosec G304 - dest is a sanitized basename written into the 
workspace dir
+       f, err := os.Create(dest)
+       if err != nil {
+               return fmt.Errorf("creating %q: %w", dest, err)
+       }
+       defer f.Close()
+
+       if _, err := io.Copy(f, resp.Body); err != nil {
+               return fmt.Errorf("writing %q: %w", dest, err)
+       }
+
+       return nil
+}
+
+// verifyChecksum checks the downloaded file against the expected digests, when
+// provided. A missing checksum is not fatal here, as license-check is
+// best-effort.
+func verifyChecksum(file, expectedSHA256, expectedSHA512 string) error {
+       check := func(h hash.Hash, expected, algo string) error {
+               f, err := os.Open(file) // #nosec G304 - file is a sanitized 
basename in the workspace dir
+               if err != nil {
+                       return err
+               }
+               defer f.Close()
+               if _, err := io.Copy(h, f); err != nil {
+                       return err
+               }
+               got := hex.EncodeToString(h.Sum(nil))
+               if !strings.EqualFold(got, strings.TrimSpace(expected)) {
+                       return fmt.Errorf("%s mismatch for %q: expected %s, got 
%s", algo, file, expected, got)
+               }
+               return nil
+       }
+
+       if expectedSHA256 != "" {
+               return check(sha256.New(), expectedSHA256, "sha256")
+       }
+       if expectedSHA512 != "" {
+               return check(sha512.New(), expectedSHA512, "sha512")
+       }
+       return nil
+}
+
+// applyPatchStep applies patch files, a native equivalent of the built-in
+// `patch` pipeline. Each patch path is resolved relative to workDir, opened in
+// Go, and fed to `patch` on stdin.
+func applyPatchStep(ctx context.Context, patches string, stripComponents, fuzz 
int, workDir string) error {
+       log := clog.FromContext(ctx)
+
+       for patch := range strings.FieldsSeq(patches) {
+               // Keep patch paths within the workspace directory.
+               if filepath.IsAbs(patch) {
+                       return fmt.Errorf("absolute patch paths are not 
allowed: %q", patch)
+               }
+               patchPath := filepath.Join(workDir, patch)
+               if rel, err := filepath.Rel(workDir, patchPath); err != nil || 
rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
+                       return fmt.Errorf("patch path %q escapes the workspace 
directory", patch)
+               }
+
+               log.Infof("Applying patch %s", patchPath)
+
+               f, err := os.Open(patchPath) // #nosec G304 G703 - patch path 
validated to stay within the workspace dir
+               if err != nil {
+                       return fmt.Errorf("opening patch %q: %w", patchPath, 
err)
+               }
+
+               // #nosec G204 - argv-only invocation; strip/fuzz are ints, no 
shell
+               cmd := exec.CommandContext(ctx, "patch",
+                       fmt.Sprintf("-p%d", stripComponents),
+                       fmt.Sprintf("--fuzz=%d", fuzz))
+               cmd.Dir = workDir
+               cmd.Stdin = f
+               cmd.Stdout = os.Stdout
+               cmd.Stderr = os.Stderr
+               err = cmd.Run()
+               f.Close()
+               if err != nil {
+                       return fmt.Errorf("applying patch %q: %w", patchPath, 
err)
+               }
+       }
+
+       return nil
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/melange-0.59.0/pkg/source/source.go 
new/melange-0.59.2/pkg/source/source.go
--- old/melange-0.59.0/pkg/source/source.go     2026-08-13 17:23:43.000000000 
+0200
+++ new/melange-0.59.2/pkg/source/source.go     2026-08-18 14:48:42.000000000 
+0200
@@ -16,14 +16,13 @@
 
 import (
        "archive/tar"
-       "bytes"
        "compress/gzip"
        "context"
        "fmt"
        "io"
        "os"
-       "os/exec"
        "path/filepath"
+       "strconv"
        "strings"
 
        apkofs "chainguard.dev/apko/pkg/apk/fs"
@@ -31,27 +30,180 @@
 
        "chainguard.dev/melange/pkg/build"
        "chainguard.dev/melange/pkg/config"
+       "chainguard.dev/melange/pkg/util"
 )
 
-// Variable to allow mocking the runCommand function in tests.
-var sourceRunPipelineStep = runPipelineStep
+// sourceRunStep dispatches a single source-fetching pipeline step to its 
native
+// handler. It is a package variable so tests can stub it out.
+var sourceRunStep = runStep
+
+// runStep executes one supported source-fetching step (fetch, git-checkout, or
+// patch) natively in Go. Input values are resolved through the substitution 
map
+// (so `${{package.version}}` and friends still work) and are then used as 
data:
+// a URL, a git remote, and argv elements.
+func runStep(ctx context.Context, step config.Pipeline, sm 
*build.SubstitutionMap, _ bool, destDir string) error {
+       // resolve returns the substituted value for a `with:` input, or "" if 
the
+       // input was not provided. The result is used as data.
+       resolve := func(key string) (string, error) {
+               v, ok := step.With[key]
+               if !ok {
+                       return "", nil
+               }
+               return util.MutateStringFromMap(sm.Substitutions, v)
+       }
 
-// Simple wrapper for executing pipeline steps.
-func runPipelineStep(ctx context.Context, step config.Pipeline) error {
-       var stdout, stderr io.Writer
-       outputBuf := &bytes.Buffer{}
-       log := clog.FromContext(ctx)
-       stdout = outputBuf
-       stderr = outputBuf
+       switch step.Uses {
+       case "fetch":
+               return runFetchStep(ctx, resolve)
+       case "git-checkout":
+               return runGitCheckoutStep(ctx, resolve, destDir)
+       case "patch":
+               return runPatchStep(ctx, resolve, destDir)
+       default:
+               return fmt.Errorf("unsupported source step %q", step.Uses)
+       }
+}
 
-       cmd := []string{"/bin/sh", "-c", step.Pipeline[0].Runs}
-       // #nosec G204 - Executing pipeline step from trusted melange 
configuration
-       proc := exec.Command(cmd[0], cmd[1:]...)
-       proc.Stdout = stdout
-       proc.Stderr = stderr
-       log.Debugf("Command output:\n%s", outputBuf.String())
+func runFetchStep(ctx context.Context, resolve func(string) (string, error)) 
error {
+       get := func(dst *string, key string) error {
+               v, err := resolve(key)
+               if err != nil {
+                       return err
+               }
+               *dst = v
+               return nil
+       }
 
-       return proc.Run()
+       opts := &FetchOptions{}
+       var stripRaw, extractRaw, deleteRaw string
+       for dst, key := range map[*string]string{
+               &opts.URI:            "uri",
+               &opts.Directory:      "directory",
+               &opts.ExpectedSHA256: "expected-sha256",
+               &opts.ExpectedSHA512: "expected-sha512",
+               &stripRaw:            "strip-components",
+               &extractRaw:          "extract",
+               &deleteRaw:           "delete",
+       } {
+               if err := get(dst, key); err != nil {
+                       return err
+               }
+       }
+
+       opts.StripComponents = parseIntDefault(stripRaw, 1)
+       opts.Extract = parseBoolDefault(extractRaw, true)
+       opts.Delete = parseBoolDefault(deleteRaw, false)
+
+       return Fetch(ctx, opts)
+}
+
+func runGitCheckoutStep(ctx context.Context, resolve func(string) (string, 
error), destDir string) error {
+       repo, err := resolve("repository")
+       if err != nil {
+               return err
+       }
+       dest, err := resolve("destination")
+       if err != nil {
+               return err
+       }
+       switch {
+       case dest == "" || dest == ".":
+               dest = destDir
+       case !filepath.IsAbs(dest):
+               dest = filepath.Join(destDir, dest)
+       }
+       expectedCommit, err := resolve("expected-commit")
+       if err != nil {
+               return err
+       }
+       cherryPicks, err := resolve("cherry-picks")
+       if err != nil {
+               return err
+       }
+       recurse, err := resolve("recurse-submodules")
+       if err != nil {
+               return err
+       }
+
+       return GitCheckout(ctx, &GitCheckoutOptions{
+               Repository:        repo,
+               Destination:       dest,
+               ExpectedCommit:    expectedCommit,
+               CherryPicks:       cherryPicks,
+               WorkspaceDir:      destDir,
+               RecurseSubmodules: parseBoolDefault(recurse, false),
+       })
+}
+
+func runPatchStep(ctx context.Context, resolve func(string) (string, error), 
destDir string) error {
+       patches, err := resolve("patches")
+       if err != nil {
+               return err
+       }
+       series, err := resolve("series")
+       if err != nil {
+               return err
+       }
+       stripRaw, err := resolve("strip-components")
+       if err != nil {
+               return err
+       }
+       fuzzRaw, err := resolve("fuzz")
+       if err != nil {
+               return err
+       }
+
+       // A quilt-style series file lists patch filenames, one per line (with 
'#'
+       // comments). Fold it into the patch list when no explicit patches are 
given.
+       if strings.TrimSpace(patches) == "" && strings.TrimSpace(series) != "" {
+               // Keep the series path within the workspace directory.
+               if filepath.IsAbs(series) {
+                       return fmt.Errorf("absolute series paths are not 
allowed: %q", series)
+               }
+               seriesPath := filepath.Join(destDir, series)
+               if rel, err := filepath.Rel(destDir, seriesPath); err != nil || 
rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
+                       return fmt.Errorf("series path %q escapes the workspace 
directory", series)
+               }
+               data, err := os.ReadFile(seriesPath) // #nosec G304 - series 
path validated to stay within the workspace dir
+               if err != nil {
+                       return fmt.Errorf("reading series file %q: %w", 
seriesPath, err)
+               }
+               var names []string
+               for line := range strings.SplitSeq(string(data), "\n") {
+                       line = strings.TrimSpace(line)
+                       if line == "" || strings.HasPrefix(line, "#") {
+                               continue
+                       }
+                       names = append(names, line)
+               }
+               patches = strings.Join(names, " ")
+       }
+
+       if strings.TrimSpace(patches) == "" {
+               return fmt.Errorf("patch step: neither 'patches' nor 'series' 
was set")
+       }
+
+       return applyPatchStep(ctx, patches, parseIntDefault(stripRaw, 1), 
parseIntDefault(fuzzRaw, 2), destDir)
+}
+
+func parseIntDefault(s string, def int) int {
+       s = strings.TrimSpace(s)
+       if s == "" {
+               return def
+       }
+       n, err := strconv.Atoi(s)
+       if err != nil {
+               return def
+       }
+       return n
+}
+
+func parseBoolDefault(s string, def bool) bool {
+       s = strings.TrimSpace(s)
+       if s == "" {
+               return def
+       }
+       return s == "true"
 }
 
 // Function to extract the .melange.yaml from an apk package.
@@ -139,45 +291,14 @@
                return nil, fmt.Errorf("failed to parse melange config: %w", 
err)
        }
 
-       // Temporarily copy out the embedded files and directories from f into a
-       // temporary directory. We want to pass it later on to the pipeline
-       // compilation.
-       err = os.CopyFS(tmpDir, build.PipelinesFS)
-       if err != nil {
-               return nil, fmt.Errorf("failed to copy embedded pilelines: %w", 
err)
-       }
-
-       // Prepare the substitution map and compile the pipelines, making sure 
that
-       // the resulting pipeline run statements are all substituted with the
-       // correct values and ready for execution.
-       c := &build.Compiled{
-               PipelineDirs: []string{tmpDir},
-       }
-
-       // Now also try looking if the base directory of filePath has a 
pipelines
-       // directory. Add those to the list of directories to search for 
pipelines.
-       absFilePath, err := filepath.Abs(filePath)
-       if err != nil {
-               return nil, fmt.Errorf("failed to get absolute path of file: 
%w", err)
-       }
-       baseDir := filepath.Dir(absFilePath)
-       pipelinesDir := filepath.Join(baseDir, "pipelines")
-
-       if _, err := os.Stat(pipelinesDir); err == nil {
-               log.Infof("Found pipelines directory in base directory: %s", 
pipelinesDir)
-               c.PipelineDirs = append(c.PipelineDirs, pipelinesDir)
-       } else if !os.IsNotExist(err) {
-               return nil, fmt.Errorf("error checking pipelines directory: 
%w", err)
-       }
-
+       // Prepare the substitution map so that input values referencing 
built-in
+       // variables (e.g. ${{package.version}}) resolve correctly. The 
supported
+       // source steps are dispatched to native Go handlers below, which treat
+       // every substituted value as data.
        sm, err := build.NewSubstitutionMap(cfg, "amd64", "gnu", nil)
        if err != nil {
                return nil, fmt.Errorf("failed to create substitution map: %w", 
err)
        }
-       err = c.CompilePipelines(ctx, sm, cfg.Pipeline)
-       if err != nil {
-               return nil, fmt.Errorf("failed to compile pipelines: %w", err)
-       }
 
        // During command execution we change the working directory. We need to
        // make sure we change it back to the original working directory 
afterwards.
@@ -207,12 +328,12 @@
 
        // Iterate over the pipeline steps and look for any source fetching 
steps.
        for _, step := range cfg.Pipeline {
-               if step.Uses == "patch" && isApk {
-                       log.Warnf("Skipping patch step as we do not have 
patches available inside apk metadata yet.")
+               if step.Uses != "git-checkout" && step.Uses != "fetch" && 
step.Uses != "patch" {
                        continue
                }
 
-               if step.Uses != "git-checkout" && step.Uses != "fetch" && 
step.Uses != "patch" {
+               if step.Uses == "patch" && isApk {
+                       log.Warnf("Skipping patch step as we do not have 
patches available inside apk metadata yet.")
                        continue
                }
 
@@ -226,7 +347,7 @@
                if err != nil {
                        return nil, fmt.Errorf("failed to change directory: 
%w", err)
                }
-               err = sourceRunPipelineStep(ctx, step)
+               err = sourceRunStep(ctx, step, sm, isApk, destDir)
                if err != nil {
                        return nil, fmt.Errorf("failed to run step: %w", err)
                }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/melange-0.59.0/pkg/source/source_test.go 
new/melange-0.59.2/pkg/source/source_test.go
--- old/melange-0.59.0/pkg/source/source_test.go        2026-08-13 
17:23:43.000000000 +0200
+++ new/melange-0.59.2/pkg/source/source_test.go        2026-08-18 
14:48:42.000000000 +0200
@@ -23,6 +23,7 @@
        "path/filepath"
        "testing"
 
+       "chainguard.dev/melange/pkg/build"
        "chainguard.dev/melange/pkg/config"
 )
 
@@ -105,13 +106,13 @@
        }
 }
 
-// TestFetchSourceFromMelange tests the FetchSourceFromMelange function with 
mocked sourceRunCommand
+// TestFetchSourceFromMelange tests the FetchSourceFromMelange function with a 
mocked step dispatcher
 func TestFetchSourceFromMelange(t *testing.T) {
-       // Mock the sourceRunCommand function
+       // Mock the step dispatcher so the test stays hermetic (no network/git).
        stepsRun := []string{}
-       originalSourceRunPipelineStep := sourceRunPipelineStep
-       defer func() { sourceRunPipelineStep = originalSourceRunPipelineStep }()
-       sourceRunPipelineStep = func(ctx context.Context, step config.Pipeline) 
error {
+       originalSourceRunStep := sourceRunStep
+       defer func() { sourceRunStep = originalSourceRunStep }()
+       sourceRunStep = func(ctx context.Context, step config.Pipeline, sm 
*build.SubstitutionMap, isApk bool, destDir string) error {
                fmt.Printf("Running step: %s\n", step.Uses)
                stepsRun = append(stepsRun, step.Uses)
                return nil
@@ -175,4 +176,63 @@
                        }
                })
        }
+}
+
+// TestFetchSourceFromMelange_inputsTreatedAsData runs the real (unmocked)
+// dispatcher against configs whose inputs contain shell-like syntax, and
+// asserts those inputs are treated as literal data: the fetch/checkout fails
+// (the value is not a valid URL or git remote) and no side-effect file is
+// produced.
+func TestFetchSourceFromMelange_inputsTreatedAsData(t *testing.T) {
+       tmpDir := t.TempDir()
+       marker := filepath.Join(tmpDir, "marker")
+
+       // If a value were ever evaluated by a shell, it would create the marker
+       // file. As literal data (a URL / git remote) it cannot.
+       configs := map[string]string{
+               "fetch-uri": `package:
+  name: example
+  version: "1.0.0"
+  epoch: 0
+  copyright:
+    - license: MIT
+pipeline:
+  - uses: fetch
+    with:
+      uri: "$(touch ` + marker + `; echo x)"
+      expected-none: "true"
+`,
+               "git-checkout-repository": `package:
+  name: example
+  version: "1.0.0"
+  epoch: 0
+  copyright:
+    - license: MIT
+pipeline:
+  - uses: git-checkout
+    with:
+      repository: "$(touch ` + marker + `; echo x)"
+`,
+       }
+
+       for name, content := range configs {
+               t.Run(name, func(t *testing.T) {
+                       _ = os.Remove(marker)
+
+                       cfgPath := filepath.Join(tmpDir, name+".yaml")
+                       if err := os.WriteFile(cfgPath, []byte(content), 
0o600); err != nil {
+                               t.Fatalf("failed to write config: %v", err)
+                       }
+
+                       // The value is not a valid URL/remote, so fetching 
fails.
+                       _, err := FetchSourceFromMelange(context.Background(), 
cfgPath, filepath.Join(tmpDir, name+"-out"))
+                       if err == nil {
+                               t.Errorf("expected an error fetching from an 
invalid uri/repository, got nil")
+                       }
+
+                       if _, statErr := os.Stat(marker); statErr == nil {
+                               t.Fatalf("input was not treated as literal data 
(marker file %s was created)", marker)
+                       }
+               })
+       }
 }

++++++ melange.obsinfo ++++++
--- /var/tmp/diff_new_pack.A3ISQw/_old  2026-08-28 19:50:49.465195307 +0200
+++ /var/tmp/diff_new_pack.A3ISQw/_new  2026-08-28 19:50:49.476195691 +0200
@@ -1,5 +1,5 @@
 name: melange
-version: 0.59.0
-mtime: 1786634623
-commit: 4ec09c011312941753169b4089e69b5828d71c8e
+version: 0.59.2
+mtime: 1787057322
+commit: 2043d30b549e6f03c43e90341fb77c3532c4b790
 

++++++ vendor.tar.gz ++++++
/work/SRC/openSUSE:Factory/melange/vendor.tar.gz 
/work/SRC/openSUSE:Factory/.melange.new.1265/vendor.tar.gz differ: char 15, 
line 1

Reply via email to