|
6 | 6 |
|
7 | 7 | import argparse |
8 | 8 | import json |
| 9 | +import os |
9 | 10 | import sys |
10 | | -from typing import Sequence |
| 11 | +from pathlib import Path |
| 12 | +from typing import Any, Sequence |
11 | 13 |
|
12 | | -from . import __version__, cron, i18n, log_writer, scheduler, theme |
| 14 | +from . import __version__, cron, i18n, log_writer, scheduler, store, theme |
13 | 15 | from .i18n import _ |
14 | 16 | from .model import Task |
15 | 17 | from .sources import github_ci, github_pr, runpod |
@@ -467,6 +469,73 @@ def cmd_theme(args: argparse.Namespace) -> int: |
467 | 469 | return 0 |
468 | 470 |
|
469 | 471 |
|
| 472 | +def _detect_locale_source() -> str: |
| 473 | + """Best-effort detection of which signal won locale resolution. |
| 474 | +
|
| 475 | + Read-only; used only by `triage doctor`. Mirrors the precedence |
| 476 | + chain in `i18n.resolve_lang` but reports the source name instead |
| 477 | + of returning a code. |
| 478 | + """ |
| 479 | + if os.environ.get("TRIAGE_LANG"): |
| 480 | + return "TRIAGE_LANG" |
| 481 | + for var in ("LC_ALL", "LC_MESSAGES", "LANG"): |
| 482 | + v = os.environ.get(var) |
| 483 | + if v and v != "C" and v != "POSIX": |
| 484 | + return var |
| 485 | + try: |
| 486 | + import locale as _locale |
| 487 | + if _locale.getlocale()[0]: |
| 488 | + return "locale.getlocale()" |
| 489 | + except Exception: |
| 490 | + pass |
| 491 | + return "default" |
| 492 | + |
| 493 | + |
| 494 | +def cmd_doctor(args: argparse.Namespace) -> int: |
| 495 | + """Print a read-only environment-diagnostics summary for bug reports.""" |
| 496 | + store_root = store.default_root() if getattr(args, "home", None) is None else Path(args.home) |
| 497 | + log_disabled_env = os.environ.get("TRIAGE_NO_LOG") == "1" |
| 498 | + log_path = None if log_disabled_env else log_writer.get_path() |
| 499 | + drift = i18n.check_locales() |
| 500 | + locale_count = len(i18n.list_available()) - 1 # minus en baseline |
| 501 | + |
| 502 | + info: dict[str, Any] = { |
| 503 | + "version": __version__, |
| 504 | + "python": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}", |
| 505 | + "locale": { |
| 506 | + "resolved": i18n.current_lang(), |
| 507 | + "source": _detect_locale_source(), |
| 508 | + "available": locale_count + 1, # include en |
| 509 | + "drift": len(drift), |
| 510 | + }, |
| 511 | + "store": { |
| 512 | + "path": str(store_root), |
| 513 | + "exists": store_root.exists(), |
| 514 | + }, |
| 515 | + "log": { |
| 516 | + "path": str(log_path) if log_path else None, |
| 517 | + "enabled": log_path is not None, |
| 518 | + }, |
| 519 | + } |
| 520 | + |
| 521 | + if getattr(args, "json", False): |
| 522 | + print(json.dumps(info, indent=2, sort_keys=True)) |
| 523 | + return 0 |
| 524 | + |
| 525 | + print(f"triage v{info['version']} (python {info['python']})") |
| 526 | + print(f" locale: {info['locale']['resolved']} " |
| 527 | + f"source={info['locale']['source']} " |
| 528 | + f"available={info['locale']['available']} " |
| 529 | + f"drift={info['locale']['drift']}") |
| 530 | + print(f" store: {info['store']['path']} " |
| 531 | + f"exists={info['store']['exists']}") |
| 532 | + if info['log']['enabled']: |
| 533 | + print(f" log: {info['log']['path']}") |
| 534 | + else: |
| 535 | + print(" log: disabled") |
| 536 | + return 0 |
| 537 | + |
| 538 | + |
470 | 539 | def cmd_lang(args: argparse.Namespace) -> int: |
471 | 540 | """List available output languages (or switch via --lang on any command).""" |
472 | 541 | as_json = getattr(args, "json", False) |
@@ -681,6 +750,17 @@ def build_parser() -> argparse.ArgumentParser: |
681 | 750 | ) |
682 | 751 | lng.set_defaults(func=cmd_lang) |
683 | 752 |
|
| 753 | + doc = sub.add_parser( |
| 754 | + "doctor", |
| 755 | + help="Print a read-only environment-diagnostics summary (for bug reports).", |
| 756 | + ) |
| 757 | + doc.add_argument( |
| 758 | + "--json", |
| 759 | + action="store_true", |
| 760 | + help="Emit machine-parseable JSON instead of human-readable text.", |
| 761 | + ) |
| 762 | + doc.set_defaults(func=cmd_doctor) |
| 763 | + |
684 | 764 | return p |
685 | 765 |
|
686 | 766 |
|
|
0 commit comments