mirror of
https://github.com/hauler-dev/hauler.git
synced 2026-02-14 09:59:50 +00:00
added digests view to info (#465)
* added digests view to info * updated digests to be more accurate
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/olekukonko/tablewriter"
|
"github.com/olekukonko/tablewriter"
|
||||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
@@ -47,12 +48,20 @@ func InfoCmd(ctx context.Context, o *flags.InfoOpts, s *store.Layout) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
i := newItem(s, desc, internalManifest, fmt.Sprintf("%s/%s", internalDesc.Platform.OS, internalDesc.Platform.Architecture), o)
|
i := newItemWithDigest(
|
||||||
|
s,
|
||||||
|
internalDesc.Digest.String(),
|
||||||
|
desc,
|
||||||
|
internalManifest,
|
||||||
|
fmt.Sprintf("%s/%s", internalDesc.Platform.OS, internalDesc.Platform.Architecture),
|
||||||
|
o,
|
||||||
|
)
|
||||||
var emptyItem item
|
var emptyItem item
|
||||||
if i != emptyItem {
|
if i != emptyItem {
|
||||||
items = append(items, i)
|
items = append(items, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle "non" multi-arch images
|
// handle "non" multi-arch images
|
||||||
} else if desc.MediaType == consts.DockerManifestSchema2 || desc.MediaType == consts.OCIManifestSchema1 {
|
} else if desc.MediaType == consts.DockerManifestSchema2 || desc.MediaType == consts.OCIManifestSchema1 {
|
||||||
var m ocispec.Manifest
|
var m ocispec.Manifest
|
||||||
@@ -66,14 +75,15 @@ func InfoCmd(ctx context.Context, o *flags.InfoOpts, s *store.Layout) error {
|
|||||||
}
|
}
|
||||||
defer rc.Close()
|
defer rc.Close()
|
||||||
|
|
||||||
// Unmarshal the OCI image content
|
// unmarshal the oci image content
|
||||||
var internalManifest ocispec.Image
|
var internalManifest ocispec.Image
|
||||||
if err := json.NewDecoder(rc).Decode(&internalManifest); err != nil {
|
if err := json.NewDecoder(rc).Decode(&internalManifest); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if internalManifest.Architecture != "" {
|
if internalManifest.Architecture != "" {
|
||||||
i := newItem(s, desc, m, fmt.Sprintf("%s/%s", internalManifest.OS, internalManifest.Architecture), o)
|
i := newItem(s, desc, m,
|
||||||
|
fmt.Sprintf("%s/%s", internalManifest.OS, internalManifest.Architecture), o)
|
||||||
var emptyItem item
|
var emptyItem item
|
||||||
if i != emptyItem {
|
if i != emptyItem {
|
||||||
items = append(items, i)
|
items = append(items, i)
|
||||||
@@ -85,7 +95,8 @@ func InfoCmd(ctx context.Context, o *flags.InfoOpts, s *store.Layout) error {
|
|||||||
items = append(items, i)
|
items = append(items, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// handle the rest
|
|
||||||
|
// handle everything else (charts, files, sigs, etc.)
|
||||||
} else {
|
} else {
|
||||||
var m ocispec.Manifest
|
var m ocispec.Manifest
|
||||||
if err := json.NewDecoder(rc).Decode(&m); err != nil {
|
if err := json.NewDecoder(rc).Decode(&m); err != nil {
|
||||||
@@ -118,13 +129,13 @@ func InfoCmd(ctx context.Context, o *flags.InfoOpts, s *store.Layout) error {
|
|||||||
msg = buildJson(items...)
|
msg = buildJson(items...)
|
||||||
fmt.Println(msg)
|
fmt.Println(msg)
|
||||||
default:
|
default:
|
||||||
buildTable(items...)
|
buildTable(o.ShowDigests, items...)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildListRepos(items ...item) {
|
func buildListRepos(items ...item) {
|
||||||
// Create map to track unique repository names
|
// create map to track unique repository names
|
||||||
repos := make(map[string]bool)
|
repos := make(map[string]bool)
|
||||||
|
|
||||||
for _, i := range items {
|
for _, i := range items {
|
||||||
@@ -141,39 +152,85 @@ func buildListRepos(items ...item) {
|
|||||||
repos[repoName] = true
|
repos[repoName] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect and print unique repository names
|
// collect and print unique repository names
|
||||||
for repoName := range repos {
|
for repoName := range repos {
|
||||||
fmt.Println(repoName)
|
fmt.Println(repoName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildTable(items ...item) {
|
func buildTable(showDigests bool, items ...item) {
|
||||||
// Create a table for the results
|
|
||||||
table := tablewriter.NewWriter(os.Stdout)
|
table := tablewriter.NewWriter(os.Stdout)
|
||||||
table.SetHeader([]string{"Reference", "Type", "Platform", "# Layers", "Size"})
|
|
||||||
|
if showDigests {
|
||||||
|
table.SetHeader([]string{"Reference", "Type", "Platform", "Digest", "# Layers", "Size"})
|
||||||
|
} else {
|
||||||
|
table.SetHeader([]string{"Reference", "Type", "Platform", "# Layers", "Size"})
|
||||||
|
}
|
||||||
|
|
||||||
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
|
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
|
||||||
table.SetRowLine(false)
|
table.SetRowLine(false)
|
||||||
table.SetAutoMergeCellsByColumnIndex([]int{0})
|
table.SetAutoMergeCellsByColumnIndex([]int{0})
|
||||||
|
|
||||||
totalSize := int64(0)
|
totalSize := int64(0)
|
||||||
|
|
||||||
for _, i := range items {
|
for _, i := range items {
|
||||||
if i.Type != "" {
|
if i.Type == "" {
|
||||||
row := []string{
|
continue
|
||||||
i.Reference,
|
}
|
||||||
|
|
||||||
|
ref := truncateReference(i.Reference)
|
||||||
|
var row []string
|
||||||
|
|
||||||
|
if showDigests {
|
||||||
|
digest := i.Digest
|
||||||
|
if digest == "" {
|
||||||
|
digest = "-"
|
||||||
|
}
|
||||||
|
row = []string{
|
||||||
|
ref,
|
||||||
|
i.Type,
|
||||||
|
i.Platform,
|
||||||
|
digest,
|
||||||
|
fmt.Sprintf("%d", i.Layers),
|
||||||
|
byteCountSI(i.Size),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
row = []string{
|
||||||
|
ref,
|
||||||
i.Type,
|
i.Type,
|
||||||
i.Platform,
|
i.Platform,
|
||||||
fmt.Sprintf("%d", i.Layers),
|
fmt.Sprintf("%d", i.Layers),
|
||||||
byteCountSI(i.Size),
|
byteCountSI(i.Size),
|
||||||
}
|
}
|
||||||
totalSize += i.Size
|
|
||||||
table.Append(row)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
totalSize += i.Size
|
||||||
|
table.Append(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// align total column based on digest visibility
|
||||||
|
if showDigests {
|
||||||
|
table.SetFooter([]string{"", "", "", "", "Total", byteCountSI(totalSize)})
|
||||||
|
} else {
|
||||||
|
table.SetFooter([]string{"", "", "", "Total", byteCountSI(totalSize)})
|
||||||
}
|
}
|
||||||
table.SetFooter([]string{"", "", "", "Total", byteCountSI(totalSize)})
|
|
||||||
|
|
||||||
table.Render()
|
table.Render()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// truncateReference shortens the digest of a reference
|
||||||
|
func truncateReference(ref string) string {
|
||||||
|
const prefix = "@sha256:"
|
||||||
|
idx := strings.Index(ref, prefix)
|
||||||
|
if idx == -1 {
|
||||||
|
return ref
|
||||||
|
}
|
||||||
|
if len(ref) > idx+len(prefix)+12 {
|
||||||
|
return ref[:idx+len(prefix)+12] + "…"
|
||||||
|
}
|
||||||
|
return ref
|
||||||
|
}
|
||||||
|
|
||||||
func buildJson(item ...item) string {
|
func buildJson(item ...item) string {
|
||||||
data, err := json.MarshalIndent(item, "", " ")
|
data, err := json.MarshalIndent(item, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -186,6 +243,7 @@ type item struct {
|
|||||||
Reference string
|
Reference string
|
||||||
Type string
|
Type string
|
||||||
Platform string
|
Platform string
|
||||||
|
Digest string
|
||||||
Layers int
|
Layers int
|
||||||
Size int64
|
Size int64
|
||||||
}
|
}
|
||||||
@@ -210,6 +268,13 @@ func (a byReferenceAndArch) Less(i, j int) bool {
|
|||||||
return a[i].Reference < a[j].Reference
|
return a[i].Reference < a[j].Reference
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// overrides the digest with a specific per platform digest
|
||||||
|
func newItemWithDigest(s *store.Layout, digestStr string, desc ocispec.Descriptor, m ocispec.Manifest, plat string, o *flags.InfoOpts) item {
|
||||||
|
item := newItem(s, desc, m, plat, o)
|
||||||
|
item.Digest = digestStr
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
func newItem(s *store.Layout, desc ocispec.Descriptor, m ocispec.Manifest, plat string, o *flags.InfoOpts) item {
|
func newItem(s *store.Layout, desc ocispec.Descriptor, m ocispec.Manifest, plat string, o *flags.InfoOpts) item {
|
||||||
var size int64 = 0
|
var size int64 = 0
|
||||||
for _, l := range m.Layers {
|
for _, l := range m.Layers {
|
||||||
@@ -255,6 +320,7 @@ func newItem(s *store.Layout, desc ocispec.Descriptor, m ocispec.Manifest, plat
|
|||||||
Reference: ref.Name(),
|
Reference: ref.Name(),
|
||||||
Type: ctype,
|
Type: ctype,
|
||||||
Platform: plat,
|
Platform: plat,
|
||||||
|
Digest: desc.Digest.String(),
|
||||||
Layers: len(m.Layers),
|
Layers: len(m.Layers),
|
||||||
Size: size,
|
Size: size,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ type InfoOpts struct {
|
|||||||
TypeFilter string
|
TypeFilter string
|
||||||
SizeUnit string
|
SizeUnit string
|
||||||
ListRepos bool
|
ListRepos bool
|
||||||
|
ShowDigests bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *InfoOpts) AddFlags(cmd *cobra.Command) {
|
func (o *InfoOpts) AddFlags(cmd *cobra.Command) {
|
||||||
@@ -17,4 +18,5 @@ func (o *InfoOpts) AddFlags(cmd *cobra.Command) {
|
|||||||
f.StringVarP(&o.OutputFormat, "output", "o", "table", "(Optional) Specify the output format (table | json)")
|
f.StringVarP(&o.OutputFormat, "output", "o", "table", "(Optional) Specify the output format (table | json)")
|
||||||
f.StringVarP(&o.TypeFilter, "type", "t", "all", "(Optional) Filter on content type (image | chart | file | sigs | atts | sbom)")
|
f.StringVarP(&o.TypeFilter, "type", "t", "all", "(Optional) Filter on content type (image | chart | file | sigs | atts | sbom)")
|
||||||
f.BoolVar(&o.ListRepos, "list-repos", false, "(Optional) List all repository names")
|
f.BoolVar(&o.ListRepos, "list-repos", false, "(Optional) List all repository names")
|
||||||
|
f.BoolVar(&o.ShowDigests, "digests", false, "(Optional) Show digests of each artifact in the output table")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user