Skip to content

Commit 636c49b

Browse files
committed
Add script to remove duplicate issues
1 parent 776815a commit 636c49b

File tree

2 files changed

+111
-0
lines changed

2 files changed

+111
-0
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Duplicate issues removal script
2+
3+
This script helps remove duplicate issues from a GitHub repository by closing newer duplicate issues.
4+
5+
## Prerequisites
6+
7+
1. Set up environment variables:
8+
- Create a `.env` file in the root directory
9+
- Add the GitHub personal access token of the bot that manage issues on your collection with repo permissions:
10+
```
11+
OTA_ENGINE_GITHUB_TOKEN=your_github_token
12+
```
13+
14+
2. Configure the target repository in `config/development.json`:
15+
```json
16+
{
17+
"@opentermsarchive/engine": {
18+
"reporter": {
19+
"githubIssues": {
20+
"repositories": {
21+
"declarations": "owner/repository"
22+
}
23+
}
24+
}
25+
}
26+
}
27+
```
28+
29+
## Usage
30+
31+
Run the script using:
32+
33+
```
34+
node scripts/reporter/duplicate/index.js
35+
```
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import 'dotenv/config';
2+
import config from 'config';
3+
import { Octokit } from 'octokit';
4+
5+
async function removeDuplicateIssues() {
6+
try {
7+
const repository = config.get('@opentermsarchive/engine.reporter.githubIssues.repositories.declarations');
8+
const [ owner, repo ] = repository.split('/');
9+
10+
if (!repository) {
11+
throw new Error('Repository configuration is not set');
12+
}
13+
14+
const octokit = new Octokit({ auth: process.env.OTA_ENGINE_GITHUB_TOKEN });
15+
16+
console.log(`Getting issues from repository ${repository}…`);
17+
18+
const issues = await octokit.paginate('GET /repos/{owner}/{repo}/issues', {
19+
owner,
20+
repo,
21+
state: 'open',
22+
per_page: 100,
23+
});
24+
25+
const onlyIssues = issues.filter(issue => !issue.pull_request);
26+
const issuesByTitle = new Map();
27+
let counter = 0;
28+
29+
console.log(`Found ${onlyIssues.length} issues`);
30+
31+
for (const issue of onlyIssues) {
32+
if (!issuesByTitle.has(issue.title)) {
33+
issuesByTitle.set(issue.title, issue);
34+
continue;
35+
}
36+
37+
const existingIssue = issuesByTitle.get(issue.title);
38+
39+
console.log(`Found duplicate for issue "${issue.title}"`);
40+
41+
let issueToClose;
42+
43+
if (new Date(issue.created_at) > new Date(existingIssue.created_at)) {
44+
issueToClose = issue;
45+
} else {
46+
issueToClose = existingIssue;
47+
issuesByTitle.set(issue.title, issue);
48+
}
49+
50+
await octokit.request('PATCH /repos/{owner}/{repo}/issues/{issue_number}', { /* eslint-disable-line no-await-in-loop */
51+
owner,
52+
repo,
53+
issue_number: issueToClose.number,
54+
state: 'closed',
55+
});
56+
57+
await octokit.request('POST /repos/{owner}/{repo}/issues/{issue_number}/comments', { /* eslint-disable-line no-await-in-loop */
58+
owner,
59+
repo,
60+
issue_number: issueToClose.number,
61+
body: 'Closed duplicate issue.',
62+
});
63+
64+
counter++;
65+
console.log(`Closed issue #${issueToClose.number}: ${issueToClose.html_url}`);
66+
}
67+
68+
console.log(`Removed ${counter} issues`);
69+
console.log('Duplicate removal process completed');
70+
} catch (error) {
71+
console.log(`Failed to remove duplicate issues: ${error.stack}`);
72+
process.exit(1);
73+
}
74+
}
75+
76+
removeDuplicateIssues();

0 commit comments

Comments
 (0)