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-10-27 16:59:09
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/melange (Old)
 and      /work/SRC/openSUSE:Factory/.melange.new.2252680 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "melange"

Tue Oct 27 16:59:09 2026 rev:187 rq:1392963 version:0.63.2

Changes:
--------
--- /work/SRC/openSUSE:Factory/melange/melange.changes  2026-10-24 
22:06:55.826404475 +0200
+++ /work/SRC/openSUSE:Factory/.melange.new.2252680/melange.changes     
2026-10-27 17:00:00.091219098 +0100
@@ -1,0 +2,11 @@
+Tue Oct 27 06:31:28 UTC 2026 - Johannes Kastl 
<[email protected]>
+
+- Update to version 0.63.2:
+  * perf(git-checkout): read verify_worktree's hashes in step with
+    the index (#2718)
+  * fix(pipelines): strip the whole userinfo from recorded source
+    URIs (#2717)
+  * fix(qemu): wait for SSH on a wall-clock deadline instead of a
+    try count (#2714)
+
+-------------------------------------------------------------------

Old:
----
  melange-0.63.1.obscpio

New:
----
  melange-0.63.2.obscpio

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

Other differences:
------------------
++++++ melange.spec ++++++
--- /var/tmp/diff_new_pack.d65f8O/_old  2026-10-27 17:00:02.295311400 +0100
+++ /var/tmp/diff_new_pack.d65f8O/_new  2026-10-27 17:00:02.297311483 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           melange
-Version:        0.63.1
+Version:        0.63.2
 Release:        0
 Summary:        Build APKs from source code
 License:        Apache-2.0

++++++ _service ++++++
--- /var/tmp/diff_new_pack.d65f8O/_old  2026-10-27 17:00:02.341313326 +0100
+++ /var/tmp/diff_new_pack.d65f8O/_new  2026-10-27 17:00:02.344313452 +0100
@@ -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.63.1</param>
+    <param name="revision">refs/tags/v0.63.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.d65f8O/_old  2026-10-27 17:00:02.379314917 +0100
+++ /var/tmp/diff_new_pack.d65f8O/_new  2026-10-27 17:00:02.383315085 +0100
@@ -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">04c0a4045867d1462842d383487f1f02f80ff7ac</param></service></servicedata>
+              <param 
name="changesrevision">402004649f817966440662612d0ea7302b95a858</param></service></servicedata>
 (No newline at EOF)
 

++++++ melange-0.63.1.obscpio -> melange-0.63.2.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/melange-0.63.1/pkg/build/pipelines/fetch.yaml 
new/melange-0.63.2/pkg/build/pipelines/fetch.yaml
--- old/melange-0.63.1/pkg/build/pipelines/fetch.yaml   2026-10-21 
00:14:41.000000000 +0200
+++ new/melange-0.63.2/pkg/build/pipelines/fetch.yaml   2026-10-23 
17:44:26.000000000 +0200
@@ -103,7 +103,7 @@
         # bytes, so only a build that asked to restore from a companion --
         # whose cache the pin in the binary package has verified -- looks;
         # an ordinary build with a cache it did not vet fetches as before.
-        uri=$(printf '%s\n' '${{inputs.uri}}' | sed -e 's|://[^/@]*@|://|' -e 
's|[?#].*$||')
+        uri=$(printf '%s\n' '${{inputs.uri}}' | sed -e 's|://[^/?#]*@|://|' -e 
's|[?#].*$||')
         for u in /var/cache/melange/sha256:*.uri; do
           [ -f "$u" ] || continue
           [ "$(head -n 1 "$u")" = "$uri" ] || continue
@@ -153,7 +153,7 @@
         cp "$bn" "$MELANGE_SOURCE_STASH/$key"
         # the URL as provenance: scheme, host and path only, since userinfo,
         # query and fragment are where credentials travel
-        printf '%s\n' '${{inputs.uri}}' | sed -e 's|://[^/@]*@|://|' -e 
's|[?#].*$||' > "$MELANGE_SOURCE_STASH/$key.uri"
+        printf '%s\n' '${{inputs.uri}}' | sed -e 's|://[^/?#]*@|://|' -e 
's|[?#].*$||' > "$MELANGE_SOURCE_STASH/$key.uri"
         printf "fetch: kept $key for the -source package\n"
       fi
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/melange-0.63.1/pkg/build/pipelines/git-checkout.yaml 
new/melange-0.63.2/pkg/build/pipelines/git-checkout.yaml
--- old/melange-0.63.1/pkg/build/pipelines/git-checkout.yaml    2026-10-21 
00:14:41.000000000 +0200
+++ new/melange-0.63.2/pkg/build/pipelines/git-checkout.yaml    2026-10-23 
17:44:26.000000000 +0200
@@ -349,7 +349,7 @@
           # when detached, as a tag checkout leaves it): builds read .git/HEAD
           # the URL as provenance: scheme, host and path only, since userinfo,
           # query and fragment are where credentials travel
-          printf '%s\n%s\n%s\n%s\n' "$(printf '%s' "$repo" | sed -e 
's|://[^/@]*@|://|' -e 's|[?#].*$||')" "$head" "$(git -C "$dest" log -1 
--format=%ct)" \
+          printf '%s\n%s\n%s\n%s\n' "$(printf '%s' "$repo" | sed -e 
's|://[^/?#]*@|://|' -e 's|[?#].*$||')" "$head" "$(git -C "$dest" log -1 
--format=%ct)" \
               "$(git -C "$dest" symbolic-ref --short -q HEAD || true)" > 
"$stash/$key.uri"
           git -C "$dest" cat-file commit "$head" > "$stash/$key.commit" || rm 
-f "$stash/$key.commit"
           # the commits the checkout stacked on the expected one (cherry-picks)
@@ -538,7 +538,7 @@
       # would leave them, so status and describe --dirty read clean. Gitlinks
       # are checked by verify_submodules.
       verify_worktree() {
-          local entries="" paths="" raw="" clean="" tab="" bad="" assume="" 
n=0 line="" mode="" sha="" path="" r="" c=""
+          local entries="" paths="" raw="" clean="" tab="" bad="" assume="" 
line="" mode="" sha="" path="" r="" c=""
           tab=$(printf '\t')
           entries=$(mktemp); paths=$(mktemp); raw=$(mktemp); clean=$(mktemp); 
bad=$(mktemp); assume=$(mktemp)
           git -c core.quotePath=false ls-files -s > "$entries"
@@ -547,13 +547,13 @@
               git hash-object -w --no-filters --stdin-paths < "$paths" > 
"$raw" || return 1
               git hash-object -w --stdin-paths < "$paths" > "$clean" || return 
1
           fi
-          n=0
+          # line i of raw and clean hashes the i-th regular file, so read
+          # them in step with the entries
           while IFS="$tab" read -r meta path; do
               mode=${meta%% *}; sha=${meta#* }; sha=${sha%% *}
               case "$mode" in
                   100644|100755)
-                      n=$((n + 1))
-                      r=$(sed -n "${n}p" "$raw"); c=$(sed -n "${n}p" "$clean")
+                      r=""; c=""; read -r r <&3 || :; read -r c <&4 || :
                       if [ "$r" = "$sha" ]; then
                           [ "$c" = "$sha" ] || printf '%s\n' "$path" >> 
"$assume"
                       elif [ "$c" != "$sha" ]; then
@@ -563,7 +563,7 @@
                       [ "$(printf '%s' "$(readlink "$path")" | git hash-object 
-w --stdin)" = "$sha" ] ||
                           printf '%s\n' "$path" >> "$bad" ;;
               esac
-          done < "$entries"
+          done < "$entries" 3< "$raw" 4< "$clean"
           # and nothing the tree does not name may be present: a git archive
           # holds exactly the tree, so anything else was put there
           git -c core.quotePath=false ls-files -o > "$paths"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/melange-0.63.1/pkg/build/source_uri_credentials_test.go 
new/melange-0.63.2/pkg/build/source_uri_credentials_test.go
--- old/melange-0.63.1/pkg/build/source_uri_credentials_test.go 1970-01-01 
01:00:00.000000000 +0100
+++ new/melange-0.63.2/pkg/build/source_uri_credentials_test.go 2026-10-23 
17:44:26.000000000 +0200
@@ -0,0 +1,72 @@
+// 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 build
+
+import (
+       "os/exec"
+       "regexp"
+       "strings"
+       "testing"
+
+       "github.com/stretchr/testify/require"
+
+       "chainguard.dev/melange/pkg/config"
+)
+
+var uriCredentialStrip = regexp.MustCompile(`sed -e 's\|://[^']*\|://\|' -e 
's\|\[\?#\]\.\*\$\|\|'`)
+
+// The by-URL lookup and the .uri records must normalise a URL identically, or
+// a record stops matching its own lookup. A userinfo may contain '@', so the
+// strip has to run to the last one in the authority.
+func TestSourceURICredentialStrip(t *testing.T) {
+       t.Parallel()
+       if testing.Short() {
+               t.Skip("runs shell commands")
+       }
+       for _, tool := range []string{"sh", "sed"} {
+               if _, err := exec.LookPath(tool); err != nil {
+                       t.Skipf("%s not available", tool)
+               }
+       }
+
+       found := make([]string, 0, 3)
+       for _, p := range []config.Pipeline{
+               {Uses: "fetch", With: map[string]string{"uri": "https://h/x";, 
"expected-sha256": strings.Repeat("0", 64)}},
+               {Uses: "git-checkout", With: map[string]string{"repository": 
"https://h/r";, "destination": "d"}},
+       } {
+               sm := &SubstitutionMap{Substitutions: map[string]string{
+                       config.SubstitutionPackageName: "hello", 
config.SubstitutionPackageVersion: "1.0",
+               }}
+               require.NoError(t, (&Compiled{}).compilePipeline(t.Context(), 
sm, &p, nil))
+               found = append(found, 
uriCredentialStrip.FindAllString(p.Pipeline[0].Runs, -1)...)
+       }
+       require.Len(t, found, 3, "fetch lookup, fetch record, git-checkout 
record")
+       require.Equal(t, found[0], found[1])
+       require.Equal(t, found[0], found[2])
+
+       for in, want := range map[string]string{
+               "https://u:p@ss@h/x?t=1":   "https://h/x";,
+               "https://u:pw@h/x":         "https://h/x";,
+               "https://h/@scope/pkg.tgz": "https://h/@scope/pkg.tgz";,
+               "https://h?x=a@b":          "https://h";,
+               "https://u:p@[::1]:8080/x": "https://[::1]:8080/x";,
+               "ssh://git@h:22/r":         "ssh://h:22/r",
+               "git@h:o/r":                "git@h:o/r",
+       } {
+               out, err := exec.CommandContext(t.Context(), "sh", "-c", 
`printf '%s\n' "$1" | `+found[0], "sh", in).CombinedOutput()
+               require.NoError(t, err, string(out))
+               require.Equal(t, want+"\n", string(out), in)
+       }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/melange-0.63.1/pkg/container/qemu_runner.go 
new/melange-0.63.2/pkg/container/qemu_runner.go
--- old/melange-0.63.1/pkg/container/qemu_runner.go     2026-10-21 
00:14:41.000000000 +0200
+++ new/melange-0.63.2/pkg/container/qemu_runner.go     2026-10-23 
17:44:26.000000000 +0200
@@ -83,6 +83,13 @@
        // sshKeepaliveRequestTimeout is how long to wait for a single keepalive
        // response before counting it as missed.
        sshKeepaliveRequestTimeout = 10 * time.Second
+
+       // defaultSSHWaitTimeout is how long to wait for the guest's SSH server
+       // to come up after starting the VM. Before sshd starts, the guest
+       // extracts the whole rootfs tarball onto its scratch disk, so large
+       // build/test environments take well over a minute to boot. Can be
+       // overridden with QEMU_SSH_WAIT_TIMEOUT.
+       defaultSSHWaitTimeout = 5 * time.Minute
 )
 
 type qemu struct{}
@@ -633,6 +640,15 @@
 
 func createMicroVM(ctx context.Context, cfg *Config) error {
        log := clog.FromContext(ctx)
+
+       // Validate the SSH wait timeout before any side effects, so a bad
+       // QEMU_SSH_WAIT_TIMEOUT fails the VM start instead of leaking a
+       // running VM and its disks.
+       sshWaitTimeout, err := sshWaitTimeoutFromEnv()
+       if err != nil {
+               return err
+       }
+
        log.Debug("qemu: ssh - create ssh key pair")
        pubKey, err := generateSSHKeys(ctx, cfg)
        if err != nil {
@@ -957,7 +973,11 @@
        // qemu-system-x86_64 or qemu-system-aarch64...
        // #nosec G204 - Architecture is from validated configuration, not user 
input
        qemuCmd := exec.CommandContext(ctx, fmt.Sprintf("qemu-system-%s", 
cfg.Arch.ToAPK()), baseargs...)
-       clog.FromContext(ctx).Infof("qemu: VM resources: arch=%s cpus=%d 
memory=%dMiB", cfg.Arch.ToAPK(), nproc, mem/1024)
+       rootfsSize := int64(0)
+       if fi, err := os.Stat(cfg.ImgRef); err == nil {
+               rootfsSize = fi.Size()
+       }
+       clog.FromContext(ctx).Infof("qemu: VM resources: arch=%s cpus=%d 
memory=%dMiB rootfs=%dMiB", cfg.Arch.ToAPK(), nproc, mem/1024, 
rootfsSize/1024/1024)
        clog.FromContext(ctx).Info("qemu: starting VM")
        clog.FromContext(ctx).Debugf("qemu: executing - %s", 
strings.Join(qemuCmd.Args, " "))
 
@@ -1011,12 +1031,14 @@
 
        started := make(chan struct{})
 
-       clog.FromContext(ctx).Info("qemu: waiting for SSH")
+       log.Infof("qemu: waiting for SSH (timeout %s)", sshWaitTimeout)
+       sshWaitStart := time.Now()
        go func() {
-               // 100ms timeout with a 100ms sleep
-               retries := 60
+               deadline := sshWaitStart.Add(sshWaitTimeout)
                try := 0
-               for try < retries {
+               var lastErr error
+               lastHeartbeat := sshWaitStart
+               for time.Now().Before(deadline) {
                        if logCtx.Err() != nil {
                                return
                        }
@@ -1024,22 +1046,31 @@
                        try++
                        time.Sleep(time.Millisecond * 100)
 
-                       log.Debugf("qemu: waiting for ssh to come up, try %d of 
%d", try, retries)
-                       err = checkSSHServer(cfg.SSHAddress)
-                       if err == nil {
+                       log.Debugf("qemu: waiting for ssh to come up, try %d", 
try)
+                       if err := checkSSHServer(cfg.SSHAddress); err == nil {
                                close(started)
                                return
                        } else {
-                               log.Info(err.Error())
+                               log.Debug(err.Error())
+                               lastErr = err
+                       }
+
+                       if time.Since(lastHeartbeat) >= 30*time.Second {
+                               log.Infof("qemu: still waiting for SSH after 
%.0fs (last probe error: %s)", time.Since(sshWaitStart).Seconds(), lastErr)
+                               lastHeartbeat = time.Now()
                        }
                }
 
-               cancel(fmt.Errorf("ssh server never came up"))
+               if lastErr != nil {
+                       cancel(fmt.Errorf("ssh server never came up within %s 
(last probe error: %w)", sshWaitTimeout, lastErr))
+               } else {
+                       cancel(fmt.Errorf("ssh server never came up within %s", 
sshWaitTimeout))
+               }
        }()
 
        select {
        case <-started:
-               log.Info("qemu: VM started successfully, SSH server is up")
+               log.Infof("qemu: VM started successfully, SSH server is up 
after %.1fs", time.Since(sshWaitStart).Seconds())
        case err := <-qemuExit:
                defer os.Remove(cfg.ImgRef)
                defer os.Remove(cfg.Disk)
@@ -1522,6 +1553,24 @@
        return diskName.Name(), os.Truncate(diskName.Name(), size)
 }
 
+// sshWaitTimeoutFromEnv returns how long to wait for the guest's SSH
+// server to come up, honoring the QEMU_SSH_WAIT_TIMEOUT environment
+// variable (a time.ParseDuration string, e.g. "90s" or "10m").
+func sshWaitTimeoutFromEnv() (time.Duration, error) {
+       v, ok := os.LookupEnv("QEMU_SSH_WAIT_TIMEOUT")
+       if !ok || v == "" {
+               return defaultSSHWaitTimeout, nil
+       }
+       d, err := time.ParseDuration(v)
+       if err == nil && d <= 0 {
+               err = errors.New("must be positive")
+       }
+       if err != nil {
+               return 0, fmt.Errorf("invalid QEMU_SSH_WAIT_TIMEOUT value %q: 
%w", v, err)
+       }
+       return d, nil
+}
+
 // qemu will open the port way before the ssh daemon is ready to listen
 // so we need to check if we get the SSH banner in order to value if the server
 // is up or not.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/melange-0.63.1/pkg/container/qemu_runner_test.go 
new/melange-0.63.2/pkg/container/qemu_runner_test.go
--- old/melange-0.63.1/pkg/container/qemu_runner_test.go        2026-10-21 
00:14:41.000000000 +0200
+++ new/melange-0.63.2/pkg/container/qemu_runner_test.go        2026-10-23 
17:44:26.000000000 +0200
@@ -25,6 +25,7 @@
        "path/filepath"
        "runtime"
        "testing"
+       "time"
 
        "github.com/chainguard-dev/clog"
        "github.com/chainguard-dev/clog/slogtest"
@@ -1233,4 +1234,87 @@
                        }
                })
        }
+}
+
+func TestSSHWaitTimeoutFromEnv(t *testing.T) {
+       tests := []struct {
+               name          string
+               envValue      string
+               envSet        bool
+               expectTimeout time.Duration
+               expectError   bool
+       }{
+               {
+                       name:          "env not set",
+                       envSet:        false,
+                       expectTimeout: defaultSSHWaitTimeout,
+               },
+               {
+                       name:          "env set to empty string",
+                       envValue:      "",
+                       envSet:        true,
+                       expectTimeout: defaultSSHWaitTimeout,
+               },
+               {
+                       name:          "env set to seconds",
+                       envValue:      "90s",
+                       envSet:        true,
+                       expectTimeout: 90 * time.Second,
+               },
+               {
+                       name:          "env set to minutes",
+                       envValue:      "10m",
+                       envSet:        true,
+                       expectTimeout: 10 * time.Minute,
+               },
+               {
+                       name:        "env set to invalid value",
+                       envValue:    "invalid",
+                       envSet:      true,
+                       expectError: true,
+               },
+               {
+                       name:        "env set to bare number",
+                       envValue:    "300",
+                       envSet:      true,
+                       expectError: true,
+               },
+               {
+                       name:        "env set to zero",
+                       envValue:    "0s",
+                       envSet:      true,
+                       expectError: true,
+               },
+               {
+                       name:        "env set to negative duration",
+                       envValue:    "-30s",
+                       envSet:      true,
+                       expectError: true,
+               },
+       }
+
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       if tt.envSet {
+                               t.Setenv("QEMU_SSH_WAIT_TIMEOUT", tt.envValue)
+                       } else {
+                               // t.Setenv registers the cleanup that restores 
the
+                               // original value after the subtest.
+                               t.Setenv("QEMU_SSH_WAIT_TIMEOUT", "")
+                               os.Unsetenv("QEMU_SSH_WAIT_TIMEOUT")
+                       }
+
+                       timeout, err := sshWaitTimeoutFromEnv()
+
+                       if tt.expectError && err == nil {
+                               t.Error("expected error but got nil")
+                       }
+                       if !tt.expectError && err != nil {
+                               t.Errorf("unexpected error: %v", err)
+                       }
+                       if !tt.expectError && timeout != tt.expectTimeout {
+                               t.Errorf("sshWaitTimeoutFromEnv() = %v, 
expected %v", timeout, tt.expectTimeout)
+                       }
+               })
+       }
 }

++++++ melange.obsinfo ++++++
--- /var/tmp/diff_new_pack.d65f8O/_old  2026-10-27 17:00:04.320396205 +0100
+++ /var/tmp/diff_new_pack.d65f8O/_new  2026-10-27 17:00:04.329396582 +0100
@@ -1,5 +1,5 @@
 name: melange
-version: 0.63.1
-mtime: 1792534481
-commit: 04c0a4045867d1462842d383487f1f02f80ff7ac
+version: 0.63.2
+mtime: 1792770266
+commit: 402004649f817966440662612d0ea7302b95a858
 

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

Reply via email to