Skip to content

Commit 5fe95f0

Browse files
betegonclaude
andcommitted
feat(install): add SENTRY_INIT env var to run wizard after install
curl -fsSL https://cli.sentry.dev/install | SENTRY_INIT=1 bash Closes #682 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 73573b9 commit 5fe95f0

File tree

6 files changed

+84
-0
lines changed

6 files changed

+84
-0
lines changed

install

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,11 @@ Options:
8989
Environment Variables:
9090
SENTRY_INSTALL_DIR Override the installation directory
9191
SENTRY_VERSION Install a specific version (e.g., 0.19.0, nightly)
92+
SENTRY_INIT Set to 1 to run \`sentry init\` after installing
9293
9394
Examples:
9495
curl -fsSL https://cli.sentry.dev/install | bash
96+
curl -fsSL https://cli.sentry.dev/install | SENTRY_INIT=1 bash
9597
curl -fsSL https://cli.sentry.dev/install | bash -s -- --version nightly
9698
curl -fsSL https://cli.sentry.dev/install | bash -s -- --version 0.19.0
9799
SENTRY_VERSION=nightly curl -fsSL https://cli.sentry.dev/install | bash
@@ -276,3 +278,19 @@ trap - EXIT
276278

277279
# shellcheck disable=SC2086
278280
"$tmp_binary" cli setup $setup_args
281+
282+
# Optionally launch the setup wizard after install.
283+
# </dev/tty reopens stdin from the terminal since `curl | bash` consumes it.
284+
if [[ "${SENTRY_INIT:-}" == "1" ]]; then
285+
sentry_bin=""
286+
for dir in "${SENTRY_INSTALL_DIR:-}" "$HOME/.local/bin" "$HOME/bin" "$HOME/.sentry/bin"; do
287+
if [[ -x "${dir}/sentry" ]]; then
288+
sentry_bin="${dir}/sentry"
289+
break
290+
fi
291+
done
292+
if [[ -z "$sentry_bin" ]]; then
293+
die "Cannot find installed sentry binary" "init"
294+
fi
295+
"$sentry_bin" init </dev/tty
296+
fi

src/lib/dsn/code-scanner.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,10 @@ function scanDirectory(
686686

687687
const { files, dirMtimes } = collectResult;
688688

689+
log.debug(
690+
`Code scan: collected ${files.length} files from ${Object.keys(dirMtimes).length} directories`
691+
);
692+
689693
span.setAttribute("dsn.files_collected", files.length);
690694

691695
if (files.length === 0) {
@@ -699,6 +703,10 @@ function scanDirectory(
699703
stopOnFirst
700704
);
701705

706+
log.debug(
707+
`Code scan: scanned ${filesScanned} files, found ${results.size} DSN(s)`
708+
);
709+
702710
span.setAttributes({
703711
"dsn.files_scanned": filesScanned,
704712
"dsn.dsns_found": results.size,

src/lib/dsn/detector.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
getCachedProjectRoot,
2525
setCachedProjectRoot,
2626
} from "../db/project-root-cache.js";
27+
import { logger } from "../logger.js";
2728
import {
2829
extractFirstDsnFromContent,
2930
scanCodeForDsns,
@@ -44,6 +45,8 @@ import type {
4445
DsnSource,
4546
} from "./types.js";
4647

48+
const log = logger.withTag("dsn-detect");
49+
4750
/**
4851
* Detect DSN with project root detection and caching support.
4952
*
@@ -86,6 +89,7 @@ export async function detectDsn(cwd: string): Promise<DetectedDsn | null> {
8689
}
8790

8891
// Cache hit! Return with resolved info if available
92+
log.debug(`DSN cache hit for ${projectRoot}`);
8993
return {
9094
...verified,
9195
resolved: cached.resolved,
@@ -98,6 +102,9 @@ export async function detectDsn(cwd: string): Promise<DetectedDsn | null> {
98102
const detected = await fullScanFirst(projectRoot);
99103

100104
if (detected) {
105+
log.debug(
106+
`DSN detected: ${detected.raw} from ${getDsnSourceDescription(detected)}`
107+
);
101108
// Cache for next time (without resolved info yet)
102109
setCachedDsn(projectRoot, {
103110
dsn: detected.raw,
@@ -106,6 +113,8 @@ export async function detectDsn(cwd: string): Promise<DetectedDsn | null> {
106113
source: detected.source,
107114
sourcePath: detected.sourcePath,
108115
});
116+
} else {
117+
log.debug(`No DSN found after full scan of ${projectRoot}`);
109118
}
110119

111120
return detected;
@@ -144,11 +153,16 @@ export async function detectAllDsns(cwd: string): Promise<DsnDetectionResult> {
144153
});
145154
}
146155

156+
log.debug(`DSN detection starting from project root: ${projectRoot}`);
157+
147158
// 2. Try cached detection result
148159
const cachedDetection = await getCachedDetection(projectRoot);
149160

150161
if (cachedDetection) {
151162
// Cache hit! Return cached result
163+
log.debug(
164+
`DSN detection cache hit: ${cachedDetection.allDsns.length} DSN(s) cached`
165+
);
152166
return {
153167
primary: cachedDetection.allDsns[0] ?? null,
154168
all: cachedDetection.allDsns,
@@ -182,6 +196,9 @@ export async function detectAllDsns(cwd: string): Promise<DsnDetectionResult> {
182196
}
183197
Object.assign(allSourceMtimes, codeMtimes);
184198
Object.assign(allDirMtimes, codeDirMtimes);
199+
if (codeDsns.length > 0) {
200+
log.debug(`Found ${codeDsns.length} DSN(s) in source code`);
201+
}
185202

186203
// 3b. Check all .env files from project root (includes monorepo packages/apps)
187204
const { dsns: envFileDsns, sourceMtimes: envMtimes } =
@@ -190,11 +207,15 @@ export async function detectAllDsns(cwd: string): Promise<DsnDetectionResult> {
190207
addDsn(dsn);
191208
}
192209
Object.assign(allSourceMtimes, envMtimes);
210+
if (envFileDsns.length > 0) {
211+
log.debug(`Found ${envFileDsns.length} DSN(s) in .env files`);
212+
}
193213

194214
// 3c. Check env var (lowest priority) - no mtime for env vars
195215
const envDsn = detectFromEnv();
196216
if (envDsn) {
197217
addDsn(envDsn);
218+
log.debug("Found DSN in SENTRY_DSN environment variable");
198219
}
199220

200221
// 4. Compute fingerprint and cache result
@@ -222,6 +243,16 @@ export async function detectAllDsns(cwd: string): Promise<DsnDetectionResult> {
222243
// Multiple DSNs is valid in monorepos (different packages/apps)
223244
const hasMultiple = allDsns.length > 1;
224245

246+
if (allDsns.length > 0) {
247+
log.debug(`DSN detection complete: ${allDsns.length} total DSN(s) found`);
248+
for (const dsn of allDsns) {
249+
const isPrimary = dsn === allDsns[0] ? " [primary]" : "";
250+
log.debug(` ${dsn.raw}${getDsnSourceDescription(dsn)}${isPrimary}`);
251+
}
252+
} else {
253+
log.debug("DSN detection complete: no DSNs found");
254+
}
255+
225256
return {
226257
primary: allDsns[0] ?? null,
227258
all: allDsns,

src/lib/dsn/env-file.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,15 @@
1010

1111
import { opendir } from "node:fs/promises";
1212
import { join } from "node:path";
13+
import { logger } from "../logger.js";
1314
import { withTracingSpan } from "../telemetry.js";
1415
import { createDetectedDsn } from "./parser.js";
1516
import { scanSpecificFiles } from "./scanner.js";
1617
import type { DetectedDsn } from "./types.js";
1718
import { MONOREPO_ROOTS } from "./types.js";
1819

20+
const log = logger.withTag("dsn-env-file");
21+
1922
/**
2023
* Result of scanning env files, including mtimes for caching.
2124
*/
@@ -156,6 +159,9 @@ export async function detectFromAllEnvFiles(
156159
});
157160
allDsns.push(...rootDsns);
158161
Object.assign(allMtimes, rootMtimes);
162+
if (rootDsns.length > 0) {
163+
log.debug(`Found ${rootDsns.length} DSN(s) in root .env files`);
164+
}
159165

160166
// 2. Check monorepo package directories
161167
const {
@@ -165,6 +171,11 @@ export async function detectFromAllEnvFiles(
165171
} = await detectFromMonorepoEnvFiles(cwd);
166172
allDsns.push(...monorepoDsns);
167173
Object.assign(allMtimes, monorepoMtimes);
174+
if (packagesScanned > 0) {
175+
log.debug(
176+
`Scanned ${packagesScanned} monorepo packages, found ${monorepoDsns.length} DSN(s) in package .env files`
177+
);
178+
}
168179

169180
// Root checks ENV_FILES.length names; each monorepo package also checks ENV_FILES.length
170181
const filesChecked = ENV_FILES.length * (1 + packagesScanned);

src/lib/dsn/project-root.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@
1616
import { opendir, stat } from "node:fs/promises";
1717
import { homedir } from "node:os";
1818
import { dirname, join, resolve } from "node:path";
19+
import { logger } from "../logger.js";
1920
import { anyTrue } from "../promises.js";
2021
import { withFsSpan, withTracingSpan } from "../telemetry.js";
2122
import { ENV_FILES, extractDsnFromEnvContent } from "./env-file.js";
2223
import { handleFileError } from "./fs-utils.js";
2324
import { createDetectedDsn } from "./parser.js";
2425
import type { DetectedDsn } from "./types.js";
2526

27+
const log = logger.withTag("dsn-project-root");
28+
2629
/** Why a directory was chosen as project root */
2730
export type ProjectRootReason =
2831
| "env_dsn" // Found .env with SENTRY_DSN
@@ -554,6 +557,10 @@ export function findProjectRoot(startDir: string): Promise<ProjectRootResult> {
554557

555558
const result = await walkUpDirectories(resolvedStart, stopBoundary);
556559

560+
log.debug(
561+
`Project root: ${result.projectRoot} (reason: ${result.reason})`
562+
);
563+
557564
span.setAttributes({
558565
"dsn.found": result.foundDsn !== undefined,
559566
"dsn.reason": result.reason,

src/lib/resolve-target.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,15 @@ async function resolveDetectedDsns(
926926
// Count DSNs that couldn't be resolved (API errors, permissions, etc.)
927927
const unresolvedCount = resolvedTargets.filter((t) => t === null).length;
928928

929+
for (const t of targets) {
930+
log.debug(`Resolved: ${t.org}/${t.project} (from DSN)`);
931+
}
932+
if (unresolvedCount > 0) {
933+
log.debug(
934+
`DSN resolution: ${unresolvedCount} DSN(s) could not be resolved`
935+
);
936+
}
937+
929938
if (targets.length === 0) {
930939
return {
931940
targets: [],

0 commit comments

Comments
 (0)