chore(deps): bump github.com/google/go-containerregistry (#3206)

Bumps [github.com/google/go-containerregistry](https://github.com/google/go-containerregistry) from 0.19.1 to 0.19.2.
- [Release notes](https://github.com/google/go-containerregistry/releases)
- [Changelog](https://github.com/google/go-containerregistry/blob/main/.goreleaser.yml)
- [Commits](https://github.com/google/go-containerregistry/compare/v0.19.1...v0.19.2)

---
updated-dependencies:
- dependency-name: github.com/google/go-containerregistry
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
dependabot[bot] 2024-06-17 12:20:48 -07:00 committed by GitHub
parent 50850006ce
commit 407904a688
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 138 additions and 32 deletions

2
go.mod
View File

@ -17,7 +17,7 @@ require (
github.com/go-git/go-git/v5 v5.12.0 github.com/go-git/go-git/v5 v5.12.0
github.com/golang/mock v1.6.0 github.com/golang/mock v1.6.0
github.com/google/go-cmp v0.6.0 github.com/google/go-cmp v0.6.0
github.com/google/go-containerregistry v0.19.1 github.com/google/go-containerregistry v0.19.2
github.com/google/go-github v17.0.0+incompatible github.com/google/go-github v17.0.0+incompatible
github.com/google/slowjam v1.1.1 github.com/google/slowjam v1.1.1
github.com/karrick/godirwalk v1.16.1 github.com/karrick/godirwalk v1.16.1

4
go.sum
View File

@ -276,8 +276,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-containerregistry v0.19.1 h1:yMQ62Al6/V0Z7CqIrrS1iYoA5/oQCm88DeNujc7C1KY= github.com/google/go-containerregistry v0.19.2 h1:TannFKE1QSajsP6hPWb5oJNgKe1IKjHukIKDUmvsV6w=
github.com/google/go-containerregistry v0.19.1/go.mod h1:YCMFNQeeXeLF+dnhhWkqDItx/JSkH01j1Kis4PsjzFI= github.com/google/go-containerregistry v0.19.2/go.mod h1:YCMFNQeeXeLF+dnhhWkqDItx/JSkH01j1Kis4PsjzFI=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=

View File

@ -15,6 +15,7 @@
package authn package authn
import ( import (
"context"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -27,6 +28,22 @@ type Authenticator interface {
Authorization() (*AuthConfig, error) Authorization() (*AuthConfig, error)
} }
// ContextAuthenticator is like Authenticator, but allows for context to be passed in.
type ContextAuthenticator interface {
// Authorization returns the value to use in an http transport's Authorization header.
AuthorizationContext(context.Context) (*AuthConfig, error)
}
// Authorization calls AuthorizationContext with ctx if the given [Authenticator] implements [ContextAuthenticator],
// otherwise it calls Resolve with the given [Resource].
func Authorization(ctx context.Context, authn Authenticator) (*AuthConfig, error) {
if actx, ok := authn.(ContextAuthenticator); ok {
return actx.AuthorizationContext(ctx)
}
return authn.Authorization()
}
// AuthConfig contains authorization information for connecting to a Registry // AuthConfig contains authorization information for connecting to a Registry
// Inlined what we use from github.com/docker/cli/cli/config/types // Inlined what we use from github.com/docker/cli/cli/config/types
type AuthConfig struct { type AuthConfig struct {

View File

@ -15,6 +15,7 @@
package authn package authn
import ( import (
"context"
"os" "os"
"path/filepath" "path/filepath"
"sync" "sync"
@ -45,6 +46,11 @@ type Keychain interface {
Resolve(Resource) (Authenticator, error) Resolve(Resource) (Authenticator, error)
} }
// ContextKeychain is like Keychain, but allows for context to be passed in.
type ContextKeychain interface {
ResolveContext(context.Context, Resource) (Authenticator, error)
}
// defaultKeychain implements Keychain with the semantics of the standard Docker // defaultKeychain implements Keychain with the semantics of the standard Docker
// credential keychain. // credential keychain.
type defaultKeychain struct { type defaultKeychain struct {
@ -62,8 +68,23 @@ const (
DefaultAuthKey = "https://" + name.DefaultRegistry + "/v1/" DefaultAuthKey = "https://" + name.DefaultRegistry + "/v1/"
) )
// Resolve implements Keychain. // Resolve calls ResolveContext with ctx if the given [Keychain] implements [ContextKeychain],
// otherwise it calls Resolve with the given [Resource].
func Resolve(ctx context.Context, keychain Keychain, target Resource) (Authenticator, error) {
if rctx, ok := keychain.(ContextKeychain); ok {
return rctx.ResolveContext(ctx, target)
}
return keychain.Resolve(target)
}
// ResolveContext implements ContextKeychain.
func (dk *defaultKeychain) Resolve(target Resource) (Authenticator, error) { func (dk *defaultKeychain) Resolve(target Resource) (Authenticator, error) {
return dk.ResolveContext(context.Background(), target)
}
// Resolve implements Keychain.
func (dk *defaultKeychain) ResolveContext(ctx context.Context, target Resource) (Authenticator, error) {
dk.mu.Lock() dk.mu.Lock()
defer dk.mu.Unlock() defer dk.mu.Unlock()
@ -86,8 +107,8 @@ func (dk *defaultKeychain) Resolve(target Resource) (Authenticator, error) {
// config.Load, which may fail if the config can't be parsed. // config.Load, which may fail if the config can't be parsed.
// //
// If neither was found, look for Podman's auth at // If neither was found, look for Podman's auth at
// $XDG_RUNTIME_DIR/containers/auth.json and attempt to load it as a // $REGISTRY_AUTH_FILE or $XDG_RUNTIME_DIR/containers/auth.json
// Docker config. // and attempt to load it as a Docker config.
// //
// If neither are found, fallback to Anonymous. // If neither are found, fallback to Anonymous.
var cf *configfile.ConfigFile var cf *configfile.ConfigFile
@ -96,16 +117,28 @@ func (dk *defaultKeychain) Resolve(target Resource) (Authenticator, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
} else { } else if fileExists(os.Getenv("REGISTRY_AUTH_FILE")) {
f, err := os.Open(filepath.Join(os.Getenv("XDG_RUNTIME_DIR"), "containers/auth.json")) f, err := os.Open(os.Getenv("REGISTRY_AUTH_FILE"))
if err != nil { if err != nil {
return Anonymous, nil return nil, err
} }
defer f.Close() defer f.Close()
cf, err = config.LoadFromReader(f) cf, err = config.LoadFromReader(f)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} else if fileExists(filepath.Join(os.Getenv("XDG_RUNTIME_DIR"), "containers/auth.json")) {
f, err := os.Open(filepath.Join(os.Getenv("XDG_RUNTIME_DIR"), "containers/auth.json"))
if err != nil {
return nil, err
}
defer f.Close()
cf, err = config.LoadFromReader(f)
if err != nil {
return nil, err
}
} else {
return Anonymous, nil
} }
// See: // See:
@ -168,6 +201,10 @@ func NewKeychainFromHelper(h Helper) Keychain { return wrapper{h} }
type wrapper struct{ h Helper } type wrapper struct{ h Helper }
func (w wrapper) Resolve(r Resource) (Authenticator, error) { func (w wrapper) Resolve(r Resource) (Authenticator, error) {
return w.ResolveContext(context.Background(), r)
}
func (w wrapper) ResolveContext(ctx context.Context, r Resource) (Authenticator, error) {
u, p, err := w.h.Get(r.RegistryStr()) u, p, err := w.h.Get(r.RegistryStr())
if err != nil { if err != nil {
return Anonymous, nil return Anonymous, nil
@ -194,8 +231,12 @@ type refreshingKeychain struct {
} }
func (r *refreshingKeychain) Resolve(target Resource) (Authenticator, error) { func (r *refreshingKeychain) Resolve(target Resource) (Authenticator, error) {
return r.ResolveContext(context.Background(), target)
}
func (r *refreshingKeychain) ResolveContext(ctx context.Context, target Resource) (Authenticator, error) {
last := time.Now() last := time.Now()
auth, err := r.keychain.Resolve(target) auth, err := Resolve(ctx, r.keychain, target)
if err != nil || auth == Anonymous { if err != nil || auth == Anonymous {
return auth, err return auth, err
} }
@ -224,17 +265,21 @@ type refreshing struct {
} }
func (r *refreshing) Authorization() (*AuthConfig, error) { func (r *refreshing) Authorization() (*AuthConfig, error) {
return r.AuthorizationContext(context.Background())
}
func (r *refreshing) AuthorizationContext(ctx context.Context) (*AuthConfig, error) {
r.Lock() r.Lock()
defer r.Unlock() defer r.Unlock()
if r.cached == nil || r.expired() { if r.cached == nil || r.expired() {
r.last = r.now() r.last = r.now()
auth, err := r.keychain.Resolve(r.target) auth, err := Resolve(ctx, r.keychain, r.target)
if err != nil { if err != nil {
return nil, err return nil, err
} }
r.cached = auth r.cached = auth
} }
return r.cached.Authorization() return Authorization(ctx, r.cached)
} }
func (r *refreshing) now() time.Time { func (r *refreshing) now() time.Time {

View File

@ -14,6 +14,8 @@
package authn package authn
import "context"
type multiKeychain struct { type multiKeychain struct {
keychains []Keychain keychains []Keychain
} }
@ -28,8 +30,12 @@ func NewMultiKeychain(kcs ...Keychain) Keychain {
// Resolve implements Keychain. // Resolve implements Keychain.
func (mk *multiKeychain) Resolve(target Resource) (Authenticator, error) { func (mk *multiKeychain) Resolve(target Resource) (Authenticator, error) {
return mk.ResolveContext(context.Background(), target)
}
func (mk *multiKeychain) ResolveContext(ctx context.Context, target Resource) (Authenticator, error) {
for _, kc := range mk.keychains { for _, kc := range mk.keychains {
auth, err := kc.Resolve(target) auth, err := Resolve(ctx, kc, target)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -17,6 +17,7 @@ package name
import ( import (
// nolint: depguard // nolint: depguard
_ "crypto/sha256" // Recommended by go-digest. _ "crypto/sha256" // Recommended by go-digest.
"encoding/json"
"strings" "strings"
"github.com/opencontainers/go-digest" "github.com/opencontainers/go-digest"
@ -59,6 +60,25 @@ func (d Digest) String() string {
return d.original return d.original
} }
// MarshalJSON formats the digest into a string for JSON serialization.
func (d Digest) MarshalJSON() ([]byte, error) {
return json.Marshal(d.String())
}
// UnmarshalJSON parses a JSON string into a Digest.
func (d *Digest) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
n, err := NewDigest(s)
if err != nil {
return err
}
*d = n
return nil
}
// NewDigest returns a new Digest representing the given name. // NewDigest returns a new Digest representing the given name.
func NewDigest(name string, opts ...Option) (Digest, error) { func NewDigest(name string, opts ...Option) (Digest, error) {
// Split on "@" // Split on "@"

View File

@ -31,7 +31,7 @@ import (
const cloudPlatformScope = "https://www.googleapis.com/auth/cloud-platform" const cloudPlatformScope = "https://www.googleapis.com/auth/cloud-platform"
// GetGcloudCmd is exposed so we can test this. // GetGcloudCmd is exposed so we can test this.
var GetGcloudCmd = func() *exec.Cmd { var GetGcloudCmd = func(ctx context.Context) *exec.Cmd {
// This is odd, but basically what docker-credential-gcr does. // This is odd, but basically what docker-credential-gcr does.
// //
// config-helper is undocumented, but it's purportedly the only supported way // config-helper is undocumented, but it's purportedly the only supported way
@ -39,15 +39,15 @@ var GetGcloudCmd = func() *exec.Cmd {
// //
// --force-auth-refresh means we are getting a token that is valid for about // --force-auth-refresh means we are getting a token that is valid for about
// an hour (we reuse it until it's expired). // an hour (we reuse it until it's expired).
return exec.Command("gcloud", "config", "config-helper", "--force-auth-refresh", "--format=json(credential)") return exec.CommandContext(ctx, "gcloud", "config", "config-helper", "--force-auth-refresh", "--format=json(credential)")
} }
// NewEnvAuthenticator returns an authn.Authenticator that generates access // NewEnvAuthenticator returns an authn.Authenticator that generates access
// tokens from the environment we're running in. // tokens from the environment we're running in.
// //
// See: https://godoc.org/golang.org/x/oauth2/google#FindDefaultCredentials // See: https://godoc.org/golang.org/x/oauth2/google#FindDefaultCredentials
func NewEnvAuthenticator() (authn.Authenticator, error) { func NewEnvAuthenticator(ctx context.Context) (authn.Authenticator, error) {
ts, err := googauth.DefaultTokenSource(context.Background(), cloudPlatformScope) ts, err := googauth.DefaultTokenSource(ctx, cloudPlatformScope)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -62,14 +62,14 @@ func NewEnvAuthenticator() (authn.Authenticator, error) {
// NewGcloudAuthenticator returns an oauth2.TokenSource that generates access // NewGcloudAuthenticator returns an oauth2.TokenSource that generates access
// tokens by shelling out to the gcloud sdk. // tokens by shelling out to the gcloud sdk.
func NewGcloudAuthenticator() (authn.Authenticator, error) { func NewGcloudAuthenticator(ctx context.Context) (authn.Authenticator, error) {
if _, err := exec.LookPath("gcloud"); err != nil { if _, err := exec.LookPath("gcloud"); err != nil {
// gcloud is not available, fall back to anonymous // gcloud is not available, fall back to anonymous
logs.Warn.Println("gcloud binary not found") logs.Warn.Println("gcloud binary not found")
return authn.Anonymous, nil return authn.Anonymous, nil
} }
ts := gcloudSource{GetGcloudCmd} ts := gcloudSource{ctx, GetGcloudCmd}
// Attempt to fetch a token to ensure gcloud is installed and we can run it. // Attempt to fetch a token to ensure gcloud is installed and we can run it.
token, err := ts.Token() token, err := ts.Token()
@ -143,13 +143,15 @@ type gcloudOutput struct {
} }
type gcloudSource struct { type gcloudSource struct {
ctx context.Context
// This is passed in so that we mock out gcloud and test Token. // This is passed in so that we mock out gcloud and test Token.
exec func() *exec.Cmd exec func(ctx context.Context) *exec.Cmd
} }
// Token implements oauath2.TokenSource. // Token implements oauath2.TokenSource.
func (gs gcloudSource) Token() (*oauth2.Token, error) { func (gs gcloudSource) Token() (*oauth2.Token, error) {
cmd := gs.exec() cmd := gs.exec(gs.ctx)
var out bytes.Buffer var out bytes.Buffer
cmd.Stdout = &out cmd.Stdout = &out

View File

@ -15,6 +15,7 @@
package google package google
import ( import (
"context"
"strings" "strings"
"sync" "sync"
@ -52,26 +53,31 @@ type googleKeychain struct {
// In general, we don't worry about that here because we expect to use the same // In general, we don't worry about that here because we expect to use the same
// gcloud configuration in the scope of this one process. // gcloud configuration in the scope of this one process.
func (gk *googleKeychain) Resolve(target authn.Resource) (authn.Authenticator, error) { func (gk *googleKeychain) Resolve(target authn.Resource) (authn.Authenticator, error) {
return gk.ResolveContext(context.Background(), target)
}
// ResolveContext implements authn.ContextKeychain.
func (gk *googleKeychain) ResolveContext(ctx context.Context, target authn.Resource) (authn.Authenticator, error) {
// Only authenticate GCR and AR so it works with authn.NewMultiKeychain to fallback. // Only authenticate GCR and AR so it works with authn.NewMultiKeychain to fallback.
if !isGoogle(target.RegistryStr()) { if !isGoogle(target.RegistryStr()) {
return authn.Anonymous, nil return authn.Anonymous, nil
} }
gk.once.Do(func() { gk.once.Do(func() {
gk.auth = resolve() gk.auth = resolve(ctx)
}) })
return gk.auth, nil return gk.auth, nil
} }
func resolve() authn.Authenticator { func resolve(ctx context.Context) authn.Authenticator {
auth, envErr := NewEnvAuthenticator() auth, envErr := NewEnvAuthenticator(ctx)
if envErr == nil && auth != authn.Anonymous { if envErr == nil && auth != authn.Anonymous {
logs.Debug.Println("google.Keychain: using Application Default Credentials") logs.Debug.Println("google.Keychain: using Application Default Credentials")
return auth return auth
} }
auth, gErr := NewGcloudAuthenticator() auth, gErr := NewGcloudAuthenticator(ctx)
if gErr == nil && auth != authn.Anonymous { if gErr == nil && auth != authn.Anonymous {
logs.Debug.Println("google.Keychain: using gcloud fallback") logs.Debug.Println("google.Keychain: using gcloud fallback")
return auth return auth

View File

@ -22,6 +22,8 @@ import (
"io" "io"
"os" "os"
"path/filepath" "path/filepath"
"runtime"
"sync"
"github.com/google/go-containerregistry/pkg/logs" "github.com/google/go-containerregistry/pkg/logs"
v1 "github.com/google/go-containerregistry/pkg/v1" v1 "github.com/google/go-containerregistry/pkg/v1"
@ -37,6 +39,9 @@ var layoutFile = `{
"imageLayoutVersion": "1.0.0" "imageLayoutVersion": "1.0.0"
}` }`
// renameMutex guards os.Rename calls in AppendImage on Windows only.
var renameMutex sync.Mutex
// AppendImage writes a v1.Image to the Path and updates // AppendImage writes a v1.Image to the Path and updates
// the index.json to reference it. // the index.json to reference it.
func (l Path) AppendImage(img v1.Image, options ...Option) error { func (l Path) AppendImage(img v1.Image, options ...Option) error {
@ -259,6 +264,11 @@ func (l Path) writeBlob(hash v1.Hash, size int64, rc io.ReadCloser, renamer func
} }
renamePath := l.path("blobs", finalHash.Algorithm, finalHash.Hex) renamePath := l.path("blobs", finalHash.Algorithm, finalHash.Hex)
if runtime.GOOS == "windows" {
renameMutex.Lock()
defer renameMutex.Unlock()
}
return os.Rename(w.Name(), renamePath) return os.Rename(w.Name(), renamePath)
} }

View File

@ -47,7 +47,7 @@ type fetcher struct {
func makeFetcher(ctx context.Context, target resource, o *options) (*fetcher, error) { func makeFetcher(ctx context.Context, target resource, o *options) (*fetcher, error) {
auth := o.auth auth := o.auth
if o.keychain != nil { if o.keychain != nil {
kauth, err := o.keychain.Resolve(target) kauth, err := authn.Resolve(ctx, o.keychain, target)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -33,7 +33,7 @@ var _ http.RoundTripper = (*basicTransport)(nil)
// RoundTrip implements http.RoundTripper // RoundTrip implements http.RoundTripper
func (bt *basicTransport) RoundTrip(in *http.Request) (*http.Response, error) { func (bt *basicTransport) RoundTrip(in *http.Request) (*http.Response, error) {
if bt.auth != authn.Anonymous { if bt.auth != authn.Anonymous {
auth, err := bt.auth.Authorization() auth, err := authn.Authorization(in.Context(), bt.auth)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -49,7 +49,7 @@ func Exchange(ctx context.Context, reg name.Registry, auth authn.Authenticator,
if err != nil { if err != nil {
return nil, err return nil, err
} }
authcfg, err := auth.Authorization() authcfg, err := authn.Authorization(ctx, auth)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -190,7 +190,7 @@ func (bt *bearerTransport) RoundTrip(in *http.Request) (*http.Response, error) {
// The basic token exchange is attempted first, falling back to the oauth flow. // The basic token exchange is attempted first, falling back to the oauth flow.
// If the IdentityToken is set, this indicates that we should start with the oauth flow. // If the IdentityToken is set, this indicates that we should start with the oauth flow.
func (bt *bearerTransport) refresh(ctx context.Context) error { func (bt *bearerTransport) refresh(ctx context.Context) error {
auth, err := bt.basic.Authorization() auth, err := authn.Authorization(ctx, bt.basic)
if err != nil { if err != nil {
return err return err
} }
@ -295,7 +295,7 @@ func canonicalAddress(host, scheme string) (address string) {
// https://docs.docker.com/registry/spec/auth/oauth/ // https://docs.docker.com/registry/spec/auth/oauth/
func (bt *bearerTransport) refreshOauth(ctx context.Context) ([]byte, error) { func (bt *bearerTransport) refreshOauth(ctx context.Context) ([]byte, error) {
auth, err := bt.basic.Authorization() auth, err := authn.Authorization(ctx, bt.basic)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -76,7 +76,7 @@ type writer struct {
func makeWriter(ctx context.Context, repo name.Repository, ls []v1.Layer, o *options) (*writer, error) { func makeWriter(ctx context.Context, repo name.Repository, ls []v1.Layer, o *options) (*writer, error) {
auth := o.auth auth := o.auth
if o.keychain != nil { if o.keychain != nil {
kauth, err := o.keychain.Resolve(repo) kauth, err := authn.Resolve(ctx, o.keychain, repo)
if err != nil { if err != nil {
return nil, err return nil, err
} }

2
vendor/modules.txt vendored
View File

@ -740,7 +740,7 @@ github.com/google/go-cmp/cmp/internal/diff
github.com/google/go-cmp/cmp/internal/flags github.com/google/go-cmp/cmp/internal/flags
github.com/google/go-cmp/cmp/internal/function github.com/google/go-cmp/cmp/internal/function
github.com/google/go-cmp/cmp/internal/value github.com/google/go-cmp/cmp/internal/value
# github.com/google/go-containerregistry v0.19.1 # github.com/google/go-containerregistry v0.19.2
## explicit; go 1.18 ## explicit; go 1.18
github.com/google/go-containerregistry/internal/and github.com/google/go-containerregistry/internal/and
github.com/google/go-containerregistry/internal/compression github.com/google/go-containerregistry/internal/compression