-
-
Notifications
You must be signed in to change notification settings - Fork 12
feat(doctor): add sentry doctor #1501
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
romtsn
wants to merge
36
commits into
main
Choose a base branch
from
spec/sentry-doctor
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
36 commits
Select commit
Hold shift + click to select a range
3ddb979
docs(doctor): add sentry doctor design spec
romtsn 26e9989
docs(doctor): split remediation, fix report side effect, add PostHog …
romtsn 51109fd
docs(doctor): liveness by default, seven flags down to three
romtsn 514e146
docs: add sentry doctor implementation plan
romtsn 31b25b2
fix(init): skip post-init verification under --dry-run
romtsn 2795d94
docs(plan): narrow Task 1's dry-run guarantee to what is actually true
romtsn 8642384
test(init): assert verifySetup gating behaviourally, not via source r…
romtsn 0fe64c9
feat(doctor): add check types and isolated check runner
romtsn 136bee3
feat(doctor): add capture-boundary redaction and input allowlists
romtsn d26db33
feat(doctor): add delimiter-table config block scanner
romtsn c3429e5
feat(doctor): add init/build marker tables and manifest parsing
romtsn afb26df
feat(doctor): add filesystem capture stage
romtsn 7abfe68
feat(doctor): add Sentry API resolve stage
romtsn 24ef313
feat(doctor): add tier-1 server-truth checks
romtsn 3c3e7fe
feat(doctor): add tier-2 ecosystem checks and the check registry
romtsn 6047803
feat(doctor): add human and JSON renderers
romtsn b4bc925
fix(doctor): validate evidence paths in fixBlock via safeFilePath
romtsn 360c73b
feat(doctor): wire up the sentry doctor command
romtsn 8efdb7c
feat(doctor): add --send-test-event round-trip check
romtsn 7b46e55
feat(doctor): add tier-3 configuration judgement
romtsn 514939e
feat(doctor): add consent-gated support export
romtsn 000a75c
feat(doctor): add --fix escalation to the setup workflow
romtsn daa5644
test(doctor): add integration coverage against a real template
romtsn 8f89c30
fix(doctor): pipe human output through renderMarkdown for ANSI colors
romtsn 9b70b47
style(doctor): render title as a markdown heading
romtsn 6c51854
fix(doctor): limit grep to 1 match per file, send report as attachment
romtsn 11eb7e8
fix(doctor): make test event title human-friendly
romtsn 67f22a9
fix(doctor): send test event as TestError exception
romtsn 64afe3c
style(doctor): add stethoscope emoji to title
romtsn 7aa32ae
docs(doctor): flesh out agent skill reference
romtsn 3893f86
fix(doctor): explain why server checks were skipped
romtsn 20228d8
fix(doctor): dogfood capture, report, and DSN host gaps
romtsn 6875d3a
chore(doctor): drop superpowers spec and plan from the PR
romtsn 189b1c4
test(init): match formatResult args when verifySetup is stubbed
romtsn cc56445
chore: regenerate docs
github-actions[bot] 3a9f554
fix(doctor): Close capture and artifact gaps from review
romtsn File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| ## Examples | ||
|
|
||
| ```bash | ||
| # Read-only health check | ||
| sentry doctor | ||
|
|
||
| # Machine-readable report (every result, including passes) | ||
| sentry doctor --json | ||
|
|
||
| # Send a test event and confirm ingest (a write) | ||
| sentry doctor --send-test-event | ||
| ``` | ||
|
|
||
| `--json` is this run's results (`id`, `status`, `detail`, optional `evidence` / `remediation`) plus `capture` and `server`. It is not a catalog of what each check means. | ||
|
|
||
| `skip` = could not tell. `warn` never fails the run. Only `fail` exits 1. | ||
|
|
||
| ## Checks | ||
|
|
||
| ### Server (what Sentry knows) | ||
|
|
||
| | Check | Means | | ||
| |---|---| | ||
| | `dsn.present` | A DSN exists somewhere in the project | | ||
| | `dsn.placeholder` | That DSN is not the docs example | | ||
| | `dsn.conflict` | More than one distinct DSN — events may split | | ||
| | `dsn.resolves` | The DSN maps to a project you can access | | ||
| | `project.first_event` | That project has received at least one event, ever | | ||
| | `project.last_event` | Recent activity (warns if last issue is >30 days old) | | ||
| | `project.key_active` | This DSN's key still exists and is enabled | | ||
| | `project.environments` | Events are tagged with an environment | | ||
| | `release.attribution` | Events are tied to a release | | ||
| | `artifacts.uploaded` | Source maps / debug files exist on the project | | ||
|
|
||
| ### Local (what the repo says) | ||
|
|
||
| | Check | Means | | ||
| |---|---| | ||
| | `init.present` | An init call (or platform auto-init) exists | | ||
| | `config.dsn_set` | That init actually sets a DSN | | ||
| | `config.environment` | `environment` is set (else local + prod mix) | | ||
| | `config.debug` | `debug: true` is not hardcoded on | | ||
| | `config.sample_rate` | Trace sample rate isn't 0 or 1.0 | | ||
| | `build.upload_configured` | A bundler / dSYM / Proguard upload plugin is present | | ||
| | `capture.complete` | Doctor finished scanning the tree | | ||
|
|
||
| ### Opt-in / extra | ||
|
|
||
| | Check | Means | | ||
| |---|---| | ||
| | `live.roundtrip` | `--send-test-event`: ingest accepted it, and (if DSN resolved) search found it | | ||
| | `judge.*` | LLM pass over captured config; skip if no model | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
packages/cli/plugins/sentry-cli/skills/sentry-cli/references/doctor.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| --- | ||
| name: sentry-cli-doctor | ||
| version: 0.45.0-dev.0 | ||
| description: Check whether Sentry is correctly set up and actually working | ||
| requires: | ||
| bins: ["sentry"] | ||
| auth: true | ||
| --- | ||
|
|
||
| # Doctor Commands | ||
|
|
||
| Check whether Sentry is correctly set up and actually working | ||
|
|
||
| ### `sentry doctor` | ||
|
|
||
| Check whether Sentry is correctly set up and actually working | ||
|
|
||
| **Flags:** | ||
| - `--sendTestEvent - Send a synthetic event to the configured DSN and confirm it arrives (a write)` | ||
| - `--fix - After reporting, run the setup workflow to produce a fix plan` | ||
|
|
||
| **Examples:** | ||
|
|
||
| ```bash | ||
| # Read-only health check | ||
| sentry doctor | ||
|
|
||
| # Machine-readable report (every result, including passes) | ||
| sentry doctor --json | ||
|
|
||
| # Send a test event and confirm ingest (a write) | ||
| sentry doctor --send-test-event | ||
| ``` | ||
|
|
||
| All commands also support `--json`, `--fields`, `--help`, `--log-level`, and `--verbose` flags. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,127 @@ | ||
| /** | ||
| * `sentry doctor` — is Sentry actually working in this project? | ||
| * | ||
| * Four stages, only the first two do I/O. `auth: false` so an unauthenticated | ||
| * run reports "unauthorized" as a finding rather than crashing, following the | ||
| * `info.ts` pattern. | ||
| */ | ||
|
|
||
| import type { SentryContext } from "../context.js"; | ||
| import { buildCommand } from "../lib/command.js"; | ||
| import { CLI_VERSION } from "../lib/constants.js"; | ||
| import { capture } from "../lib/doctor/capture.js"; | ||
| import { REGISTRY } from "../lib/doctor/checks/index.js"; | ||
| import { | ||
| buildReport, | ||
| type DoctorReport, | ||
| exitCodeFor, | ||
| formatDoctorReport, | ||
| } from "../lib/doctor/render.js"; | ||
| import { resolveServerFacts } from "../lib/doctor/resolve.js"; | ||
| import { runChecks } from "../lib/doctor/types.js"; | ||
| import { CommandOutput } from "../lib/formatters/output.js"; | ||
| import { withProgress } from "../lib/polling.js"; | ||
|
|
||
| export type DoctorFlags = { | ||
| sendTestEvent: boolean; | ||
| fix: boolean; | ||
| json?: boolean; | ||
| }; | ||
|
|
||
| /** The whole command, minus presentation — so tests never touch the CLI. */ | ||
| export async function runDoctor( | ||
| ctx: SentryContext, | ||
| flags: Partial<DoctorFlags> = {}, | ||
| setMessage: (msg: string) => void = () => { | ||
| /* tests / JSON mode */ | ||
| } | ||
| ): Promise<{ report: DoctorReport; exitCode: 0 | 1 }> { | ||
| const started = Date.now(); | ||
|
|
||
| setMessage("Scanning this project..."); | ||
| const captured = await capture(ctx.cwd); | ||
| setMessage("Asking Sentry..."); | ||
| const server = await resolveServerFacts(captured); | ||
| setMessage("Checking configuration..."); | ||
| const results = runChecks(REGISTRY, { capture: captured, server }); | ||
|
|
||
| const { judge } = await import("../lib/doctor/judge.js"); | ||
| results.push(...(await judge(captured))); | ||
|
|
||
| if (flags.sendTestEvent) { | ||
| setMessage("Sending a test event..."); | ||
| const { liveRoundtripCheck } = await import("../lib/doctor/live.js"); | ||
| results.push(await liveRoundtripCheck(captured, server)); | ||
| } else { | ||
| results.push({ | ||
| id: "live.roundtrip", | ||
| status: "skip", | ||
| detail: "Not requested. Run with --send-test-event.", | ||
| }); | ||
| } | ||
|
|
||
| return { | ||
| report: buildReport({ | ||
| capture: captured, | ||
| server, | ||
| results, | ||
| cliVersion: CLI_VERSION, | ||
| timestamp: new Date(started).toISOString(), | ||
| elapsedMs: Date.now() - started, | ||
| }), | ||
| exitCode: exitCodeFor(results), | ||
| }; | ||
| } | ||
|
|
||
| export const doctorCommand = buildCommand({ | ||
| // Runs unauthenticated; a missing session becomes a finding, not a crash. | ||
| auth: false, | ||
| docs: { | ||
| brief: "Check whether Sentry is correctly set up and actually working", | ||
| fullDescription: | ||
| "Inspects this project's Sentry configuration, asks Sentry what it has " + | ||
| "actually received, and reports what is wrong along with instructions " + | ||
| "to fix it. Reads only, unless you pass --send-test-event.", | ||
| }, | ||
| output: { human: formatDoctorReport }, | ||
| parameters: { | ||
| flags: { | ||
| sendTestEvent: { | ||
| kind: "boolean", | ||
| brief: | ||
| "Send a synthetic event to the configured DSN and confirm it arrives (a write)", | ||
| default: false, | ||
| }, | ||
| fix: { | ||
| kind: "boolean", | ||
| brief: "After reporting, run the setup workflow to produce a fix plan", | ||
| default: false, | ||
| }, | ||
| }, | ||
| positional: { kind: "tuple", parameters: [] }, | ||
| }, | ||
| async *func(this: SentryContext, flags: DoctorFlags) { | ||
| const { report, exitCode } = await withProgress( | ||
| { message: "Scanning this project...", json: flags.json }, | ||
| (setMessage) => runDoctor(this, flags, setMessage) | ||
| ); | ||
|
|
||
| yield new CommandOutput(report); | ||
|
|
||
| const { offerSupportExport } = await import("../lib/doctor/report.js"); | ||
| await offerSupportExport(report, flags.json); | ||
|
|
||
| if ( | ||
| flags.fix && | ||
| report.results.some((r) => r.status === "fail" || r.status === "warn") | ||
| ) { | ||
| const { runFix } = await import("../lib/doctor/fix.js"); | ||
| await runFix(this, report); | ||
| } | ||
|
|
||
| // Set last: a broken project is a finding, and the report is the payload. | ||
| this.process.exitCode = exitCode; | ||
| }, | ||
| }); | ||
|
|
||
| export default doctorCommand; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rename this to
onlineor something to make it not sound like a subcommand.