From f2bf7c35dfff79d4d9a17289e82ad1b587b23f8d Mon Sep 17 00:00:00 2001 From: BagToad <47394200+BagToad@users.noreply.github.com> Date: Fri, 28 Aug 2026 12:21:31 -0600 Subject: [PATCH] Limit attachment batches to 50 files Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- internal/attachments/flags.go | 8 +++++++- internal/attachments/flags_test.go | 15 +++++++++++++++ pkg/cmd/issue/comment/comment.go | 1 + pkg/cmd/issue/create/create.go | 1 + pkg/cmd/issue/edit/edit.go | 1 + pkg/cmd/pr/comment/comment.go | 1 + pkg/cmd/pr/create/create.go | 1 + pkg/cmd/pr/edit/edit.go | 1 + skills/gh/SKILL.md | 2 ++ 9 files changed, 30 insertions(+), 1 deletion(-) diff --git a/internal/attachments/flags.go b/internal/attachments/flags.go index fb6d2cdf7e9..c47117c79fc 100644 --- a/internal/attachments/flags.go +++ b/internal/attachments/flags.go @@ -10,7 +10,10 @@ import ( "github.com/spf13/pflag" ) -const flagName = "attach" +const ( + flagName = "attach" + maxAttachments = 50 +) // errEmptyPath is reported for a --attach that named no file, whether the flag // held nothing else or the empty value sat beside a real one. @@ -42,6 +45,9 @@ func (f *Flag) UserAssets() ([]UserAsset, error) { if !f.Changed() { return nil, nil } + if len(f.values) > maxAttachments { + return nil, fmt.Errorf("`--attach` accepts at most %d values per command", maxAttachments) + } return userAssetsFromArgs(f.values) } diff --git a/internal/attachments/flags_test.go b/internal/attachments/flags_test.go index 31b603424f0..1238acc1519 100644 --- a/internal/attachments/flags_test.go +++ b/internal/attachments/flags_test.go @@ -1,8 +1,10 @@ package attachments import ( + "fmt" "io/fs" "os" + "strings" "testing" "github.com/google/shlex" @@ -136,6 +138,11 @@ func TestFlagUserAssets(t *testing.T) { input: "--attach ./b.png --attach ./a.png", wantPaths: []string{"./b.png", "./a.png"}, }, + { + name: "too many attachments are rejected before filesystem validation", + input: strings.Repeat("--attach ./missing.txt ", maxAttachments+1), + wantErr: "`--attach` accepts at most 50 values per command", + }, { name: "a file that does not exist", input: "--attach ./gone.png", @@ -262,4 +269,12 @@ func TestFlagUserAssets(t *testing.T) { } }) } + + t.Run("maximum number of attachments", func(t *testing.T) { + names := make([]string, maxAttachments) + for i := range names { + names[i] = fmt.Sprintf("attachment-%d.png", i) + } + assert.Len(t, NewTestAssets(t, names...), maxAttachments) + }) } diff --git a/pkg/cmd/issue/comment/comment.go b/pkg/cmd/issue/comment/comment.go index c6e74daabfa..c85f17d20ae 100644 --- a/pkg/cmd/issue/comment/comment.go +++ b/pkg/cmd/issue/comment/comment.go @@ -39,6 +39,7 @@ func NewCmdComment(f *cmdutil.Factory, runF func(*prShared.CommentableOptions) e attached file, such as %[1]s![alt](./login.png)%[1]s, that reference is rewritten to point at the uploaded asset. Any attached file the body does not reference is appended to the end of the comment. + You can attach up to 50 files per command. Alt text for an image follows the path after %[1]s#%[1]s, as in %[1]s--attach './login.png#The login error state'%[1]s. Without it the filename is used. diff --git a/pkg/cmd/issue/create/create.go b/pkg/cmd/issue/create/create.go index c62bbcd6bd2..0fd18db0dd6 100644 --- a/pkg/cmd/issue/create/create.go +++ b/pkg/cmd/issue/create/create.go @@ -83,6 +83,7 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co Use %[1]s--attach%[1]s to upload an image or video. The attachment is appended to the body. If the body references an attached file, such as %[1]s![alt](./login.png)%[1]s, that reference is rewritten to point at the uploaded asset instead. + You can attach up to 50 files per command. Alt text for an image follows the path after %[1]s#%[1]s, as in %[1]s--attach './login.png#The login error state'%[1]s. Without it the filename is used. diff --git a/pkg/cmd/issue/edit/edit.go b/pkg/cmd/issue/edit/edit.go index ff5c66cf1ed..1329e1a5c58 100644 --- a/pkg/cmd/issue/edit/edit.go +++ b/pkg/cmd/issue/edit/edit.go @@ -85,6 +85,7 @@ func NewCmdEdit(f *cmdutil.Factory, runF func(*EditOptions) error) *cobra.Comman flag the issue keeps the body it already has and the attachment is appended to it. If the body references an attached file, such as %[1]s![alt](./login.png)%[1]s, that reference is rewritten to point at the uploaded asset instead. + You can attach up to 50 files per command. Alt text for an image follows the path after %[1]s#%[1]s, as in %[1]s--attach './login.png#The login error state'%[1]s. Without it the filename is used. diff --git a/pkg/cmd/pr/comment/comment.go b/pkg/cmd/pr/comment/comment.go index bde2b7cbd68..cd9c7927edb 100644 --- a/pkg/cmd/pr/comment/comment.go +++ b/pkg/cmd/pr/comment/comment.go @@ -37,6 +37,7 @@ func NewCmdComment(f *cmdutil.Factory, runF func(*shared.CommentableOptions) err attached file, such as %[1]s![alt](./login.png)%[1]s, that reference is rewritten to point at the uploaded asset. Any attached file the body does not reference is appended to the end of the comment. + You can attach up to 50 files per command. Alt text for an image follows the path after %[1]s#%[1]s, as in %[1]s--attach './login.png#The login error state'%[1]s. Without it the filename is used. diff --git a/pkg/cmd/pr/create/create.go b/pkg/cmd/pr/create/create.go index b85ea1548d1..11706462d28 100644 --- a/pkg/cmd/pr/create/create.go +++ b/pkg/cmd/pr/create/create.go @@ -245,6 +245,7 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co Use %[1]s--attach%[1]s to upload an image or video. The attachment is appended to the body. If the body references an attached file, such as %[1]s![alt](./login.png)%[1]s, that reference is rewritten to point at the uploaded asset instead. + You can attach up to 50 files per command. Alt text for an image follows the path after %[1]s#%[1]s, as in %[1]s--attach './login.png#The login error state'%[1]s. Without it the filename is used. diff --git a/pkg/cmd/pr/edit/edit.go b/pkg/cmd/pr/edit/edit.go index 5482c002fdc..fb4dc268e8a 100644 --- a/pkg/cmd/pr/edit/edit.go +++ b/pkg/cmd/pr/edit/edit.go @@ -76,6 +76,7 @@ func NewCmdEdit(f *cmdutil.Factory, runF func(*EditOptions) error) *cobra.Comman request keeps the body it already has and the attachment is appended to it. If the body references an attached file, such as %[1]s![alt](./login.png)%[1]s, that reference is rewritten to point at the uploaded asset instead. + You can attach up to 50 files per command. Alt text for an image follows the path after %[1]s#%[1]s, as in %[1]s--attach './login.png#The login error state'%[1]s. Without it the filename is used. diff --git a/skills/gh/SKILL.md b/skills/gh/SKILL.md index e491b3eaa6b..e1f974aca72 100644 --- a/skills/gh/SKILL.md +++ b/skills/gh/SKILL.md @@ -100,6 +100,8 @@ blocked-by/blocking relationships. - Repeat `--attach` to upload multiple files: `gh issue comment 12 --attach ./before.png --attach ./after.png`. +- Each command invocation accepts at most 50 `--attach` values total across + images and videos. - Supported files are `png`, `jpg`, `jpeg`, `gif`, `webp`, `svg`, `mp4`, `mov`, and `webm`. - For an image, append alt text to the path after `#`. Quote the value so the