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
10 changes: 6 additions & 4 deletions apps/cli-docs/src/content/docs/library-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,14 @@ const help = await sdk.run("help", "issue");
## Authentication

The `token` option provides an auth token for the current invocation. When
omitted, it falls back to environment variables and stored credentials:
omitted, it falls back to stored credentials and environment variables:

1. `token` option (highest priority)
2. `SENTRY_AUTH_TOKEN` environment variable
3. `SENTRY_TOKEN` environment variable
4. Stored OAuth token from `sentry auth login`
2. Stored OAuth token from `sentry auth login` (if not expired)
3. `SENTRY_AUTH_TOKEN` environment variable
4. `SENTRY_TOKEN` environment variable

Set `SENTRY_FORCE_ENV_TOKEN=1` to make environment variable tokens take priority over stored OAuth.

```typescript
// Explicit token
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/lib/sdk-invoke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ function buildIsolatedEnv(
const env: NodeJS.ProcessEnv = { ...process.env };
if (options?.token) {
env.SENTRY_AUTH_TOKEN = options.token;
env.SENTRY_FORCE_ENV_TOKEN = "1";
}
if (options?.url) {
env.SENTRY_HOST = options.url;
Expand Down
23 changes: 20 additions & 3 deletions packages/cli/test/lib/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
import { afterEach, beforeEach, describe, expect, test } from "vitest";
import createSentrySDK, { SentryError } from "../../src/index.js";
import { mockFetch } from "../helpers.js";
import { setAuthToken } from "../../src/lib/db/auth.js";
import { mockFetch, useTestConfigDir } from "../helpers.js";

useTestConfigDir("sdk-library-");

describe("createSentrySDK() library API", () => {
// Silence unmocked fetch calls from resolution cascade.
// SDK tests that call commands like "issue list" or "org list" trigger
// the org/project resolution cascade which hits real API endpoints.
// A silent 404 prevents preload warnings while preserving error behavior.
let originalFetch: typeof globalThis.fetch;
let authorizationHeaders: string[];

beforeEach(() => {
originalFetch = globalThis.fetch;
authorizationHeaders = [];
// Return empty successes rather than 404s so the resolution cascade
// terminates cleanly without triggering follow-up requests that could
// outlive the test and spill into later test files.
globalThis.fetch = mockFetch(async (input) => {
globalThis.fetch = mockFetch(async (input, init) => {
const request = new Request(input, init);
authorizationHeaders.push(request.headers.get("Authorization") ?? "");
let url: string;
if (typeof input === "string") {
url = input;
Expand All @@ -26,7 +33,7 @@ describe("createSentrySDK() library API", () => {
if (url.includes("/regions/")) {
return new Response(JSON.stringify({ regions: [] }), { status: 200 });
}
if (url.includes("/organizations/")) {
if (url.includes("/organizations")) {
return new Response(JSON.stringify([]), { status: 200 });
}
// Return empty 200 for all other endpoints (projects, issues, etc.)
Expand Down Expand Up @@ -133,6 +140,16 @@ describe("createSentrySDK() library API", () => {
}
});

test("explicit token overrides stored OAuth credentials", async () => {
setAuthToken("stored-oauth-token", 3600);
const sdk = createSentrySDK({ token: "explicit-sdk-token" });

await sdk.auth.status();

expect(authorizationHeaders).toContain("Bearer explicit-sdk-token");
expect(authorizationHeaders).not.toContain("Bearer stored-oauth-token");
});

test("sdk.run returns AsyncIterable for streaming flag --follow", () => {
const sdk = createSentrySDK();
const result = sdk.run("log", "list", "--follow");
Expand Down
Loading