Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
blank_issues_enabled: true

contact_links:
- name: "Has your issue already been reported?"
url: "https://github.com/issues?q=repo%3Aruncat-dev%2FRunCat365"
about: "Please check the list of existing issues before opening a new one to avoid duplicates."
67 changes: 67 additions & 0 deletions .github/scripts/check-duplicates.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
module.exports = async ({ github, context }) => {
const issue = context.payload.issue;
const owner = context.repo.owner;
const repo = context.repo.repo;
const title = issue.title;

// Clean special characters but keep the natural phrasing
const cleanTitle = title
.replace(/[^\w\s]/gi, ' ')
.replace(/\s+/g, ' ')
.trim();

const words = cleanTitle.split(/\s+/);

if (cleanTitle.length < 10 || words.length < 3) {
console.log("Title is too short or lacks context to search for duplicates. Skipping.");
return;
}

const query = `repo:${owner}/${repo} in:title ${cleanTitle} -number:${issue.number}`;
console.log(`Searching with hybrid query: "${query}"`);

try {
const response = await github.rest.search.issuesAndPullRequests({
q: query,
search_type: 'hybrid',
per_page: 5
});

const items = response.data.items;

if (items && items.length > 0) {
let commentBody = `### 🔍 Potential Duplicate Issues or PRs Found\n\n`;
commentBody += `Hello @${issue.user.login},\n\n`;
commentBody += `We found some existing issues or Pull Requests that might cover a similar topic. Please review them to see if they already address your concern:\n\n`;

for (const item of items) {
const type = item.pull_request ? 'Pull Request' : 'Issue';
const state = item.state === 'open' ? '🟢 Open' : '🔴 Closed';
commentBody += `- [${type} #${item.number}](${item.html_url}) - *${item.title}* (${state})\n`;
}

commentBody += `\n---\n`;
commentBody += `*This is an automated check. Maintainers will review this issue and decide whether to close it as a duplicate or keep it open.*`;

await github.rest.issues.createComment({
owner: owner,
repo: repo,
issue_number: issue.number,
body: commentBody
});

await github.rest.issues.addLabels({
owner: owner,
repo: repo,
issue_number: issue.number,
labels: ['potential-duplicate']
});

console.log(`Found ${items.length} potential duplicates and commented.`);
} else {
console.log("No potential duplicates found.");
}
} catch (error) {
console.error("Error searching for duplicates:", error);
}
};
23 changes: 23 additions & 0 deletions .github/workflows/check-duplicates.yml

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feature was implemented using AI assistance and is pending verification to ensure it works as expected

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Check Duplicate Issues and PRs

on:
issues:
types: [opened]

permissions:
issues: write
pull-requests: read

jobs:
check-duplicates:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Search and notify duplicates
uses: actions/github-script@v7
with:
script: |
const script = require('./.github/scripts/check-duplicates.cjs');
await script({ github, context });