diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 4aa93810d..7e8117c46 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Initialize CodeQL uses: github/codeql-action/init@v2 diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 1f1a24b1d..6b951cd68 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -34,7 +34,7 @@ jobs: if: contains(inputs.platforms, 'linux') steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v4 with: @@ -68,7 +68,7 @@ jobs: if: contains(inputs.platforms, 'macos') steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v4 with: @@ -125,7 +125,7 @@ jobs: if: contains(inputs.platforms, 'windows') steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v4 with: @@ -209,11 +209,11 @@ jobs: if: inputs.release steps: - name: Checkout cli/cli - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Merge built artifacts uses: actions/download-artifact@v3 - name: Checkout documentation site - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: github/cli.github.com path: site diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index a375e2d8f..13fbfb7f1 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -19,7 +19,7 @@ jobs: go-version: 1.21 - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Restore Go modules cache uses: actions/cache@v3 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index c8defbf3a..bb6526faa 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -25,7 +25,7 @@ jobs: go-version: 1.21 - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Restore Go modules cache uses: actions/cache@v3 diff --git a/pkg/cmd/project/template/template.go b/pkg/cmd/project/mark-template/mark_template.go similarity index 75% rename from pkg/cmd/project/template/template.go rename to pkg/cmd/project/mark-template/mark_template.go index 9ef0732b9..92eb87ca6 100644 --- a/pkg/cmd/project/template/template.go +++ b/pkg/cmd/project/mark-template/mark_template.go @@ -1,4 +1,4 @@ -package template +package marktemplate import ( "fmt" @@ -14,7 +14,7 @@ import ( "github.com/spf13/cobra" ) -type templateOpts struct { +type markTemplateOpts struct { owner string undo bool number int32 @@ -22,9 +22,9 @@ type templateOpts struct { format string } -type templateConfig struct { +type markTemplateConfig struct { client *queries.Client - opts templateOpts + opts markTemplateOpts io *iostreams.IOStreams } @@ -39,17 +39,17 @@ type unmarkProjectTemplateMutation struct { } `graphql:"unmarkProjectV2AsTemplate(input:$input)"` } -func NewCmdTemplate(f *cmdutil.Factory, runF func(config templateConfig) error) *cobra.Command { - opts := templateOpts{} - templateCmd := &cobra.Command{ +func NewCmdMarkTemplate(f *cmdutil.Factory, runF func(config markTemplateConfig) error) *cobra.Command { + opts := markTemplateOpts{} + markTemplateCmd := &cobra.Command{ Short: "Mark a project as a template", - Use: "template []", + Use: "mark-template []", Example: heredoc.Doc(` # mark the github org's project "1" as a template - gh project template 1 --owner "github" + gh project mark-template 1 --owner "github" # unmark the github org's project "1" as a template - gh project template 1 --owner "github" --undo + gh project mark-template 1 --owner "github" --undo `), Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { @@ -66,7 +66,7 @@ func NewCmdTemplate(f *cmdutil.Factory, runF func(config templateConfig) error) opts.number = int32(num) } - config := templateConfig{ + config := markTemplateConfig{ client: client, opts: opts, io: f.IOStreams, @@ -76,18 +76,18 @@ func NewCmdTemplate(f *cmdutil.Factory, runF func(config templateConfig) error) if runF != nil { return runF(config) } - return runTemplate(config) + return runMarkTemplate(config) }, } - templateCmd.Flags().StringVar(&opts.owner, "owner", "", "Login of the org owner.") - templateCmd.Flags().BoolVar(&opts.undo, "undo", false, "Unmark the project as a template.") - cmdutil.StringEnumFlag(templateCmd, &opts.format, "format", "", "", []string{"json"}, "Output format") + markTemplateCmd.Flags().StringVar(&opts.owner, "owner", "", "Login of the org owner.") + markTemplateCmd.Flags().BoolVar(&opts.undo, "undo", false, "Unmark the project as a template.") + cmdutil.StringEnumFlag(markTemplateCmd, &opts.format, "format", "", "", []string{"json"}, "Output format") - return templateCmd + return markTemplateCmd } -func runTemplate(config templateConfig) error { +func runMarkTemplate(config markTemplateConfig) error { canPrompt := config.io.CanPrompt() owner, err := config.client.NewOwner(canPrompt, config.opts.owner) if err != nil { @@ -127,7 +127,7 @@ func runTemplate(config templateConfig) error { return printResults(config, query.TemplateProject.Project) } -func markTemplateArgs(config templateConfig) (*markProjectTemplateMutation, map[string]interface{}) { +func markTemplateArgs(config markTemplateConfig) (*markProjectTemplateMutation, map[string]interface{}) { return &markProjectTemplateMutation{}, map[string]interface{}{ "input": githubv4.MarkProjectV2AsTemplateInput{ ProjectID: githubv4.ID(config.opts.projectID), @@ -139,7 +139,7 @@ func markTemplateArgs(config templateConfig) (*markProjectTemplateMutation, map[ } } -func unmarkTemplateArgs(config templateConfig) (*unmarkProjectTemplateMutation, map[string]interface{}) { +func unmarkTemplateArgs(config markTemplateConfig) (*unmarkProjectTemplateMutation, map[string]interface{}) { return &unmarkProjectTemplateMutation{}, map[string]interface{}{ "input": githubv4.UnmarkProjectV2AsTemplateInput{ ProjectID: githubv4.ID(config.opts.projectID), @@ -151,7 +151,7 @@ func unmarkTemplateArgs(config templateConfig) (*unmarkProjectTemplateMutation, } } -func printResults(config templateConfig, project queries.Project) error { +func printResults(config markTemplateConfig, project queries.Project) error { if !config.io.IsStdoutTTY() { return nil } @@ -165,7 +165,7 @@ func printResults(config templateConfig, project queries.Project) error { return err } -func printJSON(config templateConfig, project queries.Project) error { +func printJSON(config markTemplateConfig, project queries.Project) error { b, err := format.JSONProject(project) if err != nil { return err diff --git a/pkg/cmd/project/template/template_test.go b/pkg/cmd/project/mark-template/mark_template_test.go similarity index 91% rename from pkg/cmd/project/template/template_test.go rename to pkg/cmd/project/mark-template/mark_template_test.go index df42ae5ca..581c4d8f6 100644 --- a/pkg/cmd/project/template/template_test.go +++ b/pkg/cmd/project/mark-template/mark_template_test.go @@ -1,4 +1,4 @@ -package template +package marktemplate import ( "testing" @@ -11,11 +11,11 @@ import ( "gopkg.in/h2non/gock.v1" ) -func TestNewCmdTemplate(t *testing.T) { +func TestNewCmdMarkTemplate(t *testing.T) { tests := []struct { name string cli string - wants templateOpts + wants markTemplateOpts wantsErr bool wantsErrMsg string }{ @@ -28,21 +28,21 @@ func TestNewCmdTemplate(t *testing.T) { { name: "number", cli: "123", - wants: templateOpts{ + wants: markTemplateOpts{ number: 123, }, }, { name: "owner", cli: "--owner monalisa", - wants: templateOpts{ + wants: markTemplateOpts{ owner: "monalisa", }, }, { name: "undo", cli: "--undo", - wants: templateOpts{ + wants: markTemplateOpts{ undo: true, }, }, @@ -50,7 +50,7 @@ func TestNewCmdTemplate(t *testing.T) { { name: "json", cli: "--format json", - wants: templateOpts{ + wants: markTemplateOpts{ format: "json", }, }, @@ -68,8 +68,8 @@ func TestNewCmdTemplate(t *testing.T) { argv, err := shlex.Split(tt.cli) assert.NoError(t, err) - var gotOpts templateOpts - cmd := NewCmdTemplate(f, func(config templateConfig) error { + var gotOpts markTemplateOpts + cmd := NewCmdMarkTemplate(f, func(config markTemplateConfig) error { gotOpts = config.opts return nil }) @@ -164,8 +164,8 @@ func TestRunMarkTemplate_Org(t *testing.T) { ios, _, stdout, _ := iostreams.Test() ios.SetStdoutTTY(true) - config := templateConfig{ - opts: templateOpts{ + config := markTemplateConfig{ + opts: markTemplateOpts{ owner: "github", number: 1, }, @@ -173,7 +173,7 @@ func TestRunMarkTemplate_Org(t *testing.T) { io: ios, } - err := runTemplate(config) + err := runMarkTemplate(config) assert.NoError(t, err) assert.Equal( t, @@ -256,8 +256,8 @@ func TestRunUnmarkTemplate_Org(t *testing.T) { ios, _, stdout, _ := iostreams.Test() ios.SetStdoutTTY(true) - config := templateConfig{ - opts: templateOpts{ + config := markTemplateConfig{ + opts: markTemplateOpts{ owner: "github", number: 1, undo: true, @@ -266,7 +266,7 @@ func TestRunUnmarkTemplate_Org(t *testing.T) { io: ios, } - err := runTemplate(config) + err := runMarkTemplate(config) assert.NoError(t, err) assert.Equal( t, diff --git a/pkg/cmd/project/project.go b/pkg/cmd/project/project.go index d9858953d..cb5e54149 100644 --- a/pkg/cmd/project/project.go +++ b/pkg/cmd/project/project.go @@ -17,7 +17,7 @@ import ( cmdItemEdit "github.com/cli/cli/v2/pkg/cmd/project/item-edit" cmdItemList "github.com/cli/cli/v2/pkg/cmd/project/item-list" cmdList "github.com/cli/cli/v2/pkg/cmd/project/list" - cmdTemplate "github.com/cli/cli/v2/pkg/cmd/project/template" + cmdTemplate "github.com/cli/cli/v2/pkg/cmd/project/mark-template" cmdView "github.com/cli/cli/v2/pkg/cmd/project/view" "github.com/cli/cli/v2/pkg/cmdutil" "github.com/spf13/cobra" @@ -44,7 +44,7 @@ func NewCmdProject(f *cmdutil.Factory) *cobra.Command { cmd.AddCommand(cmdDelete.NewCmdDelete(f, nil)) cmd.AddCommand(cmdEdit.NewCmdEdit(f, nil)) cmd.AddCommand(cmdView.NewCmdView(f, nil)) - cmd.AddCommand(cmdTemplate.NewCmdTemplate(f, nil)) + cmd.AddCommand(cmdTemplate.NewCmdMarkTemplate(f, nil)) // items cmd.AddCommand(cmdItemList.NewCmdList(f, nil)) diff --git a/pkg/cmd/status/status.go b/pkg/cmd/status/status.go index 7e39d63d0..4e6bd3781 100644 --- a/pkg/cmd/status/status.go +++ b/pkg/cmd/status/status.go @@ -286,7 +286,28 @@ func (s *StatusGetter) LoadNotifications() error { if !ok { return nil } - if actual, err := s.ActualMention(n.Subject.LatestCommentURL); actual != "" && err == nil { + actual, err := s.ActualMention(n.Subject.LatestCommentURL) + + if err != nil { + var httpErr api.HTTPError + httpStatusCode := -1 + + if errors.As(err, &httpErr) { + httpStatusCode = httpErr.StatusCode + } + + switch httpStatusCode { + case 403: + s.addAuthError(httpErr.Message, factory.SSOURL()) + case 404: + return nil + default: + abortFetching() + return fmt.Errorf("could not fetch comment: %w", err) + } + } + + if actual != "" { // I'm so sorry split := strings.Split(n.Subject.URL, "/") fetched <- StatusItem{ @@ -295,14 +316,6 @@ func (s *StatusGetter) LoadNotifications() error { preview: actual, index: n.index, } - } else if err != nil { - var httpErr api.HTTPError - if errors.As(err, &httpErr) && httpErr.StatusCode == 403 { - s.addAuthError(httpErr.Message, factory.SSOURL()) - } else { - abortFetching() - return fmt.Errorf("could not fetch comment: %w", err) - } } } } diff --git a/pkg/cmd/status/status_test.go b/pkg/cmd/status/status_test.go index c592c90a0..d9fdd54bc 100644 --- a/pkg/cmd/status/status_test.go +++ b/pkg/cmd/status/status_test.go @@ -295,6 +295,63 @@ func TestStatusRun(t *testing.T) { warning: You don't have permission to access this resource. `), }, + { + name: "not found errors", + httpStubs: func(reg *httpmock.Registry) { + reg.Register( + httpmock.GraphQL("UserCurrent"), + httpmock.StringResponse(`{"data": {"viewer": {"login": "jillvalentine"}}}`)) + reg.Register( + httpmock.REST("GET", "repos/rpd/todo/issues/110"), + httpmock.StringResponse(`{"body":"hello @jillvalentine how are you"}`)) + reg.Register( + httpmock.REST("GET", "repos/rpd/todo/issues/4113"), + httpmock.StringResponse(`{"body":"this is a comment"}`)) + reg.Register( + httpmock.REST("GET", "repos/cli/cli/issues/1096"), + httpmock.StringResponse(`{"body":"@jillvalentine hi"}`)) + reg.Register( + httpmock.REST("GET", "repos/rpd/todo/issues/comments/1065"), + httpmock.StatusStringResponse(404, `{ + "message": "Not Found." + }`)) + reg.Register( + httpmock.REST("GET", "repos/vilmibm/gh-screensaver/issues/comments/10"), + httpmock.StringResponse(`{"body":"a message for @jillvalentine"}`)) + reg.Register( + httpmock.GraphQL("AssignedSearch"), + httpmock.FileResponse("./fixtures/search.json")) + reg.Register( + httpmock.REST("GET", "notifications"), + httpmock.FileResponse("./fixtures/notifications.json")) + reg.Register( + httpmock.REST("GET", "users/jillvalentine/received_events"), + httpmock.FileResponse("./fixtures/events.json")) + }, + opts: &StatusOptions{}, + wantOut: heredoc.Doc(` + Assigned Issues │ Assigned Pull Requests + vilmibm/testing#157 yolo │ cli/cli#5272 Pin extensions + cli/cli#3223 Repo garden...│ rpd/todo#73 Board up RPD windows + rpd/todo#514 Reducing zo...│ cli/cli#4768 Issue Frecency + vilmibm/testing#74 welp │ + adreyer/arkestrator#22 complete mo...│ + │ + Review Requests │ Mentions + cli/cli#5272 Pin extensions │ rpd/todo#110 hello @j... + vilmibm/testing#1234 Foobar │ cli/cli#1096 @jillval... + rpd/todo#50 Welcome party...│ vilmibm/gh-screensaver#15 a messag... + cli/cli#4671 This pull req...│ + rpd/todo#49 Haircut for Leon│ + │ + Repository Activity + rpd/todo#5326 new PR Only write UTF-8 BOM on W... + vilmibm/testing#5325 comment on Ability to sea... We are working on dedicat... + cli/cli#5319 comment on [Codespaces] D... Wondering if we shouldn't... + cli/cli#5300 new issue Terminal bell when a runn... + + `), + }, { name: "notification errors", httpStubs: func(reg *httpmock.Registry) {