mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-07-26 14:32:28 +00:00
Improve bug report triage requirements
This commit is contained in:
parent
ee5c923091
commit
ed7da63eb2
2 changed files with 105 additions and 17 deletions
26
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
26
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -9,10 +9,11 @@ body:
|
|||
value: |
|
||||
Thanks for reporting a bug.
|
||||
|
||||
If we can reproduce it, we can usually fix it. This form is just to get the basics in one place.
|
||||
If we can reproduce it, we can usually fix it. Please describe the bug in enough detail that someone else can understand what failed without watching a video or guessing from the title.
|
||||
Please replace the default title with a short summary of the actual problem.
|
||||
|
||||
If the app crashes, logs are required. Crash reports without logs may be labeled `needs-info`.
|
||||
Vague reports such as "not working", "broken", or "same issue" may be labeled `needs-info` and closed if the missing details are not added.
|
||||
If the app crashes or force closes, logs are required. Crash reports without logs will be closed.
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
|
@ -121,11 +122,26 @@ body:
|
|||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Bug description
|
||||
description: |
|
||||
Explain the problem in detail. Do not only write "not working" or "same issue".
|
||||
Include what screen/flow you were using, what you tapped or tried to do, what changed on screen, and any visible error text.
|
||||
placeholder: |
|
||||
I was trying to ...
|
||||
The app ...
|
||||
I saw ...
|
||||
This happens on ...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: steps
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Exact steps. If it depends on specific content, describe it (movie/series, season/episode, source/addon name) without sharing private links.
|
||||
description: Exact steps, one action per line. If it depends on specific content, describe it (movie/series, season/episode, source/addon name) without sharing private links.
|
||||
placeholder: |
|
||||
1. Open ...
|
||||
2. Navigate to ...
|
||||
|
|
@ -146,7 +162,7 @@ body:
|
|||
id: actual
|
||||
attributes:
|
||||
label: Actual behavior
|
||||
placeholder: "What actually happened (include any on-screen error text/codes)."
|
||||
placeholder: "What actually happened? Include any on-screen error text/codes and whether the app froze, crashed, ignored input, showed the wrong content, etc."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
|
@ -197,7 +213,7 @@ body:
|
|||
attributes:
|
||||
label: Logs (required for crash reports)
|
||||
description: |
|
||||
Required if the app crashes or force closes.
|
||||
Required if the app crashes or force closes. Crash reports without logs will be closed.
|
||||
For other bug reports, logs are optional but still helpful.
|
||||
|
||||
**Android:** `adb logcat -d | tail -n 300`
|
||||
|
|
|
|||
96
.github/workflows/triage-needs-info.yml
vendored
96
.github/workflows/triage-needs-info.yml
vendored
|
|
@ -55,10 +55,37 @@ jobs:
|
|||
return (value || "").replace(/\s+/g, " ").trim();
|
||||
}
|
||||
|
||||
function normalizedMeaningfulText(value) {
|
||||
return normalizeText(value)
|
||||
.replace(/^```[a-zA-Z0-9_-]*/i, "")
|
||||
.replace(/```$/i, "")
|
||||
.trim();
|
||||
}
|
||||
|
||||
function stripIssuePrefix(value) {
|
||||
return normalizeText(value).replace(/^\[[^\]]+\]:\s*/i, "").trim();
|
||||
}
|
||||
|
||||
function isEmptyish(value) {
|
||||
const text = normalizedMeaningfulText(value);
|
||||
return !text || /^(_?no response_?|n\/a|na|none|no|not available|-|\.)$/i.test(text);
|
||||
}
|
||||
|
||||
function isVague(value) {
|
||||
const text = normalizedMeaningfulText(value);
|
||||
return /^(same|same issue|me too|not working|doesn't work|doesnt work|broken|bug|issue|problem|help|please fix|crash|crashes|it crashes|see video|see screenshot|as title)$/i.test(text);
|
||||
}
|
||||
|
||||
function hasUsefulText(value, minimumLength) {
|
||||
const text = normalizedMeaningfulText(value);
|
||||
return !isEmptyish(text) && !isVague(text) && text.length >= minimumLength;
|
||||
}
|
||||
|
||||
const description = extractFirstSection([
|
||||
"Bug description",
|
||||
"Describe the bug",
|
||||
"Detailed description",
|
||||
]);
|
||||
const steps = extractSection("Steps to reproduce");
|
||||
const expected = extractSection("Expected behavior");
|
||||
const actual = extractSection("Actual behavior");
|
||||
|
|
@ -69,7 +96,7 @@ jobs:
|
|||
const extra = extractSection("Anything else? (optional)");
|
||||
const summaryTitle = stripIssuePrefix(title);
|
||||
|
||||
const looksLikeBugForm = !!(steps || expected || actual);
|
||||
const looksLikeBugForm = !!(description || steps || expected || actual);
|
||||
const isBugIssue = hasLabel("bug") || looksLikeBugForm;
|
||||
const isFeatureIssue =
|
||||
hasLabel("enhancement") ||
|
||||
|
|
@ -84,20 +111,20 @@ jobs:
|
|||
const genericTitle = /^(bug|issue|problem|help|question|crash|broken|error|bug report|short summary here|title here)$/i;
|
||||
const numericOnlyTitle = /^#?\d+$/;
|
||||
const crashPattern = /\b(crash|crashes|crashed|crashing|force close|force closes|force closed|fatal exception|app closes|app closed unexpectedly)\b/i;
|
||||
const crashContext = [summaryTitle, steps, actual, extra].map(normalizeText).join("\n");
|
||||
const crashContext = [summaryTitle, description, steps, actual, extra].map(normalizeText).join("\n");
|
||||
const isCrashIssue = crashPattern.test(crashContext);
|
||||
const normalizedLogs = normalizeText(logs);
|
||||
const hasLogs = normalizedLogs.length >= 20 && !/^(n\/a|na|none|no|not available)$/i.test(normalizedLogs);
|
||||
const normalizedLogs = normalizedMeaningfulText(logs);
|
||||
const hasLogs = normalizedLogs.length >= 20 && !isEmptyish(normalizedLogs);
|
||||
|
||||
if (!summaryTitle || summaryTitle.length < 8 || genericTitle.test(summaryTitle) || numericOnlyTitle.test(summaryTitle)) {
|
||||
problems.push("Issue title (replace the default `[Bug]:` prefix with a short summary of the actual problem)");
|
||||
}
|
||||
if (!steps || steps.length < 30) problems.push("Steps to reproduce (please list exact steps)");
|
||||
if (!expected || expected.length < 10) problems.push("Expected behavior");
|
||||
if (!actual || actual.length < 10) problems.push("Actual behavior (include any on-screen error text)");
|
||||
if (isCrashIssue && !hasLogs) {
|
||||
problems.push("Logs (required for crash reports; include a log snippet or stack trace)");
|
||||
if (!hasUsefulText(description, 50)) {
|
||||
problems.push("Bug description (explain the problem in detail, not just \"not working\" or \"same issue\")");
|
||||
}
|
||||
if (!hasUsefulText(steps, 40)) problems.push("Steps to reproduce (please list exact steps)");
|
||||
if (!hasUsefulText(expected, 10)) problems.push("Expected behavior");
|
||||
if (!hasUsefulText(actual, 20)) problems.push("Actual behavior (include any on-screen error text)");
|
||||
|
||||
async function ensureLabel(name, color, description) {
|
||||
try {
|
||||
|
|
@ -111,6 +138,50 @@ jobs:
|
|||
|
||||
const hasNeedsInfo = hasLabel(NEEDS_INFO);
|
||||
|
||||
if (isCrashIssue && !hasLogs) {
|
||||
await ensureLabel(NEEDS_INFO, NEEDS_INFO_COLOR, NEEDS_INFO_DESC);
|
||||
if (!hasNeedsInfo) {
|
||||
await github.rest.issues.addLabels({
|
||||
owner,
|
||||
repo,
|
||||
issue_number,
|
||||
labels: [NEEDS_INFO],
|
||||
});
|
||||
}
|
||||
|
||||
const marker = "<!-- nuvio-bot:crash-logs-required-close -->";
|
||||
const comments = await github.paginate(github.rest.issues.listComments, {
|
||||
owner,
|
||||
repo,
|
||||
issue_number,
|
||||
per_page: 100,
|
||||
});
|
||||
const alreadyCommented = comments.some(c => (c.body || "").includes(marker));
|
||||
if (!alreadyCommented) {
|
||||
await github.rest.issues.createComment({
|
||||
owner,
|
||||
repo,
|
||||
issue_number,
|
||||
body:
|
||||
`${marker}\n` +
|
||||
`Closing this crash report because crash reports must include logs.\n\n` +
|
||||
`Please open a new report, or ask us to reopen this one, with a log snippet or stack trace from around the crash.\n\n` +
|
||||
`Useful examples:\n` +
|
||||
`- Android: \`adb logcat -d | tail -n 300\`\n` +
|
||||
`- iOS: crash log from Xcode Organizer or Console.app\n` +
|
||||
`- Desktop: terminal/console output from around the crash`,
|
||||
});
|
||||
}
|
||||
await github.rest.issues.update({
|
||||
owner,
|
||||
repo,
|
||||
issue_number,
|
||||
state: "closed",
|
||||
state_reason: "not_planned",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (problems.length > 0) {
|
||||
await ensureLabel(NEEDS_INFO, NEEDS_INFO_COLOR, NEEDS_INFO_DESC);
|
||||
if (!hasNeedsInfo) {
|
||||
|
|
@ -125,12 +196,13 @@ jobs:
|
|||
const marker = "<!-- nuvio-bot:needs-info -->";
|
||||
const commentBody =
|
||||
`${marker}\n` +
|
||||
`Thanks for the report. Could you add a bit more detail so we can reproduce it?\n\n` +
|
||||
`Missing / too short:\n` +
|
||||
`Thanks for the report. Could you add more detail so we can reproduce it?\n\n` +
|
||||
`Missing / too vague:\n` +
|
||||
problems.map(p => `- ${p}`).join("\n") +
|
||||
`\n\n` +
|
||||
`Use a specific title, for example: \`[Bug]: Playback freezes when switching audio tracks on iOS\`.\n` +
|
||||
`${isCrashIssue ? `Crash reports must include logs.\n` : `Logs are optional for most issues, but they help a lot.\n`}`;
|
||||
`Reports that only say "not working", "broken", or "same issue" are not enough to debug.\n` +
|
||||
`Logs are optional for most issues, but they help a lot. Crash reports without logs are closed automatically.\n`;
|
||||
|
||||
const comments = await github.paginate(github.rest.issues.listComments, {
|
||||
owner,
|
||||
|
|
|
|||
Loading…
Reference in a new issue