From 096ee6bbf8e8c8d1a4b2d74ddbb2471496d88ffe Mon Sep 17 00:00:00 2001 From: Lewis Denny Date: Wed, 22 Oct 2025 06:02:35 +1000 Subject: [PATCH] feat(libartifact): Default to latest tag when pulling artifacts When an artifact is referenced by name but without a specific tag, the code should default to using the 'latest' tag. This aligns with the behavior of image pulls in Podman and other container tools. This change modifies the artifact store to correctly resolve untagged names to the 'latest' tag, ensuring a more intuitive and consistent user experience. Related: https://github.com/containers/podman/issues/27083 Signed-off-by: Lewis Denny --- common/pkg/libartifact/artifact.go | 16 +++++++++- common/pkg/libartifact/store/store.go | 43 +++++++++++++++++++-------- 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/common/pkg/libartifact/artifact.go b/common/pkg/libartifact/artifact.go index 2d5934c09d..412d9d24a9 100644 --- a/common/pkg/libartifact/artifact.go +++ b/common/pkg/libartifact/artifact.go @@ -7,6 +7,7 @@ import ( "github.com/opencontainers/go-digest" "go.podman.io/common/pkg/libartifact/types" + "go.podman.io/image/v5/docker/reference" "go.podman.io/image/v5/manifest" ) @@ -58,9 +59,22 @@ type ArtifactList []*Artifact // GetByNameOrDigest returns an artifact, if present, by a given name // Returns an error if not found. func (al ArtifactList) GetByNameOrDigest(nameOrDigest string) (*Artifact, bool, error) { + named, err := reference.Parse(nameOrDigest) + if err != nil { + return nil, false, fmt.Errorf("parsing reference %q: %w", nameOrDigest, err) + } + + // Assert that named implements reference.Named interface + namedRef, ok := named.(reference.Named) + if !ok { + return nil, false, fmt.Errorf("reference %q is not a Named reference", nameOrDigest) + } + + refStr := reference.TagNameOnly(namedRef).String() + // This is the hot route through for _, artifact := range al { - if artifact.Name == nameOrDigest { + if artifact.Name == refStr { return artifact, false, nil } } diff --git a/common/pkg/libartifact/store/store.go b/common/pkg/libartifact/store/store.go index 0665cbe73c..f54b6e6cde 100644 --- a/common/pkg/libartifact/store/store.go +++ b/common/pkg/libartifact/store/store.go @@ -25,6 +25,7 @@ import ( "go.podman.io/common/libimage" "go.podman.io/common/pkg/libartifact" libartTypes "go.podman.io/common/pkg/libartifact/types" + "go.podman.io/image/v5/docker/reference" "go.podman.io/image/v5/image" "go.podman.io/image/v5/manifest" "go.podman.io/image/v5/oci/layout" @@ -98,14 +99,14 @@ func (as ArtifactStore) Remove(ctx context.Context, name string) (*digest.Digest return nil, err } - arty, nameIsDigest, err := artifacts.GetByNameOrDigest(name) + arty, _, err := artifacts.GetByNameOrDigest(name) if err != nil { return nil, err } - if nameIsDigest { - name = arty.Name - } - ir, err := layout.NewReference(as.storePath, name) + + // Use the canonical name from the found artifact, which will include the tag + // if one was resolved by GetByNameOrDigest. + ir, err := layout.NewReference(as.storePath, arty.Name) if err != nil { return nil, err } @@ -186,7 +187,17 @@ func (as ArtifactStore) Push(ctx context.Context, src, dest string, opts libimag as.lock.Lock() defer as.lock.Unlock() - srcRef, err := layout.NewReference(as.storePath, src) + artifacts, err := as.getArtifacts(ctx, nil) + if err != nil { + return "", err + } + + arty, _, err := artifacts.GetByNameOrDigest(src) + if err != nil { + return "", err + } + + srcRef, err := layout.NewReference(as.storePath, arty.Name) if err != nil { return "", err } @@ -214,6 +225,18 @@ func (as ArtifactStore) Add(ctx context.Context, dest string, artifactBlobs []li return nil, ErrEmptyArtifactName } + named, err := reference.Parse(dest) + if err != nil { + return nil, fmt.Errorf("parsing reference %q: %w", dest, err) + } + + namedRef, ok := named.(reference.Named) + if !ok { + return nil, fmt.Errorf("reference %q is not a Named reference", dest) + } + + dest = reference.TagNameOnly(namedRef).String() + if options.Append && len(options.ArtifactMIMEType) > 0 { return nil, errors.New("append option is not compatible with type option") } @@ -416,20 +439,16 @@ func getArtifactAndImageSource(ctx context.Context, as ArtifactStore, nameOrDige return nil, nil, err } - arty, nameIsDigest, err := artifacts.GetByNameOrDigest(nameOrDigest) + arty, _, err := artifacts.GetByNameOrDigest(nameOrDigest) if err != nil { return nil, nil, err } - name := nameOrDigest - if nameIsDigest { - name = arty.Name - } if len(arty.Manifest.Layers) == 0 { return nil, nil, errors.New("the artifact has no blobs, nothing to extract") } - ir, err := layout.NewReference(as.storePath, name) + ir, err := layout.NewReference(as.storePath, arty.Name) if err != nil { return nil, nil, err }