@@ -164,6 +164,106 @@ function parseCommaList(value: string | undefined): string[] | undefined {
164164 . filter ( Boolean ) ;
165165}
166166
167+ /**
168+ * Build the SearchRepositoryIssuesBody from parsed CLI options.
169+ * Resolves tool names/UUIDs via the Codacy API when --tools is provided.
170+ */
171+ async function buildFilterBody ( opts : Record < string , any > ) : Promise < SearchRepositoryIssuesBody > {
172+ const body : SearchRepositoryIssuesBody = { } ;
173+
174+ if ( opts . branch ) body . branchName = opts . branch ;
175+
176+ const patterns = parseCommaList ( opts . patterns ) ;
177+ if ( patterns ) body . patternIds = patterns ;
178+
179+ const severity = parseCommaList ( opts . severities ) ;
180+ if ( severity ) body . levels = severity . map ( normalizeSeverity ) ;
181+
182+ const category = parseCommaList ( opts . categories ) ;
183+ if ( category ) body . categories = category . map ( normalizeCategory ) ;
184+
185+ const language = parseCommaList ( opts . languages ) ;
186+ if ( language ) body . languages = language ;
187+
188+ const tags = parseCommaList ( opts . tags ) ;
189+ if ( tags ) body . tags = tags ;
190+
191+ const author = parseCommaList ( opts . authors ) ;
192+ if ( author ) body . authorEmails = author ;
193+
194+ // --false-positives and --bulk-ignore both restrict the API query to FP issues only
195+ if ( opts . falsePositives || opts . bulkIgnore ) body . onlyPotentialFalsePositives = true ;
196+
197+ const toolInputs = parseCommaList ( opts . tools ) ;
198+ if ( toolInputs ) {
199+ body . toolUuids = await resolveToolUuids ( toolInputs , async ( ) => {
200+ const tools : Tool [ ] = [ ] ;
201+ let cursor : string | undefined ;
202+ do {
203+ const resp = await ToolsService . listTools ( cursor , 100 ) ;
204+ tools . push ( ...resp . data ) ;
205+ cursor = resp . pagination ?. cursor ;
206+ } while ( cursor ) ;
207+ return tools ;
208+ } ) ;
209+ }
210+
211+ return body ;
212+ }
213+
214+ /**
215+ * Fetch every false positive issue (all pages) then ignore them in batches of
216+ * BULK_BATCH_SIZE. Prints progress via spinners and exits when done.
217+ */
218+ async function executeBulkIgnore (
219+ provider : string ,
220+ organization : string ,
221+ repository : string ,
222+ body : SearchRepositoryIssuesBody ,
223+ comment : string | undefined ,
224+ ) : Promise < void > {
225+ const fetchSpinner = ora ( "Fetching false positive issues..." ) . start ( ) ;
226+ const allIssues : CommitIssue [ ] = [ ] ;
227+ let cursor : string | undefined ;
228+
229+ do {
230+ const resp = await AnalysisService . searchRepositoryIssues (
231+ provider ,
232+ organization ,
233+ repository ,
234+ cursor ,
235+ 100 ,
236+ body ,
237+ ) ;
238+ allIssues . push ( ...resp . data ) ;
239+ cursor = resp . pagination ?. cursor ;
240+ } while ( cursor ) ;
241+
242+ fetchSpinner . stop ( ) ;
243+
244+ if ( allIssues . length === 0 ) {
245+ console . log ( ansis . green ( "No false positive issues found." ) ) ;
246+ return ;
247+ }
248+
249+ const count = allIssues . length ;
250+ const plural = count === 1 ? "" : "s" ;
251+ console . log ( `Found ${ ansis . bold ( String ( count ) ) } false positive issue${ plural } .` ) ;
252+
253+ const ignoreSpinner = ora ( `Ignoring ${ count } issue${ plural } ...` ) . start ( ) ;
254+ const issueIds = allIssues . map ( ( i ) => i . issueId ) ;
255+
256+ for ( let i = 0 ; i < issueIds . length ; i += BULK_BATCH_SIZE ) {
257+ await AnalysisService . bulkIgnoreIssues ( provider , organization , repository , {
258+ issueIds : issueIds . slice ( i , i + BULK_BATCH_SIZE ) ,
259+ reason : "FalsePositive" ,
260+ comment : comment || undefined ,
261+ } ) ;
262+ }
263+
264+ ignoreSpinner . succeed ( `Ignored ${ ansis . bold ( String ( count ) ) } false positive issue${ plural } .` ) ;
265+ }
266+
167267export function registerIssuesCommand ( program : Command ) {
168268 program
169269 . command ( "issues" )
@@ -190,7 +290,7 @@ export function registerIssuesCommand(program: Command) {
190290 . option ( "-O, --overview" , "show issue count totals instead of the issues list" )
191291 . option ( "-F, --false-positives" , "only show issues that are potential false positives" )
192292 . option ( "-I, --bulk-ignore" , "ignore all false positive issues matching the current filters" )
193- . option ( "-m, --comment <comment>" , "optional comment when using --bulk-ignore" )
293+ . option ( "-m, --ignore- comment <comment>" , "optional comment when using --bulk-ignore" )
194294 . addHelpText (
195295 "after" ,
196296 `
@@ -202,7 +302,7 @@ Examples:
202302 $ codacy issues gh my-org my-repo --limit 500
203303 $ codacy issues gh my-org my-repo --false-positives
204304 $ codacy issues gh my-org my-repo --bulk-ignore --branch main
205- $ codacy issues gh my-org my-repo --bulk-ignore --patterns security-rule --comment "Confirmed FPs"
305+ $ codacy issues gh my-org my-repo --bulk-ignore --patterns security-rule --ignore- comment "Confirmed FPs"
206306 $ codacy issues gh my-org my-repo --output json` ,
207307 )
208308 . action ( async function (
@@ -216,84 +316,15 @@ Examples:
216316 const opts = this . opts ( ) ;
217317 const format = getOutputFormat ( this ) ;
218318 const isOverview = ! ! opts . overview ;
219- const isBulkIgnore = ! ! opts . bulkIgnore ;
220-
221- // Build the shared filter body from CLI options
222- const body : SearchRepositoryIssuesBody = { } ;
223- if ( opts . branch ) body . branchName = opts . branch ;
224- const patterns = parseCommaList ( opts . patterns ) ;
225- if ( patterns ) body . patternIds = patterns ;
226- const severity = parseCommaList ( opts . severities ) ;
227- if ( severity ) body . levels = severity . map ( normalizeSeverity ) ;
228- const category = parseCommaList ( opts . categories ) ;
229- if ( category ) body . categories = category . map ( normalizeCategory ) ;
230- const language = parseCommaList ( opts . languages ) ;
231- if ( language ) body . languages = language ;
232- const tags = parseCommaList ( opts . tags ) ;
233- if ( tags ) body . tags = tags ;
234- const author = parseCommaList ( opts . authors ) ;
235- if ( author ) body . authorEmails = author ;
236- // --false-positives and --bulk-ignore both restrict the API query to FP issues only
237- if ( opts . falsePositives || isBulkIgnore ) body . onlyPotentialFalsePositives = true ;
238-
239- const toolInputs = parseCommaList ( opts . tools ) ;
240- if ( toolInputs ) {
241- body . toolUuids = await resolveToolUuids ( toolInputs , async ( ) => {
242- const tools : Tool [ ] = [ ] ;
243- let cursor : string | undefined ;
244- do {
245- const resp = await ToolsService . listTools ( cursor , 100 ) ;
246- tools . push ( ...resp . data ) ;
247- cursor = resp . pagination ?. cursor ;
248- } while ( cursor ) ;
249- return tools ;
250- } ) ;
251- }
252319
320+ const body = await buildFilterBody ( opts ) ;
253321 const limit = Math . min ( Math . max ( parseInt ( opts . limit , 10 ) || 100 , 1 ) , 1000 ) ;
254322
255- // --bulk-ignore: fetch all FP issues (all pages) then call bulkIgnoreIssues in batches
256- if ( isBulkIgnore ) {
257- const fetchSpinner = ora ( "Fetching false positive issues..." ) . start ( ) ;
258- const allIssues : CommitIssue [ ] = [ ] ;
259- let cursor : string | undefined ;
260-
261- do {
262- const resp = await AnalysisService . searchRepositoryIssues (
263- provider ,
264- organization ,
265- repository ,
266- cursor ,
267- 100 ,
268- body ,
269- ) ;
270- allIssues . push ( ...resp . data ) ;
271- cursor = resp . pagination ?. cursor ;
272- } while ( cursor ) ;
273-
274- fetchSpinner . stop ( ) ;
275-
276- if ( allIssues . length === 0 ) {
277- console . log ( ansis . green ( "No false positive issues found." ) ) ;
278- return ;
323+ if ( opts . bulkIgnore ) {
324+ if ( this . getOptionValueSource ( "limit" ) === "cli" ) {
325+ this . error ( "--limit cannot be used with --bulk-ignore; the bulk-ignore path always processes all matching issues" ) ;
279326 }
280-
281- const count = allIssues . length ;
282- const plural = count === 1 ? "" : "s" ;
283- console . log ( `Found ${ ansis . bold ( String ( count ) ) } false positive issue${ plural } .` ) ;
284-
285- const ignoreSpinner = ora ( `Ignoring ${ count } issue${ plural } ...` ) . start ( ) ;
286- const issueIds = allIssues . map ( ( i ) => i . issueId ) ;
287-
288- for ( let i = 0 ; i < issueIds . length ; i += BULK_BATCH_SIZE ) {
289- await AnalysisService . bulkIgnoreIssues ( provider , organization , repository , {
290- issueIds : issueIds . slice ( i , i + BULK_BATCH_SIZE ) ,
291- reason : "FalsePositive" ,
292- comment : opts . comment || undefined ,
293- } ) ;
294- }
295-
296- ignoreSpinner . succeed ( `Ignored ${ ansis . bold ( String ( count ) ) } false positive issue${ plural } .` ) ;
327+ await executeBulkIgnore ( provider , organization , repository , body , opts . ignoreComment ) ;
297328 return ;
298329 }
299330
0 commit comments