From 3cbd4a7903c30c98aa97bead0603510305da6d52 Mon Sep 17 00:00:00 2001 From: Arnav Nagzirkar <113314200+arnavnagzirkar@users.noreply.github.com> Date: Mon, 8 Jun 2026 21:48:38 -0700 Subject: [PATCH] fix(show): resolve OCI tag from chart version for annotations Build the manifest reference from the concrete version of the downloaded chart instead of recombining with client.Version, which can be a range such as ">0.0.0-0" when --devel is used and is not a valid tag. Show.Run now appends the resolved chart version when the reference has no explicit tag or digest, so annotations also work without an explicit --version. Adds a regression test. Signed-off-by: Arnav Nagzirkar <113314200+arnavnagzirkar@users.noreply.github.com> --- pkg/action/show.go | 12 ++++++++- pkg/action/show_test.go | 57 +++++++++++++++++++++++++++++++++++++++++ pkg/cmd/show.go | 11 ++++---- 3 files changed, 73 insertions(+), 7 deletions(-) diff --git a/pkg/action/show.go b/pkg/action/show.go index c1a6d1992..a24113bda 100644 --- a/pkg/action/show.go +++ b/pkg/action/show.go @@ -19,6 +19,7 @@ package action import ( "bytes" "fmt" + "path" "strings" "k8s.io/cli-runtime/pkg/printers" @@ -100,7 +101,16 @@ func (s *Show) Run(chartpath string) (string, error) { if s.OutputFormat == ShowChart || s.OutputFormat == ShowAll { fmt.Fprintf(&out, "%s\n", cf) if s.OCIRef != "" && s.registryClient != nil { - annotations, err := s.registryClient.GetManifestAnnotations(s.OCIRef) + ref := s.OCIRef + // When the reference has no explicit tag or digest, fall back to the + // resolved chart version. Helm stores an OCI chart under a tag that + // matches its version, and s.chart.Metadata.Version is the concrete + // version actually downloaded (the requested version may be a range). + if s.chart.Metadata != nil && s.chart.Metadata.Version != "" && + !strings.Contains(path.Base(ref), ":") && !strings.Contains(ref, "@") { + ref = fmt.Sprintf("%s:%s", ref, s.chart.Metadata.Version) + } + annotations, err := s.registryClient.GetManifestAnnotations(ref) if err == nil && len(annotations) > 0 { annotationsYAML, marshalErr := yaml.Marshal(annotations) if marshalErr == nil { diff --git a/pkg/action/show_test.go b/pkg/action/show_test.go index 9c9ef67a4..520cb0d96 100644 --- a/pkg/action/show_test.go +++ b/pkg/action/show_test.go @@ -254,3 +254,60 @@ func TestShowOCIAnnotationsNoRegistry(t *testing.T) { // No annotations section, no "---" separator assert.NotContains(t, output, "---") } + +func TestShowOCIAnnotationsResolvesVersion(t *testing.T) { + const manifestJSON = `{ + "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "config": { + "mediaType": "application/vnd.cncf.helm.config.v1+json", + "digest": "sha256:abc123", + "size": 100 + }, + "layers": [], + "annotations": { + "org.opencontainers.image.created": "2025-04-11T20:12:25Z" + } + }` + + var requestedManifest string + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/v2/" { + w.WriteHeader(http.StatusOK) + return + } + if strings.Contains(r.URL.Path, "/manifests/") { + requestedManifest = r.URL.Path + w.Header().Set("Content-Type", "application/vnd.oci.image.manifest.v1+json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(manifestJSON)) + return + } + http.NotFound(w, r) + })) + defer srv.Close() + + host := strings.TrimPrefix(srv.URL, "http://") + + registryClient, err := registry.NewClient( + registry.ClientOptWriter(io.Discard), + registry.ClientOptPlainHTTP(), + ) + require.NoError(t, err) + + config := actionConfigFixture(t) + client := NewShow(ShowChart, config) + client.SetRegistryClient(registryClient) + // OCIRef has no explicit tag; the concrete tag is taken from the chart version. + client.OCIRef = host + "/test/chart" + client.chart = &chart.Chart{ + Metadata: &chart.Metadata{Name: "test-chart", Version: "1.0.0"}, + } + + output, err := client.Run("") + require.NoError(t, err) + + assert.Contains(t, output, "org.opencontainers.image.created") + assert.Contains(t, output, "2025-04-11T20:12:25Z") + assert.Contains(t, requestedManifest, "/manifests/1.0.0") +} diff --git a/pkg/cmd/show.go b/pkg/cmd/show.go index 73aba9f04..fb36ec3a5 100644 --- a/pkg/cmd/show.go +++ b/pkg/cmd/show.go @@ -21,7 +21,6 @@ import ( "io" "log" "log/slog" - "path" "strings" "github.com/spf13/cobra" @@ -222,11 +221,11 @@ func runShow(args []string, client *action.Show) (string, error) { } if registry.IsOCI(args[0]) { - ref := strings.TrimPrefix(args[0], registry.OCIScheme+"://") - if client.Version != "" && !strings.Contains(path.Base(ref), ":") { - ref = fmt.Sprintf("%s:%s", ref, client.Version) - } - client.OCIRef = ref + // Store the bare OCI reference (without the oci:// scheme). The concrete + // tag is resolved from the downloaded chart version in Show.Run, because + // client.Version may be a range (for example ">0.0.0-0" with --devel) + // rather than an exact tag. + client.OCIRef = strings.TrimPrefix(args[0], registry.OCIScheme+"://") } cp, err := client.LocateChart(args[0], settings)