161ef94b4f
Check engine pin consistency / Dockerfile / CI pin consistency (push) Successful in 8s
Sirius CI/CD Pipeline / Detect Changes (push) Successful in 23s
Validate Docker Configuration / Validate Docker Compose Configuration (push) Successful in 47s
Sirius CI/CD Pipeline / Build API (${{ matrix.platform }}) (push) Has been cancelled
Sirius CI/CD Pipeline / Build UI (${{ matrix.platform }}) (push) Has been cancelled
Sirius CI/CD Pipeline / Merge Engine Manifest (push) Has been cancelled
Sirius CI/CD Pipeline / Merge API Manifest (push) Has been cancelled
Sirius CI/CD Pipeline / Merge UI Manifest (push) Has been cancelled
Sirius CI/CD Pipeline / Build Engine (${{ matrix.platform }}) (push) Has been cancelled
Sirius CI/CD Pipeline / Build Infra (${{ matrix.service }}, ${{ matrix.platform }}) (push) Has been cancelled
Sirius CI/CD Pipeline / Merge Infra Manifest (sirius-postgres) (push) Has been cancelled
Sirius CI/CD Pipeline / Merge Infra Manifest (sirius-rabbitmq) (push) Has been cancelled
Sirius CI/CD Pipeline / Merge Infra Manifest (sirius-valkey) (push) Has been cancelled
Sirius CI/CD Pipeline / Integration Test (push) Has been cancelled
Sirius CI/CD Pipeline / Public Stack Contract (push) Has been cancelled
Sirius CI/CD Pipeline / Dispatch Demo Deployment (sirius-demo branch) (push) Has been cancelled
Sirius CI/CD Pipeline / Dispatch Demo Canary (main branch) (push) Has been cancelled
Sirius CI/CD Pipeline / Guard Registry Namespace (push) Has been cancelled
198 lines
8.0 KiB
YAML
198 lines
8.0 KiB
YAML
name: Issue Triage
|
|
|
|
on:
|
|
issues:
|
|
types: [opened, edited]
|
|
|
|
permissions:
|
|
contents: read
|
|
issues: write
|
|
|
|
jobs:
|
|
triage:
|
|
name: Deterministic issue triage
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Apply labels and publish triage card
|
|
uses: actions/github-script@v7
|
|
with:
|
|
script: |
|
|
const issue = context.payload.issue;
|
|
const issueNumber = issue.number;
|
|
const owner = context.repo.owner;
|
|
const repo = context.repo.repo;
|
|
const text = `${issue.title}\n${issue.body || ""}`.toLowerCase();
|
|
|
|
const statusLabels = new Set([
|
|
"status:needs-triage",
|
|
"status:needs-info",
|
|
"status:repro-ready",
|
|
"status:confirmed",
|
|
"status:in-progress",
|
|
"status:blocked",
|
|
"status:ready-to-merge",
|
|
"status:done",
|
|
]);
|
|
|
|
const nextLabels = new Set();
|
|
const reasons = [];
|
|
|
|
// Type classification
|
|
if (/(security|vulnerability|cve|hardening|auth bypass|cors)/.test(text)) {
|
|
nextLabels.add("type:security");
|
|
reasons.push("Detected security-oriented keywords.");
|
|
} else if (/(question|how do i|help|clarify)/.test(text)) {
|
|
nextLabels.add("type:question");
|
|
reasons.push("Detected question/support language.");
|
|
} else if (/(docs|documentation|readme|guide)/.test(text)) {
|
|
nextLabels.add("type:docs");
|
|
reasons.push("Detected documentation-related language.");
|
|
} else if (/(feature|enhancement|improve|request)/.test(text)) {
|
|
nextLabels.add("type:enhancement");
|
|
reasons.push("Detected enhancement/request language.");
|
|
} else {
|
|
nextLabels.add("type:bug");
|
|
reasons.push("Defaulted to bug classification.");
|
|
}
|
|
|
|
// Area classification
|
|
if (/(sirius_api_key|x-api-key|401|auth|nextauth|jwt|session)/.test(text)) {
|
|
nextLabels.add("area:auth");
|
|
reasons.push("Matched auth/API-key indicators.");
|
|
}
|
|
if (/(installer|startup|secret|nextauth_secret|initial_admin_password|postgres_password)/.test(text)) {
|
|
nextLabels.add("area:installer-secrets");
|
|
reasons.push("Matched installer/startup secret indicators.");
|
|
}
|
|
if (/(docker-compose\.dev|dev overlay|volume mount|mount shadow|prisma)/.test(text)) {
|
|
nextLabels.add("area:compose-dev");
|
|
reasons.push("Matched dev-overlay/compose indicators.");
|
|
}
|
|
if (/(docker-compose\.prod|production overlay|hardened production)/.test(text)) {
|
|
nextLabels.add("area:compose-prod");
|
|
reasons.push("Matched production overlay indicators.");
|
|
}
|
|
if (/(ui|frontend|next\.js|trpc|browser)/.test(text)) nextLabels.add("area:ui");
|
|
if (/(api|fiber|endpoint|rest)/.test(text)) nextLabels.add("area:api");
|
|
if (/(engine|scanner|grpc|agent)/.test(text)) nextLabels.add("area:engine");
|
|
if (/(rabbitmq|queue|amqp)/.test(text)) nextLabels.add("area:rabbitmq");
|
|
if (/(postgres|database|migration|prisma schema)/.test(text)) nextLabels.add("area:postgres");
|
|
if (/(valkey|redis|cache)/.test(text)) nextLabels.add("area:valkey");
|
|
if (/(windows|wsl|crlf)/.test(text)) nextLabels.add("platform:windows");
|
|
|
|
// Severity hint
|
|
if (/(critical|auth bypass|remote code execution|data loss|privilege escalation)/.test(text)) {
|
|
nextLabels.add("sev:critical");
|
|
} else if (/(stack won't boot|cannot start|service down|outage|unusable|401 everywhere)/.test(text)) {
|
|
nextLabels.add("sev:high");
|
|
} else if (/(intermittent|major|fails often)/.test(text)) {
|
|
nextLabels.add("sev:medium");
|
|
} else {
|
|
nextLabels.add("sev:low");
|
|
}
|
|
|
|
// Missing info checks for fast mobile triage
|
|
const missing = [];
|
|
if (!/(repro|steps to reproduce|reproduction)/.test(text)) {
|
|
missing.push("Reproduction steps");
|
|
}
|
|
if (!/(docker compose logs|logs --no-color|stack trace|error:)/.test(text)) {
|
|
missing.push("Relevant service logs");
|
|
}
|
|
if (!/(docker compose config --quiet|installer|sirius-installer)/.test(text)) {
|
|
missing.push("Installer/config-render validation evidence");
|
|
}
|
|
|
|
if (missing.length > 0) {
|
|
nextLabels.add("status:needs-info");
|
|
reasons.push("Missing diagnostics were detected.");
|
|
} else {
|
|
nextLabels.add("status:repro-ready");
|
|
reasons.push("Sufficient diagnostics detected for reproduction.");
|
|
}
|
|
|
|
// Remove existing status labels to keep lifecycle single-valued
|
|
for (const label of issue.labels.map((l) => l.name)) {
|
|
if (statusLabels.has(label) && !nextLabels.has(label)) {
|
|
try {
|
|
await github.rest.issues.removeLabel({
|
|
owner,
|
|
repo,
|
|
issue_number: issueNumber,
|
|
name: label,
|
|
});
|
|
} catch (error) {
|
|
core.warning(`Could not remove label ${label}: ${error.message}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add computed labels
|
|
const labelsToAdd = [...nextLabels];
|
|
if (labelsToAdd.length > 0) {
|
|
await github.rest.issues.addLabels({
|
|
owner,
|
|
repo,
|
|
issue_number: issueNumber,
|
|
labels: labelsToAdd,
|
|
});
|
|
}
|
|
|
|
const triageMarker = "<!-- sirius-triage-card -->";
|
|
const missingBlock = missing.length
|
|
? missing.map((m) => `- [ ] ${m}`).join("\n")
|
|
: "- [x] Issue includes baseline diagnostics.";
|
|
|
|
const body = `${triageMarker}
|
|
## Triage Card
|
|
|
|
**Summary**
|
|
- Type: ${[...nextLabels].find((l) => l.startsWith("type:")) || "not-set"}
|
|
- Areas: ${[...nextLabels].filter((l) => l.startsWith("area:") || l.startsWith("platform:")).join(", ") || "not-set"}
|
|
- Severity hint: ${[...nextLabels].find((l) => l.startsWith("sev:")) || "not-set"}
|
|
- Status: ${[...nextLabels].find((l) => l.startsWith("status:")) || "not-set"}
|
|
|
|
**Why this classification**
|
|
${reasons.map((r) => `- ${r}`).join("\n")}
|
|
|
|
**Missing info checklist**
|
|
${missingBlock}
|
|
|
|
**Relevant runbooks**
|
|
- API Key Operations: \`documentation/dev/operations/README.api-key-operations.md\`
|
|
- Startup/Secrets ADR: \`documentation/dev/architecture/ADR.startup-secrets-model.md\`
|
|
- Auth Surface Matrix: \`documentation/dev/architecture/README.auth-surface-matrix.md\`
|
|
|
|
**Maintainer mobile commands**
|
|
- \`/triage needs-info\`
|
|
- \`/triage repro-ready\`
|
|
- \`/triage confirmed\`
|
|
- \`/test health\`
|
|
- \`/test integration\`
|
|
- \`/test security auth-surface\`
|
|
`;
|
|
|
|
const comments = await github.paginate(github.rest.issues.listComments, {
|
|
owner,
|
|
repo,
|
|
issue_number: issueNumber,
|
|
per_page: 100,
|
|
});
|
|
|
|
const existing = comments.find((c) => c.body && c.body.includes(triageMarker));
|
|
if (existing) {
|
|
await github.rest.issues.updateComment({
|
|
owner,
|
|
repo,
|
|
comment_id: existing.id,
|
|
body,
|
|
});
|
|
} else {
|
|
await github.rest.issues.createComment({
|
|
owner,
|
|
repo,
|
|
issue_number: issueNumber,
|
|
body,
|
|
});
|
|
}
|