diff --git a/cmd/podman/artifact/list.go b/cmd/podman/artifact/list.go index 31a4d40001a..4bd7b88f774 100644 --- a/cmd/podman/artifact/list.go +++ b/cmd/podman/artifact/list.go @@ -1,6 +1,7 @@ package artifact import ( + "encoding/json" "fmt" "os" "time" @@ -41,6 +42,7 @@ type artifactListOutput struct { Digest string Repository string Size string + sizeBytes int64 Tag string created time.Time VirtualSize string @@ -84,13 +86,8 @@ func list(cmd *cobra.Command, _ []string) error { return err } - return outputTemplate(cmd, reports) -} - -func outputTemplate(cmd *cobra.Command, lrs []*entities.ArtifactListReport) error { - var err error artifacts := make([]artifactListOutput, 0) - for _, lr := range lrs { + for _, lr := range reports { var tag string artifactName, err := lr.Artifact.GetName() if err != nil { @@ -130,6 +127,7 @@ func outputTemplate(cmd *cobra.Command, lrs []*entities.ArtifactListReport) erro Digest: artifactHash, Repository: named.Name(), Size: units.HumanSize(float64(lr.Artifact.TotalSizeBytes())), + sizeBytes: lr.Artifact.TotalSizeBytes(), Tag: tag, created: createdTime, VirtualSize: fmt.Sprintf("%d", lr.Artifact.TotalSizeBytes()), @@ -137,6 +135,49 @@ func outputTemplate(cmd *cobra.Command, lrs []*entities.ArtifactListReport) erro }) } + switch { + case report.IsJSON(listFlag.format): + return writeJSON(artifacts) + default: + return writeTemplate(cmd, artifacts) + } + +} + +func writeJSON(artifacts []artifactListOutput) error { + type artifact struct { + Repository string `json:"Repository,omitempty"` + CreatedAt string + Size int64 + Tag string `json:"Tag,omitempty"` + Digest string + } + + arti := make([]artifact, 0, len(artifacts)) + + for _, e := range artifacts { + var a artifact + a.Repository = e.Repository + a.CreatedAt = e.created.Format(time.RFC3339Nano) + a.Size = e.sizeBytes + a.Repository = e.Repository + a.Tag = e.Tag + a.Digest = e.Digest + + arti = append(arti, a) + } + + prettyJSON, err := json.MarshalIndent(arti, "", " ") + if err != nil { + return err + } + fmt.Println(string(prettyJSON)) + return nil +} + +func writeTemplate(cmd *cobra.Command, artifacts []artifactListOutput) error { + var err error + headers := report.Headers(artifactListOutput{}, map[string]string{ "REPOSITORY": "REPOSITORY", "Tag": "TAG", diff --git a/docs/source/markdown/podman-artifact-ls.1.md.in b/docs/source/markdown/podman-artifact-ls.1.md.in index 930999defc7..8e46fd2f57f 100644 --- a/docs/source/markdown/podman-artifact-ls.1.md.in +++ b/docs/source/markdown/podman-artifact-ls.1.md.in @@ -62,6 +62,19 @@ ab609fad386d 2.097GB cd734b558ceb 12.58MB ``` +List artifact details in json format +``` +$ podman artifact ls --format json" +[ + { + "Repository": "quay.io/artifact/foobar1", + "CreatedAt": "2026-06-16T00:00:57Z", + "Size": 12580000, + "Tag": "special", + "Digest": "cd734b558ceb" + } +] +``` ## SEE ALSO diff --git a/test/e2e/artifact_test.go b/test/e2e/artifact_test.go index 4eb4f96abe5..75c93ad989a 100644 --- a/test/e2e/artifact_test.go +++ b/test/e2e/artifact_test.go @@ -3,6 +3,7 @@ package integration import ( + "encoding/json" "fmt" "os" "path/filepath" @@ -51,6 +52,23 @@ var _ = Describe("Podman artifact", func() { Expect(output).To(ContainElement(artifact1Name)) Expect(output).To(ContainElement(artifact2Name)) + // --format=json should work, produce valid json + listFormatJsonSession := podmanTest.PodmanExitCleanly("artifact", "ls", "--format", "{{.Repository}}") + Expect(listFormatJsonSession).Should(ExitCleanly()) + jsonOutput := listFormatJsonSession.OutputToString() + Expect(jsonOutput).To(BeValidJSON()) + + // --format=json should produce + jsonArtifactsList := []map[string]any{} + jsonUnmarshalErr := json.Unmarshal([]byte(jsonOutput), &jsonArtifactsList) + + Expect(jsonUnmarshalErr).ToNot(HaveOccurred()) + Expect(jsonArtifactsList).ToNot(BeEmpty()) + + for _, image := range jsonArtifactsList { + Expect(image).To(HaveKey("Repository")) + Expect(image).To(HaveKey("Tag")) + } // Check default digest length (should be 12) defaultFormatSession := podmanTest.PodmanExitCleanly("artifact", "ls", "--format", "{{.Digest}}") defaultOutput := defaultFormatSession.OutputToStringArray()[0]