@@ -41,6 +41,12 @@ type Options struct {
4141 DenyList * filters.Filter `yaml:"deny-list"`
4242 // DuplicateIssueCheck is a bool to enable duplicate tracking issue check and update the newest
4343 DuplicateIssueCheck bool `yaml:"duplicate-issue-check" default:"false"`
44+ // DuplicateIssuePageSize controls how many issues are fetched per page when searching for duplicates.
45+ // If unset or <=0, a default of 100 is used.
46+ DuplicateIssuePageSize int `yaml:"duplicate-issue-page-size" default:"100"`
47+ // DuplicateIssueMaxPages limits how many pages are fetched when searching for duplicates.
48+ // If unset or <=0, all pages are fetched until exhaustion.
49+ DuplicateIssueMaxPages int `yaml:"duplicate-issue-max-pages" default:"0"`
4450
4551 HttpClient * retryablehttp.Client `yaml:"-"`
4652 OmitRaw bool `yaml:"-"`
@@ -80,39 +86,36 @@ func (i *Integration) CreateIssue(event *output.ResultEvent) (*filters.CreateIss
8086 }
8187 customLabels := gitlab .LabelOptions (labels )
8288 assigneeIDs := []int {i .userID }
89+
90+ var issue * gitlab.Issue
8391 if i .options .DuplicateIssueCheck {
84- searchIn := "title"
85- searchState := "all"
86- issues , _ , err := i .client .Issues .ListProjectIssues (i .options .ProjectName , & gitlab.ListProjectIssuesOptions {
87- In : & searchIn ,
88- State : & searchState ,
89- Search : & summary ,
92+ var err error
93+ issue , err = i .findIssueByTitle (summary )
94+ if err != nil {
95+ return nil , err
96+ }
97+ }
98+
99+ if issue != nil {
100+ _ , _ , err := i .client .Notes .CreateIssueNote (i .options .ProjectName , issue .IID , & gitlab.CreateIssueNoteOptions {
101+ Body : & description ,
90102 })
91103 if err != nil {
92104 return nil , err
93105 }
94- if len ( issues ) > 0 {
95- issue := issues [ 0 ]
96- _ , _ , err := i .client .Notes . CreateIssueNote (i .options .ProjectName , issue .IID , & gitlab.CreateIssueNoteOptions {
97- Body : & description ,
106+ if issue . State == "closed" {
107+ reopen := "reopen"
108+ _ , _ , err := i .client .Issues . UpdateIssue (i .options .ProjectName , issue .IID , & gitlab.UpdateIssueOptions {
109+ StateEvent : & reopen ,
98110 })
99111 if err != nil {
100112 return nil , err
101113 }
102- if issue .State == "closed" {
103- reopen := "reopen"
104- _ , _ , err := i .client .Issues .UpdateIssue (i .options .ProjectName , issue .IID , & gitlab.UpdateIssueOptions {
105- StateEvent : & reopen ,
106- })
107- if err != nil {
108- return nil , err
109- }
110- }
111- return & filters.CreateIssueResponse {
112- IssueID : strconv .FormatInt (int64 (issue .ID ), 10 ),
113- IssueURL : issue .WebURL ,
114- }, nil
115114 }
115+ return & filters.CreateIssueResponse {
116+ IssueID : strconv .FormatInt (int64 (issue .ID ), 10 ),
117+ IssueURL : issue .WebURL ,
118+ }, nil
116119 }
117120 createdIssue , _ , err := i .client .Issues .CreateIssue (i .options .ProjectName , & gitlab.CreateIssueOptions {
118121 Title : & summary ,
@@ -134,23 +137,15 @@ func (i *Integration) Name() string {
134137}
135138
136139func (i * Integration ) CloseIssue (event * output.ResultEvent ) error {
137- searchIn := "title"
138- searchState := "all"
139-
140140 summary := format .Summary (event )
141- issues , _ , err := i .client .Issues .ListProjectIssues (i .options .ProjectName , & gitlab.ListProjectIssuesOptions {
142- In : & searchIn ,
143- State : & searchState ,
144- Search : & summary ,
145- })
141+ issue , err := i .findIssueByTitle (summary )
146142 if err != nil {
147143 return err
148144 }
149- if len ( issues ) <= 0 {
145+ if issue == nil {
150146 return nil
151147 }
152148
153- issue := issues [0 ]
154149 state := "close"
155150 _ , _ , err = i .client .Issues .UpdateIssue (i .options .ProjectName , issue .IID , & gitlab.UpdateIssueOptions {
156151 StateEvent : & state ,
@@ -161,6 +156,49 @@ func (i *Integration) CloseIssue(event *output.ResultEvent) error {
161156 return nil
162157}
163158
159+ func (i * Integration ) findIssueByTitle (title string ) (* gitlab.Issue , error ) {
160+ pageSize := i .options .DuplicateIssuePageSize
161+ if pageSize <= 0 {
162+ pageSize = 100
163+ }
164+ maxPages := i .options .DuplicateIssueMaxPages
165+
166+ searchIn := "title"
167+ searchState := "all"
168+ page := 1
169+
170+ for {
171+ if maxPages > 0 && page > maxPages {
172+ return nil , nil
173+ }
174+
175+ issues , _ , err := i .client .Issues .ListProjectIssues (i .options .ProjectName , & gitlab.ListProjectIssuesOptions {
176+ In : & searchIn ,
177+ State : & searchState ,
178+ Search : & title ,
179+ ListOptions : gitlab.ListOptions {
180+ Page : page ,
181+ PerPage : pageSize ,
182+ },
183+ })
184+ if err != nil {
185+ return nil , err
186+ }
187+
188+ for _ , issue := range issues {
189+ if issue .Title == title {
190+ return issue , nil
191+ }
192+ }
193+
194+ if len (issues ) < pageSize {
195+ return nil , nil
196+ }
197+
198+ page ++
199+ }
200+ }
201+
164202// ShouldFilter determines if an issue should be logged to this tracker
165203func (i * Integration ) ShouldFilter (event * output.ResultEvent ) bool {
166204 if i .options .AllowList != nil && ! i .options .AllowList .GetMatch (event ) {
0 commit comments