From d026fdfd8d556774f220c99a9c17fe7def96e290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Beteg=C3=B3n?= Date: Mon, 6 Apr 2026 11:29:03 +0200 Subject: [PATCH 1/3] feat(init): add detect-sentry local-op for cross-language Sentry detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The server's check-existing-sentry step was hardcoded for JS/TS only. This adds a detect-sentry local-op that delegates to the CLI's existing detectDsn() — which already scans source code, .env files, and env vars across all 140+ supported platforms. Closes getsentry/cli-init-api#30 Co-Authored-By: Claude Opus 4.6 (1M context) --- src/lib/init/local-ops.ts | 27 +++++++++++++++++++++++++++ src/lib/init/types.ts | 12 +++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/lib/init/local-ops.ts b/src/lib/init/local-ops.ts index 0189cccea..08e28e51c 100644 --- a/src/lib/init/local-ops.ts +++ b/src/lib/init/local-ops.ts @@ -29,6 +29,7 @@ import { resolveOrgPrefetched } from "./prefetch.js"; import type { ApplyPatchsetPayload, CreateSentryProjectPayload, + DetectSentryPayload, DirEntry, FileExistsBatchPayload, ListDirPayload, @@ -324,6 +325,8 @@ export async function handleLocalOp( return await applyPatchset(payload, options.dryRun); case "create-sentry-project": return await createSentryProject(payload, options); + case "detect-sentry": + return await detectSentry(payload); default: return { ok: false, @@ -789,6 +792,30 @@ export async function detectExistingProject(cwd: string): Promise<{ return null; } +async function detectSentry( + payload: DetectSentryPayload +): Promise { + try { + const { detectDsn } = await import("../dsn/index.js"); + const dsn = await detectDsn(payload.cwd); + + if (!dsn) { + return { ok: true, data: { status: "none", signals: [] } }; + } + + const signals = [ + `dsn: ${dsn.source}${dsn.sourcePath ? ` (${dsn.sourcePath})` : ""}`, + ]; + + return { + ok: true, + data: { status: "installed", signals, dsn: dsn.raw }, + }; + } catch { + return { ok: true, data: { status: "none", signals: [] } }; + } +} + async function createSentryProject( payload: CreateSentryProjectPayload, options: WizardOptions diff --git a/src/lib/init/types.ts b/src/lib/init/types.ts index c8ff46e01..2f5f39f1e 100644 --- a/src/lib/init/types.ts +++ b/src/lib/init/types.ts @@ -25,7 +25,8 @@ export type LocalOpPayload = | FileExistsBatchPayload | RunCommandsPayload | ApplyPatchsetPayload - | CreateSentryProjectPayload; + | CreateSentryProjectPayload + | DetectSentryPayload; export type ListDirPayload = { type: "local-op"; @@ -91,6 +92,15 @@ export type CreateSentryProjectPayload = { }; }; +export type DetectSentryPayload = { + type: "local-op"; + operation: "detect-sentry"; + /** Human-readable spinner hint from the server (≤ 120 chars, sensitive values redacted). */ + detail?: string; + cwd: string; + params: Record; +}; + export type LocalOpResult = { ok: boolean; error?: string; From 8752f9031b3fb6e5a38a9ddd262419f1a429458f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Beteg=C3=B3n?= Date: Mon, 6 Apr 2026 11:47:58 +0200 Subject: [PATCH 2/3] refactor(init): drop redundant try/catch in detectSentry handleLocalOp already wraps the entire switch in a try/catch, so the inner one was unnecessary. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/lib/init/local-ops.ts | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/lib/init/local-ops.ts b/src/lib/init/local-ops.ts index 08e28e51c..5ad09220f 100644 --- a/src/lib/init/local-ops.ts +++ b/src/lib/init/local-ops.ts @@ -795,25 +795,21 @@ export async function detectExistingProject(cwd: string): Promise<{ async function detectSentry( payload: DetectSentryPayload ): Promise { - try { - const { detectDsn } = await import("../dsn/index.js"); - const dsn = await detectDsn(payload.cwd); - - if (!dsn) { - return { ok: true, data: { status: "none", signals: [] } }; - } - - const signals = [ - `dsn: ${dsn.source}${dsn.sourcePath ? ` (${dsn.sourcePath})` : ""}`, - ]; + const { detectDsn } = await import("../dsn/index.js"); + const dsn = await detectDsn(payload.cwd); - return { - ok: true, - data: { status: "installed", signals, dsn: dsn.raw }, - }; - } catch { + if (!dsn) { return { ok: true, data: { status: "none", signals: [] } }; } + + const signals = [ + `dsn: ${dsn.source}${dsn.sourcePath ? ` (${dsn.sourcePath})` : ""}`, + ]; + + return { + ok: true, + data: { status: "installed", signals, dsn: dsn.raw }, + }; } async function createSentryProject( From adaaf17c8c1c5097129d5385f268ed53d93ae01d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Beteg=C3=B3n?= Date: Mon, 6 Apr 2026 12:43:48 +0200 Subject: [PATCH 3/3] fix(init): add spinner message for detect-sentry operation Without this, describeLocalOp falls through to the default branch and shows the raw operation name "detect-sentry..." to the user. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/lib/init/wizard-runner.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/init/wizard-runner.ts b/src/lib/init/wizard-runner.ts index 92eedf310..e8edf55c0 100644 --- a/src/lib/init/wizard-runner.ts +++ b/src/lib/init/wizard-runner.ts @@ -129,6 +129,8 @@ export function describeLocalOp(payload: LocalOpPayload): string { return "Listing directory..."; case "create-sentry-project": return `Creating project "${payload.params.name}" (${payload.params.platform})...`; + case "detect-sentry": + return "Checking for existing Sentry setup..."; default: return `${(payload as { operation: string }).operation}...`; }