From 3f0044fd94ace79714ee93df89b5efbd5b8bf242 Mon Sep 17 00:00:00 2001 From: majiayu000 <1835304752@qq.com> Date: Fri, 26 Dec 2025 14:43:52 +0800 Subject: [PATCH 01/19] fix: error when --remote flag used with repo argument When a repository argument is provided to `gh repo fork`, the command operates independently of the current local repository. Using --remote in this context is incompatible because there's no local repository to add the remote to. This change returns an explicit error when these flags are combined, providing clear feedback instead of silently ignoring the --remote flag. Fixes #2722 Signed-off-by: majiayu000 <1835304752@qq.com> --- pkg/cmd/repo/fork/fork.go | 4 ++++ pkg/cmd/repo/fork/fork_test.go | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/pkg/cmd/repo/fork/fork.go b/pkg/cmd/repo/fork/fork.go index b620291d6af..3ebb02413bc 100644 --- a/pkg/cmd/repo/fork/fork.go +++ b/pkg/cmd/repo/fork/fork.go @@ -113,6 +113,10 @@ func NewCmdFork(f *cmdutil.Factory, runF func(*ForkOptions) error) *cobra.Comman opts.Rename = true // Any existing 'origin' will be renamed to upstream } + if opts.Repository != "" && cmd.Flags().Changed("remote") { + return cmdutil.FlagErrorf("the `--remote` flag is unsupported when a repository argument is provided") + } + if promptOk { // We can prompt for these if they were not specified. opts.PromptClone = !cmd.Flags().Changed("clone") diff --git a/pkg/cmd/repo/fork/fork_test.go b/pkg/cmd/repo/fork/fork_test.go index 1f0b9cef1e3..edf5f2763b9 100644 --- a/pkg/cmd/repo/fork/fork_test.go +++ b/pkg/cmd/repo/fork/fork_test.go @@ -144,6 +144,12 @@ func TestNewCmdFork(t *testing.T) { Rename: false, }, }, + { + name: "remote with repo argument", + cli: "foo/bar --remote", + wantErr: true, + errMsg: "the `--remote` flag is unsupported when a repository argument is provided", + }, } for _, tt := range tests { From 6f739036b8aa23936a89a7e69a4fb2f7acf647af Mon Sep 17 00:00:00 2001 From: elijahthis Date: Sun, 1 Feb 2026 23:12:01 +0100 Subject: [PATCH 02/19] fix: clarify scope error while creating issues for projects --- api/client.go | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/api/client.go b/api/client.go index e6ff59c592b..b83e9b70739 100644 --- a/api/client.go +++ b/api/client.go @@ -10,6 +10,7 @@ import ( "regexp" "strings" + "github.com/cli/cli/v2/pkg/set" ghAPI "github.com/cli/go-gh/v2/pkg/api" ghauth "github.com/cli/go-gh/v2/pkg/auth" ) @@ -178,6 +179,10 @@ func handleResponse(err error) error { var gqlErr *ghAPI.GraphQLError if errors.As(err, &gqlErr) { + scopeErr := GenerateScopeErrorForGQL(gqlErr) + if scopeErr != nil { + return scopeErr + } return GraphQLError{ GraphQLError: gqlErr, } @@ -186,6 +191,40 @@ func handleResponse(err error) error { return err } +func GenerateScopeErrorForGQL(gqlErr *ghAPI.GraphQLError) error { + missing := set.NewStringSet() + for _, e := range gqlErr.Errors { + if e.Type != "INSUFFICIENT_SCOPES" { + continue + } + missing.AddValues(requiredScopesFromServerMessage(e.Message)) + } + if missing.Len() > 0 { + s := missing.ToSlice() + // TODO: this duplicates parts of generateScopesSuggestion + return fmt.Errorf( + "error: your authentication token is missing required scopes %v\n"+ + "To request it, run: gh auth refresh -s %s", + s, + strings.Join(s, ",")) + } + return nil +} + +var scopesRE = regexp.MustCompile(`one of the following scopes: \[(.+?)]`) + +func requiredScopesFromServerMessage(msg string) []string { + m := scopesRE.FindStringSubmatch(msg) + if m == nil { + return nil + } + var scopes []string + for _, mm := range strings.Split(m[1], ",") { + scopes = append(scopes, strings.Trim(mm, "' ")) + } + return scopes +} + // ScopesSuggestion is an error messaging utility that prints the suggestion to request additional OAuth // scopes in case a server response indicates that there are missing scopes. func ScopesSuggestion(resp *http.Response) string { From 71564fd4a15df4687e5d85962922067d69755b79 Mon Sep 17 00:00:00 2001 From: elijahthis Date: Sun, 1 Feb 2026 23:26:03 +0100 Subject: [PATCH 03/19] test: TestGenerateScopeErrorForGQL and TestRequiredScopesFromServerMessage --- api/client_test.go | 77 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/api/client_test.go b/api/client_test.go index 1701a17a967..d961b05dce8 100644 --- a/api/client_test.go +++ b/api/client_test.go @@ -10,6 +10,7 @@ import ( "github.com/cli/cli/v2/pkg/httpmock" "github.com/cli/cli/v2/pkg/iostreams" + "github.com/cli/go-gh/v2/pkg/api" "github.com/stretchr/testify/assert" ) @@ -255,3 +256,79 @@ func TestHTTPHeaders(t *testing.T) { } assert.Equal(t, "", stderr.String()) } + +func TestGenerateScopeErrorForGQL(t *testing.T) { + tests := []struct { + name string + gqlError *api.GraphQLError + wantErr bool + expected string + }{ + { + name: "missing scope", + gqlError: &api.GraphQLError{ + Errors: []api.GraphQLErrorItem{ + { + Type: "INSUFFICIENT_SCOPES", + Message: "The 'addProjectV2ItemById' field requires one of the following scopes: ['project']", + }, + }, + }, + wantErr: true, + expected: "error: your authentication token is missing required scopes [project]\n" + + "To request it, run: gh auth refresh -s project", + }, + + { + name: "ignore non-scope errors", + gqlError: &api.GraphQLError{ + Errors: []api.GraphQLErrorItem{ + { + Type: "NOT_FOUND", + Message: "Could not resolve to a Repository", + }, + }, + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := GenerateScopeErrorForGQL(tt.gqlError) + if tt.wantErr { + assert.NotNil(t, err) + assert.Equal(t, tt.expected, err.Error()) + } else { + assert.Nil(t, err) + } + }) + } +} + +func TestRequiredScopesFromServerMessage(t *testing.T) { + tests := []struct { + msg string + expected []string + }{ + { + msg: "requires one of the following scopes: ['project']", + expected: []string{"project"}, + }, + { + msg: "requires one of the following scopes: ['repo', 'read:org']", + expected: []string{"repo", "read:org"}, + }, + { + msg: "no match here", + expected: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.msg, func(t *testing.T) { + output := requiredScopesFromServerMessage(tt.msg) + assert.Equal(t, tt.expected, output) + }) + } +} From fc8c0f1110171c3e4dffc77b3e8b7d4ed70785ba Mon Sep 17 00:00:00 2001 From: elijahthis Date: Tue, 3 Feb 2026 23:59:15 +0100 Subject: [PATCH 04/19] trigger rerun From b32b7eab3911caf9ee4a90e71d89ce7842a7d37c Mon Sep 17 00:00:00 2001 From: vishnuvv27 Date: Mon, 9 Feb 2026 15:31:55 +0530 Subject: [PATCH 05/19] Fix redundant API call in gh issue view --comments (#12606) --- .../view/fixtures/issueView_previewSingleComment.json | 8 ++++++-- pkg/cmd/issue/view/http.go | 11 +++++------ pkg/cmd/issue/view/view.go | 2 -- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/pkg/cmd/issue/view/fixtures/issueView_previewSingleComment.json b/pkg/cmd/issue/view/fixtures/issueView_previewSingleComment.json index be099c14b1f..8959acec67d 100644 --- a/pkg/cmd/issue/view/fixtures/issueView_previewSingleComment.json +++ b/pkg/cmd/issue/view/fixtures/issueView_previewSingleComment.json @@ -138,10 +138,14 @@ ] } ], - "totalCount": 6 + "totalCount": 6, + "pageInfo": { + "hasNextPage": true, + "endCursor": "Y3Vyc29yOnYyOjg5" + } }, "url": "https://github.com/OWNER/REPO/issues/123" } } } -} +} \ No newline at end of file diff --git a/pkg/cmd/issue/view/http.go b/pkg/cmd/issue/view/http.go index 4adc71802dc..2982fbbe3a2 100644 --- a/pkg/cmd/issue/view/http.go +++ b/pkg/cmd/issue/view/http.go @@ -20,14 +20,13 @@ func preloadIssueComments(client *http.Client, repo ghrepo.Interface, issue *api } `graphql:"node(id: $id)"` } + if !issue.Comments.PageInfo.HasNextPage { + return nil + } + variables := map[string]interface{}{ "id": githubv4.ID(issue.ID), - "endCursor": (*githubv4.String)(nil), - } - if issue.Comments.PageInfo.HasNextPage { - variables["endCursor"] = githubv4.String(issue.Comments.PageInfo.EndCursor) - } else { - issue.Comments.Nodes = issue.Comments.Nodes[0:0] + "endCursor": githubv4.String(issue.Comments.PageInfo.EndCursor), } gql := api.NewClientFromHTTP(client) diff --git a/pkg/cmd/issue/view/view.go b/pkg/cmd/issue/view/view.go index 41c01ef40d9..e41ad6acffe 100644 --- a/pkg/cmd/issue/view/view.go +++ b/pkg/cmd/issue/view/view.go @@ -144,8 +144,6 @@ func viewRun(opts *ViewOptions) error { } if lookupFields.Contains("comments") { - // FIXME: this re-fetches the comments connection even though the initial set of 100 were - // fetched in the previous request. err := preloadIssueComments(httpClient, baseRepo, issue) if err != nil { return err From 48951aca01fd5bfeaf0c07747f01acd2b1140d71 Mon Sep 17 00:00:00 2001 From: Kynan Ware <47394200+BagToad@users.noreply.github.com> Date: Wed, 18 Feb 2026 17:52:01 -0700 Subject: [PATCH 06/19] Fix invalid ANSI SGR escape code in JSON and diff colorization Replace `1;38` with `1;37` (bold white) in the delimiter/header color constant. SGR parameter 38 is the extended foreground color prefix and requires sub-parameters (e.g. `38;5;n` or `38;2;r;g;b`), so using it bare produces an invalid escape sequence. Most terminals silently ignore the malformed parameter, masking the bug. Fixes cli/cli#12683 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- pkg/cmd/pr/diff/diff.go | 2 +- pkg/cmd/pr/diff/diff_test.go | 4 ++-- pkg/jsoncolor/jsoncolor.go | 2 +- pkg/jsoncolor/jsoncolor_test.go | 10 +++++----- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pkg/cmd/pr/diff/diff.go b/pkg/cmd/pr/diff/diff.go index acf17462c5e..6d37bf1e5b8 100644 --- a/pkg/cmd/pr/diff/diff.go +++ b/pkg/cmd/pr/diff/diff.go @@ -190,7 +190,7 @@ func fetchDiff(httpClient *http.Client, baseRepo ghrepo.Interface, prNumber int, const lineBufferSize = 4096 var ( - colorHeader = []byte("\x1b[1;38m") + colorHeader = []byte("\x1b[1;37m") colorAddition = []byte("\x1b[32m") colorRemoval = []byte("\x1b[31m") colorReset = []byte("\x1b[m") diff --git a/pkg/cmd/pr/diff/diff_test.go b/pkg/cmd/pr/diff/diff_test.go index 28a83bfc42a..ceb93a18790 100644 --- a/pkg/cmd/pr/diff/diff_test.go +++ b/pkg/cmd/pr/diff/diff_test.go @@ -179,7 +179,7 @@ func Test_diffRun(t *testing.T) { Patch: false, }, wantFields: []string{"number"}, - wantStdout: fmt.Sprintf(testDiff, "\x1b[m", "\x1b[1;38m", "\x1b[32m", "\x1b[31m"), + wantStdout: fmt.Sprintf(testDiff, "\x1b[m", "\x1b[1;37m", "\x1b[32m", "\x1b[31m"), httpStubs: func(reg *httpmock.Registry) { stubDiffRequest(reg, "application/vnd.github.v3.diff", fmt.Sprintf(testDiff, "", "", "", "")) }, @@ -313,7 +313,7 @@ func Test_colorDiffLines(t *testing.T) { "%[4]s+foo%[2]s\n%[5]s-b%[1]sr%[2]s\n%[3]s+++ baz%[2]s\n", strings.Repeat("a", 2*lineBufferSize), "\x1b[m", - "\x1b[1;38m", + "\x1b[1;37m", "\x1b[32m", "\x1b[31m", ), diff --git a/pkg/jsoncolor/jsoncolor.go b/pkg/jsoncolor/jsoncolor.go index 8e20a11611a..b9ff9525362 100644 --- a/pkg/jsoncolor/jsoncolor.go +++ b/pkg/jsoncolor/jsoncolor.go @@ -9,7 +9,7 @@ import ( ) const ( - colorDelim = "1;38" // bright white + colorDelim = "1;37" // bold white colorKey = "1;34" // bright blue colorNull = "36" // cyan colorString = "32" // green diff --git a/pkg/jsoncolor/jsoncolor_test.go b/pkg/jsoncolor/jsoncolor_test.go index 9e2eda34314..d2a22b90bc0 100644 --- a/pkg/jsoncolor/jsoncolor_test.go +++ b/pkg/jsoncolor/jsoncolor_test.go @@ -34,7 +34,7 @@ func TestWrite(t *testing.T) { r: bytes.NewBufferString(`{}`), indent: "", }, - wantW: "\x1b[1;38m{\x1b[m\x1b[1;38m}\x1b[m\n", + wantW: "\x1b[1;37m{\x1b[m\x1b[1;37m}\x1b[m\n", wantErr: false, }, { @@ -43,9 +43,9 @@ func TestWrite(t *testing.T) { r: bytes.NewBufferString(`{"hash":{"a":1,"b":2},"array":[3,4]}`), indent: "\t", }, - wantW: "\x1b[1;38m{\x1b[m\n\t\x1b[1;34m\"hash\"\x1b[m\x1b[1;38m:\x1b[m " + - "\x1b[1;38m{\x1b[m\n\t\t\x1b[1;34m\"a\"\x1b[m\x1b[1;38m:\x1b[m 1\x1b[1;38m,\x1b[m\n\t\t\x1b[1;34m\"b\"\x1b[m\x1b[1;38m:\x1b[m 2\n\t\x1b[1;38m}\x1b[m\x1b[1;38m,\x1b[m" + - "\n\t\x1b[1;34m\"array\"\x1b[m\x1b[1;38m:\x1b[m \x1b[1;38m[\x1b[m\n\t\t3\x1b[1;38m,\x1b[m\n\t\t4\n\t\x1b[1;38m]\x1b[m\n\x1b[1;38m}\x1b[m\n", + wantW: "\x1b[1;37m{\x1b[m\n\t\x1b[1;34m\"hash\"\x1b[m\x1b[1;37m:\x1b[m " + + "\x1b[1;37m{\x1b[m\n\t\t\x1b[1;34m\"a\"\x1b[m\x1b[1;37m:\x1b[m 1\x1b[1;37m,\x1b[m\n\t\t\x1b[1;34m\"b\"\x1b[m\x1b[1;37m:\x1b[m 2\n\t\x1b[1;37m}\x1b[m\x1b[1;37m,\x1b[m" + + "\n\t\x1b[1;34m\"array\"\x1b[m\x1b[1;37m:\x1b[m \x1b[1;37m[\x1b[m\n\t\t3\x1b[1;37m,\x1b[m\n\t\t4\n\t\x1b[1;37m]\x1b[m\n\x1b[1;37m}\x1b[m\n", wantErr: false, }, { @@ -63,7 +63,7 @@ func TestWrite(t *testing.T) { r: bytes.NewBufferString(`{{`), indent: "", }, - wantW: "\x1b[1;38m{\x1b[m\n", + wantW: "\x1b[1;37m{\x1b[m\n", wantErr: true, }, } From 6057bfadbfcf4570031a3c2ee972184f917cc935 Mon Sep 17 00:00:00 2001 From: itchyny Date: Wed, 25 Feb 2026 19:43:45 +0900 Subject: [PATCH 07/19] Use pre-compiled regexp for matching Content-Type --- pkg/cmd/api/api.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/cmd/api/api.go b/pkg/cmd/api/api.go index 53c07f3f033..8e4b2edd45d 100644 --- a/pkg/cmd/api/api.go +++ b/pkg/cmd/api/api.go @@ -456,6 +456,8 @@ func apiRun(opts *ApiOptions) error { return tmpl.Flush() } +var jsonContentTypeRE = regexp.MustCompile(`[/+]json(;|$)`) + func processResponse(resp *http.Response, opts *ApiOptions, bodyWriter, headersWriter io.Writer, template *template.Template, isFirstPage, isLastPage bool) (endCursor string, err error) { if opts.ShowResponseHeaders { fmt.Fprintln(headersWriter, resp.Proto, resp.Status) @@ -469,7 +471,7 @@ func processResponse(resp *http.Response, opts *ApiOptions, bodyWriter, headersW var responseBody io.Reader = resp.Body defer resp.Body.Close() - isJSON, _ := regexp.MatchString(`[/+]json(;|$)`, resp.Header.Get("Content-Type")) + isJSON := jsonContentTypeRE.MatchString(resp.Header.Get("Content-Type")) var serverError string if isJSON && (opts.RequestPath == "graphql" || resp.StatusCode >= 400) { From d21544c085f1661bb9cbe197517f8c26f7a259fb Mon Sep 17 00:00:00 2001 From: ManManavadaria Date: Thu, 26 Feb 2026 16:23:37 +0530 Subject: [PATCH 08/19] fix(project/item-edit): preserve title/body when editing draft issue with partial flags --- pkg/cmd/project/item-edit/item_edit.go | 72 ++++++-- pkg/cmd/project/item-edit/item_edit_test.go | 193 ++++++++++++++++++-- pkg/cmd/project/shared/queries/queries.go | 5 + 3 files changed, 244 insertions(+), 26 deletions(-) diff --git a/pkg/cmd/project/item-edit/item_edit.go b/pkg/cmd/project/item-edit/item_edit.go index 43aff835ab5..6eb44caf4e3 100644 --- a/pkg/cmd/project/item-edit/item_edit.go +++ b/pkg/cmd/project/item-edit/item_edit.go @@ -16,9 +16,11 @@ import ( type editItemOpts struct { // updateDraftIssue - title string - body string - itemID string + title string + titleChanged bool + body string + bodyChanged bool + itemID string // updateItem fieldID string projectID string @@ -45,6 +47,12 @@ type EditProjectDraftIssue struct { } `graphql:"updateProjectV2DraftIssue(input:$input)"` } +type DraftIssueQuery struct { + DraftIssueNode struct { + DraftIssue queries.DraftIssue `graphql:"... on DraftIssue"` + } `graphql:"node(id: $id)"` +} + type UpdateProjectV2FieldValue struct { Update struct { Item queries.ProjectItem `graphql:"projectV2Item"` @@ -78,6 +86,8 @@ func NewCmdEditItem(f *cmdutil.Factory, runF func(config editItemConfig) error) `), RunE: func(cmd *cobra.Command, args []string) error { opts.numberChanged = cmd.Flags().Changed("number") + opts.titleChanged = cmd.Flags().Changed("title") + opts.bodyChanged = cmd.Flags().Changed("body") if err := cmdutil.MutuallyExclusive( "only one of `--text`, `--number`, `--date`, `--single-select-option-id` or `--iteration-id` may be used", opts.text != "", @@ -143,7 +153,7 @@ func runEditItem(config editItemConfig) error { } // update draft issue - if config.opts.title != "" || config.opts.body != "" { + if config.opts.titleChanged || config.opts.bodyChanged { return updateDraftIssue(config) } @@ -158,13 +168,41 @@ func runEditItem(config editItemConfig) error { return cmdutil.SilentError } -func buildEditDraftIssue(config editItemConfig) (*EditProjectDraftIssue, map[string]interface{}) { +func fetchDraftIssueByID(config editItemConfig, draftIssueID string) (*queries.DraftIssue, error) { + var query DraftIssueQuery + variables := map[string]interface{}{ + "id": githubv4.ID(draftIssueID), + } + + err := config.client.Query("DraftIssueByID", &query, variables) + if err != nil { + return nil, err + } + + return &query.DraftIssueNode.DraftIssue, nil +} + +func buildEditDraftIssue(config editItemConfig, currentDraftIssue *queries.DraftIssue) (*EditProjectDraftIssue, map[string]interface{}) { + input := githubv4.UpdateProjectV2DraftIssueInput{ + DraftIssueID: githubv4.ID(config.opts.itemID), + } + + if config.opts.titleChanged { + input.Title = githubv4.NewString(githubv4.String(config.opts.title)) + } else if currentDraftIssue != nil { + // Preserve existing if title is not provided + input.Title = githubv4.NewString(githubv4.String(currentDraftIssue.Title)) + } + + if config.opts.bodyChanged { + input.Body = githubv4.NewString(githubv4.String(config.opts.body)) + } else if currentDraftIssue != nil { + // Preserve existing if body is not provided + input.Body = githubv4.NewString(githubv4.String(currentDraftIssue.Body)) + } + return &EditProjectDraftIssue{}, map[string]interface{}{ - "input": githubv4.UpdateProjectV2DraftIssueInput{ - Body: githubv4.NewString(githubv4.String(config.opts.body)), - DraftIssueID: githubv4.ID(config.opts.itemID), - Title: githubv4.NewString(githubv4.String(config.opts.title)), - }, + "input": input, } } @@ -250,9 +288,19 @@ func updateDraftIssue(config editItemConfig) error { return cmdutil.FlagErrorf("ID must be the ID of the draft issue content which is prefixed with `DI_`") } - query, variables := buildEditDraftIssue(config) + // Fetch current draft issue to preserve fields that aren't being updated + var currentDraftIssue *queries.DraftIssue + var err error + if !config.opts.titleChanged || !config.opts.bodyChanged { + currentDraftIssue, err = fetchDraftIssueByID(config, config.opts.itemID) + if err != nil { + return err + } + } + + query, variables := buildEditDraftIssue(config, currentDraftIssue) - err := config.client.Mutate("EditDraftIssueItem", query, variables) + err = config.client.Mutate("EditDraftIssueItem", query, variables) if err != nil { return err } diff --git a/pkg/cmd/project/item-edit/item_edit_test.go b/pkg/cmd/project/item-edit/item_edit_test.go index 916bb5899e6..64852bad555 100644 --- a/pkg/cmd/project/item-edit/item_edit_test.go +++ b/pkg/cmd/project/item-edit/item_edit_test.go @@ -129,6 +129,15 @@ func TestNewCmdeditItem(t *testing.T) { }, wantsExporter: true, }, + { + name: "draft issue body only", + cli: "--id 123 --body foobar", + wants: editItemOpts{ + itemID: "123", + body: "foobar", + bodyChanged: true, + }, + }, } t.Setenv("GH_TOKEN", "auth-token") @@ -170,6 +179,9 @@ func TestNewCmdeditItem(t *testing.T) { assert.Equal(t, tt.wants.singleSelectOptionID, gotOpts.singleSelectOptionID) assert.Equal(t, tt.wants.iterationID, gotOpts.iterationID) assert.Equal(t, tt.wants.clear, gotOpts.clear) + assert.Equal(t, tt.wants.titleChanged, gotOpts.titleChanged) + assert.Equal(t, tt.wants.bodyChanged, gotOpts.bodyChanged) + assert.Equal(t, tt.wants.body, gotOpts.body) }) } } @@ -202,9 +214,11 @@ func TestRunItemEdit_Draft(t *testing.T) { config := editItemConfig{ io: ios, opts: editItemOpts{ - title: "a title", - body: "a new body", - itemID: "DI_item_id", + title: "a title", + titleChanged: true, + body: "a new body", + bodyChanged: true, + itemID: "DI_item_id", }, client: client, } @@ -217,6 +231,154 @@ func TestRunItemEdit_Draft(t *testing.T) { stdout.String()) } +func TestRunItemEdit_DraftTitleOnly(t *testing.T) { + defer gock.Off() + + gock.New("https://api.github.com"). + Post("/graphql"). + BodyString(`{"query":"query DraftIssueByID.*","variables":{"id":"DI_item_id"}}`). + Reply(200). + JSON(map[string]interface{}{ + "data": map[string]interface{}{ + "node": map[string]interface{}{ + "id": "DI_item_id", + "title": "existing title", + "body": "existing body", + }, + }, + }) + + gock.New("https://api.github.com"). + Post("/graphql"). + BodyString(`{"query":"mutation EditDraftIssueItem.*","variables":{"input":{"draftIssueId":"DI_item_id","title":"new title","body":"existing body"}}}`). + Reply(200). + JSON(map[string]interface{}{ + "data": map[string]interface{}{ + "updateProjectV2DraftIssue": map[string]interface{}{ + "draftIssue": map[string]interface{}{ + "title": "new title", + "body": "existing body", + }, + }, + }, + }) + + client := queries.NewTestClient() + + ios, _, stdout, _ := iostreams.Test() + ios.SetStdoutTTY(true) + + config := editItemConfig{ + io: ios, + opts: editItemOpts{ + title: "new title", + titleChanged: true, + bodyChanged: false, + itemID: "DI_item_id", + }, + client: client, + } + + err := runEditItem(config) + assert.NoError(t, err) + assert.Equal( + t, + "Edited draft issue \"new title\"\n", + stdout.String()) +} + +func TestRunItemEdit_DraftBodyOnly(t *testing.T) { + defer gock.Off() + + gock.New("https://api.github.com"). + Post("/graphql"). + BodyString(`{"query":"query DraftIssueByID.*","variables":{"id":"DI_item_id"}}`). + Reply(200). + JSON(map[string]interface{}{ + "data": map[string]interface{}{ + "node": map[string]interface{}{ + "id": "DI_item_id", + "title": "existing title", + "body": "existing body", + }, + }, + }) + + gock.New("https://api.github.com"). + Post("/graphql"). + BodyString(`{"query":"mutation EditDraftIssueItem.*","variables":{"input":{"draftIssueId":"DI_item_id","title":"existing title","body":"new body"}}}`). + Reply(200). + JSON(map[string]interface{}{ + "data": map[string]interface{}{ + "updateProjectV2DraftIssue": map[string]interface{}{ + "draftIssue": map[string]interface{}{ + "title": "existing title", + "body": "new body", + }, + }, + }, + }) + + client := queries.NewTestClient() + + ios, _, stdout, _ := iostreams.Test() + ios.SetStdoutTTY(true) + + config := editItemConfig{ + io: ios, + opts: editItemOpts{ + titleChanged: false, + body: "new body", + bodyChanged: true, + itemID: "DI_item_id", + }, + client: client, + } + + err := runEditItem(config) + assert.NoError(t, err) + assert.Equal( + t, + "Edited draft issue \"existing title\"\n", + stdout.String()) +} + +func TestRunItemEdit_DraftFetchError(t *testing.T) { + defer gock.Off() + + gock.New("https://api.github.com"). + Post("/graphql"). + BodyString(`{"query":"query DraftIssueByID.*","variables":{"id":"DI_item_id"}}`). + Reply(200). + JSON(map[string]interface{}{ + "errors": []map[string]interface{}{ + { + "type": "NOT_FOUND", + "message": "Could not resolve to a node with the global id of 'DI_item_id' (node)", + }, + }, + }) + + client := queries.NewTestClient() + + ios, _, _, _ := iostreams.Test() + + config := editItemConfig{ + io: ios, + opts: editItemOpts{ + title: "new title", + titleChanged: true, + bodyChanged: false, + itemID: "DI_item_id", + }, + client: client, + } + + err := runEditItem(config) + assert.Error(t, err) + assert.Contains(t, err.Error(), "Could not resolve to a node") +} + func TestRunItemEdit_Text(t *testing.T) { defer gock.Off() // gock.Observe(gock.DumpRequest) @@ -232,10 +394,9 @@ func TestRunItemEdit_Text(t *testing.T) { "projectV2Item": map[string]interface{}{ "ID": "item_id", "content": map[string]interface{}{ - "__typename": "Issue", - "body": "body", - "title": "title", - "number": 1, + "body": "body", + "title": "title", + "number": 1, "repository": map[string]interface{}{ "nameWithOwner": "my-repo", }, @@ -544,9 +705,11 @@ func TestRunItemEdit_InvalidID(t *testing.T) { client := queries.NewTestClient() config := editItemConfig{ opts: editItemOpts{ - title: "a title", - body: "a new body", - itemID: "item_id", + title: "a title", + titleChanged: true, + body: "a new body", + bodyChanged: true, + itemID: "item_id", }, client: client, } @@ -630,10 +793,12 @@ func TestRunItemEdit_JSON(t *testing.T) { config := editItemConfig{ io: ios, opts: editItemOpts{ - title: "a title", - body: "a new body", - itemID: "DI_item_id", - exporter: cmdutil.NewJSONExporter(), + title: "a title", + titleChanged: true, + body: "a new body", + bodyChanged: true, + itemID: "DI_item_id", + exporter: cmdutil.NewJSONExporter(), }, client: client, } diff --git a/pkg/cmd/project/shared/queries/queries.go b/pkg/cmd/project/shared/queries/queries.go index d56d611079c..9a3bd490902 100644 --- a/pkg/cmd/project/shared/queries/queries.go +++ b/pkg/cmd/project/shared/queries/queries.go @@ -103,6 +103,11 @@ func (c *Client) Mutate(operationName string, query interface{}, variables map[s return handleError(err) } +func (c *Client) Query(operationName string, query interface{}, variables map[string]interface{}) error { + err := c.apiClient.Query(operationName, query, variables) + return handleError(err) +} + // PageInfo is a PageInfo GraphQL object https://docs.github.com/en/graphql/reference/objects#pageinfo. type PageInfo struct { EndCursor githubv4.String From 0d9e6af3da7e880fe045fd4cb3575cbd98f3964f Mon Sep 17 00:00:00 2001 From: Max Beizer Date: Sat, 28 Feb 2026 09:20:13 -0600 Subject: [PATCH 09/19] Add --json support to `gh agent-task list` Add --json, --jq, and --template flags to `gh agent-task list`, consistent with the pattern used by `gh pr list --json`, `gh issue list --json`, etc. This implements the ExportData interface on capi.Session and defines SessionFields for the available JSON fields: id, name, status, repository, createdAt, updatedAt, pullRequestNumber, pullRequestUrl. Closes https://github.com/cli/cli/issues/12805 (partial) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- pkg/cmd/agent-task/capi/sessions.go | 52 ++++++++++++++++++++++++++++ pkg/cmd/agent-task/list/list.go | 7 ++++ pkg/cmd/agent-task/list/list_test.go | 34 ++++++++++++++++++ 3 files changed, 93 insertions(+) diff --git a/pkg/cmd/agent-task/capi/sessions.go b/pkg/cmd/agent-task/capi/sessions.go index 8e3969d69f8..c1f7fd84675 100644 --- a/pkg/cmd/agent-task/capi/sessions.go +++ b/pkg/cmd/agent-task/capi/sessions.go @@ -102,6 +102,58 @@ type SessionError struct { Message string } +// SessionFields defines the available fields for JSON export of a Session. +var SessionFields = []string{ + "id", + "name", + "status", + "repository", + "createdAt", + "updatedAt", + "pullRequestNumber", + "pullRequestUrl", +} + +// ExportData implements the exportable interface for JSON output. +func (s *Session) ExportData(fields []string) map[string]interface{} { + data := make(map[string]interface{}, len(fields)) + for _, f := range fields { + switch f { + case "id": + data[f] = s.ID + case "name": + data[f] = s.Name + case "status": + data[f] = s.State + case "repository": + if s.PullRequest != nil && s.PullRequest.Repository != nil { + data[f] = s.PullRequest.Repository.NameWithOwner + } else { + data[f] = nil + } + case "createdAt": + data[f] = s.CreatedAt + case "updatedAt": + data[f] = s.LastUpdatedAt + case "pullRequestNumber": + if s.PullRequest != nil { + data[f] = s.PullRequest.Number + } else { + data[f] = nil + } + case "pullRequestUrl": + if s.PullRequest != nil { + data[f] = s.PullRequest.URL + } else { + data[f] = nil + } + default: + data[f] = nil + } + } + return data +} + type resource struct { ID string `json:"id"` UserID uint64 `json:"user_id"` diff --git a/pkg/cmd/agent-task/list/list.go b/pkg/cmd/agent-task/list/list.go index 211dc07ea24..913a22295a3 100644 --- a/pkg/cmd/agent-task/list/list.go +++ b/pkg/cmd/agent-task/list/list.go @@ -25,6 +25,7 @@ type ListOptions struct { CapiClient func() (capi.CapiClient, error) Web bool Browser browser.Browser + Exporter cmdutil.Exporter } // NewCmdList creates the list command @@ -54,6 +55,8 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman cmd.Flags().IntVarP(&opts.Limit, "limit", "L", defaultLimit, "Maximum number of agent tasks to fetch") cmd.Flags().BoolVarP(&opts.Web, "web", "w", false, "Open agent tasks in the browser") + cmdutil.AddJSONFlags(cmd, &opts.Exporter, capi.SessionFields) + return cmd } @@ -91,6 +94,10 @@ func listRun(opts *ListOptions) error { return cmdutil.NewNoResultsError("no agent tasks found") } + if opts.Exporter != nil { + return opts.Exporter.Write(opts.IO, sessions) + } + if err := opts.IO.StartPager(); err == nil { defer opts.IO.StopPager() } else { diff --git a/pkg/cmd/agent-task/list/list_test.go b/pkg/cmd/agent-task/list/list_test.go index 74765028397..3e146c93478 100644 --- a/pkg/cmd/agent-task/list/list_test.go +++ b/pkg/cmd/agent-task/list/list_test.go @@ -99,6 +99,7 @@ func Test_listRun(t *testing.T) { capiStubs func(*testing.T, *capi.CapiClientMock) limit int web bool + jsonFields []string wantOut string wantErr error wantStderr string @@ -286,6 +287,33 @@ func Test_listRun(t *testing.T) { wantStderr: "Opening https://github.com/copilot/agents in your browser.\n", wantBrowserURL: "https://github.com/copilot/agents", }, + { + name: "json output", + tty: false, + capiStubs: func(t *testing.T, m *capi.CapiClientMock) { + m.ListLatestSessionsForViewerFunc = func(ctx context.Context, limit int) ([]*capi.Session, error) { + return []*capi.Session{ + { + ID: "abc-123", + Name: "s1", + State: "completed", + CreatedAt: sampleDate, + LastUpdatedAt: sampleDate, + ResourceType: "pull", + PullRequest: &api.PullRequest{ + Number: 101, + URL: "https://github.com/OWNER/REPO/pull/101", + Repository: &api.PRRepository{ + NameWithOwner: "OWNER/REPO", + }, + }, + }, + }, nil + } + }, + jsonFields: []string{"id", "name", "status", "repository", "pullRequestNumber", "pullRequestUrl"}, + wantOut: "[{\"id\":\"abc-123\",\"name\":\"s1\",\"pullRequestNumber\":101,\"pullRequestUrl\":\"https://github.com/OWNER/REPO/pull/101\",\"repository\":\"OWNER/REPO\",\"status\":\"completed\"}]\n", + }, } for _, tt := range tests { @@ -316,6 +344,12 @@ func Test_listRun(t *testing.T) { }, } + if tt.jsonFields != nil { + exporter := cmdutil.NewJSONExporter() + exporter.SetFields(tt.jsonFields) + opts.Exporter = exporter + } + err := listRun(opts) if tt.wantErr != nil { assert.Error(t, err) From 7241b42ecff71488673a12dda99a3d1dcf0f23f3 Mon Sep 17 00:00:00 2001 From: Max Beizer Date: Sat, 28 Feb 2026 09:22:28 -0600 Subject: [PATCH 10/19] Add --json support to `gh agent-task view` Add --json, --jq, and --template flags to `gh agent-task view`, consistent with the pattern used by `gh pr view --json`, `gh issue view --json`, etc. This reuses the same ExportData interface and SessionFields defined for list, applying them to the single-session view output. Closes https://github.com/cli/cli/issues/12805 (partial) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- pkg/cmd/agent-task/capi/sessions.go | 52 ++++++++++++++++++++++++++++ pkg/cmd/agent-task/view/view.go | 7 ++++ pkg/cmd/agent-task/view/view_test.go | 42 ++++++++++++++++++++++ 3 files changed, 101 insertions(+) diff --git a/pkg/cmd/agent-task/capi/sessions.go b/pkg/cmd/agent-task/capi/sessions.go index 8e3969d69f8..c1f7fd84675 100644 --- a/pkg/cmd/agent-task/capi/sessions.go +++ b/pkg/cmd/agent-task/capi/sessions.go @@ -102,6 +102,58 @@ type SessionError struct { Message string } +// SessionFields defines the available fields for JSON export of a Session. +var SessionFields = []string{ + "id", + "name", + "status", + "repository", + "createdAt", + "updatedAt", + "pullRequestNumber", + "pullRequestUrl", +} + +// ExportData implements the exportable interface for JSON output. +func (s *Session) ExportData(fields []string) map[string]interface{} { + data := make(map[string]interface{}, len(fields)) + for _, f := range fields { + switch f { + case "id": + data[f] = s.ID + case "name": + data[f] = s.Name + case "status": + data[f] = s.State + case "repository": + if s.PullRequest != nil && s.PullRequest.Repository != nil { + data[f] = s.PullRequest.Repository.NameWithOwner + } else { + data[f] = nil + } + case "createdAt": + data[f] = s.CreatedAt + case "updatedAt": + data[f] = s.LastUpdatedAt + case "pullRequestNumber": + if s.PullRequest != nil { + data[f] = s.PullRequest.Number + } else { + data[f] = nil + } + case "pullRequestUrl": + if s.PullRequest != nil { + data[f] = s.PullRequest.URL + } else { + data[f] = nil + } + default: + data[f] = nil + } + } + return data +} + type resource struct { ID string `json:"id"` UserID uint64 `json:"user_id"` diff --git a/pkg/cmd/agent-task/view/view.go b/pkg/cmd/agent-task/view/view.go index 38c1e0e1295..137efdb6409 100644 --- a/pkg/cmd/agent-task/view/view.go +++ b/pkg/cmd/agent-task/view/view.go @@ -37,6 +37,7 @@ type ViewOptions struct { Finder prShared.PRFinder Prompter prompter.Prompter Browser browser.Browser + Exporter cmdutil.Exporter LogRenderer func() shared.LogRenderer Sleep func(d time.Duration) @@ -125,6 +126,8 @@ func NewCmdView(f *cmdutil.Factory, runF func(*ViewOptions) error) *cobra.Comman cmd.Flags().BoolVar(&opts.Log, "log", false, "Show agent session logs") cmd.Flags().BoolVar(&opts.Follow, "follow", false, "Follow agent session logs") + cmdutil.AddJSONFlags(cmd, &opts.Exporter, capi.SessionFields) + return cmd } @@ -289,6 +292,10 @@ func viewRun(opts *ViewOptions) error { return printLogs(opts, capiClient, session.ID) } + if opts.Exporter != nil { + return opts.Exporter.Write(opts.IO, session) + } + printSession(opts, session) return nil } diff --git a/pkg/cmd/agent-task/view/view_test.go b/pkg/cmd/agent-task/view/view_test.go index 68cc377e303..335569d3a87 100644 --- a/pkg/cmd/agent-task/view/view_test.go +++ b/pkg/cmd/agent-task/view/view_test.go @@ -168,6 +168,7 @@ func Test_viewRun(t *testing.T) { promptStubs func(*testing.T, *prompter.MockPrompter) capiStubs func(*testing.T, *capi.CapiClientMock) logRendererStubs func(*testing.T, *shared.LogRendererMock) + jsonFields []string wantOut string wantErr error wantStderr string @@ -1209,6 +1210,41 @@ func Test_viewRun(t *testing.T) { (rendered:) `), }, + { + name: "json output (tty)", + tty: true, + opts: ViewOptions{ + SelectorArg: "some-session-id", + SessionID: "some-session-id", + }, + capiStubs: func(t *testing.T, m *capi.CapiClientMock) { + m.GetSessionFunc = func(_ context.Context, id string) (*capi.Session, error) { + return &capi.Session{ + ID: "some-session-id", + Name: "Fix login bug", + State: "completed", + CreatedAt: sampleDate, + LastUpdatedAt: sampleDate, + CompletedAt: sampleCompletedAt, + ResourceType: "pull", + PullRequest: &api.PullRequest{ + Number: 42, + URL: "https://github.com/OWNER/REPO/pull/42", + Title: "Fix login bug", + State: "OPEN", + Repository: &api.PRRepository{ + NameWithOwner: "OWNER/REPO", + }, + }, + User: &api.GitHubUser{ + Login: "testuser", + }, + }, nil + } + }, + wantOut: "{\"id\":\"some-session-id\",\"name\":\"Fix login bug\",\"pullRequestNumber\":42,\"pullRequestUrl\":\"https://github.com/OWNER/REPO/pull/42\",\"repository\":\"OWNER/REPO\",\"status\":\"completed\"}\n", + jsonFields: []string{"id", "name", "status", "repository", "pullRequestNumber", "pullRequestUrl"}, + }, } for _, tt := range tests { @@ -1244,6 +1280,12 @@ func Test_viewRun(t *testing.T) { return logRenderer } + if tt.jsonFields != nil { + exporter := cmdutil.NewJSONExporter() + exporter.SetFields(tt.jsonFields) + opts.Exporter = exporter + } + err := viewRun(&opts) if tt.wantErr != nil { assert.Error(t, err) From 250d5a850ade0508f1cfdbdef63fbdde67ae29f0 Mon Sep 17 00:00:00 2001 From: Max Beizer Date: Sun, 1 Mar 2026 08:57:53 -0600 Subject: [PATCH 11/19] Fix gofmt alignment in view_test.go Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- pkg/cmd/agent-task/view/view_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmd/agent-task/view/view_test.go b/pkg/cmd/agent-task/view/view_test.go index 335569d3a87..f2abe4d5282 100644 --- a/pkg/cmd/agent-task/view/view_test.go +++ b/pkg/cmd/agent-task/view/view_test.go @@ -1242,7 +1242,7 @@ func Test_viewRun(t *testing.T) { }, nil } }, - wantOut: "{\"id\":\"some-session-id\",\"name\":\"Fix login bug\",\"pullRequestNumber\":42,\"pullRequestUrl\":\"https://github.com/OWNER/REPO/pull/42\",\"repository\":\"OWNER/REPO\",\"status\":\"completed\"}\n", + wantOut: "{\"id\":\"some-session-id\",\"name\":\"Fix login bug\",\"pullRequestNumber\":42,\"pullRequestUrl\":\"https://github.com/OWNER/REPO/pull/42\",\"repository\":\"OWNER/REPO\",\"status\":\"completed\"}\n", jsonFields: []string{"id", "name", "status", "repository", "pullRequestNumber", "pullRequestUrl"}, }, } From c5c107aa8b2f4d33f53dcc3071401e795924bbc4 Mon Sep 17 00:00:00 2001 From: Max Beizer Date: Sun, 1 Mar 2026 09:08:18 -0600 Subject: [PATCH 12/19] Polish --json support for agent-task list - Fix empty results returning error instead of [] when --json is used - Rename 'status' field to 'state' for consistency with struct and UI - Add missing JSON fields: completedAt, user, pullRequestTitle, pullRequestState - Add test for empty results with --json - Add test for nil PullRequest with --json Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- pkg/cmd/agent-task/capi/sessions.go | 32 +++++++++++++++++++++-- pkg/cmd/agent-task/list/list.go | 2 +- pkg/cmd/agent-task/list/list_test.go | 39 ++++++++++++++++++++++++++-- 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/pkg/cmd/agent-task/capi/sessions.go b/pkg/cmd/agent-task/capi/sessions.go index c1f7fd84675..3a74ef3d9e4 100644 --- a/pkg/cmd/agent-task/capi/sessions.go +++ b/pkg/cmd/agent-task/capi/sessions.go @@ -106,12 +106,16 @@ type SessionError struct { var SessionFields = []string{ "id", "name", - "status", + "state", "repository", + "user", "createdAt", "updatedAt", + "completedAt", "pullRequestNumber", "pullRequestUrl", + "pullRequestTitle", + "pullRequestState", } // ExportData implements the exportable interface for JSON output. @@ -123,7 +127,7 @@ func (s *Session) ExportData(fields []string) map[string]interface{} { data[f] = s.ID case "name": data[f] = s.Name - case "status": + case "state": data[f] = s.State case "repository": if s.PullRequest != nil && s.PullRequest.Repository != nil { @@ -131,10 +135,22 @@ func (s *Session) ExportData(fields []string) map[string]interface{} { } else { data[f] = nil } + case "user": + if s.User != nil { + data[f] = s.User.Login + } else { + data[f] = nil + } case "createdAt": data[f] = s.CreatedAt case "updatedAt": data[f] = s.LastUpdatedAt + case "completedAt": + if s.CompletedAt.IsZero() { + data[f] = nil + } else { + data[f] = s.CompletedAt + } case "pullRequestNumber": if s.PullRequest != nil { data[f] = s.PullRequest.Number @@ -147,6 +163,18 @@ func (s *Session) ExportData(fields []string) map[string]interface{} { } else { data[f] = nil } + case "pullRequestTitle": + if s.PullRequest != nil { + data[f] = s.PullRequest.Title + } else { + data[f] = nil + } + case "pullRequestState": + if s.PullRequest != nil { + data[f] = s.PullRequest.State + } else { + data[f] = nil + } default: data[f] = nil } diff --git a/pkg/cmd/agent-task/list/list.go b/pkg/cmd/agent-task/list/list.go index 913a22295a3..559389b5c79 100644 --- a/pkg/cmd/agent-task/list/list.go +++ b/pkg/cmd/agent-task/list/list.go @@ -90,7 +90,7 @@ func listRun(opts *ListOptions) error { opts.IO.StopProgressIndicator() - if len(sessions) == 0 { + if len(sessions) == 0 && opts.Exporter == nil { return cmdutil.NewNoResultsError("no agent tasks found") } diff --git a/pkg/cmd/agent-task/list/list_test.go b/pkg/cmd/agent-task/list/list_test.go index 3e146c93478..d46240b5933 100644 --- a/pkg/cmd/agent-task/list/list_test.go +++ b/pkg/cmd/agent-task/list/list_test.go @@ -299,9 +299,13 @@ func Test_listRun(t *testing.T) { State: "completed", CreatedAt: sampleDate, LastUpdatedAt: sampleDate, + CompletedAt: sampleDate, ResourceType: "pull", + User: &api.GitHubUser{Login: "monalisa"}, PullRequest: &api.PullRequest{ Number: 101, + Title: "Fix login bug", + State: "MERGED", URL: "https://github.com/OWNER/REPO/pull/101", Repository: &api.PRRepository{ NameWithOwner: "OWNER/REPO", @@ -311,8 +315,39 @@ func Test_listRun(t *testing.T) { }, nil } }, - jsonFields: []string{"id", "name", "status", "repository", "pullRequestNumber", "pullRequestUrl"}, - wantOut: "[{\"id\":\"abc-123\",\"name\":\"s1\",\"pullRequestNumber\":101,\"pullRequestUrl\":\"https://github.com/OWNER/REPO/pull/101\",\"repository\":\"OWNER/REPO\",\"status\":\"completed\"}]\n", + jsonFields: []string{"id", "name", "state", "repository", "user", "pullRequestNumber", "pullRequestUrl", "pullRequestTitle", "pullRequestState"}, + wantOut: "[{\"id\":\"abc-123\",\"name\":\"s1\",\"pullRequestNumber\":101,\"pullRequestState\":\"MERGED\",\"pullRequestTitle\":\"Fix login bug\",\"pullRequestUrl\":\"https://github.com/OWNER/REPO/pull/101\",\"repository\":\"OWNER/REPO\",\"state\":\"completed\",\"user\":\"monalisa\"}]\n", + }, + { + name: "json output with no sessions returns empty array", + tty: false, + capiStubs: func(t *testing.T, m *capi.CapiClientMock) { + m.ListLatestSessionsForViewerFunc = func(ctx context.Context, limit int) ([]*capi.Session, error) { + return nil, nil + } + }, + jsonFields: []string{"id", "name", "state"}, + wantOut: "[]\n", + }, + { + name: "json output with nil pull request", + tty: false, + capiStubs: func(t *testing.T, m *capi.CapiClientMock) { + m.ListLatestSessionsForViewerFunc = func(ctx context.Context, limit int) ([]*capi.Session, error) { + return []*capi.Session{ + { + ID: "abc-456", + Name: "s2", + State: "in_progress", + CreatedAt: sampleDate, + LastUpdatedAt: sampleDate, + ResourceType: "pull", + }, + }, nil + } + }, + jsonFields: []string{"id", "name", "state", "repository", "user", "pullRequestNumber", "pullRequestUrl", "pullRequestTitle", "pullRequestState"}, + wantOut: "[{\"id\":\"abc-456\",\"name\":\"s2\",\"pullRequestNumber\":null,\"pullRequestState\":null,\"pullRequestTitle\":null,\"pullRequestUrl\":null,\"repository\":null,\"state\":\"in_progress\",\"user\":null}]\n", }, } From ceb8cf2561e7caf1ea272d62e75a1bf7c531e157 Mon Sep 17 00:00:00 2001 From: Max Beizer Date: Sun, 1 Mar 2026 09:09:08 -0600 Subject: [PATCH 13/19] Polish --json support for agent-task view - Rename 'status' field to 'state' for consistency with struct and UI - Add missing JSON fields: completedAt, user, pullRequestTitle, pullRequestState - Add test for nil PullRequest with --json - Expand existing JSON test to cover new fields Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- pkg/cmd/agent-task/capi/sessions.go | 32 ++++++++++++++++++++++++++-- pkg/cmd/agent-task/view/view_test.go | 28 +++++++++++++++++++++--- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/pkg/cmd/agent-task/capi/sessions.go b/pkg/cmd/agent-task/capi/sessions.go index c1f7fd84675..3a74ef3d9e4 100644 --- a/pkg/cmd/agent-task/capi/sessions.go +++ b/pkg/cmd/agent-task/capi/sessions.go @@ -106,12 +106,16 @@ type SessionError struct { var SessionFields = []string{ "id", "name", - "status", + "state", "repository", + "user", "createdAt", "updatedAt", + "completedAt", "pullRequestNumber", "pullRequestUrl", + "pullRequestTitle", + "pullRequestState", } // ExportData implements the exportable interface for JSON output. @@ -123,7 +127,7 @@ func (s *Session) ExportData(fields []string) map[string]interface{} { data[f] = s.ID case "name": data[f] = s.Name - case "status": + case "state": data[f] = s.State case "repository": if s.PullRequest != nil && s.PullRequest.Repository != nil { @@ -131,10 +135,22 @@ func (s *Session) ExportData(fields []string) map[string]interface{} { } else { data[f] = nil } + case "user": + if s.User != nil { + data[f] = s.User.Login + } else { + data[f] = nil + } case "createdAt": data[f] = s.CreatedAt case "updatedAt": data[f] = s.LastUpdatedAt + case "completedAt": + if s.CompletedAt.IsZero() { + data[f] = nil + } else { + data[f] = s.CompletedAt + } case "pullRequestNumber": if s.PullRequest != nil { data[f] = s.PullRequest.Number @@ -147,6 +163,18 @@ func (s *Session) ExportData(fields []string) map[string]interface{} { } else { data[f] = nil } + case "pullRequestTitle": + if s.PullRequest != nil { + data[f] = s.PullRequest.Title + } else { + data[f] = nil + } + case "pullRequestState": + if s.PullRequest != nil { + data[f] = s.PullRequest.State + } else { + data[f] = nil + } default: data[f] = nil } diff --git a/pkg/cmd/agent-task/view/view_test.go b/pkg/cmd/agent-task/view/view_test.go index f2abe4d5282..34036cfa518 100644 --- a/pkg/cmd/agent-task/view/view_test.go +++ b/pkg/cmd/agent-task/view/view_test.go @@ -1231,7 +1231,7 @@ func Test_viewRun(t *testing.T) { Number: 42, URL: "https://github.com/OWNER/REPO/pull/42", Title: "Fix login bug", - State: "OPEN", + State: "MERGED", Repository: &api.PRRepository{ NameWithOwner: "OWNER/REPO", }, @@ -1242,8 +1242,30 @@ func Test_viewRun(t *testing.T) { }, nil } }, - wantOut: "{\"id\":\"some-session-id\",\"name\":\"Fix login bug\",\"pullRequestNumber\":42,\"pullRequestUrl\":\"https://github.com/OWNER/REPO/pull/42\",\"repository\":\"OWNER/REPO\",\"status\":\"completed\"}\n", - jsonFields: []string{"id", "name", "status", "repository", "pullRequestNumber", "pullRequestUrl"}, + wantOut: "{\"id\":\"some-session-id\",\"name\":\"Fix login bug\",\"pullRequestNumber\":42,\"pullRequestState\":\"MERGED\",\"pullRequestTitle\":\"Fix login bug\",\"pullRequestUrl\":\"https://github.com/OWNER/REPO/pull/42\",\"repository\":\"OWNER/REPO\",\"state\":\"completed\",\"user\":\"testuser\"}\n", + jsonFields: []string{"id", "name", "state", "repository", "user", "pullRequestNumber", "pullRequestUrl", "pullRequestTitle", "pullRequestState"}, + }, + { + name: "json output with nil pull request", + tty: false, + opts: ViewOptions{ + SelectorArg: "some-session-id", + SessionID: "some-session-id", + }, + capiStubs: func(t *testing.T, m *capi.CapiClientMock) { + m.GetSessionFunc = func(_ context.Context, id string) (*capi.Session, error) { + return &capi.Session{ + ID: "some-session-id", + Name: "New task", + State: "in_progress", + CreatedAt: sampleDate, + LastUpdatedAt: sampleDate, + ResourceType: "pull", + }, nil + } + }, + wantOut: "{\"id\":\"some-session-id\",\"name\":\"New task\",\"pullRequestNumber\":null,\"pullRequestUrl\":null,\"repository\":null,\"state\":\"in_progress\",\"user\":null}\n", + jsonFields: []string{"id", "name", "state", "repository", "user", "pullRequestNumber", "pullRequestUrl"}, }, } From 0d05a8acca4b4e5d45ee428cb2be4690ffbc6ab7 Mon Sep 17 00:00:00 2001 From: Max Beizer Date: Sun, 1 Mar 2026 10:18:21 -0600 Subject: [PATCH 14/19] Address Copilot review feedback - Prioritize --json output over --log/--follow so JSON is not silently ignored - Emit null for zero createdAt/updatedAt values, consistent with completedAt Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- pkg/cmd/agent-task/capi/sessions.go | 12 ++++++++++-- pkg/cmd/agent-task/view/view.go | 8 ++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/pkg/cmd/agent-task/capi/sessions.go b/pkg/cmd/agent-task/capi/sessions.go index 3a74ef3d9e4..4b457d799bb 100644 --- a/pkg/cmd/agent-task/capi/sessions.go +++ b/pkg/cmd/agent-task/capi/sessions.go @@ -142,9 +142,17 @@ func (s *Session) ExportData(fields []string) map[string]interface{} { data[f] = nil } case "createdAt": - data[f] = s.CreatedAt + if s.CreatedAt.IsZero() { + data[f] = nil + } else { + data[f] = s.CreatedAt + } case "updatedAt": - data[f] = s.LastUpdatedAt + if s.LastUpdatedAt.IsZero() { + data[f] = nil + } else { + data[f] = s.LastUpdatedAt + } case "completedAt": if s.CompletedAt.IsZero() { data[f] = nil diff --git a/pkg/cmd/agent-task/view/view.go b/pkg/cmd/agent-task/view/view.go index 137efdb6409..854faa73def 100644 --- a/pkg/cmd/agent-task/view/view.go +++ b/pkg/cmd/agent-task/view/view.go @@ -288,14 +288,14 @@ func viewRun(opts *ViewOptions) error { opts.IO.StopProgressIndicator() } - if opts.Log { - return printLogs(opts, capiClient, session.ID) - } - if opts.Exporter != nil { return opts.Exporter.Write(opts.IO, session) } + if opts.Log { + return printLogs(opts, capiClient, session.ID) + } + printSession(opts, session) return nil } From d908af348499a5eca6da1806881e04b02da2940c Mon Sep 17 00:00:00 2001 From: Max Beizer Date: Sun, 1 Mar 2026 10:18:37 -0600 Subject: [PATCH 15/19] Emit null for zero createdAt/updatedAt values Consistent with completedAt handling. Addresses Copilot review feedback from companion PR #12807. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- pkg/cmd/agent-task/capi/sessions.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pkg/cmd/agent-task/capi/sessions.go b/pkg/cmd/agent-task/capi/sessions.go index 3a74ef3d9e4..4b457d799bb 100644 --- a/pkg/cmd/agent-task/capi/sessions.go +++ b/pkg/cmd/agent-task/capi/sessions.go @@ -142,9 +142,17 @@ func (s *Session) ExportData(fields []string) map[string]interface{} { data[f] = nil } case "createdAt": - data[f] = s.CreatedAt + if s.CreatedAt.IsZero() { + data[f] = nil + } else { + data[f] = s.CreatedAt + } case "updatedAt": - data[f] = s.LastUpdatedAt + if s.LastUpdatedAt.IsZero() { + data[f] = nil + } else { + data[f] = s.LastUpdatedAt + } case "completedAt": if s.CompletedAt.IsZero() { data[f] = nil From ab399f09e12eab7a8717fa4ddb2e32dfc09c6e65 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 16:02:34 +0000 Subject: [PATCH 16/19] chore(deps): bump actions/attest-build-provenance from 3.2.0 to 4.1.0 Bumps [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance) from 3.2.0 to 4.1.0. - [Release notes](https://github.com/actions/attest-build-provenance/releases) - [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md) - [Commits](https://github.com/actions/attest-build-provenance/compare/96278af6caaf10aea03fd8d33a09a777ca52d62f...a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32) --- updated-dependencies: - dependency-name: actions/attest-build-provenance dependency-version: 4.1.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/deployment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 0470a6644c1..03814e9cf7d 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -334,7 +334,7 @@ jobs: rpmsign --addsign dist/*.rpm - name: Attest release artifacts if: inputs.environment == 'production' - uses: actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # v3.2.0 + uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 with: subject-path: "dist/gh_*" create-storage-record: false # (default: true) From 8c72314f6c41756b9740c9cc730f224a79254027 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 16:02:39 +0000 Subject: [PATCH 17/19] chore(deps): bump google.golang.org/grpc from 1.78.0 to 1.79.1 Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.78.0 to 1.79.1. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.78.0...v1.79.1) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-version: 1.79.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 9 +++++---- go.sum | 26 ++++++++++++++------------ 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index 68cf9d6ff39..27506ee42ee 100644 --- a/go.mod +++ b/go.mod @@ -54,7 +54,7 @@ require ( golang.org/x/sync v0.19.0 golang.org/x/term v0.40.0 golang.org/x/text v0.34.0 - google.golang.org/grpc v1.78.0 + google.golang.org/grpc v1.79.1 google.golang.org/protobuf v1.36.11 gopkg.in/h2non/gock.v1 v1.1.2 gopkg.in/yaml.v3 v3.0.1 @@ -72,6 +72,7 @@ require ( github.com/aymerick/douceur v0.2.0 // indirect github.com/blang/semver v3.5.1+incompatible // indirect github.com/catppuccin/go v0.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 // indirect github.com/charmbracelet/bubbletea v1.3.10 // indirect github.com/charmbracelet/colorprofile v0.3.1 // indirect @@ -173,9 +174,9 @@ require ( github.com/yuin/goldmark-emoji v1.0.6 // indirect go.mongodb.org/mongo-driver v1.17.6 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/otel v1.38.0 // indirect - go.opentelemetry.io/otel/metric v1.38.0 // indirect - go.opentelemetry.io/otel/trace v1.38.0 // indirect + go.opentelemetry.io/otel v1.39.0 // indirect + go.opentelemetry.io/otel/metric v1.39.0 // indirect + go.opentelemetry.io/otel/trace v1.39.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/mod v0.32.0 // indirect golang.org/x/net v0.49.0 // indirect diff --git a/go.sum b/go.sum index e9460e53b28..ba2d9a2af2a 100644 --- a/go.sum +++ b/go.sum @@ -102,6 +102,8 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 h1:JFgG/xnwFfbezlUnFMJy0nusZvytYysV4SCS2cYbvws= github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7/go.mod h1:ISC1gtLcVilLOf23wvTfoQuYbW2q0JevFxPfUzZ9Ybw= github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= @@ -548,16 +550,16 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= -go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= go.step.sm/crypto v0.74.0 h1:/APBEv45yYR4qQFg47HA8w1nesIGcxh44pGyQNw6JRA= go.step.sm/crypto v0.74.0/go.mod h1:UoXqCAJjjRgzPte0Llaqen7O9P7XjPmgjgTHQGkKCDk= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -638,8 +640,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1: google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto= google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU= google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= -google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= -google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= +google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= +google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 0f53ed545c1a159a65105b1db43b9850c4f61a36 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 16:02:45 +0000 Subject: [PATCH 18/19] chore(deps): bump github.com/gabriel-vasile/mimetype Bumps [github.com/gabriel-vasile/mimetype](https://github.com/gabriel-vasile/mimetype) from 1.4.11 to 1.4.13. - [Release notes](https://github.com/gabriel-vasile/mimetype/releases) - [Commits](https://github.com/gabriel-vasile/mimetype/compare/v1.4.11...v1.4.13) --- updated-dependencies: - dependency-name: github.com/gabriel-vasile/mimetype dependency-version: 1.4.13 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 68cf9d6ff39..a6a9daca938 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/creack/pty v1.1.24 github.com/digitorus/timestamp v0.0.0-20250524132541-c45532741eea github.com/distribution/reference v0.6.0 - github.com/gabriel-vasile/mimetype v1.4.11 + github.com/gabriel-vasile/mimetype v1.4.13 github.com/gdamore/tcell/v2 v2.13.8 github.com/golang/snappy v1.0.0 github.com/google/go-cmp v0.7.0 diff --git a/go.sum b/go.sum index e9460e53b28..10f643f9313 100644 --- a/go.sum +++ b/go.sum @@ -193,8 +193,8 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik= -github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= +github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw= github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo= github.com/gdamore/tcell/v2 v2.13.8 h1:Mys/Kl5wfC/GcC5Cx4C2BIQH9dbnhnkPgS9/wF3RlfU= From b1df464f5225e6a5dc289fc651f6fac3b1291bde Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 16:03:04 +0000 Subject: [PATCH 19/19] chore(deps): bump goreleaser/goreleaser-action from 6.4.0 to 7.0.0 Bumps [goreleaser/goreleaser-action](https://github.com/goreleaser/goreleaser-action) from 6.4.0 to 7.0.0. - [Release notes](https://github.com/goreleaser/goreleaser-action/releases) - [Commits](https://github.com/goreleaser/goreleaser-action/compare/e435ccd777264be153ace6237001ef4d979d3a7a...ec59f474b9834571250b370d4735c50f8e2d1e29) --- updated-dependencies: - dependency-name: goreleaser/goreleaser-action dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/deployment.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 0470a6644c1..9d845f798d3 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -50,7 +50,7 @@ jobs: with: go-version-file: 'go.mod' - name: Install GoReleaser - uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0 + uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0 with: # The version is pinned not only for security purposes, but also to avoid breaking # our scripts, which rely on the specific file names generated by GoReleaser. @@ -111,7 +111,7 @@ jobs: security set-key-partition-list -S "apple-tool:,apple:,codesign:" -s -k "$keychain_password" "$keychain" rm "$RUNNER_TEMP/cert.p12" - name: Install GoReleaser - uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0 + uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0 with: # The version is pinned not only for security purposes, but also to avoid breaking # our scripts, which rely on the specific file names generated by GoReleaser. @@ -173,7 +173,7 @@ jobs: with: go-version-file: 'go.mod' - name: Install GoReleaser - uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0 + uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0 with: # The version is pinned not only for security purposes, but also to avoid breaking # our scripts, which rely on the specific file names generated by GoReleaser.