Repository: regclient/regclient (go)

Issue:
[Feature] Wasm: support `application/vnd.wasm.config.v0+json` media type
<!--
Note: Please search to see if an issue already exists for the bug you encountered.
-->

### Current Behavior

```bash
> docker pull ghcr.io/webassembly/wasi/http:0.2.0
0.2.0: Pulling from webassembly/wasi/http
Digest: sha256:b292aade5066edbbd860b0c87180085a813121c61103702ecd09772a4c03283a
Status: Downloaded newer image for ghcr.io/webassembly/wasi/http:0.2.0
ghcr.io/webassembly/wasi/http:0.2.0

> regctl image inspect ghcr.io/webassembly/wasi/http:0.2.0                                      
unsupported config media type application/vnd.wasm.config.v0+json: unsupported media type
```

### Expected Behavior

regctl should be able to inspect the wasm image.

### Example Solution

<!--
Example: steps to result in the desired behavior:
1. In this environment...
2. With this config...
3. Run '...'
4. Desired result...
-->

### Version

```text
(paste output of `regctl version` or similar for binaries, or `docker image inspect regclient/...` on the image and paste the labels)
```

### Environment

<!--
Please include:
- How you are running the tool: downloaded binary or from within a container
- Your platform: Linux, Windows, or Mac, x86 or ARM.
- Your registry: Docker Hub, ECR, GCR, ACR, Harbor, registry:2, etc.
-->

- Running as binary or container:
- Host platform:
- Registry description:

### Anything else

<!--
Links? References? Anything that will give us more context about the issue that you are encountering!
-->

The wasm meida type is standardized by the CNCF WebAssembly Working Group. Please see this for detail:
https://tag-runtime.cncf.io/wgs/wasm/deliverables/wasm-oci-artifact/


Pull request description:
Option to output config with regctl artifact get
<!--

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 #791
<!-- If this is a bug fix, include "fixes #xxxx", or "closes #xxxx" -->

### Describe the change

The `regctl artifact get` command allows a config to be output to a file, but doesn't have an option to just show the config, and it still wants to return layers even when the config is saved. This adds a `--config` option to only return the config.
<!-- 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 artifact get --config ghcr.io/webassembly/wasi/http:0.2.0
```
<!-- Include steps that can be taken to verify the change -->

### Changelog text

- Feat: Add `regctl artifact get --config` option to only return the config.
<!-- 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/artifact.go b/cmd/regctl/artifact.go
index a572d79..7ddd77f 100644
--- a/cmd/regctl/artifact.go
+++ b/cmd/regctl/artifact.go
@@ -71,6 +71,7 @@ type artifactCmd struct {
 	formatList       string
 	formatPut        string
 	formatTree       string
+	getConfig        bool
 	index            bool
 	latest           bool
 	outputDir        string
@@ -104,7 +105,10 @@ regctl artifact get registry.example.org/helm-charts/chart:0.0.1 > chart.tgz
 regctl artifact get \
   --subject ghcr.io/regclient/regsync:latest \
   --filter-artifact-type application/spdx+json \
-  --platform local | jq .`,
+  --platform local | jq .
+  
+# retrieve the artifact config rather than the artifact itself
+regctl artifact get registry.example.org/artifact:0.0.1 --config`,
 		Args:      cobra.RangeArgs(0, 1),
 		ValidArgs: []string{}, // do not auto complete repository/tag
 		RunE:      artifactOpts.runArtifactGet,
@@ -177,7 +181,8 @@ regctl artifact tree --digest-tags ghcr.io/regclient/regsync:latest`,
 	artifactGetCmd.Flags().StringVarP(&artifactOpts.platform, "platform", "p", "", "Specify platform of a subject (e.g. linux/amd64 or local)")
 	artifactGetCmd.Flags().StringVar(&artifactOpts.filterAT, "filter-artifact-type", "", "Filter referrers by artifactType")
 	artifactGetCmd.Flags().StringArrayVar(&artifactOpts.filterAnnot, "filter-annotation", []string{}, "Filter referrers by annotation (key=value)")
-	artifactGetCmd.Flags().StringVar(&artifactOpts.artifactConfig, "config-file", "", "Config filename to output")
+	artifactGetCmd.Flags().BoolVar(&artifactOpts.getConfig, "config", false, "Show the config, overrides file options")
+	artifactGetCmd.Flags().StringVar(&artifactOpts.artifactConfig, "config-file", "", "Output config to a file")
 	artifactGetCmd.Flags().StringArrayVarP(&artifactOpts.artifactFile, "file", "f", []string{}, "Filter by artifact filename")
 	artifactGetCmd.Flags().StringArrayVarP(&artifactOpts.artifactFileMT, "file-media-type", "m", []string{}, "Filter by artifact media-type")
 	_ = artifactGetCmd.RegisterFlagCompletionFunc("file-media-type", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
@@ -360,7 +365,7 @@ func (artifactOpts *artifactCmd) runArtifactGet(cmd *cobra.Command, args []strin
 	}
 
 	// if config-file defined, create file as writer, perform a blob get
-	if artifactOpts.artifactConfig != "" {
+	if artifactOpts.artifactConfig != "" || artifactOpts.getConfig {
 		d, err := mi.GetConfig()
 		if err != nil {
 			return err
@@ -370,14 +375,25 @@ func (artifactOpts *artifactCmd) runArtifactGet(cmd *cobra.Command, args []strin
 			return err
 		}
 		defer rdr.Close()
-		fh, err := os.Create(artifactOpts.artifactConfig)
-		if err != nil {
-			return err
+		if artifactOpts.artifactConfig != "" {
+			fh, err := os.Create(artifactOpts.artifactConfig)
+			if err != nil {
+				return err
+			}
+			defer fh.Close()
+			_, err = io.Copy(fh, rdr)
+			if err != nil {
+				return err
+			}
+		} else {
+			_, err = io.Copy(cmd.OutOrStdout(), rdr)
+			if err != nil {
+				return err
+			}
 		}
-		defer fh.Close()
-		_, err = io.Copy(fh, rdr)
-		if err != nil {
-			return err
+		if artifactOpts.getConfig {
+			// do not return layer contents if request is only for a config
+			return nil
 		}
 	}
 


Test patch:
diff --git a/cmd/regctl/artifact_test.go b/cmd/regctl/artifact_test.go
index 1d57c3b..2052207 100644
--- a/cmd/regctl/artifact_test.go
+++ b/cmd/regctl/artifact_test.go
@@ -50,6 +50,11 @@ func TestArtifactGet(t *testing.T) {
 			args:      []string{"artifact", "get", "ocidir://../../testdata/testrepo:ai", "--filter-annotation", "type=sbom"},
 			expectOut: "eggs",
 		},
+		{
+			name:      "Get Config",
+			args:      []string{"artifact", "get", "ocidir://../../testdata/testrepo:ai", "--filter-annotation", "type=sbom", "--config"},
+			expectOut: "{}",
+		},
 	}
 	for _, tc := range tt {
 		t.Run(tc.name, func(t *testing.T) {