Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package apko for openSUSE:Factory checked in at 2025-12-17 17:43:27 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/apko (Old) and /work/SRC/openSUSE:Factory/.apko.new.1939 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "apko" Wed Dec 17 17:43:27 2025 rev:82 rq:1323256 version:0.30.32 Changes: -------- --- /work/SRC/openSUSE:Factory/apko/apko.changes 2025-12-16 15:59:56.520161231 +0100 +++ /work/SRC/openSUSE:Factory/.apko.new.1939/apko.changes 2025-12-17 17:43:34.389821730 +0100 @@ -1,0 +2,7 @@ +Wed Dec 17 06:20:02 UTC 2025 - Johannes Kastl <[email protected]> + +- Update to version 0.30.32: + * lock: populate auto-discovered keys into the json lock file + (#1985) + +------------------------------------------------------------------- Old: ---- apko-0.30.31.obscpio New: ---- apko-0.30.32.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ apko.spec ++++++ --- /var/tmp/diff_new_pack.uHSeWr/_old 2025-12-17 17:43:37.785964532 +0100 +++ /var/tmp/diff_new_pack.uHSeWr/_new 2025-12-17 17:43:37.785964532 +0100 @@ -17,7 +17,7 @@ Name: apko -Version: 0.30.31 +Version: 0.30.32 Release: 0 Summary: Build OCI images from APK packages directly without Dockerfile License: Apache-2.0 ++++++ _service ++++++ --- /var/tmp/diff_new_pack.uHSeWr/_old 2025-12-17 17:43:37.853967392 +0100 +++ /var/tmp/diff_new_pack.uHSeWr/_new 2025-12-17 17:43:37.861967728 +0100 @@ -3,7 +3,7 @@ <param name="url">https://github.com/chainguard-dev/apko</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">v0.30.31</param> + <param name="revision">v0.30.32</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> <param name="changesgenerate">enable</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.uHSeWr/_old 2025-12-17 17:43:37.897969242 +0100 +++ /var/tmp/diff_new_pack.uHSeWr/_new 2025-12-17 17:43:37.901969410 +0100 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/chainguard-dev/apko</param> - <param name="changesrevision">f62a5012bc08971cd94f3fd0d3a67dd1855a946c</param></service></servicedata> + <param name="changesrevision">4586db5bfba0a88824ca7d79b097e0004c55d29d</param></service></servicedata> (No newline at EOF) ++++++ apko-0.30.31.obscpio -> apko-0.30.32.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apko-0.30.31/internal/cli/lock.go new/apko-0.30.32/internal/cli/lock.go --- old/apko-0.30.31/internal/cli/lock.go 2025-12-12 15:29:00.000000000 +0100 +++ new/apko-0.30.32/internal/cli/lock.go 2025-12-17 02:20:06.000000000 +0100 @@ -18,16 +18,20 @@ "context" "encoding/base64" "fmt" + "net/http" "os" "path/filepath" "slices" + "sort" "strings" + "time" "github.com/spf13/cobra" "github.com/chainguard-dev/clog" "chainguard.dev/apko/pkg/apk/apk" + "chainguard.dev/apko/pkg/apk/auth" apkfs "chainguard.dev/apko/pkg/apk/fs" "chainguard.dev/apko/pkg/build" "chainguard.dev/apko/pkg/build/types" @@ -169,6 +173,10 @@ }) } + // Discover and add auto-discovered keys from repositories + discoveredKeys := discoverKeysForLock(ctx, ic, archs) + lock.Contents.Keyrings = append(lock.Contents.Keyrings, discoveredKeys...) + // TODO: If the archs can't agree on package versions (e.g., arm builds are ahead of x86) then we should fail instead of producing inconsistent locks. for _, arch := range archs { log := log.With("arch", arch.ToAPK()) @@ -236,6 +244,12 @@ lock.Contents.Repositories = append(lock.Contents.Repositories, repoLock) } } + + // Sort keyrings by name for reproducible lock files + sort.Slice(lock.Contents.Keyrings, func(i, j int) bool { + return lock.Contents.Keyrings[i].Name < lock.Contents.Keyrings[j].Name + }) + return lock.SaveToFile(output) } @@ -262,3 +276,94 @@ "http://", ) } + +// discoverKeysForLock discovers keys from repositories and returns them as LockKeyring entries +func discoverKeysForLock(ctx context.Context, ic *types.ImageConfiguration, archs []types.Architecture) []pkglock.LockKeyring { + log := clog.FromContext(ctx) + + // Collect all unique repositories + repoSet := make(map[string]struct{}) + for _, repo := range ic.Contents.BuildRepositories { + repoSet[repo] = struct{}{} + } + for _, repo := range ic.Contents.RuntimeOnlyRepositories { + repoSet[repo] = struct{}{} + } + for _, repo := range ic.Contents.Repositories { + repoSet[repo] = struct{}{} + } + + // Map to track discovered keys by URL to avoid duplicates + discoveredKeyMap := make(map[string]pkglock.LockKeyring) + + // Fetch Alpine releases once (cached by HTTP client) + client := &http.Client{} + var alpineReleases *apk.Releases + + // Discover keys for each repository and architecture + for repo := range repoSet { + // Try Alpine-style key discovery + if ver, ok := apk.ParseAlpineVersion(repo); ok { + // Fetch releases.json if not already fetched + if alpineReleases == nil { + releases, err := apk.FetchAlpineReleases(ctx, client) + if err != nil { + log.Warnf("Failed to fetch Alpine releases: %v", err) + continue + } + alpineReleases = releases + } + + branch := alpineReleases.GetReleaseBranch(ver) + if branch == nil { + log.Debugf("Alpine version %s not found in releases", ver) + continue + } + + // Get keys for each architecture + for _, arch := range archs { + log.Debugf("Discovering Alpine keys for %s (version %s, arch %s)", repo, ver, arch.ToAPK()) + urls := branch.KeysFor(arch.ToAPK(), time.Now()) + if len(urls) == 0 { + log.Debugf("No keys found for arch %s and version %s", arch.ToAPK(), ver) + continue + } + + // Add discovered key URLs to the map + for _, u := range urls { + discoveredKeyMap[u] = pkglock.LockKeyring{ + Name: stripURLScheme(u), + URL: u, + } + } + } + } + + // Try Chainguard-style key discovery + log.Debugf("Attempting Chainguard-style key discovery for %s", repo) + keys, err := apk.DiscoverKeys(ctx, client, auth.DefaultAuthenticators, repo) + if err != nil { + log.Debugf("Chainguard-style key discovery failed for %s: %v", repo, err) + } else if len(keys) > 0 { + log.Debugf("Discovered %d Chainguard-style keys for %s", len(keys), repo) + // For each JWKS key, emit a URL: repository + "/" + KeyID + repoBase := strings.TrimSuffix(repo, "/") + for _, key := range keys { + keyURL := repoBase + "/" + key.ID + discoveredKeyMap[keyURL] = pkglock.LockKeyring{ + Name: stripURLScheme(keyURL), + URL: keyURL, + } + } + } + } + + // Convert map to slice + discoveredKeys := make([]pkglock.LockKeyring, 0, len(discoveredKeyMap)) + for _, key := range discoveredKeyMap { + discoveredKeys = append(discoveredKeys, key) + } + + log.Infof("Discovered %d auto-discovered keys", len(discoveredKeys)) + return discoveredKeys +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apko-0.30.31/internal/cli/lock_test.go new/apko-0.30.32/internal/cli/lock_test.go --- old/apko-0.30.31/internal/cli/lock_test.go 2025-12-12 15:29:00.000000000 +0100 +++ new/apko-0.30.32/internal/cli/lock_test.go 2025-12-17 02:20:06.000000000 +0100 @@ -33,25 +33,38 @@ ctx := context.Background() tmp := t.TempDir() - golden := filepath.Join("testdata", "apko.lock.json") - - config := "apko.yaml" - archs := types.ParseArchitectures([]string{"amd64", "arm64"}) - opts := []build.Option{build.WithConfig(config, []string{"testdata"})} - outputPath := filepath.Join(tmp, "apko.lock.json") - - err := cli.LockCmd(ctx, outputPath, archs, opts) - require.NoError(t, err) - - want, err := os.ReadFile(golden) - require.NoError(t, err) - got, err := os.ReadFile(outputPath) - require.NoError(t, err) - - if !bytes.Equal(want, got) { - if diff := cmp.Diff(want, got); diff != "" { - t.Errorf("Mismatched lock files: (-%q +%q):\n%s", golden, outputPath, diff) - } + tests := []struct { + basename string + }{ + { + basename: "apko", + }, { + basename: "apko-discover", + }, + } + for _, tt := range tests { + t.Run(tt.basename, func(t *testing.T) { + golden := filepath.Join("testdata", tt.basename+".lock.json") + + config := tt.basename + ".yaml" + archs := types.ParseArchitectures([]string{"amd64", "arm64"}) + opts := []build.Option{build.WithConfig(config, []string{"testdata"})} + outputPath := filepath.Join(tmp, tt.basename+".lock.json") + + err := cli.LockCmd(ctx, outputPath, archs, opts) + require.NoError(t, err) + + want, err := os.ReadFile(golden) + require.NoError(t, err) + got, err := os.ReadFile(outputPath) + require.NoError(t, err) + + if !bytes.Equal(want, got) { + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("Mismatched lock files: (-%q +%q):\n%s", golden, outputPath, diff) + } + } + }) } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apko-0.30.31/internal/cli/testdata/apko-discover.lock.json new/apko-0.30.32/internal/cli/testdata/apko-discover.lock.json --- old/apko-0.30.31/internal/cli/testdata/apko-discover.lock.json 1970-01-01 01:00:00.000000000 +0100 +++ new/apko-0.30.32/internal/cli/testdata/apko-discover.lock.json 2025-12-17 02:20:06.000000000 +0100 @@ -0,0 +1,147 @@ +{ + "version": "v1", + "config": { + "name": "apko-discover.yaml", + "checksum": "sha256-D1mtHLoTBln0iH+VqWZMIPQOScXMWprVolB2sihPWAk=" + }, + "contents": { + "keyring": [ + { + "name": "./testdata/melange.rsa.pub", + "url": "./testdata/melange.rsa.pub" + }, + { + "name": "alpinelinux.org/keys/alpine-devel%40lists.alpinelinux.org-6165ee59.rsa.pub", + "url": "https://alpinelinux.org/keys/alpine-devel%40lists.alpinelinux.org-6165ee59.rsa.pub" + }, + { + "name": "alpinelinux.org/keys/alpine-devel%40lists.alpinelinux.org-616ae350.rsa.pub", + "url": "https://alpinelinux.org/keys/alpine-devel%40lists.alpinelinux.org-616ae350.rsa.pub" + }, + { + "name": "apk.cgr.dev/chainguard/chainguard-6ea100e7571978828f8455393090cc468e1f22dfc771ad2d26df14e52b73c37f.rsa.pub", + "url": "https://apk.cgr.dev/chainguard/chainguard-6ea100e7571978828f8455393090cc468e1f22dfc771ad2d26df14e52b73c37f.rsa.pub" + }, + { + "name": "apk.cgr.dev/chainguard/chainguard-7fb528a64a862d44bbea6069093f1fec29fa864ba7e9754828eeceed3f487239.rsa.pub", + "url": "https://apk.cgr.dev/chainguard/chainguard-7fb528a64a862d44bbea6069093f1fec29fa864ba7e9754828eeceed3f487239.rsa.pub" + }, + { + "name": "apk.cgr.dev/chainguard/chainguard-a87b67b52e9fa08414a5535a99adc7b66643536ccd52f4da66057b67adf21363.rsa.pub", + "url": "https://apk.cgr.dev/chainguard/chainguard-a87b67b52e9fa08414a5535a99adc7b66643536ccd52f4da66057b67adf21363.rsa.pub" + } + ], + "build_repositories": [], + "runtime_repositories": [], + "repositories": [ + { + "name": "dl-cdn.alpinelinux.org/alpine/v3.22/main/x86_64", + "url": "https://dl-cdn.alpinelinux.org/alpine/v3.22/main/x86_64/APKINDEX.tar.gz", + "architecture": "x86_64" + }, + { + "name": "apk.cgr.dev/chainguard/x86_64", + "url": "https://apk.cgr.dev/chainguard/x86_64/APKINDEX.tar.gz", + "architecture": "x86_64" + }, + { + "name": "./testdata/packages/x86_64", + "url": "./testdata/packages/x86_64/APKINDEX.tar.gz", + "architecture": "x86_64" + }, + { + "name": "dl-cdn.alpinelinux.org/alpine/v3.22/main/aarch64", + "url": "https://dl-cdn.alpinelinux.org/alpine/v3.22/main/aarch64/APKINDEX.tar.gz", + "architecture": "aarch64" + }, + { + "name": "apk.cgr.dev/chainguard/aarch64", + "url": "https://apk.cgr.dev/chainguard/aarch64/APKINDEX.tar.gz", + "architecture": "aarch64" + }, + { + "name": "./testdata/packages/aarch64", + "url": "./testdata/packages/aarch64/APKINDEX.tar.gz", + "architecture": "aarch64" + } + ], + "packages": [ + { + "name": "pretend-baselayout", + "url": "./testdata/packages/x86_64/pretend-baselayout-1.0.0-r0.apk", + "version": "1.0.0-r0", + "architecture": "x86_64", + "signature": { + "range": "bytes=0-648", + "checksum": "sha1-V+Htugmm+Ru2ogsWm7VgD4A1DsQ=" + }, + "control": { + "range": "bytes=649-1562", + "checksum": "sha1-DRtLIHolxOMB++9L4ZjkeUFaKYc=" + }, + "data": { + "range": "bytes=1563-2767", + "checksum": "sha256-dZB1iTdQ2sfndKG6Ohf5VwWFjqz6kjSzZbqYU17BSRM=" + }, + "checksum": "Q1DRtLIHolxOMB++9L4ZjkeUFaKYc=" + }, + { + "name": "replayout", + "url": "./testdata/packages/x86_64/replayout-1.0.0-r0.apk", + "version": "1.0.0-r0", + "architecture": "x86_64", + "signature": { + "range": "bytes=0-647", + "checksum": "sha1-ZrPCeQ4XeDjZSQw+IhJ4g4BcUlo=" + }, + "control": { + "range": "bytes=648-1589", + "checksum": "sha1-IvTcfj6zzLipr9akZ+YRTIyQCr8=" + }, + "data": { + "range": "bytes=1590-2786", + "checksum": "sha256-IIzbGjwv4H9h6N1bEbF8p4cqkV0Ex54sXEsvf6txnEo=" + }, + "checksum": "Q1IvTcfj6zzLipr9akZ+YRTIyQCr8=" + }, + { + "name": "pretend-baselayout", + "url": "./testdata/packages/aarch64/pretend-baselayout-1.0.0-r0.apk", + "version": "1.0.0-r0", + "architecture": "aarch64", + "signature": { + "range": "bytes=0-644", + "checksum": "sha1-n9SJ91H1UwE+mkVVCifh6ziTwbc=" + }, + "control": { + "range": "bytes=645-1555", + "checksum": "sha1-URAMn9SfiCvjs6C812GovkgRgVo=" + }, + "data": { + "range": "bytes=1556-2762", + "checksum": "sha256-igCqZKcxRp6yHq2IlyozkM68Z6v9PMr/PDAIuSh2W3A=" + }, + "checksum": "Q1URAMn9SfiCvjs6C812GovkgRgVo=" + }, + { + "name": "replayout", + "url": "./testdata/packages/aarch64/replayout-1.0.0-r0.apk", + "version": "1.0.0-r0", + "architecture": "aarch64", + "signature": { + "range": "bytes=0-646", + "checksum": "sha1-1ifrimC4bUlo4O6aCGXcjOKAcTo=" + }, + "control": { + "range": "bytes=647-1586", + "checksum": "sha1-SWYSZF3dGLrN8kebGjOBfDH6vG4=" + }, + "data": { + "range": "bytes=1587-2787", + "checksum": "sha256-U85iWddHApXsVosa4S0srhkr3SSlzuoBrEpZg5f1irQ=" + }, + "checksum": "Q1SWYSZF3dGLrN8kebGjOBfDH6vG4=" + } + ] + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apko-0.30.31/internal/cli/testdata/apko-discover.yaml new/apko-0.30.32/internal/cli/testdata/apko-discover.yaml --- old/apko-0.30.31/internal/cli/testdata/apko-discover.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/apko-0.30.32/internal/cli/testdata/apko-discover.yaml 2025-12-17 02:20:06.000000000 +0100 @@ -0,0 +1,16 @@ +contents: + keyring: + - ./testdata/melange.rsa.pub + repositories: + - https://dl-cdn.alpinelinux.org/alpine/v3.22/main + - https://apk.cgr.dev/chainguard + - ./testdata/packages + packages: + - replayout + +entrypoint: + command: /bin/sh -l + +archs: +- x86_64 +- aarch64 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/apko-0.30.31/pkg/apk/apk/implementation.go new/apko-0.30.32/pkg/apk/apk/implementation.go --- old/apko-0.30.31/pkg/apk/apk/implementation.go 2025-12-12 15:29:00.000000000 +0100 +++ new/apko-0.30.32/pkg/apk/apk/implementation.go 2025-12-17 02:20:06.000000000 +0100 @@ -321,10 +321,10 @@ // Perform key discovery for the various build-time repositories. for _, repo := range buildRepos { - if ver, ok := parseAlpineVersion(repo); ok { + if ver, ok := ParseAlpineVersion(repo); ok { if err := a.fetchAlpineKeys(ctx, ver); err != nil { var nokeysErr *NoKeysFoundError - if !errors.As(err, &nokeysErr) { + if !a.cache.offline && !errors.As(err, &nokeysErr) { return fmt.Errorf("failed to fetch alpine-keys: %w", err) } log.Debugf("ignoring missing keys: %v", err) @@ -449,7 +449,9 @@ var repoRE = regexp.MustCompile(`^http[s]?://.+\/alpine\/([^\/]+)\/[^\/]+$`) -func parseAlpineVersion(repo string) (version string, ok bool) { +// ParseAlpineVersion parses the Alpine version from a repository URL. +// Returns the version string (e.g., "v3.21") and true if successful. +func ParseAlpineVersion(repo string) (version string, ok bool) { parts := repoRE.FindStringSubmatch(repo) if len(parts) < 2 { return "", false @@ -872,36 +874,45 @@ return fmt.Sprintf("no keys found for arch %s and releases %v", e.arch, e.releases) } -// fetchAlpineKeys fetches the public keys for the repositories in the APK database. -func (a *APK) fetchAlpineKeys(ctx context.Context, alpineVersions ...string) error { - ctx, span := otel.Tracer("go-apk").Start(ctx, "fetchAlpineKeys") - defer span.End() - +// FetchAlpineReleases fetches and returns the Alpine releases metadata from alpinelinux.org. +func FetchAlpineReleases(ctx context.Context, client *http.Client) (*Releases, error) { u := alpineReleasesURL - client := a.client - if a.cache != nil { - client = a.cache.client(client, true) - } req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil) if err != nil { - return err + return nil, err } // NB: Not setting basic auth, since we know Alpine doesn't support it. res, err := client.Do(req) if err != nil { - return fmt.Errorf("failed to fetch alpine releases: %w", err) + return nil, fmt.Errorf("failed to fetch alpine releases: %w", err) } defer res.Body.Close() if res.StatusCode != http.StatusOK { - return fmt.Errorf("unable to get alpine releases at %s: %v", u, res.Status) + return nil, fmt.Errorf("unable to get alpine releases at %s: %v", u, res.Status) } b, err := io.ReadAll(res.Body) if err != nil { - return fmt.Errorf("failed to read alpine releases: %w", err) + return nil, fmt.Errorf("failed to read alpine releases: %w", err) } var releases Releases if err := json.Unmarshal(b, &releases); err != nil { - return fmt.Errorf("failed to unmarshal alpine releases: %w", err) + return nil, fmt.Errorf("failed to unmarshal alpine releases: %w", err) + } + return &releases, nil +} + +// fetchAlpineKeys fetches the public keys for the repositories in the APK database. +func (a *APK) fetchAlpineKeys(ctx context.Context, alpineVersions ...string) error { + ctx, span := otel.Tracer("go-apk").Start(ctx, "fetchAlpineKeys") + defer span.End() + + client := a.client + if a.cache != nil { + client = a.cache.client(client, true) + } + releases, err := FetchAlpineReleases(ctx, client) + if err != nil { + return err } var urls []string // now just need to get the keys for the desired architecture and releases ++++++ apko.obsinfo ++++++ --- /var/tmp/diff_new_pack.uHSeWr/_old 2025-12-17 17:43:39.110020207 +0100 +++ /var/tmp/diff_new_pack.uHSeWr/_new 2025-12-17 17:43:39.118020543 +0100 @@ -1,5 +1,5 @@ name: apko -version: 0.30.31 -mtime: 1765549740 -commit: f62a5012bc08971cd94f3fd0d3a67dd1855a946c +version: 0.30.32 +mtime: 1765934406 +commit: 4586db5bfba0a88824ca7d79b097e0004c55d29d ++++++ vendor.tar.gz ++++++ /work/SRC/openSUSE:Factory/apko/vendor.tar.gz /work/SRC/openSUSE:Factory/.apko.new.1939/vendor.tar.gz differ: char 91, line 2
