mirror of
https://github.com/herdrdev/herdr.git
synced 2026-09-22 00:01:06 +00:00
ci: remove automatic issue gate
This commit is contained in:
@@ -1,256 +0,0 @@
|
||||
name: Issue Gate
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
check-template:
|
||||
if: ${{ !github.event.issue.pull_request }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Close non-template issues
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
github-token: ${{ secrets.KANGAL_GITHUB_TOKEN }}
|
||||
script: |
|
||||
const issue = context.payload.issue;
|
||||
const author = issue.user.login;
|
||||
const sender = context.payload.sender?.login ?? author;
|
||||
const bugConfirmationPattern = /^\s*-\s*\[[xX]\]\s*I confirm this is a reproducible bug, not a feature request, idea, question, contribution proposal, or direction check\.\s*$/m;
|
||||
const reproductionConfirmationPattern = /^\s*-\s*\[[xX]\]\s*I reproduced this bug on the version and environment reported below using the exact steps provided\.\s*$/m;
|
||||
const requiredSections = [
|
||||
'### Is this a reproducible bug?',
|
||||
'### Current behavior',
|
||||
'### Expected behavior',
|
||||
'### Reproduction',
|
||||
'### Impact',
|
||||
'### Environment',
|
||||
];
|
||||
const maxBodyLength = 8000;
|
||||
const maxExtraHeadings = 1;
|
||||
|
||||
function escapeRegExp(value) {
|
||||
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
|
||||
function extractSection(body, heading) {
|
||||
const pattern = new RegExp(`^${escapeRegExp(heading)}\\s*$`, 'm');
|
||||
const match = pattern.exec(body);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const sectionStart = match.index + match[0].length;
|
||||
const rest = body.slice(sectionStart);
|
||||
const headingIndex = requiredSections.indexOf(heading);
|
||||
const nextTemplateHeading = requiredSections
|
||||
.slice(headingIndex + 1)
|
||||
.map((section) => {
|
||||
const nextPattern = new RegExp(`^${escapeRegExp(section)}\\s*$`, 'm');
|
||||
const nextMatch = nextPattern.exec(rest);
|
||||
return nextMatch ? nextMatch.index : -1;
|
||||
})
|
||||
.filter((index) => index !== -1)
|
||||
.sort((left, right) => left - right)[0];
|
||||
return (nextTemplateHeading === undefined ? rest : rest.slice(0, nextTemplateHeading)).trim();
|
||||
}
|
||||
|
||||
function hasContent(section) {
|
||||
return typeof section === 'string' && section.trim().length > 0;
|
||||
}
|
||||
|
||||
function hasEnvironmentContent(section) {
|
||||
if (!hasContent(section)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return section.split(/\r?\n/).some((line) => {
|
||||
const value = line.trim();
|
||||
return value.length > 0 && !/^(?:-\s*)?[^:]+:\s*$/.test(value);
|
||||
});
|
||||
}
|
||||
|
||||
function bodyWithoutFencedCodeBlocks(value) {
|
||||
let inFence = false;
|
||||
return value
|
||||
.split(/\r?\n/)
|
||||
.filter((line) => {
|
||||
if (/^\s{0,3}(```|~~~)/.test(line)) {
|
||||
inFence = !inFence;
|
||||
return false;
|
||||
}
|
||||
return !inFence;
|
||||
})
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
async function getPermission(username) {
|
||||
try {
|
||||
const { data: permissionLevel } = await github.rest.repos.getCollaboratorPermissionLevel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
username,
|
||||
});
|
||||
return permissionLevel.permission;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const maintainerPermissions = ['admin', 'maintain', 'write'];
|
||||
const authorPermission = await getPermission(author);
|
||||
if (maintainerPermissions.includes(authorPermission)) {
|
||||
console.log(`${author} is a collaborator with ${authorPermission} access; leaving issue open`);
|
||||
return;
|
||||
}
|
||||
|
||||
const senderPermission = sender === author ? authorPermission : await getPermission(sender);
|
||||
if (maintainerPermissions.includes(senderPermission)) {
|
||||
console.log(`${sender} opened this issue with ${senderPermission} access; leaving issue open`);
|
||||
return;
|
||||
}
|
||||
|
||||
const body = issue.body || '';
|
||||
const translationConfirmationPattern = /^\s*-\s*\[[xX]\]\s*I confirm this is a translation issue in the herdr docs, not a bug report, feature request, or question\.\s*$/m;
|
||||
const translationSections = ['### Page', '### Language', '### What is wrong'];
|
||||
if (
|
||||
translationConfirmationPattern.test(body) &&
|
||||
translationSections.every((section) => body.includes(section))
|
||||
) {
|
||||
console.log(`#${issue.number} matches the translation issue template; leaving issue open`);
|
||||
return;
|
||||
}
|
||||
|
||||
const hasBugConfirmation = bugConfirmationPattern.test(body) && reproductionConfirmationPattern.test(body);
|
||||
const allowedHeadings = new Set(requiredSections);
|
||||
const headingBody = bodyWithoutFencedCodeBlocks(body);
|
||||
const headings = [...headingBody.matchAll(/^\s{0,3}#{1,6}\s+(.+?)\s*$/gm)].map((match) => match[0].trim());
|
||||
const headingCounts = new Map(
|
||||
requiredSections.map((section) => [
|
||||
section,
|
||||
headings.filter((heading) => heading === section).length,
|
||||
]),
|
||||
);
|
||||
const hasEveryRequiredHeading = requiredSections.every(
|
||||
(section) => headingCounts.get(section) > 0,
|
||||
);
|
||||
const hasBugTemplate = requiredSections.every(
|
||||
(section) => headingCounts.get(section) === 1,
|
||||
);
|
||||
const repeatedTemplateHeadings = requiredSections.filter(
|
||||
(section) => headingCounts.get(section) > 1,
|
||||
);
|
||||
const extraHeadings = headings.filter((heading) => !allowedHeadings.has(heading));
|
||||
const structuralViolations = [];
|
||||
if (repeatedTemplateHeadings.length > 0) {
|
||||
structuralViolations.push('one or more required template headings are repeated');
|
||||
}
|
||||
if (extraHeadings.length > maxExtraHeadings) {
|
||||
structuralViolations.push(`${extraHeadings.length} extra markdown headings were added`);
|
||||
}
|
||||
if (body.length > maxBodyLength) {
|
||||
structuralViolations.push(`the report is ${body.length} characters; the limit is ${maxBodyLength}`);
|
||||
}
|
||||
|
||||
const currentBehavior = extractSection(body, '### Current behavior');
|
||||
const expectedBehavior = extractSection(body, '### Expected behavior');
|
||||
const reproduction = extractSection(body, '### Reproduction');
|
||||
const impact = extractSection(body, '### Impact');
|
||||
const environment = extractSection(body, '### Environment');
|
||||
const hasRequiredContent = [
|
||||
currentBehavior,
|
||||
expectedBehavior,
|
||||
reproduction,
|
||||
impact,
|
||||
].every(hasContent);
|
||||
const hasEnvironment = hasEnvironmentContent(environment);
|
||||
|
||||
if (hasBugConfirmation && hasEveryRequiredHeading && structuralViolations.length > 0) {
|
||||
const message = [
|
||||
`hi @${author}, thanks for opening this.`,
|
||||
'',
|
||||
'this report exceeds the bug report structure limits:',
|
||||
...structuralViolations.map((violation) => `- ${violation}`),
|
||||
'',
|
||||
'please keep the report within the required template, under 8,000 characters, and focused on observed behavior, exact reproduction, impact, and environment.',
|
||||
'',
|
||||
'closing this so the issue tracker stays limited to concise, actionable bug reports.',
|
||||
].join('\n');
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
body: message,
|
||||
});
|
||||
|
||||
await github.rest.issues.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
state: 'closed',
|
||||
state_reason: 'not_planned',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasBugConfirmation && hasBugTemplate && hasRequiredContent && hasEnvironment) {
|
||||
console.log(`#${issue.number} matches the bug report template`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasBugConfirmation && hasBugTemplate && hasRequiredContent && !hasEnvironment) {
|
||||
const message = [
|
||||
`hi @${author}, thanks for opening this.`,
|
||||
'',
|
||||
'this bug report has no filled environment details. please edit the environment section with the Herdr version, update channel, operating system, and terminal.',
|
||||
'',
|
||||
'shell and relevant config are optional, but they help when they affect the bug.',
|
||||
].join('\n');
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
body: message,
|
||||
});
|
||||
|
||||
console.log(`#${issue.number} has no environment details; leaving issue open for correction`);
|
||||
return;
|
||||
}
|
||||
|
||||
const message = [
|
||||
`hi @${author}, thanks for opening this.`,
|
||||
'',
|
||||
'this issue does not appear to use the full bug report template. herdr issues are only for reproducible bugs and maintainer-created or maintainer-converted work items.',
|
||||
'',
|
||||
'bug reports must include the reproducible-bug confirmation, current behavior, expected behavior, reproduction, impact, and environment fields with herdr version, update channel, operating system, and terminal.',
|
||||
'',
|
||||
'feature requests, ideas, questions, contribution proposals, and direction checks belong in discussions:',
|
||||
`https://github.com/${context.repo.owner}/${context.repo.repo}/discussions`,
|
||||
'',
|
||||
'please read the contributing guidelines before opening issues or prs:',
|
||||
`https://github.com/${context.repo.owner}/${context.repo.repo}/blob/${context.payload.repository.default_branch}/CONTRIBUTING.md`,
|
||||
'',
|
||||
'closing this so the issue tracker stays limited to actionable bug reports.',
|
||||
].join('\n');
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
body: message,
|
||||
});
|
||||
|
||||
await github.rest.issues.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
state: 'closed',
|
||||
state_reason: 'not_planned',
|
||||
});
|
||||
Reference in New Issue
Block a user