feat: augment outage report template with debugging context
- Expand service list: add Home Assistant, Actual Budget, Audiobookshelf, Linkwarden, Matrix, Paperless, Tandoor, FreshRSS, Frigate, HackMD, Excalidraw, Wealthfolio, Send, Stirling PDF - Add structured debugging fields: error type, scope (just me vs others), when it started, URL accessed - Fix user report parser to extract all form fields into status.json - Show error type, scope, and start time in status page report cards [ci skip] Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c00a908610
commit
3e9231ae0d
3 changed files with 95 additions and 20 deletions
71
.github/ISSUE_TEMPLATE/outage-report.yml
vendored
71
.github/ISSUE_TEMPLATE/outage-report.yml
vendored
|
|
@ -8,16 +8,30 @@ body:
|
|||
label: Affected Service
|
||||
description: Which service is affected?
|
||||
options:
|
||||
- Nextcloud
|
||||
- Immich
|
||||
- Vaultwarden
|
||||
- Grafana
|
||||
- Nextcloud (files, calendar, contacts)
|
||||
- Immich (photos)
|
||||
- Vaultwarden (passwords)
|
||||
- Mail (email, roundcube)
|
||||
- Home Assistant
|
||||
- Actual Budget
|
||||
- Navidrome (music)
|
||||
- Audiobookshelf (audiobooks)
|
||||
- Plex / Jellyfin
|
||||
- Mail
|
||||
- Grafana (dashboards)
|
||||
- Linkwarden (bookmarks)
|
||||
- Matrix (chat)
|
||||
- Paperless-ngx (documents)
|
||||
- Tandoor (recipes)
|
||||
- FreshRSS (news)
|
||||
- Frigate (cameras)
|
||||
- HackMD (notes)
|
||||
- Excalidraw (whiteboard)
|
||||
- Wealthfolio / Finance
|
||||
- Headscale / VPN
|
||||
- DNS
|
||||
- VPN / Tailscale
|
||||
- Website / Blog
|
||||
- Music (Navidrome / Freedify)
|
||||
- Send (file sharing)
|
||||
- Stirling PDF
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
|
|
@ -29,6 +43,49 @@ body:
|
|||
placeholder: "e.g., Getting 502 errors when trying to access Nextcloud since about 3pm"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: error_type
|
||||
attributes:
|
||||
label: What kind of error?
|
||||
description: This helps us narrow down the issue faster.
|
||||
options:
|
||||
- Page won't load (timeout / connection refused)
|
||||
- 502 Bad Gateway
|
||||
- 503 Service Unavailable
|
||||
- Login / authentication not working
|
||||
- Slow / degraded performance
|
||||
- Specific feature broken (app loads but something inside doesn't work)
|
||||
- Data missing or incorrect
|
||||
- Other / not sure
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: scope
|
||||
attributes:
|
||||
label: Is it just you or others too?
|
||||
description: Helps us tell apart service outages from account/device issues.
|
||||
options:
|
||||
- Just me (others seem fine)
|
||||
- Multiple people affected
|
||||
- Not sure
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
id: when
|
||||
attributes:
|
||||
label: When did it start?
|
||||
description: Approximate time helps us correlate with logs and deployments.
|
||||
placeholder: "e.g., about 3pm today, or since yesterday morning"
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
id: url
|
||||
attributes:
|
||||
label: URL you were accessing (optional)
|
||||
description: The exact URL helps us check the right endpoint.
|
||||
placeholder: "e.g., https://nextcloud.viktorbarzin.me/apps/files"
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
id: contact
|
||||
attributes:
|
||||
|
|
|
|||
|
|
@ -209,6 +209,9 @@ footer { color: var(--fg3); font-size: 11px; margin-top: 32px; padding-top: 16px
|
|||
html+='<div class="inc-info"><div class="inc-title">'+esc(inc.title)+'</div>';
|
||||
html+='<div class="inc-meta"><span>'+ago(created)+'</span>';
|
||||
if(!isReport)html+='<span>'+dur(created,end)+'</span>';
|
||||
if(isReport&&inc.error_type)html+='<span>'+esc(inc.error_type)+'</span>';
|
||||
if(isReport&&inc.scope)html+='<span>'+esc(inc.scope)+'</span>';
|
||||
if(isReport&&inc.when_started)html+='<span>Since: '+esc(inc.when_started)+'</span>';
|
||||
if(resolved)html+='<span style="color:var(--green)">Resolved</span>';
|
||||
html+='</div>';
|
||||
if(inc.affected_services&&inc.affected_services.length){
|
||||
|
|
|
|||
|
|
@ -385,22 +385,33 @@ ISSUES_REPO = "ViktorBarzin/infra"
|
|||
def has_label(issue, name):
|
||||
return any(l["name"].lower() == name.lower() for l in issue.get("labels", []))
|
||||
|
||||
def parse_user_report_service(body):
|
||||
"""Extract service from GitHub Issue Form dropdown response."""
|
||||
def parse_form_field(body, heading):
|
||||
"""Extract value after a ### heading from GitHub Issue Form response."""
|
||||
if not body:
|
||||
return None
|
||||
for line in body.split("\n"):
|
||||
stripped = line.strip()
|
||||
if stripped and not stripped.startswith("#") and not stripped.startswith("_") and not stripped.startswith("<!"):
|
||||
prev_was_heading = False
|
||||
for i, ln in enumerate(body.split("\n")):
|
||||
if "affected service" in ln.lower():
|
||||
prev_was_heading = True
|
||||
continue
|
||||
if prev_was_heading and ln.strip():
|
||||
return ln.strip()
|
||||
lines = body.split("\n")
|
||||
for i, ln in enumerate(lines):
|
||||
if heading.lower() in ln.lower() and ln.strip().startswith("#"):
|
||||
for j in range(i + 1, len(lines)):
|
||||
val = lines[j].strip()
|
||||
if val and not val.startswith("#") and val != "_No response_":
|
||||
return val
|
||||
return None
|
||||
|
||||
def parse_user_report_context(body):
|
||||
"""Extract structured fields from the issue form body."""
|
||||
service = parse_form_field(body, "affected service")
|
||||
# Strip parenthetical hints: "Nextcloud (files, calendar)" -> "Nextcloud"
|
||||
if service and "(" in service:
|
||||
service = service[:service.index("(")].strip()
|
||||
return {
|
||||
"service": service,
|
||||
"error_type": parse_form_field(body, "what kind of error"),
|
||||
"scope": parse_form_field(body, "is it just you"),
|
||||
"when": parse_form_field(body, "when did it start"),
|
||||
"url": parse_form_field(body, "url you were accessing"),
|
||||
}
|
||||
|
||||
try:
|
||||
issues_url = "https://api.github.com/repos/" + ISSUES_REPO + "/issues"
|
||||
|
||||
|
|
@ -435,7 +446,8 @@ try:
|
|||
continue
|
||||
if has_label(issue, "incident"):
|
||||
continue # Already promoted to incident, skip duplicate
|
||||
svc = parse_user_report_service(issue.get("body"))
|
||||
ctx = parse_user_report_context(issue.get("body"))
|
||||
svc = ctx["service"]
|
||||
user_reports.append({
|
||||
"id": issue["number"],
|
||||
"title": issue["title"],
|
||||
|
|
@ -443,6 +455,9 @@ try:
|
|||
"status": "open",
|
||||
"created_at": issue["created_at"],
|
||||
"affected_services": [svc] if svc else [],
|
||||
"error_type": ctx.get("error_type"),
|
||||
"scope": ctx.get("scope"),
|
||||
"when_started": ctx.get("when"),
|
||||
"url": issue["html_url"],
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue