feat: add issue templates for bug reports, documentation updates, feature requests, and questions

This commit is contained in:
ciat-777
2026-03-15 13:30:55 +08:00
parent 4aff5e6e27
commit 723fee99bf
8 changed files with 484 additions and 0 deletions
+80
View File
@@ -0,0 +1,80 @@
name: Bug report
description: Report a reproducible problem in Metapi
title: "[Bug]: "
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Thanks for reporting a bug. Please include enough detail for someone else to reproduce it.
- type: checkboxes
id: checks
attributes:
label: Before submitting
options:
- label: I searched existing issues to avoid creating a duplicate
required: true
- label: I tested on the latest release or current main branch
required: true
- type: dropdown
id: deployment
attributes:
label: Deployment type
description: Where did you reproduce the problem?
options:
- Web app
- Desktop app
- Server only
- Docker deployment
- Other
validations:
required: true
- type: textarea
id: summary
attributes:
label: What happened?
description: Describe the bug clearly and concisely.
placeholder: Describe the problem you observed.
validations:
required: true
- type: textarea
id: reproduce
attributes:
label: Steps to reproduce
placeholder: |
1. Go to ...
2. Configure ...
3. Send request ...
4. Observe ...
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
placeholder: Describe what you expected to happen.
validations:
required: true
- type: textarea
id: logs
attributes:
label: Logs, screenshots, or error output
description: Paste logs or attach screenshots if available.
render: shell
- type: textarea
id: environment
attributes:
label: Environment
description: Include OS, browser, app version, database type, or other relevant details.
placeholder: |
OS:
Browser:
Metapi version:
Database:
+5
View File
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Metapi README
url: https://github.com/cita-777/metapi#readme
about: Start with the README for setup, usage, and deployment details.
+40
View File
@@ -0,0 +1,40 @@
name: Documentation update
description: Report missing, incorrect, or unclear documentation
title: "[Docs]: "
labels: ["docs"]
body:
- type: markdown
attributes:
value: |
Use this template when documentation is missing, incorrect, or hard to follow.
- type: checkboxes
id: checks
attributes:
label: Before submitting
options:
- label: I searched existing issues to avoid creating a duplicate
required: true
- type: input
id: location
attributes:
label: Documentation location
description: Link the page or file that needs attention.
placeholder: e.g. README.md or docs/getting-started.md
validations:
required: true
- type: textarea
id: issue
attributes:
label: What is wrong or missing?
placeholder: Describe the missing, inaccurate, or confusing part.
validations:
required: true
- type: textarea
id: fix
attributes:
label: Suggested improvement
placeholder: Describe how the documentation could be improved.
@@ -0,0 +1,49 @@
name: Feature request
description: Suggest a new capability or improvement
title: "[Feature]: "
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: |
Thanks for the suggestion. Keep this issue focused on one feature request.
- type: checkboxes
id: checks
attributes:
label: Before submitting
options:
- label: I searched existing issues to avoid creating a duplicate
required: true
- label: I confirmed this feature is not already available in the latest release or main branch
required: true
- type: textarea
id: problem
attributes:
label: Problem or use case
description: What problem are you trying to solve?
placeholder: Explain the workflow or pain point behind this request.
validations:
required: true
- type: textarea
id: proposal
attributes:
label: Proposed solution
description: Describe the change you want to see.
placeholder: Explain the feature or behavior you want added.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives considered
description: Describe any workaround or alternative you considered.
- type: textarea
id: context
attributes:
label: Additional context
description: Add screenshots, examples, or links if they help explain the request.
+39
View File
@@ -0,0 +1,39 @@
name: Question
description: Ask for clarification or usage guidance
title: "[Question]: "
labels: ["question"]
body:
- type: markdown
attributes:
value: |
Use this template for support or clarification questions that are not bug reports or feature requests.
- type: checkboxes
id: checks
attributes:
label: Before submitting
options:
- label: I searched existing issues and the README for an answer
required: true
- type: textarea
id: question
attributes:
label: Question
description: What do you need help understanding?
placeholder: Ask one clear question and include the relevant context.
validations:
required: true
- type: textarea
id: tried
attributes:
label: What have you tried?
description: Include commands, config, or investigation you already attempted.
- type: textarea
id: environment
attributes:
label: Relevant environment or config
description: Paste config snippets, request examples, or environment details if needed.
render: shell
+45
View File
@@ -0,0 +1,45 @@
"area: db":
- changed-files:
- any-glob-to-any-file:
- "src/server/db/**"
- "drizzle/**"
"area: server":
- all:
- changed-files:
- any-glob-to-any-file:
- "src/server/**"
- all-globs-to-all-files:
- "!src/server/db/**"
"area: web":
- changed-files:
- any-glob-to-any-file:
- "src/web/**"
"area: desktop":
- changed-files:
- any-glob-to-any-file:
- "src/desktop/**"
- "electron-builder.yml"
- "tsconfig.desktop.json"
"area: docs":
- changed-files:
- any-glob-to-any-file:
- "docs/**"
- "README.md"
- "README_EN.md"
"area: scripts":
- changed-files:
- any-glob-to-any-file:
- "scripts/**"
"area: ci":
- changed-files:
- any-glob-to-any-file:
- ".github/**"
- "docker/**"
- "render.yaml"
- "zeabur-template.yaml"
+155
View File
@@ -0,0 +1,155 @@
name: Backfill PR Labels
on:
workflow_dispatch:
inputs:
state:
description: Pull request state to scan
required: true
default: all
type: choice
options:
- open
- closed
- all
limit:
description: Maximum number of pull requests to inspect
required: true
default: "100"
type: string
permissions:
contents: read
pull-requests: write
jobs:
backfill:
name: Backfill missing PR labels
runs-on: ubuntu-latest
steps:
- name: Backfill PR labels
uses: actions/github-script@v7
with:
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
const requestedState = core.getInput('state');
const limit = Number.parseInt(core.getInput('limit'), 10);
const sizeLabels = ['size: XS', 'size: S', 'size: M', 'size: L', 'size: XL', 'size: XXL'];
const areaRules = [
{ label: 'area: db', include: ['src/server/db/', 'drizzle/'] },
{ label: 'area: server', include: ['src/server/'], exclude: ['src/server/db/'] },
{ label: 'area: web', include: ['src/web/'] },
{ label: 'area: desktop', include: ['src/desktop/', 'electron-builder.yml', 'tsconfig.desktop.json'] },
{ label: 'area: docs', include: ['docs/', 'README.md', 'README_EN.md'] },
{ label: 'area: scripts', include: ['scripts/'] },
{ label: 'area: ci', include: ['.github/', 'docker/', 'render.yaml', 'zeabur-template.yaml'] },
];
if (!Number.isFinite(limit) || limit <= 0) {
core.setFailed('The limit input must be a positive integer.');
return;
}
function matchesRule(filename, rule) {
const included = rule.include.some((prefix) => filename === prefix || filename.startsWith(prefix));
if (!included) return false;
if (!rule.exclude) return true;
return !rule.exclude.some((prefix) => filename === prefix || filename.startsWith(prefix));
}
function computeAreaLabels(files) {
return areaRules
.filter((rule) => files.some((file) => matchesRule(file.filename, rule)))
.map((rule) => rule.label);
}
function computeSizeLabel(totalChanges) {
if (totalChanges < 50) return 'size: XS';
if (totalChanges < 200) return 'size: S';
if (totalChanges < 500) return 'size: M';
if (totalChanges < 1000) return 'size: L';
if (totalChanges < 2000) return 'size: XL';
return 'size: XXL';
}
const state = requestedState === 'all' ? 'all' : requestedState;
const pulls = await github.paginate(github.rest.pulls.list, {
owner,
repo,
state,
per_page: 100,
});
let inspected = 0;
let updated = 0;
let skipped = 0;
for (const pull of pulls) {
if (inspected >= limit) {
break;
}
inspected += 1;
const files = await github.paginate(github.rest.pulls.listFiles, {
owner,
repo,
pull_number: pull.number,
per_page: 100,
});
const desiredAreaLabels = computeAreaLabels(files);
const desiredSizeLabel = computeSizeLabel(pull.additions + pull.deletions);
const currentLabels = pull.labels.map((label) => label.name);
const missingAreaLabels = desiredAreaLabels.filter((label) => !currentLabels.includes(label));
const currentSizeLabels = currentLabels.filter((label) => sizeLabels.includes(label));
const sizeMatches = currentSizeLabels.length === 1 && currentSizeLabels[0] === desiredSizeLabel;
if (missingAreaLabels.length === 0 && sizeMatches) {
skipped += 1;
continue;
}
for (const label of currentSizeLabels) {
if (label === desiredSizeLabel) {
continue;
}
try {
await github.rest.issues.removeLabel({
owner,
repo,
issue_number: pull.number,
name: label,
});
} catch (error) {
if (error.status !== 404) {
throw error;
}
}
}
const labelsToAdd = [...missingAreaLabels];
if (!sizeMatches) {
labelsToAdd.push(desiredSizeLabel);
}
if (labelsToAdd.length > 0) {
await github.rest.issues.addLabels({
owner,
repo,
issue_number: pull.number,
labels: labelsToAdd,
});
}
updated += 1;
}
core.summary
.addHeading('Backfill summary')
.addRaw(`Inspected PRs: ${inspected}\n`)
.addRaw(`Updated PRs: ${updated}\n`)
.addRaw(`Skipped PRs: ${skipped}\n`)
.write();
+71
View File
@@ -0,0 +1,71 @@
name: Label Pull Requests
on:
pull_request_target:
types: [opened, synchronize, reopened, ready_for_review]
permissions:
contents: read
pull-requests: write
jobs:
apply-labels:
name: Apply area and size labels
runs-on: ubuntu-latest
steps:
- name: Apply area labels
uses: actions/labeler@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
sync-labels: false
- name: Apply size label
uses: actions/github-script@v7
with:
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
const pull_number = context.payload.pull_request.number;
const sizeLabels = ['size: XS', 'size: S', 'size: M', 'size: L', 'size: XL', 'size: XXL'];
const { data: pull } = await github.rest.pulls.get({
owner,
repo,
pull_number,
});
const totalChanges = pull.additions + pull.deletions;
let nextSize = 'size: XXL';
if (totalChanges < 50) nextSize = 'size: XS';
else if (totalChanges < 200) nextSize = 'size: S';
else if (totalChanges < 500) nextSize = 'size: M';
else if (totalChanges < 1000) nextSize = 'size: L';
else if (totalChanges < 2000) nextSize = 'size: XL';
const currentLabels = pull.labels.map((label) => label.name);
const existingSizeLabels = currentLabels.filter((label) => sizeLabels.includes(label));
const labelsToRemove = existingSizeLabels.filter((label) => label !== nextSize);
for (const label of labelsToRemove) {
try {
await github.rest.issues.removeLabel({
owner,
repo,
issue_number: pull_number,
name: label,
});
} catch (error) {
if (error.status !== 404) {
throw error;
}
}
}
if (!currentLabels.includes(nextSize)) {
await github.rest.issues.addLabels({
owner,
repo,
issue_number: pull_number,
labels: [nextSize],
});
}