Skip to content

Commit 047e8a8

Browse files
feat: Restore Support bundle download from Settings → About
Operators can create a session-authenticated, allowlisted diagnostic ZIP for troubleshooting after mandatory review. Ships the locked v1 contract assets, deep generator (no legacy CarePackage path), Settings Create → Inspect → Share flow, disclosure docs, terminology guard, and dialect/CI coverage for #590.
1 parent d132c73 commit 047e8a8

29 files changed

Lines changed: 6325 additions & 13 deletions

.changeset/support-bundle.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"comicarr": minor
3+
---
4+
5+
Settings → About can create a Support bundle for troubleshooting. Review the three files in the ZIP before attaching it to a public issue; share it privately with a maintainer if anything looks sensitive.

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ assignees: ''
1111
**Describe the bug**
1212
A clear and concise description of what the bug is.
1313

14-
**CarePackage**
15-
On the config page, there is a button labelled "CarePackage". Pressing this button will download a zip file containing a redacted copy of your config, your database, a summary of your operating environment, and your logs. Upload that file here.
14+
**Support bundle**
15+
1. Go to **Settings → About → Support bundle**.
16+
2. Choose **Create support bundle**, then **Create and download**.
17+
3. Open the ZIP and review its three files (`README.txt`, `manifest.json`, `diagnostics.json`).
18+
4. Attach the archive to this issue only if you are comfortable with every value; otherwise share it privately with a maintainer.
1619

17-
If you're uncomfortable with pasting your CarePackage, include a DEBUG log containing the error from disk.
18-
19-
Do not paste from the GUI log.
20-
Do not just paste a traceback/error.
20+
The bundle contains allowlisted diagnostic facts only. It is not a copy of your database, settings values, raw logs, library names, paths, or free text.
2121

2222

2323
**Environment (please complete the following information):**

.github/workflows/test.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ jobs:
139139
DATABASE_URL: ${{ matrix.database_url }}
140140
run: uv run pytest tests/integration/test_schema_migration_dialects.py -q
141141

142+
- name: Support bundle dialect projection
143+
env:
144+
DATABASE_URL: ${{ matrix.database_url }}
145+
run: uv run pytest tests/integration/test_support_bundle_dialects.py -q
146+
142147
- name: Check adopted schema
143148
env:
144149
DATABASE_URL: ${{ matrix.database_url }}

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ When to add one:
202202
## Reporting Issues
203203

204204
- Use the [Bug Report](https://github.com/frankieramirez/comicarr/issues/new?template=bug_report.md) template
205-
- Include a CarePackage (available on the config page) when reporting bugs
205+
- Include a Support bundle from **Settings → About** when reporting bugs (review the three files first; share privately with a maintainer if anything looks sensitive)
206206
- For feature requests, use the [Feature Request](https://github.com/frankieramirez/comicarr/issues/new?template=feature_request.md) template
207207

208208
## License

SECURITY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ You should receive a response within 72 hours. We will work with you to understa
2323
- Configuration files with credentials (`config.ini`) are excluded from version control
2424
- The Docker image runs as a non-root user with configurable PUID/PGID
2525
- All database queries use parameterized statements to prevent SQL injection
26-
- The CarePackage feature redacts sensitive fields before export
26+
- The Support bundle exports only allowlisted diagnostic facts; operators must review the archive before public attachment
2727

2828
## Known Scanner Findings
2929

comicarr/app/system/router.py

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import json
1919
import threading
2020

21-
from fastapi import APIRouter, Depends, Request
21+
from fastapi import APIRouter, Depends, Request, Response
2222
from fastapi.responses import JSONResponse
2323
from sse_starlette.sse import EventSourceResponse, ServerSentEvent
2424

@@ -32,6 +32,7 @@
3232
validate_jwt_token,
3333
)
3434
from comicarr.app.system import service as system_service
35+
from comicarr.app.system import support_bundle as support_bundle_module
3536

3637
router = APIRouter(prefix="/api", tags=["system"])
3738

@@ -625,3 +626,53 @@ async def release_acquisition_canary(
625626
reason=reason,
626627
)
627628
return _repair_response(result)
629+
630+
631+
# ---------------------------------------------------------------------------
632+
# Support bundle
633+
# ---------------------------------------------------------------------------
634+
635+
636+
@router.post("/system/support-bundle", dependencies=[Depends(require_session)])
637+
def create_support_bundle(ctx: AppContext = Depends(get_context)):
638+
"""Generate and download a Support bundle ZIP (session-authenticated only)."""
639+
try:
640+
artifact = support_bundle_module.generate_support_bundle(ctx)
641+
except support_bundle_module.SupportBundleInProgress:
642+
body = support_bundle_module.error_body("support_bundle_in_progress")
643+
return JSONResponse(
644+
status_code=409,
645+
content=body,
646+
headers={"Retry-After": "2"},
647+
)
648+
except support_bundle_module.SupportBundleUnavailable:
649+
return JSONResponse(
650+
status_code=503,
651+
content=support_bundle_module.error_body("support_bundle_unavailable"),
652+
)
653+
except support_bundle_module.SupportBundleValidationFailed:
654+
return JSONResponse(
655+
status_code=500,
656+
content=support_bundle_module.error_body("support_bundle_validation_failed"),
657+
)
658+
except support_bundle_module.SupportBundleError as exc:
659+
status = 503 if exc.code == "support_bundle_unavailable" else 500
660+
return JSONResponse(status_code=status, content=support_bundle_module.error_body(exc.code))
661+
except Exception as e:
662+
logger.error("[SUPPORT-BUNDLE] unexpected adapter failure: %s" % type(e).__name__)
663+
return JSONResponse(
664+
status_code=500,
665+
content=support_bundle_module.error_body("support_bundle_generation_failed"),
666+
)
667+
668+
return Response(
669+
content=artifact.content,
670+
media_type="application/zip",
671+
headers={
672+
"Content-Disposition": f'attachment; filename="{artifact.filename}"',
673+
"Cache-Control": "no-store, private",
674+
"Pragma": "no-cache",
675+
"X-Comicarr-Support-Bundle-Contract": str(artifact.contract_version),
676+
"X-Comicarr-Support-Bundle-Status": artifact.status,
677+
},
678+
)

0 commit comments

Comments
 (0)