From 9da58b3d15b5f0ed48ada2b33fc630d8b12747df Mon Sep 17 00:00:00 2001 From: Firdous2307 Date: Sun, 20 Oct 2024 23:49:24 +0000 Subject: [PATCH 1/8] feat: add iteration filter to issue list command --- commands/issuable/list/issuable_list.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/commands/issuable/list/issuable_list.go b/commands/issuable/list/issuable_list.go index a0ec9f9c5..9fa688248 100644 --- a/commands/issuable/list/issuable_list.go +++ b/commands/issuable/list/issuable_list.go @@ -60,6 +60,8 @@ type ListOptions struct { HTTPClient func() (*gitlab.Client, error) JSONOutput bool + + IterationID int } func NewCmdList(f *cmdutils.Factory, runE func(opts *ListOptions) error, issueType issuable.IssueType) *cobra.Command { @@ -239,6 +241,11 @@ func listRun(opts *ListOptions) error { opts.ListType = "search" } + if opts.IterationID != 0 { + listOpts.IterationID = gitlab.Ptr(opts.IterationID) + opts.ListType = "search" + } + issueType := "issue" if opts.IssueType != "" { listOpts.IssueType = gitlab.Ptr(opts.IssueType) -- GitLab From b27181b56901e2430dafb0f13b1b3f288916519f Mon Sep 17 00:00:00 2001 From: Firdous2307 Date: Mon, 21 Oct 2024 00:01:15 +0000 Subject: [PATCH 2/8] test: add test for iteration filter in issue list command --- commands/issuable/list/issuable_list_test.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/commands/issuable/list/issuable_list_test.go b/commands/issuable/list/issuable_list_test.go index c1a3d9c0c..19d788941 100644 --- a/commands/issuable/list/issuable_list_test.go +++ b/commands/issuable/list/issuable_list_test.go @@ -385,3 +385,23 @@ func TestIssueListMutualOutputFlags(t *testing.T) { assert.NotNil(t, err) assert.EqualError(t, err, "if any flags in the group [output output-format] are set none of the others can be; [output output-format] were all set") } + +func TestIssueList_List_With_IterationID(t *testing.T) { + fakeHTTP := httpmock.New() + defer fakeHTTP.Verify(t) + + fakeHTTP.RegisterResponder( + "GET", + "/api/v4/projects/OWNER/REPO/issues?iteration_id=1&state=opened", + httpmock.NewStringResponse(200, `[{"id":1}]`), + ) + + output, err := runCommand("issue", fakeHTTP, true, "list --iteration-id 1", nil, "") + + if err != nil { + t.Errorf("error running command `issue list`: %v", err) + } + + assert.Equal(t, "", output.String()) + assert.Equal(t, "Showing 1 open issue\n\n", output.Stderr()) +} -- GitLab From ebca704eba5aa21eead7020697f406707b1ebea7 Mon Sep 17 00:00:00 2001 From: Firdous2307 Date: Mon, 21 Oct 2024 00:18:21 +0000 Subject: [PATCH 3/8] feat: add iteration list command --- commands/issuable/list/issuable_list_test.go | 1 - commands/iteration/list/iteration_list.go | 75 ++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 commands/iteration/list/iteration_list.go diff --git a/commands/issuable/list/issuable_list_test.go b/commands/issuable/list/issuable_list_test.go index 19d788941..06351e109 100644 --- a/commands/issuable/list/issuable_list_test.go +++ b/commands/issuable/list/issuable_list_test.go @@ -397,7 +397,6 @@ func TestIssueList_List_With_IterationID(t *testing.T) { ) output, err := runCommand("issue", fakeHTTP, true, "list --iteration-id 1", nil, "") - if err != nil { t.Errorf("error running command `issue list`: %v", err) } diff --git a/commands/iteration/list/iteration_list.go b/commands/iteration/list/iteration_list.go new file mode 100644 index 000000000..5f09264c1 --- /dev/null +++ b/commands/iteration/list/iteration_list.go @@ -0,0 +1,75 @@ +package list + +import ( + "encoding/json" + "fmt" + + "github.com/MakeNowJust/heredoc/v2" + "github.com/spf13/cobra" + "github.com/xanzy/go-gitlab" + "gitlab.com/gitlab-org/cli/api" + "gitlab.com/gitlab-org/cli/commands/cmdutils" +) + +func NewCmdList(f *cmdutils.Factory) *cobra.Command { + var outputFormat string + var groupName string + var state string + + iterationListCmd := &cobra.Command{ + Use: "list [flags]", + Short: `List iterations in the group.`, + Long: ``, + Aliases: []string{"ls"}, + Example: heredoc.Doc(` + glab iteration list + glab iteration ls + glab iteration list --group mygroup + glab iteration list --state active --output json + `), + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + apiClient, err := f.HttpClient() + if err != nil { + return err + } + + l := &gitlab.ListGroupIterationsOptions{} + + if p, _ := cmd.Flags().GetInt("page"); p != 0 { + l.Page = p + } + if p, _ := cmd.Flags().GetInt("per-page"); p != 0 { + l.PerPage = p + } + if state != "" { + l.State = gitlab.Ptr(state) + } + + iterations, err := api.ListGroupIterations(apiClient, groupName, l) + if err != nil { + return err + } + + if outputFormat == "json" { + iterationsJSON, _ := json.Marshal(iterations) + fmt.Fprintln(f.IO.StdOut, string(iterationsJSON)) + } else { + fmt.Fprintf(f.IO.StdOut, "Showing %d iterations for group %s.\n\n", len(iterations), groupName) + for _, iteration := range iterations { + fmt.Fprintf(f.IO.StdOut, "ID: %d, Title: %s, State: %s\n", iteration.ID, iteration.Title, iteration.State) + } + } + + return nil + }, + } + + iterationListCmd.Flags().IntP("page", "p", 1, "Page number.") + iterationListCmd.Flags().IntP("per-page", "P", 30, "Number of items to list per page.") + iterationListCmd.Flags().StringVarP(&outputFormat, "output", "F", "text", "Format output as: text, json.") + iterationListCmd.Flags().StringVarP(&groupName, "group", "g", "", "Name of the group") + iterationListCmd.Flags().StringVarP(&state, "state", "s", "", "Filter iterations by state (active, upcoming, opened, closed, or all)") + + return iterationListCmd +} -- GitLab From fd1cca506d6426e50e079bb31d05d32e233f9ba3 Mon Sep 17 00:00:00 2001 From: Firdous2307 Date: Mon, 21 Oct 2024 00:42:53 +0000 Subject: [PATCH 4/8] chore: new changes and updated docs --- commands/issuable/list/issuable_list_test.go | 28 ++++++++++-------- commands/issue/list/issue_list.go | 4 ++- commands/iteration/list/iteration_list.go | 31 +++++++++++++++----- docs/source/issue/list.md | 1 + 4 files changed, 42 insertions(+), 22 deletions(-) diff --git a/commands/issuable/list/issuable_list_test.go b/commands/issuable/list/issuable_list_test.go index 06351e109..a56432b54 100644 --- a/commands/issuable/list/issuable_list_test.go +++ b/commands/issuable/list/issuable_list_test.go @@ -387,20 +387,22 @@ func TestIssueListMutualOutputFlags(t *testing.T) { } func TestIssueList_List_With_IterationID(t *testing.T) { - fakeHTTP := httpmock.New() - defer fakeHTTP.Verify(t) + t.Run("Issue_List_With_IterationID", func(t *testing.T) { + fakeHTTP := httpmock.New() + defer fakeHTTP.Verify(t) - fakeHTTP.RegisterResponder( - "GET", - "/api/v4/projects/OWNER/REPO/issues?iteration_id=1&state=opened", - httpmock.NewStringResponse(200, `[{"id":1}]`), - ) + fakeHTTP.RegisterResponder( + "GET", + "/api/v4/projects/OWNER/REPO/issues?iteration_id=1&state=opened", + httpmock.NewStringResponse(200, `[{"id":1}]`), + ) - output, err := runCommand("issue", fakeHTTP, true, "list --iteration-id 1", nil, "") - if err != nil { - t.Errorf("error running command `issue list`: %v", err) - } + output, err := runCommand("issue", fakeHTTP, true, "list --iteration-id 1", nil, "") + if err != nil { + t.Errorf("error running command `issue list`: %v", err) + } - assert.Equal(t, "", output.String()) - assert.Equal(t, "Showing 1 open issue\n\n", output.Stderr()) + assert.Equal(t, "", output.String()) + assert.Equal(t, "Showing 1 open issue\n\n", output.Stderr()) + }) } diff --git a/commands/issue/list/issue_list.go b/commands/issue/list/issue_list.go index 6fb9c25ee..714f07b55 100644 --- a/commands/issue/list/issue_list.go +++ b/commands/issue/list/issue_list.go @@ -8,5 +8,7 @@ import ( ) func NewCmdList(f *cmdutils.Factory, runE func(opts *issuableListCmd.ListOptions) error) *cobra.Command { - return issuableListCmd.NewCmdList(f, runE, issuable.TypeIssue) + cmd := issuableListCmd.NewCmdList(f, runE, issuable.TypeIssue) + cmd.Flags().Int("iteration", 0, "Filter issues by iteration ID") + return cmd } diff --git a/commands/iteration/list/iteration_list.go b/commands/iteration/list/iteration_list.go index 5f09264c1..df85ba98d 100644 --- a/commands/iteration/list/iteration_list.go +++ b/commands/iteration/list/iteration_list.go @@ -7,7 +7,6 @@ import ( "github.com/MakeNowJust/heredoc/v2" "github.com/spf13/cobra" "github.com/xanzy/go-gitlab" - "gitlab.com/gitlab-org/cli/api" "gitlab.com/gitlab-org/cli/commands/cmdutils" ) @@ -29,35 +28,51 @@ func NewCmdList(f *cmdutils.Factory) *cobra.Command { `), Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { - apiClient, err := f.HttpClient() + // Retrieve the configuration object + cfg, err := f.Config() if err != nil { return err } - l := &gitlab.ListGroupIterationsOptions{} + // Get the token from the configuration using the correct section and key + token, err := cfg.Get("auth", "token") + if err != nil || token == "" { + return fmt.Errorf("failed to retrieve GitLab token from configuration") + } + + // Create a new GitLab client using the token + apiClient, err := gitlab.NewClient(token) + if err != nil { + return err + } + + // Set up the options for listing iterations + listOptions := &gitlab.ListGroupIterationsOptions{} if p, _ := cmd.Flags().GetInt("page"); p != 0 { - l.Page = p + listOptions.Page = p } if p, _ := cmd.Flags().GetInt("per-page"); p != 0 { - l.PerPage = p + listOptions.PerPage = p } if state != "" { - l.State = gitlab.Ptr(state) + listOptions.State = gitlab.String(state) } - iterations, err := api.ListGroupIterations(apiClient, groupName, l) + // Fetch the iterations for the specified group + iterations, _, err := apiClient.GroupIterations.ListGroupIterations(groupName, listOptions) if err != nil { return err } + // Output the result in the specified format if outputFormat == "json" { iterationsJSON, _ := json.Marshal(iterations) fmt.Fprintln(f.IO.StdOut, string(iterationsJSON)) } else { fmt.Fprintf(f.IO.StdOut, "Showing %d iterations for group %s.\n\n", len(iterations), groupName) for _, iteration := range iterations { - fmt.Fprintf(f.IO.StdOut, "ID: %d, Title: %s, State: %s\n", iteration.ID, iteration.Title, iteration.State) + fmt.Fprintf(f.IO.StdOut, "ID: %d, Title: %s, State: %d\n", iteration.ID, iteration.Title, iteration.State) } } diff --git a/docs/source/issue/list.md b/docs/source/issue/list.md index 84f436ac2..92db19c13 100644 --- a/docs/source/issue/list.md +++ b/docs/source/issue/list.md @@ -44,6 +44,7 @@ glab issue list --milestone release-2.0.0 --opened -g, --group string Select a group or subgroup. Ignored if a repo argument is set. --in string search in: title, description. (default "title,description") -t, --issue-type string Filter issue by its type. Options: issue, incident, test_case. + --iteration int Filter issues by iteration ID -l, --label strings Filter issue by label . -m, --milestone string Filter issue by milestone . --not-assignee strings Filter issue by not being assigneed to . -- GitLab From 26bc9b205d2676fa13d5eda761c4227a8c8feb81 Mon Sep 17 00:00:00 2001 From: Mohammed Firdous Date: Mon, 21 Oct 2024 21:30:45 +0000 Subject: [PATCH 5/8] fix: correct assignment of state in list options --- commands/iteration/list/iteration_list.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands/iteration/list/iteration_list.go b/commands/iteration/list/iteration_list.go index df85ba98d..1cbd217b0 100644 --- a/commands/iteration/list/iteration_list.go +++ b/commands/iteration/list/iteration_list.go @@ -56,7 +56,7 @@ func NewCmdList(f *cmdutils.Factory) *cobra.Command { listOptions.PerPage = p } if state != "" { - listOptions.State = gitlab.String(state) + listOptions.State = gitlab.Ptr(state) } // Fetch the iterations for the specified group -- GitLab From f5288b29ba0d1fadd738b2b5cd102d51a6a66cdd Mon Sep 17 00:00:00 2001 From: mohammedfirdouss Date: Thu, 17 Apr 2025 13:07:36 +0000 Subject: [PATCH 6/8] feat(iteration): add iteration list and filtering for issues --- commands/issuable/list/issuable_list.go | 1 - commands/issue/list/issue_list.go | 3 +- commands/iteration/iteration.go | 19 +++++++ commands/iteration/list/iteration_list.go | 2 +- .../iteration/list/iteration_list_test.go | 47 +++++++++++++++++ commands/root.go | 2 + docs/source/issue/list.md | 2 +- docs/source/iteration/help.md | 25 ++++++++++ docs/source/iteration/index.md | 24 +++++++++ docs/source/iteration/list.md | 50 +++++++++++++++++++ 10 files changed, 171 insertions(+), 4 deletions(-) create mode 100644 commands/iteration/iteration.go create mode 100644 commands/iteration/list/iteration_list_test.go create mode 100644 docs/source/iteration/help.md create mode 100644 docs/source/iteration/index.md create mode 100644 docs/source/iteration/list.md diff --git a/commands/issuable/list/issuable_list.go b/commands/issuable/list/issuable_list.go index 4258d09b2..2b765521a 100644 --- a/commands/issuable/list/issuable_list.go +++ b/commands/issuable/list/issuable_list.go @@ -276,7 +276,6 @@ func listRun(opts *ListOptions) error { if opts.IterationID != 0 { listOpts.IterationID = gitlab.Ptr(opts.IterationID) - opts.ListType = "search" } issueType := "issue" diff --git a/commands/issue/list/issue_list.go b/commands/issue/list/issue_list.go index 714f07b55..21447e868 100644 --- a/commands/issue/list/issue_list.go +++ b/commands/issue/list/issue_list.go @@ -8,7 +8,8 @@ import ( ) func NewCmdList(f *cmdutils.Factory, runE func(opts *issuableListCmd.ListOptions) error) *cobra.Command { + opts := &issuableListCmd.ListOptions{} cmd := issuableListCmd.NewCmdList(f, runE, issuable.TypeIssue) - cmd.Flags().Int("iteration", 0, "Filter issues by iteration ID") + cmd.Flags().IntVar(&opts.IterationID, "iteration-id", 0, "Filter issues by iteration ID") return cmd } diff --git a/commands/iteration/iteration.go b/commands/iteration/iteration.go new file mode 100644 index 000000000..e33ccc734 --- /dev/null +++ b/commands/iteration/iteration.go @@ -0,0 +1,19 @@ +package iteration + +import ( + "github.com/spf13/cobra" + "gitlab.com/gitlab-org/cli/commands/cmdutils" + "gitlab.com/gitlab-org/cli/commands/iteration/list" +) + +func NewCmdIteration(f *cmdutils.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "iteration ", + Short: "Work with GitLab iterations", + Long: ``, + } + + cmd.AddCommand(list.NewCmdList(f)) + + return cmd +} diff --git a/commands/iteration/list/iteration_list.go b/commands/iteration/list/iteration_list.go index 1cbd217b0..80b85d1c3 100644 --- a/commands/iteration/list/iteration_list.go +++ b/commands/iteration/list/iteration_list.go @@ -6,7 +6,7 @@ import ( "github.com/MakeNowJust/heredoc/v2" "github.com/spf13/cobra" - "github.com/xanzy/go-gitlab" + gitlab "gitlab.com/gitlab-org/api/client-go" "gitlab.com/gitlab-org/cli/commands/cmdutils" ) diff --git a/commands/iteration/list/iteration_list_test.go b/commands/iteration/list/iteration_list_test.go new file mode 100644 index 000000000..aca3f1ff0 --- /dev/null +++ b/commands/iteration/list/iteration_list_test.go @@ -0,0 +1,47 @@ +package list + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "gitlab.com/gitlab-org/cli/commands/cmdutils" +) + +func TestNewCmdList(t *testing.T) { + f := cmdutils.NewFactory() + cmd := NewCmdList(f) + + assert.Equal(t, "list", cmd.Name()) + assert.Equal(t, []string{"ls"}, cmd.Aliases) + assert.Equal(t, "List iterations in the group.", cmd.Short) +} + +func TestListOptions(t *testing.T) { + f := cmdutils.NewFactory() + cmd := NewCmdList(f) + + // Test page flag + cmd.Flags().Set("page", "2") + page, _ := cmd.Flags().GetInt("page") + assert.Equal(t, 2, page) + + // Test per-page flag + cmd.Flags().Set("per-page", "50") + perPage, _ := cmd.Flags().GetInt("per-page") + assert.Equal(t, 50, perPage) + + // Test output format flag + cmd.Flags().Set("output", "json") + output, _ := cmd.Flags().GetString("output") + assert.Equal(t, "json", output) + + // Test group flag + cmd.Flags().Set("group", "mygroup") + group, _ := cmd.Flags().GetString("group") + assert.Equal(t, "mygroup", group) + + // Test state flag + cmd.Flags().Set("state", "active") + state, _ := cmd.Flags().GetString("state") + assert.Equal(t, "active", state) +} \ No newline at end of file diff --git a/commands/root.go b/commands/root.go index ee4c4b62f..f0f667d44 100644 --- a/commands/root.go +++ b/commands/root.go @@ -34,6 +34,7 @@ import ( userCmd "gitlab.com/gitlab-org/cli/commands/user" variableCmd "gitlab.com/gitlab-org/cli/commands/variable" versionCmd "gitlab.com/gitlab-org/cli/commands/version" + iterationCmd "gitlab.com/gitlab-org/cli/commands/iteration" "gitlab.com/gitlab-org/cli/internal/glrepo" ) @@ -143,6 +144,7 @@ func NewCmdRoot(f *cmdutils.Factory, version, commit string) *cobra.Command { rootCmd.AddCommand(duoCmd.NewCmdDuo(f)) rootCmd.AddCommand(tokenCmd.NewTokenCmd(f)) rootCmd.AddCommand(stackCmd.NewCmdStack(f)) + rootCmd.AddCommand(iterationCmd.NewCmdIteration(f)) rootCmd.Flags().BoolP("version", "v", false, "show glab version information") return rootCmd diff --git a/docs/source/issue/list.md b/docs/source/issue/list.md index 2dcca1a63..3459122d6 100644 --- a/docs/source/issue/list.md +++ b/docs/source/issue/list.md @@ -46,7 +46,7 @@ ls --in string search in: title, description. (default "title,description") -t, --issue-type string Filter issue by its type. Options: issue, incident, test_case. -i, --iteration int Filter issue by iteration . - --iteration int Filter issues by iteration ID + --iteration-id int Filter issues by iteration ID -l, --label strings Filter issue by label . -m, --milestone string Filter issue by milestone . --not-assignee string Filter issue by not being assigned to . diff --git a/docs/source/iteration/help.md b/docs/source/iteration/help.md new file mode 100644 index 000000000..ca107dbf6 --- /dev/null +++ b/docs/source/iteration/help.md @@ -0,0 +1,25 @@ +--- +stage: Create +group: Code Review +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments +--- + + + +# `glab iteration help` + +Help about any command + +```plaintext +glab iteration help [command] [flags] +``` + +## Options inherited from parent commands + +```plaintext + --help Show help for this command. +``` + diff --git a/docs/source/iteration/index.md b/docs/source/iteration/index.md new file mode 100644 index 000000000..e6339e001 --- /dev/null +++ b/docs/source/iteration/index.md @@ -0,0 +1,24 @@ +--- +stage: Create +group: Code Review +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments +--- + + + +# `glab iteration` + +Work with GitLab iterations + +## Options inherited from parent commands + +```plaintext + --help Show help for this command. +``` + +## Subcommands + +- [`list`](list.md) diff --git a/docs/source/iteration/list.md b/docs/source/iteration/list.md new file mode 100644 index 000000000..5e81eb612 --- /dev/null +++ b/docs/source/iteration/list.md @@ -0,0 +1,50 @@ +--- +stage: Create +group: Code Review +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments +--- + + + +# `glab iteration list` + +List iterations in the group. + +```plaintext +glab iteration list [flags] +``` + +## Aliases + +```plaintext +ls +``` + +## Examples + +```plaintext +glab iteration list +glab iteration ls +glab iteration list --group mygroup +glab iteration list --state active --output json + +``` + +## Options + +```plaintext + -g, --group string Name of the group + -F, --output string Format output as: text, json. (default "text") + -p, --page int Page number. (default 1) + -P, --per-page int Number of items to list per page. (default 30) + -s, --state string Filter iterations by state (active, upcoming, opened, closed, or all) +``` + +## Options inherited from parent commands + +```plaintext + --help Show help for this command. +``` -- GitLab From 1ad75cd754100ab96917f0e202a7a990b13fce35 Mon Sep 17 00:00:00 2001 From: mohammedfirdouss Date: Thu, 17 Apr 2025 13:17:02 +0000 Subject: [PATCH 7/8] fix: update iteration documentation --- docs/source/iteration/help.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/source/iteration/help.md b/docs/source/iteration/help.md index ca107dbf6..4dc0e3a93 100644 --- a/docs/source/iteration/help.md +++ b/docs/source/iteration/help.md @@ -21,5 +21,4 @@ glab iteration help [command] [flags] ```plaintext --help Show help for this command. -``` - +``` \ No newline at end of file -- GitLab From f64bb62d95f362a3bad936d613e48971ab3a8e2a Mon Sep 17 00:00:00 2001 From: mohammedfirdouss Date: Fri, 2 May 2025 21:02:47 +0000 Subject: [PATCH 8/8] fix: update docs --- docs/source/iteration/help.md | 2 +- docs/source/iteration/list.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/iteration/help.md b/docs/source/iteration/help.md index 4dc0e3a93..33359248e 100644 --- a/docs/source/iteration/help.md +++ b/docs/source/iteration/help.md @@ -21,4 +21,4 @@ glab iteration help [command] [flags] ```plaintext --help Show help for this command. -``` \ No newline at end of file +``` diff --git a/docs/source/iteration/list.md b/docs/source/iteration/list.md index 5e81eb612..47a665330 100644 --- a/docs/source/iteration/list.md +++ b/docs/source/iteration/list.md @@ -25,7 +25,7 @@ ls ## Examples -```plaintext +```console glab iteration list glab iteration ls glab iteration list --group mygroup -- GitLab