Repository: regclient/regclient
Pull request #659 (2024-02-14 21:52:48)

Make artifact filename annotation optional
<!--

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

<!-- If this is a bug fix, include "fixes #xxxx", or "closes #xxxx" -->

### Describe the change

The title annotation for filenames in artifacts attempted to mirror other projects but is an atypical usage of the annotation and not documented by OCI. It could also be used to accidentally leak data. This stops including it by default but allows it with a `regctl artifact put --file-title` option for those that need it.
<!-- 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 put --artifact-type application/vnd.example --file ./some-file.txt localhost:5000/aritfact:test
```
<!-- Include steps that can be taken to verify the change -->

### Changelog text

- Breaking: `regctl artifact put` no longer includes the filename annotation by default. Use `--file-title` to include.
<!-- 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 81854b5..6c80986 100644
--- a/cmd/regctl/artifact.go
+++ b/cmd/regctl/artifact.go
@@ -60,6 +60,7 @@ type artifactCmd struct {
 	artifactConfigMT string
 	artifactFile     []string
 	artifactFileMT   []string
+	artifactTitle    bool
 	byDigest         bool
 	digestTags       bool
 	filterAT         string
@@ -168,12 +169,13 @@ func NewArtifactCmd(rootOpts *rootCmd) *cobra.Command {
 	_ = artifactPutCmd.RegisterFlagCompletionFunc("file-media-type", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
 		return artifactFileKnownTypes, cobra.ShellCompDirectiveNoFileComp
 	})
+	artifactPutCmd.Flags().BoolVar(&artifactOpts.artifactTitle, "file-title", false, "Include a title annotation with the filename")
 	artifactPutCmd.Flags().StringArrayVar(&artifactOpts.annotations, "annotation", []string{}, "Annotation to include on manifest")
 	artifactPutCmd.Flags().BoolVar(&artifactOpts.byDigest, "by-digest", false, "Push manifest by digest instead of tag")
 	artifactPutCmd.Flags().StringVar(&artifactOpts.formatPut, "format", "", "Format output with go template syntax")
 	artifactPutCmd.Flags().BoolVar(&artifactOpts.index, "index", false, "Create/append artifact to an index")
 	artifactPutCmd.Flags().StringVar(&artifactOpts.subject, "subject", "", "Set the subject to a reference (used for referrer queries)")
-	artifactPutCmd.Flags().BoolVar(&artifactOpts.stripDirs, "strip-dirs", false, "Strip directories from filenames in artifact")
+	artifactPutCmd.Flags().BoolVar(&artifactOpts.stripDirs, "strip-dirs", false, "Strip directories from filenames in file-title")
 	artifactPutCmd.Flags().StringVarP(&artifactOpts.platform, "platform", "p", "", "Specify platform of a subject (e.g. linux/amd64 or local)")
 	artifactPutCmd.Flags().StringVar(&artifactOpts.refers, "refers", "", "EXPERIMENTAL: Set a referrer to the reference")
 	_ = artifactPutCmd.Flags().MarkHidden("refers")
@@ -757,23 +759,26 @@ func (artifactOpts *artifactCmd) runArtifactPut(cmd *cobra.Command, args []strin
 				}
 				d := digester.Digest()
 				// add layer to manifest
-				af := f
-				if artifactOpts.stripDirs {
-					fSplit := strings.Split(f, "/")
-					if fSplit[len(fSplit)-1] != "" {
-						af = fSplit[len(fSplit)-1]
-					} else if len(fSplit) > 1 {
-						af = fSplit[len(fSplit)-2] + "/"
-					}
-				}
-				blobs = append(blobs, types.Descriptor{
+				desc := types.Descriptor{
 					MediaType: mt,
 					Digest:    d,
 					Size:      l,
-					Annotations: map[string]string{
+				}
+				if artifactOpts.artifactTitle {
+					af := f
+					if artifactOpts.stripDirs {
+						fSplit := strings.Split(f, "/")
+						if fSplit[len(fSplit)-1] != "" {
+							af = fSplit[len(fSplit)-1]
+						} else if len(fSplit) > 1 {
+							af = fSplit[len(fSplit)-2] + "/"
+						}
+					}
+					desc.Annotations = map[string]string{
 						ociAnnotTitle: af,
-					},
-				})
+					}
+				}
+				blobs = append(blobs, desc)
 				// if blob already exists, skip Put
 				bRdr, err := rc.BlobHead(ctx, r, types.Descriptor{Digest: d})
 				if err == nil {


Test patch:
diff --git a/cmd/regctl/artifact_test.go b/cmd/regctl/artifact_test.go
index 3eebe05..d684a25 100644
--- a/cmd/regctl/artifact_test.go
+++ b/cmd/regctl/artifact_test.go
@@ -157,6 +157,12 @@ func TestArtifactPut(t *testing.T) {
 		t.Errorf("failed creating test conf: %v", err)
 		return
 	}
+	testFileName := filepath.Join(testDir, "exFile")
+	err = os.WriteFile(testFileName, []byte(`example test file`), 0600)
+	if err != nil {
+		t.Errorf("failed creating test conf: %v", err)
+		return
+	}
 
 	tt := []struct {
 		name        string
@@ -196,19 +202,29 @@ func TestArtifactPut(t *testing.T) {
 			args: []string{"artifact", "put", "--artifact-type", "", "--config-type", "application/vnd.example", "--config-file", testConfName, "ocidir://" + testDir + ":put-example-conf-data"},
 			in:   testData,
 		},
+		{
+			name: "Put artifact example conf data and file",
+			args: []string{"artifact", "put", "--artifact-type", "", "--config-type", "application/vnd.example", "--config-file", testConfName, "--file", testFileName, "ocidir://" + testDir + ":put-example-file-data"},
+			in:   testData,
+		},
+		{
+			name: "Put artifact example conf data and file stripped",
+			args: []string{"artifact", "put", "--artifact-type", "", "--config-type", "application/vnd.example", "--config-file", testConfName, "--file", testFileName, "--file-title", "--strip-dirs", "ocidir://" + testDir + ":put-example-file-data"},
+			in:   testData,
+		},
 		{
 			name: "Put subject",
-			args: []string{"artifact", "put", "--artifact-type", "application/vnd.example", "--config-file", "", "--subject", "ocidir://" + testDir + ":put-example-at"},
+			args: []string{"artifact", "put", "--artifact-type", "application/vnd.example", "--subject", "ocidir://" + testDir + ":put-example-at"},
 			in:   testData,
 		},
 		{
 			name: "Put create index",
-			args: []string{"artifact", "put", "--artifact-type", "application/vnd.example", "--config-file", "", "--annotation", "test=a", "--platform", "linux/amd64", "--index", "ocidir://" + testDir + ":index"},
+			args: []string{"artifact", "put", "--artifact-type", "application/vnd.example", "--annotation", "test=a", "--platform", "linux/amd64", "--index", "ocidir://" + testDir + ":index"},
 			in:   testData,
 		},
 		{
 			name: "Put append index",
-			args: []string{"artifact", "put", "--artifact-type", "application/vnd.example", "--config-file", "", "--annotation", "test=b", "--platform", "linux/arm64", "--index", "ocidir://" + testDir + ":index"},
+			args: []string{"artifact", "put", "--artifact-type", "application/vnd.example", "--annotation", "test=b", "--platform", "linux/arm64", "--index", "ocidir://" + testDir + ":index"},
 			in:   testData,
 		},
 	}