Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 56 additions & 51 deletions AGENTS.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/src/content/docs/self-hosted.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ If you pass a self-hosted Sentry URL as a command argument (e.g., an issue or ev
| `SENTRY_FORCE_ENV_TOKEN` | Force env token over stored OAuth token |
| `SENTRY_ORG` | Default organization slug |
| `SENTRY_PROJECT` | Default project slug (supports `org/project` format) |
| `NODE_EXTRA_CA_CERTS` | Path to PEM file with additional CA certificates (for corporate proxies) |
<!-- GENERATED:END self-hosted-env-vars -->

See [Configuration](./configuration/) for the full environment variable reference.
4 changes: 4 additions & 0 deletions script/generate-docs-sections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,10 @@ const SELF_HOSTED_TABLE_ENTRIES: readonly [string, string][] = [
["SENTRY_FORCE_ENV_TOKEN", "Force env token over stored OAuth token"],
["SENTRY_ORG", "Default organization slug"],
["SENTRY_PROJECT", "Default project slug (supports `org/project` format)"],
[
"NODE_EXTRA_CA_CERTS",
"Path to PEM file with additional CA certificates (for corporate proxies)",
],
];

/**
Expand Down
43 changes: 39 additions & 4 deletions src/commands/cli/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,31 @@
*
* View and manage persistent CLI default settings.
*
* Supports four defaults:
* Supports these defaults:
* - `org` / `organization` — default organization slug
* - `project` — default project slug
* - `telemetry` — telemetry preference (on/off)
* - `url` — Sentry instance URL (for self-hosted)
* - `ca-cert` — path to PEM file with custom CA certificates
*/

import { resolve } from "node:path";
import type { SentryContext } from "../../context.js";
import { buildCommand } from "../../lib/command.js";
import { normalizeUrl } from "../../lib/constants.js";
import { readCaCertFile } from "../../lib/custom-ca.js";
import { parseCustomHeaders } from "../../lib/custom-headers.js";
import {
clearAllDefaults,
type DefaultsState,
getAllDefaults,
getDefaultCaCert,
getDefaultHeaders,
getDefaultOrganization,
getDefaultProject,
getDefaultUrl,
getTelemetryPreference,
setDefaultCaCert,
setDefaultHeaders,
setDefaultOrganization,
setDefaultProject,
Expand All @@ -47,7 +52,13 @@ import { computeTelemetryEffective } from "../../lib/telemetry.js";
// ---------------------------------------------------------------------------

/** Canonical key names matching DefaultsState fields */
type DefaultKey = "organization" | "project" | "telemetry" | "url" | "headers";
type DefaultKey =
| "organization"
| "project"
| "telemetry"
| "url"
| "headers"
| "ca-cert";

/** Handler for reading, writing, and clearing a single default */
type DefaultHandler = {
Expand Down Expand Up @@ -131,6 +142,25 @@ const DEFAULTS_REGISTRY: Record<DefaultKey, DefaultHandler> = {
},
clear: () => setDefaultHeaders(null),
},
"ca-cert": {
get: getDefaultCaCert,
set: (value) => {
const trimmed = value.trim();
if (!trimmed) {
throw new ValidationError(
"CA certificate path cannot be empty.",
"ca-cert"
);
}
const resolved = resolve(trimmed);
const result = readCaCertFile(resolved);
if (!result.ok) {
throw new ValidationError(result.reason, "ca-cert");
}
setDefaultCaCert(resolved);
},
clear: () => setDefaultCaCert(null),
},
};

// ---------------------------------------------------------------------------
Expand All @@ -140,6 +170,9 @@ const DEFAULTS_REGISTRY: Record<DefaultKey, DefaultHandler> = {
/** Shorthand aliases for canonical keys (e.g., "org" → "organization") */
const KEY_ALIASES: Partial<Record<string, DefaultKey>> = {
org: "organization",
ca: "ca-cert",
cert: "ca-cert",
"ca-certs": "ca-cert",
};

/** Resolve a user-provided key string to a canonical key, or null if unknown */
Expand Down Expand Up @@ -196,6 +229,7 @@ export const defaultsCommand = buildCommand({
"sentry cli defaults telemetry off # Disable telemetry\n" +
"sentry cli defaults url https://... # Set Sentry URL (self-hosted)\n" +
"sentry cli defaults headers 'X-IAP: t' # Set custom headers (self-hosted)\n" +
"sentry cli defaults ca-cert /path/to/ca.pem # Trust a custom CA certificate\n" +
"sentry cli defaults org --clear # Clear a specific default\n" +
"sentry cli defaults --clear --yes # Clear all defaults\n" +
"```\n\n" +
Expand All @@ -206,7 +240,8 @@ export const defaultsCommand = buildCommand({
"| `project` | Default project slug |\n" +
"| `telemetry` | Telemetry preference (on/off, yes/no, true/false, 1/0) |\n" +
"| `url` | Sentry instance URL (for self-hosted installations) |\n" +
"| `headers` | Custom HTTP headers for self-hosted proxies (semicolon-separated `Name: Value`) |",
"| `headers` | Custom HTTP headers for self-hosted proxies (semicolon-separated `Name: Value`) |\n" +
"| `ca-cert` | Path to PEM file with custom CA certificates (for corporate proxies) |",
},
output: {
human: formatDefaultsResult,
Expand Down Expand Up @@ -260,7 +295,7 @@ export const defaultsCommand = buildCommand({
guardNonInteractive(flags);
if (!isConfirmationBypassed(flags)) {
const confirmed = await log.prompt(
"This will clear all defaults (organization, project, telemetry, URL, headers). Continue?",
"This will clear all defaults (organization, project, telemetry, URL, headers, ca-cert). Continue?",
{ type: "confirm" }
);
if (confirmed !== true) {
Expand Down
Loading
Loading