Skip to content

Commit 9f067b6

Browse files
committed
feat(gh-actions): Implement enhanced duplicate issue prevention logic across workflows
1 parent 1fcc769 commit 9f067b6

File tree

4 files changed

+258
-16
lines changed

4 files changed

+258
-16
lines changed

.github/workflows/quality-security-checks.yml

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,10 @@ jobs:
303303
with:
304304
github-token: ${{ secrets.GITHUB_TOKEN }}
305305
script: |
306+
// Enhanced duplicate prevention logic:
307+
// - Checks both open and recently closed issues (last 7 days)
308+
// - Prevents spam from repeated workflow runs
309+
// - Uses specific labels and title patterns for precise matching
306310
const vulnerabilities = `${{ needs.security-check.outputs.vulnerabilities }}`;
307311
308312
const issueBody = `
@@ -353,15 +357,28 @@ jobs:
353357
**Priority:** High - Please address these security vulnerabilities promptly.
354358
`;
355359
356-
// Check if a similar issue already exists
357-
const existingIssues = await github.rest.issues.listForRepo({
360+
// Check if a similar issue already exists (open or recently closed)
361+
const sevenDaysAgo = new Date();
362+
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
363+
364+
const existingOpenIssues = await github.rest.issues.listForRepo({
358365
owner: context.repo.owner,
359366
repo: context.repo.repo,
360367
labels: ['security', 'vulnerability'],
361368
state: 'open'
362369
});
363370
364-
const securityIssueExists = existingIssues.data.some(issue =>
371+
const existingClosedIssues = await github.rest.issues.listForRepo({
372+
owner: context.repo.owner,
373+
repo: context.repo.repo,
374+
labels: ['security', 'vulnerability'],
375+
state: 'closed',
376+
since: sevenDaysAgo.toISOString()
377+
});
378+
379+
const allRelevantIssues = [...existingOpenIssues.data, ...existingClosedIssues.data];
380+
381+
const securityIssueExists = allRelevantIssues.some(issue =>
365382
issue.title.includes('Security Vulnerabilities Detected')
366383
);
367384
@@ -376,7 +393,7 @@ jobs:
376393
377394
console.log('Created new security vulnerability issue');
378395
} else {
379-
console.log('Security issue already exists, skipping creation');
396+
console.log('Security issue already exists or was recently closed, skipping creation');
380397
}
381398
382399
# Update README badges

.github/workflows/realtime-status-dashboard.yml

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,11 @@ jobs:
333333
with:
334334
github-token: ${{ secrets.GITHUB_TOKEN }}
335335
script: |
336+
// Enhanced duplicate prevention logic:
337+
// - Checks both open and recently closed issues (last 7 days)
338+
// - Prevents spam from repeated workflow runs
339+
// - Uses specific labels and title patterns for precise matching
340+
336341
const deployStatus = '${{ needs.status-collection.outputs.deployment-status }}';
337342
const apiStatus = '${{ needs.status-collection.outputs.api-status }}';
338343
const errorRate = '${{ needs.status-collection.outputs.error-rate }}';
@@ -398,15 +403,28 @@ jobs:
398403
**This incident was automatically detected by our monitoring system.**
399404
`;
400405
401-
// Check if similar incident already exists
402-
const existingIssues = await github.rest.issues.listForRepo({
406+
// Check if similar incident already exists (open or recently closed)
407+
const sevenDaysAgo = new Date();
408+
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
409+
410+
const existingOpenIssues = await github.rest.issues.listForRepo({
403411
owner: context.repo.owner,
404412
repo: context.repo.repo,
405413
labels: ['incident', 'monitoring'],
406414
state: 'open'
407415
});
408416
409-
const recentIncident = existingIssues.data.some(issue =>
417+
const existingClosedIssues = await github.rest.issues.listForRepo({
418+
owner: context.repo.owner,
419+
repo: context.repo.repo,
420+
labels: ['incident', 'monitoring'],
421+
state: 'closed',
422+
since: sevenDaysAgo.toISOString()
423+
});
424+
425+
const allRelevantIssues = [...existingOpenIssues.data, ...existingClosedIssues.data];
426+
427+
const recentIncident = allRelevantIssues.some(issue =>
410428
issue.title.includes('Service Incident') || issue.title.includes('Service Degradation')
411429
);
412430
@@ -421,5 +439,5 @@ jobs:
421439
422440
console.log('Created incident issue');
423441
} else {
424-
console.log('Recent incident issue already exists, skipping creation');
442+
console.log('Recent incident issue already exists or was recently closed, skipping creation');
425443
}

.github/workflows/sensitive-data-monitor.yml

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,10 @@ jobs:
410410
with:
411411
github-token: ${{ secrets.GITHUB_TOKEN }}
412412
script: |
413+
// Enhanced duplicate prevention logic:
414+
// - Checks both open and recently closed issues (last 7 days)
415+
// - Prevents spam from repeated workflow runs
416+
// - Uses specific labels and title patterns for precise matching
413417
const secretsFound = process.env.SECRETS_FOUND === 'true';
414418
const exposedData = process.env.EXPOSED_DATA === 'true';
415419
@@ -464,14 +468,45 @@ jobs:
464468
**Priority:** Critical - Address immediately to prevent security breaches.
465469
`;
466470
467-
await github.rest.issues.create({
471+
// Check if a similar sensitive data issue already exists (open or recently closed)
472+
const sevenDaysAgo = new Date();
473+
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
474+
475+
const existingOpenIssues = await github.rest.issues.listForRepo({
476+
owner: context.repo.owner,
477+
repo: context.repo.repo,
478+
labels: ['security', 'sensitive-data'],
479+
state: 'open'
480+
});
481+
482+
const existingClosedIssues = await github.rest.issues.listForRepo({
468483
owner: context.repo.owner,
469484
repo: context.repo.repo,
470-
title: '🚨 Sensitive Data Exposure - Critical Security Issue',
471-
body: issueBody,
472-
labels: ['security', 'critical', 'sensitive-data']
485+
labels: ['security', 'sensitive-data'],
486+
state: 'closed',
487+
since: sevenDaysAgo.toISOString()
473488
});
474489
490+
const allRelevantIssues = [...existingOpenIssues.data, ...existingClosedIssues.data];
491+
492+
const sensitiveDataIssueExists = allRelevantIssues.some(issue =>
493+
issue.title.includes('Sensitive Data Exposure')
494+
);
495+
496+
if (!sensitiveDataIssueExists) {
497+
await github.rest.issues.create({
498+
owner: context.repo.owner,
499+
repo: context.repo.repo,
500+
title: '🚨 Sensitive Data Exposure - Critical Security Issue',
501+
body: issueBody,
502+
labels: ['security', 'critical', 'sensitive-data']
503+
});
504+
505+
console.log('Created new sensitive data issue');
506+
} else {
507+
console.log('Sensitive data issue already exists or was recently closed, skipping creation');
508+
}
509+
475510
- name: Create CSP violation issue
476511
if: needs.content-security-scan.outputs.csp-violations == 'true'
477512
uses: actions/github-script@v7
@@ -481,6 +516,10 @@ jobs:
481516
with:
482517
github-token: ${{ secrets.GITHUB_TOKEN }}
483518
script: |
519+
// Enhanced duplicate prevention logic:
520+
// - Checks both open and recently closed issues (last 7 days)
521+
// - Prevents spam from repeated workflow runs
522+
// - Uses specific labels and title patterns for precise matching
484523
const issueBody = `
485524
## 🛡️ Content Security Policy Violations Detected
486525
@@ -517,14 +556,45 @@ jobs:
517556
**Priority:** High - Address to improve security posture.
518557
`;
519558
520-
await github.rest.issues.create({
559+
// Check if a similar CSP violation issue already exists (open or recently closed)
560+
const sevenDaysAgo = new Date();
561+
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
562+
563+
const existingOpenIssues = await github.rest.issues.listForRepo({
564+
owner: context.repo.owner,
565+
repo: context.repo.repo,
566+
labels: ['security', 'csp'],
567+
state: 'open'
568+
});
569+
570+
const existingClosedIssues = await github.rest.issues.listForRepo({
521571
owner: context.repo.owner,
522572
repo: context.repo.repo,
523-
title: '🛡️ Content Security Policy Violations',
524-
body: issueBody,
525-
labels: ['security', 'csp', 'high-priority']
573+
labels: ['security', 'csp'],
574+
state: 'closed',
575+
since: sevenDaysAgo.toISOString()
526576
});
527577
578+
const allRelevantIssues = [...existingOpenIssues.data, ...existingClosedIssues.data];
579+
580+
const cspIssueExists = allRelevantIssues.some(issue =>
581+
issue.title.includes('Content Security Policy Violations')
582+
);
583+
584+
if (!cspIssueExists) {
585+
await github.rest.issues.create({
586+
owner: context.repo.owner,
587+
repo: context.repo.repo,
588+
title: '🛡️ Content Security Policy Violations',
589+
body: issueBody,
590+
labels: ['security', 'csp', 'high-priority']
591+
});
592+
593+
console.log('Created new CSP violation issue');
594+
} else {
595+
console.log('CSP violation issue already exists or was recently closed, skipping creation');
596+
}
597+
528598
# Generate security report
529599
generate-security-report:
530600
name: 📊 Generate Security Report
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# GitHub Actions Duplicate Issue Prevention
2+
3+
## Overview
4+
5+
This document explains the duplicate issue prevention system implemented across all GitHub Actions workflows that create issues.
6+
7+
## Problem Solved
8+
9+
Previously, GitHub Actions workflows could create duplicate issues when:
10+
- Multiple workflow runs triggered the same issue type
11+
- Issues were closed and reopened by subsequent runs
12+
- Different workflows detected the same underlying problem
13+
14+
## Solution Implementation
15+
16+
### Enhanced Duplicate Prevention Logic
17+
18+
All issue-creating workflows now implement a comprehensive duplicate prevention system:
19+
20+
1. **Time-based Check**: Checks both open and recently closed issues (last 7 days)
21+
2. **Label-based Filtering**: Uses specific labels to identify relevant issues
22+
3. **Title Pattern Matching**: Matches specific title patterns for precise identification
23+
4. **State Awareness**: Considers both open and recently closed issues
24+
25+
### Modified Workflows
26+
27+
#### 1. Quality Security Checks (`quality-security-checks.yml`)
28+
29+
- **Issue Type**: Security Vulnerabilities
30+
- **Labels**: `['security', 'vulnerability']`
31+
- **Title Pattern**: "Security Vulnerabilities Detected"
32+
- **Prevention**: Checks open + 7-day closed issues
33+
34+
#### 2. Sensitive Data Monitor (`sensitive-data-monitor.yml`)
35+
36+
- **Issue Types**:
37+
- Sensitive Data Exposure (`['security', 'sensitive-data']`)
38+
- CSP Violations (`['security', 'csp']`)
39+
- **Title Patterns**:
40+
- "Sensitive Data Exposure"
41+
- "Content Security Policy Violations"
42+
- **Prevention**: Checks open + 7-day closed issues for each type
43+
44+
#### 3. Realtime Status Dashboard (`realtime-status-dashboard.yml`)
45+
46+
- **Issue Type**: Service Incidents
47+
- **Labels**: `['incident', 'monitoring']`
48+
- **Title Patterns**: "Service Incident" or "Service Degradation"
49+
- **Prevention**: Checks open + 7-day closed issues
50+
51+
## Code Implementation
52+
53+
### Example Implementation Pattern
54+
55+
```javascript
56+
// Enhanced duplicate prevention logic:
57+
// - Checks both open and recently closed issues (last 7 days)
58+
// - Prevents spam from repeated workflow runs
59+
// - Uses specific labels and title patterns for precise matching
60+
61+
const sevenDaysAgo = new Date();
62+
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
63+
64+
const existingOpenIssues = await github.rest.issues.listForRepo({
65+
owner: context.repo.owner,
66+
repo: context.repo.repo,
67+
labels: ['security', 'vulnerability'],
68+
state: 'open'
69+
});
70+
71+
const existingClosedIssues = await github.rest.issues.listForRepo({
72+
owner: context.repo.owner,
73+
repo: context.repo.repo,
74+
labels: ['security', 'vulnerability'],
75+
state: 'closed',
76+
since: sevenDaysAgo.toISOString()
77+
});
78+
79+
const allRelevantIssues = [...existingOpenIssues.data, ...existingClosedIssues.data];
80+
81+
const issueExists = allRelevantIssues.some(issue =>
82+
issue.title.includes('Security Vulnerabilities Detected')
83+
);
84+
85+
if (!issueExists) {
86+
// Create new issue
87+
} else {
88+
console.log('Issue already exists or was recently closed, skipping creation');
89+
}
90+
```
91+
92+
## Benefits
93+
94+
1. **Reduced Noise**: Eliminates duplicate issues in the repository
95+
2. **Better Organization**: Makes it easier to track and manage security issues
96+
3. **Improved Efficiency**: Prevents teams from working on duplicate reports
97+
4. **Rate Limiting**: Respects GitHub API rate limits by reducing unnecessary calls
98+
5. **Historical Awareness**: Prevents reopening recently resolved issues
99+
100+
## Configuration
101+
102+
### Adjustable Parameters
103+
104+
- **Time Window**: Currently set to 7 days, can be adjusted in each workflow
105+
- **Label Filters**: Customizable per issue type
106+
- **Title Patterns**: Can be modified for different matching strategies
107+
108+
### Future Enhancements
109+
110+
1. **Content Similarity**: Could add body content comparison for more precise matching
111+
2. **Custom Time Windows**: Different time windows for different issue types
112+
3. **Priority-based Logic**: Different behavior for critical vs. normal issues
113+
4. **Integration**: Could integrate with external issue tracking systems
114+
115+
## Monitoring
116+
117+
Each workflow logs its duplicate prevention decisions:
118+
- `"Created new [issue-type] issue"` - New issue created
119+
- `"[Issue-type] issue already exists or was recently closed, skipping creation"` - Duplicate prevented
120+
121+
## Testing
122+
123+
To test the duplicate prevention:
124+
1. Trigger a workflow that would create an issue
125+
2. Verify the issue is created
126+
3. Trigger the same workflow again immediately
127+
4. Verify no duplicate issue is created
128+
5. Close the issue and trigger again within 7 days
129+
6. Verify no new issue is created
130+
131+
## Maintenance
132+
133+
Review and update the duplicate prevention logic when:
134+
- Adding new issue types
135+
- Changing issue title formats
136+
- Modifying label strategies
137+
- Adjusting time windows for different scenarios

0 commit comments

Comments
 (0)