Skip to content

Commit fe755ee

Browse files
betegonclaude
andauthored
feat: support SENTRY_HOST as alias for SENTRY_URL (#409)
## Summary Adds `SENTRY_HOST` as a supported env var for configuring the Sentry instance URL. It takes precedence over `SENTRY_URL` so both work, but users who expect `SENTRY_HOST` (common in other Sentry tooling) get the behavior they want out of the box. Precedence: `SENTRY_HOST` > `SENTRY_URL` > default (`https://sentry.io`) ## Changes A single `getConfiguredSentryUrl()` helper in `constants.ts` replaces all 8 `process.env.SENTRY_URL` reads across 6 source files. `applySentryUrlContext()` now sets/deletes both env vars so `SENTRY_HOST` can't shadow a freshly-set `SENTRY_URL`. ## Test plan - New tests for `SENTRY_HOST` alone and precedence over `SENTRY_URL` in region, code-scanner, URL parser, and URL builder tests - All 146 tests across the 4 modified test files pass - `bun run typecheck` and `bun run lint` clean 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5bba832 commit fe755ee

17 files changed

+183
-39
lines changed

DEVELOPMENT.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,11 @@ When creating your Sentry OAuth application:
6767

6868
## Environment Variables
6969

70-
| Variable | Description | Default |
71-
| ------------------ | ------------------------------------ | -------------------- |
72-
| `SENTRY_CLIENT_ID` | Sentry OAuth app client ID | (required) |
73-
| `SENTRY_URL` | Sentry instance URL (for self-hosted)| `https://sentry.io` |
70+
| Variable | Description | Default |
71+
| ------------------ | ----------------------------------------------------- | -------------------- |
72+
| `SENTRY_CLIENT_ID` | Sentry OAuth app client ID | (required) |
73+
| `SENTRY_HOST` | Sentry instance URL (for self-hosted, takes precedence) | `https://sentry.io` |
74+
| `SENTRY_URL` | Alias for `SENTRY_HOST` | `https://sentry.io` |
7475

7576
## Building
7677

docs/src/content/docs/configuration.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,22 @@ The Sentry CLI can be configured through environment variables and a local datab
77

88
## Environment Variables
99

10-
### `SENTRY_URL`
10+
### `SENTRY_HOST`
1111

1212
Base URL of your Sentry instance. **Only needed for [self-hosted Sentry](./self-hosted/).** SaaS users (sentry.io) should not set this.
1313

1414
```bash
15-
export SENTRY_URL=https://sentry.example.com
15+
export SENTRY_HOST=https://sentry.example.com
1616
```
1717

1818
When set, all API requests (including OAuth login) are directed to this URL instead of `https://sentry.io`. The CLI also sets this automatically when you pass a self-hosted Sentry URL as a command argument.
1919

20+
`SENTRY_HOST` takes precedence over `SENTRY_URL`. Both work identically — use whichever you prefer.
21+
22+
### `SENTRY_URL`
23+
24+
Alias for `SENTRY_HOST`. If both are set, `SENTRY_HOST` takes precedence.
25+
2026
### `SENTRY_ORG`
2127

2228
Default organization slug. Skips organization auto-detection.

docs/src/content/docs/self-hosted.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ title: Self-Hosted Sentry
33
description: Using the Sentry CLI with a self-hosted Sentry instance
44
---
55

6-
The CLI works with self-hosted Sentry instances. Set the `SENTRY_URL` environment variable to point at your instance:
6+
The CLI works with self-hosted Sentry instances. Set the `SENTRY_HOST` (or `SENTRY_URL`) environment variable to point at your instance:
77

88
```bash
9-
export SENTRY_URL=https://sentry.example.com
9+
export SENTRY_HOST=https://sentry.example.com
1010
```
1111

1212
## Authenticating
@@ -27,14 +27,14 @@ The OAuth device flow requires **Sentry 26.1.0 or later** and a public OAuth app
2727
Pass your instance URL and the client ID:
2828

2929
```bash
30-
SENTRY_URL=https://sentry.example.com SENTRY_CLIENT_ID=your-client-id sentry auth login
30+
SENTRY_HOST=https://sentry.example.com SENTRY_CLIENT_ID=your-client-id sentry auth login
3131
```
3232

3333
:::tip
3434
You can export both variables in your shell profile so every CLI invocation picks them up:
3535

3636
```bash
37-
export SENTRY_URL=https://sentry.example.com
37+
export SENTRY_HOST=https://sentry.example.com
3838
export SENTRY_CLIENT_ID=your-client-id
3939
```
4040
:::
@@ -48,7 +48,7 @@ If your instance is on an older version or you prefer not to create an OAuth app
4848
3. Pass it to the CLI:
4949

5050
```bash
51-
SENTRY_URL=https://sentry.example.com sentry auth login --token YOUR_TOKEN
51+
SENTRY_HOST=https://sentry.example.com sentry auth login --token YOUR_TOKEN
5252
```
5353

5454
## After Login
@@ -66,7 +66,8 @@ If you pass a self-hosted Sentry URL as a command argument (e.g., an issue or ev
6666

6767
| Variable | Description |
6868
|----------|-------------|
69-
| `SENTRY_URL` | Base URL of your Sentry instance |
69+
| `SENTRY_HOST` | Base URL of your Sentry instance (takes precedence over `SENTRY_URL`) |
70+
| `SENTRY_URL` | Alias for `SENTRY_HOST` |
7071
| `SENTRY_CLIENT_ID` | Client ID of your public OAuth application |
7172
| `SENTRY_ORG` | Default organization slug |
7273
| `SENTRY_PROJECT` | Default project slug (supports `org/project` format) |

src/lib/constants.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ export const DEFAULT_SENTRY_HOST = "sentry.io";
1111
/** Default Sentry SaaS URL (control silo for OAuth and region discovery) */
1212
export const DEFAULT_SENTRY_URL = `https://${DEFAULT_SENTRY_HOST}`;
1313

14+
/**
15+
* Resolve the Sentry instance URL from environment variables.
16+
* Checks SENTRY_HOST first, then SENTRY_URL, then falls back to undefined.
17+
*/
18+
export function getConfiguredSentryUrl(): string | undefined {
19+
return process.env.SENTRY_HOST || process.env.SENTRY_URL || undefined;
20+
}
21+
1422
/** CLI version string, available for help output and other uses */
1523
export const CLI_VERSION =
1624
typeof SENTRY_CLI_VERSION !== "undefined" ? SENTRY_CLI_VERSION : "0.0.0-dev";

src/lib/dsn/code-scanner.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import path from "node:path";
1919
import * as Sentry from "@sentry/bun";
2020
import ignore, { type Ignore } from "ignore";
2121
import pLimit from "p-limit";
22-
import { DEFAULT_SENTRY_HOST } from "../constants.js";
22+
import { DEFAULT_SENTRY_HOST, getConfiguredSentryUrl } from "../constants.js";
2323
import { ConfigError } from "../errors.js";
2424
import { logger } from "../logger.js";
2525
import { withTracingSpan } from "../telemetry.js";
@@ -316,18 +316,18 @@ function isCommentedLine(trimmedLine: string): boolean {
316316
* @returns The expected host domain for DSN validation
317317
*/
318318
function getExpectedHost(): string {
319-
const sentryUrl = process.env.SENTRY_URL;
319+
const sentryUrl = getConfiguredSentryUrl();
320320

321321
if (sentryUrl) {
322322
// Self-hosted: only accept DSNs matching the configured host
323323
try {
324324
const url = new URL(sentryUrl);
325325
return url.host;
326326
} catch {
327-
// Invalid SENTRY_URL - throw immediately since nothing will work
327+
// Invalid SENTRY_HOST/SENTRY_URL - throw immediately since nothing will work
328328
throw new ConfigError(
329-
`SENTRY_URL "${sentryUrl}" is not a valid URL`,
330-
"Set SENTRY_URL to a valid URL (e.g., https://sentry.example.com) or unset it to use sentry.io"
329+
`SENTRY_HOST/SENTRY_URL "${sentryUrl}" is not a valid URL`,
330+
"Set SENTRY_HOST/SENTRY_URL to a valid URL (e.g., https://sentry.example.com) or unset it to use sentry.io"
331331
);
332332
}
333333
}

src/lib/oauth.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
TokenErrorResponseSchema,
1212
TokenResponseSchema,
1313
} from "../types/index.js";
14+
import { DEFAULT_SENTRY_URL, getConfiguredSentryUrl } from "./constants.js";
1415
import { setAuthToken } from "./db/auth.js";
1516
import { ApiError, AuthError, ConfigError, DeviceFlowError } from "./errors.js";
1617
import { withHttpSpan } from "./telemetry.js";
@@ -23,7 +24,7 @@ import { withHttpSpan } from "./telemetry.js";
2324
* by the device flow and token refresh.
2425
*/
2526
function getSentryUrl(): string {
26-
return process.env.SENTRY_URL ?? "https://sentry.io";
27+
return getConfiguredSentryUrl() ?? DEFAULT_SENTRY_URL;
2728
}
2829

2930
/**

src/lib/region.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
import { retrieveAnOrganization } from "@sentry/api";
9+
import { getConfiguredSentryUrl } from "./constants.js";
910
import { getOrgByNumericId, getOrgRegion, setOrgRegion } from "./db/regions.js";
1011
import { stripDsnOrgPrefix } from "./dsn/index.js";
1112
import { withAuthGuard } from "./errors.js";
@@ -66,8 +67,8 @@ export async function resolveOrgRegion(orgSlug: string): Promise<string> {
6667
* Returns false for self-hosted instances that don't have regional URLs.
6768
*/
6869
export function isMultiRegionEnabled(): boolean {
69-
// Self-hosted instances (custom SENTRY_URL) typically don't have multi-region
70-
const baseUrl = process.env.SENTRY_URL;
70+
// Self-hosted instances (custom SENTRY_HOST/SENTRY_URL) typically don't have multi-region
71+
const baseUrl = getConfiguredSentryUrl();
7172
if (baseUrl && !isSentrySaasUrl(baseUrl)) {
7273
return false;
7374
}

src/lib/sentry-client.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@
88
* through the SDK function options (baseUrl, fetch, headers).
99
*/
1010

11-
import { DEFAULT_SENTRY_URL, getUserAgent } from "./constants.js";
11+
import {
12+
DEFAULT_SENTRY_URL,
13+
getConfiguredSentryUrl,
14+
getUserAgent,
15+
} from "./constants.js";
1216
import { getAuthToken, isEnvTokenActive, refreshToken } from "./db/auth.js";
1317
import { getCachedResponse, storeCachedResponse } from "./response-cache.js";
1418
import { withHttpSpan } from "./telemetry.js";
@@ -391,7 +395,7 @@ function getAuthenticatedFetch(): typeof fetch {
391395
* Supports self-hosted instances via SENTRY_URL env var.
392396
*/
393397
export function getApiBaseUrl(): string {
394-
return process.env.SENTRY_URL || DEFAULT_SENTRY_URL;
398+
return getConfiguredSentryUrl() ?? DEFAULT_SENTRY_URL;
395399
}
396400

397401
/**
@@ -402,7 +406,7 @@ export function getApiBaseUrl(): string {
402406
* (e.g., from URL argument parsing for self-hosted instances) is respected.
403407
*/
404408
export function getControlSiloUrl(): string {
405-
return process.env.SENTRY_URL || DEFAULT_SENTRY_URL;
409+
return getConfiguredSentryUrl() ?? DEFAULT_SENTRY_URL;
406410
}
407411

408412
/**

src/lib/sentry-url-parser.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,10 +191,13 @@ export function parseSentryUrl(input: string): ParsedSentryUrl | null {
191191
export function applySentryUrlContext(baseUrl: string): void {
192192
if (isSentrySaasUrl(baseUrl)) {
193193
// Clear any self-hosted URL so API calls fall back to default SaaS routing.
194-
// Without this, a stale SENTRY_URL would route SaaS requests to the wrong host.
194+
// Without this, a stale SENTRY_HOST/SENTRY_URL would route SaaS requests to the wrong host.
195+
// biome-ignore lint/performance/noDelete: process.env requires delete to truly unset; assignment coerces to string in Node.js
196+
delete process.env.SENTRY_HOST;
195197
// biome-ignore lint/performance/noDelete: process.env requires delete to truly unset; assignment coerces to string in Node.js
196198
delete process.env.SENTRY_URL;
197199
return;
198200
}
201+
process.env.SENTRY_HOST = baseUrl;
199202
process.env.SENTRY_URL = baseUrl;
200203
}

src/lib/sentry-urls.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,18 @@
55
* Supports self-hosted instances via SENTRY_URL environment variable.
66
*/
77

8-
import { DEFAULT_SENTRY_HOST, DEFAULT_SENTRY_URL } from "./constants.js";
8+
import {
9+
DEFAULT_SENTRY_HOST,
10+
DEFAULT_SENTRY_URL,
11+
getConfiguredSentryUrl,
12+
} from "./constants.js";
913

1014
/**
1115
* Get the Sentry web base URL.
1216
* Supports self-hosted instances via SENTRY_URL env var.
1317
*/
1418
export function getSentryBaseUrl(): string {
15-
return process.env.SENTRY_URL ?? DEFAULT_SENTRY_URL;
19+
return getConfiguredSentryUrl() ?? DEFAULT_SENTRY_URL;
1620
}
1721

1822
/**

0 commit comments

Comments
 (0)