Repository: regclient/regclient (go)

Issue:
[Issue] `v0.5.2`  breaks `regctl image inspect --format {{}}`
```console
❯ docker run --rm regclient/regctl:v0.5.1 image inspect regclient/regctl:latest --format '{{ jsonPretty .Config.Labels }}'
{
  "maintainer": "",
  "org.opencontainers.image.authors": "Regclient contributors",
  "org.opencontainers.image.created": "2023-09-27T19:49:08Z",
  "org.opencontainers.image.description": "",
  "org.opencontainers.image.documentation": "https://github.com/regclient/regclient",
  "org.opencontainers.image.licenses": "Apache 2.0",
  "org.opencontainers.image.revision": "56e0d20d046938b4bd9f24a780c4c6cdc2a4cfdf",
  "org.opencontainers.image.source": "https://github.com/regclient/regclient.git",
  "org.opencontainers.image.title": "regctl",
  "org.opencontainers.image.url": "https://github.com/regclient/regclient",
  "org.opencontainers.image.vendor": "",
  "org.opencontainers.image.version": "v0.5.2"
}

❯ docker run --rm regclient/regctl:v0.5.2 image inspect regclient/regctl:latest --format '{{ jsonPretty .Config.Labels }}'
template: out:1:21: executing "out" at <.Config.Labels>: can't evaluate field Config in type *blob.BOCIConfig
```

Pull request:
Fix regression in formatted output of image config
<!--

Commits must be signed indicating your agreement to the [DCO](https://developercertificate.org/).
See [DCO missing](https://github.com/src-d/guide/blob/master/developer-community/fix-DCO.md) for steps to fix a missing signoff.

-->

### Fixes issue

Fixes #551 
<!-- If this is a bug fix, include "fixes #xxxx", or "closes #xxxx" -->

### Describe the change

Adds the image config as an anonymous field in the returned struct to allow better formatting options.
<!-- Include the type of change: bug fix, new feature, breaking change, documentation update -->
<!-- Describe what was changed, why the change was made, and how it was implemented -->

### How to verify it

```shell
regctl image inspect regclient/regctl:latest --format '{{ jsonPretty .Config.Labels }}'
```

<!-- Include steps that can be taken to verify the change -->

### Changelog text

- Fix formatting variables in `regctl image inspect`.
<!-- If the release changelog should have an entry for this, include it here -->

### Please verify and check that the pull request fulfills the following requirements

<!-- Mark the following with an [X] to verify they are included -->

- [X] Tests have been added or not applicable
- [X] Documentation has been added, updated, or not applicable
- [X] Changes have been rebased to main
- [X] Multiple commits to the same code have been squashed

<!-- markdownlint-disable-file MD041 -->


Patch:
diff --git a/cmd/regctl/image.go b/cmd/regctl/image.go
index 0a498e3..8469749 100644
--- a/cmd/regctl/image.go
+++ b/cmd/regctl/image.go
@@ -20,7 +20,9 @@ import (
 	"github.com/regclient/regclient/mod"
 	"github.com/regclient/regclient/pkg/template"
 	"github.com/regclient/regclient/types"
+	"github.com/regclient/regclient/types/blob"
 	"github.com/regclient/regclient/types/manifest"
+	v1 "github.com/regclient/regclient/types/oci/v1"
 	"github.com/regclient/regclient/types/platform"
 	"github.com/regclient/regclient/types/ref"
 	"github.com/sirupsen/logrus"
@@ -1220,6 +1222,13 @@ func (imageOpts *imageCmd) runImageInspect(cmd *cobra.Command, args []string) er
 	if err != nil {
 		return err
 	}
+	result := struct {
+		*blob.BOCIConfig
+		v1.Image
+	}{
+		BOCIConfig: blobConfig,
+		Image:      blobConfig.GetConfig(),
+	}
 	switch imageOpts.format {
 	case "raw":
 		imageOpts.format = "{{ range $key,$vals := .RawHeaders}}{{range $val := $vals}}{{printf \"%s: %s\\n\" $key $val }}{{end}}{{end}}{{printf \"\\n%s\" .RawBody}}"
@@ -1228,7 +1237,7 @@ func (imageOpts *imageCmd) runImageInspect(cmd *cobra.Command, args []string) er
 	case "rawHeaders", "raw-headers", "headers":
 		imageOpts.format = "{{ range $key,$vals := .RawHeaders}}{{range $val := $vals}}{{printf \"%s: %s\\n\" $key $val }}{{end}}{{end}}"
 	}
-	return template.Writer(cmd.OutOrStdout(), imageOpts.format, blobConfig)
+	return template.Writer(cmd.OutOrStdout(), imageOpts.format, result)
 }
 
 func (imageOpts *imageCmd) runImageMod(cmd *cobra.Command, args []string) error {


Test patch:
diff --git a/cmd/regctl/image_test.go b/cmd/regctl/image_test.go
index 55ce2b0..0fcb64e 100644
--- a/cmd/regctl/image_test.go
+++ b/cmd/regctl/image_test.go
@@ -2,6 +2,7 @@ package main
 
 import (
 	"fmt"
+	"strings"
 	"testing"
 )
 
@@ -40,6 +41,53 @@ func TestImageExportImport(t *testing.T) {
 	}
 }
 
+func TestImageInspect(t *testing.T) {
+	srcRef := "ocidir://../../testdata/testrepo:v3"
+	tt := []struct {
+		name        string
+		cmd         []string
+		expectOut   string
+		outContains bool
+	}{
+		{
+			name:        "default",
+			cmd:         []string{"image", "inspect", srcRef},
+			expectOut:   "created",
+			outContains: true,
+		},
+		{
+			name:        "format body",
+			cmd:         []string{"image", "inspect", srcRef, "--format", `body`},
+			expectOut:   "created",
+			outContains: true,
+		},
+		{
+			name:        "format config",
+			cmd:         []string{"image", "inspect", srcRef, "--format", `{{ index .Config.Labels "version" }}`},
+			expectOut:   "3",
+			outContains: false,
+		},
+		{
+			name:        "format getconfig",
+			cmd:         []string{"image", "inspect", srcRef, "--format", `{{ .GetConfig.OS}}`},
+			expectOut:   "linux",
+			outContains: false,
+		},
+	}
+	for _, tc := range tt {
+		t.Run(tc.name, func(t *testing.T) {
+			out, err := cobraTest(t, nil, tc.cmd...)
+			if err != nil {
+				t.Errorf("error: %v", err)
+				return
+			}
+			if (!tc.outContains && out != tc.expectOut) || (tc.outContains && !strings.Contains(out, tc.expectOut)) {
+				t.Errorf("unexpected output, expected %s, received %s", tc.expectOut, out)
+			}
+		})
+	}
+}
+
 func TestImageMod(t *testing.T) {
 	tmpDir := t.TempDir()
 	srcRef := "ocidir://../../testdata/testrepo:v3"