artifact: added CREATED column to artifact ls

Fixes: #27314

Signed-off-by: Volodymyr Pankin <volopank@gmail.com>
This commit is contained in:
Volodymyr Pankin
2025-10-19 18:53:07 +02:00
parent f2a559a8fe
commit 538229da90
3 changed files with 35 additions and 9 deletions

View File

@@ -3,12 +3,14 @@ package artifact
import ( import (
"fmt" "fmt"
"os" "os"
"time"
"github.com/containers/podman/v5/cmd/podman/common" "github.com/containers/podman/v5/cmd/podman/common"
"github.com/containers/podman/v5/cmd/podman/registry" "github.com/containers/podman/v5/cmd/podman/registry"
"github.com/containers/podman/v5/cmd/podman/validate" "github.com/containers/podman/v5/cmd/podman/validate"
"github.com/containers/podman/v5/pkg/domain/entities" "github.com/containers/podman/v5/pkg/domain/entities"
"github.com/docker/go-units" "github.com/docker/go-units"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"go.podman.io/common/pkg/completion" "go.podman.io/common/pkg/completion"
"go.podman.io/common/pkg/report" "go.podman.io/common/pkg/report"
@@ -40,11 +42,12 @@ type artifactListOutput struct {
Repository string Repository string
Size string Size string
Tag string Tag string
Created string
VirtualSize string VirtualSize string
} }
var ( var (
defaultArtifactListOutputFormat = "{{range .}}{{.Repository}}\t{{.Tag}}\t{{.Digest}}\t{{.Size}}\n{{end -}}" defaultArtifactListOutputFormat = "{{range .}}{{.Repository}}\t{{.Tag}}\t{{.Digest}}\t{{.Created}}\t{{.Size}}\n{{end -}}"
) )
func init() { func init() {
@@ -106,11 +109,22 @@ func outputTemplate(cmd *cobra.Command, lrs []*entities.ArtifactListReport) erro
artifactHash = artifactDigest.Encoded() artifactHash = artifactDigest.Encoded()
} }
var created string
createdAnnotation, ok := lr.Manifest.Annotations[imgspecv1.AnnotationCreated]
if ok {
createdTime, err := time.Parse(time.RFC3339Nano, createdAnnotation)
if err != nil {
return err
}
created = units.HumanDuration(time.Since(createdTime)) + " ago"
}
artifacts = append(artifacts, artifactListOutput{ artifacts = append(artifacts, artifactListOutput{
Digest: artifactHash, Digest: artifactHash,
Repository: named.Name(), Repository: named.Name(),
Size: units.HumanSize(float64(lr.Artifact.TotalSizeBytes())), Size: units.HumanSize(float64(lr.Artifact.TotalSizeBytes())),
Tag: tag, Tag: tag,
Created: created,
VirtualSize: fmt.Sprintf("%d", lr.Artifact.TotalSizeBytes()), VirtualSize: fmt.Sprintf("%d", lr.Artifact.TotalSizeBytes()),
}) })
} }
@@ -118,6 +132,7 @@ func outputTemplate(cmd *cobra.Command, lrs []*entities.ArtifactListReport) erro
headers := report.Headers(artifactListOutput{}, map[string]string{ headers := report.Headers(artifactListOutput{}, map[string]string{
"REPOSITORY": "REPOSITORY", "REPOSITORY": "REPOSITORY",
"Tag": "TAG", "Tag": "TAG",
"Created": "CREATED",
"Size": "SIZE", "Size": "SIZE",
"Digest": "DIGEST", "Digest": "DIGEST",
}) })

View File

@@ -18,6 +18,7 @@ Print results with a Go template.
| **Placeholder** | **Description** | | **Placeholder** | **Description** |
|-----------------|------------------------------------------------| |-----------------|------------------------------------------------|
| .Created | Elapsed time since the artifact was created |
| .Digest | The computed digest of the artifact's manifest | | .Digest | The computed digest of the artifact's manifest |
| .Repository | Repository name of the artifact | | .Repository | Repository name of the artifact |
| .Size | Size artifact in human readable units | | .Size | Size artifact in human readable units |
@@ -33,24 +34,24 @@ Print results with a Go template.
List artifacts in the local store List artifacts in the local store
``` ```
$ podman artifact ls $ podman artifact ls
REPOSITORY TAG DIGEST SIZE REPOSITORY TAG DIGEST CREATED SIZE
quay.io/artifact/foobar1 latest ab609fad386d 2.097GB quay.io/artifact/foobar1 latest ab609fad386d 6 hours ago 2.097GB
quay.io/artifact/foobar2 special cd734b558ceb 12.58MB quay.io/artifact/foobar2 special cd734b558ceb 53 seconds ago 12.58MB
``` ```
List artifacts in the local store without truncating the digest List artifacts in the local store without truncating the digest
``` ```
$ podman artifact ls --no-trunc $ podman artifact ls --no-trunc
REPOSITORY TAG DIGEST SIZE REPOSITORY TAG DIGEST CREATED SIZE
quay.io/artifact/foobar1 latest ab609fad386df1433f461b0643d9cf575560baf633809dcc9c190da6cc3a3c29 2.097GB quay.io/artifact/foobar1 latest ab609fad386df1433f461b0643d9cf575560baf633809dcc9c190da6cc3a3c29 6 hours ago 2.097GB
quay.io/artifact/foobar2 special cd734b558ceb8ccc0281ca76530e1dea1eb479407d3163f75fb601bffb6f73d0 12.58MB quay.io/artifact/foobar2 special cd734b558ceb8ccc0281ca76530e1dea1eb479407d3163f75fb601bffb6f73d0 53 seconds ago 12.58MB
``` ```
List artifacts in the local store without the title header List artifacts in the local store without the title header
``` ```
$ podman artifact ls --noheading $ podman artifact ls --noheading
quay.io/artifact/foobar1 latest ab609fad386d 2.097GB quay.io/artifact/foobar1 latest ab609fad386d 6 hours ago 2.097GB
quay.io/artifact/foobar2 special cd734b558ceb 12.58MB quay.io/artifact/foobar2 special cd734b558ceb 53 seconds ago 12.58MB
``` ```
List artifact digests and size using a --format List artifact digests and size using a --format

View File

@@ -78,6 +78,16 @@ var _ = Describe("Podman artifact", func() {
// Verify if the virtual size values are present in the output // Verify if the virtual size values are present in the output
Expect(virtualSizes).To(ContainElement("4192")) Expect(virtualSizes).To(ContainElement("4192"))
Expect(virtualSizes).To(ContainElement("10240")) Expect(virtualSizes).To(ContainElement("10240"))
// Check if .Created is reported correctly
createdFormatSession := podmanTest.PodmanExitCleanly("artifact", "ls", "--format", "{{.Created}}")
created := createdFormatSession.OutputToStringArray()
Expect(created).To(HaveLen(2))
// Assuming the test runs less than a minute
humanReadableDurationRegexp := `^(Less than a second|1 second|\d+ seconds) ago$`
Expect(created).To(ContainElements(MatchRegexp(humanReadableDurationRegexp), MatchRegexp(humanReadableDurationRegexp)))
}) })
It("podman artifact simple add", func() { It("podman artifact simple add", func() {