Skip to content

Commit 50b0c0f

Browse files
committed
fix(telemetry): centralize sentry.org/project tags in resolution functions
Move setOrgProjectContext() from individual command files into the shared resolution functions in resolve-target.ts, trace-target.ts, and dashboard/resolve.ts. This ensures every command that resolves an org or project automatically gets sentry.org and sentry.project telemetry tags, eliminating the class of bugs where a command forgets to call setContext(). Previously ~15 commands manually called this.setContext() after resolution, while ~15 others (all dashboard/*, event/view, project/*, trial/*, org/view) forgot — causing missing telemetry tags on error events. Changes: - Add setOrgProjectContext calls to 6 functions in resolve-target.ts - Add setOrgProjectContext calls to 2 functions in trace-target.ts - Add setOrgProjectContext call to resolveOrgFromTarget in dashboard/resolve.ts - Remove setContext from SentryContext interface and buildContext - Remove all manual setContext calls from 12 command files - Update 35 test files to remove setContext from mock contexts - Delete 7 test blocks that asserted setContext was called
1 parent 48a9a8f commit 50b0c0f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+85
-350
lines changed

AGENTS.md

Lines changed: 10 additions & 72 deletions
Large diffs are not rendered by default.

src/commands/dashboard/resolve.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { listDashboards } from "../../lib/api-client.js";
99
import type { parseOrgProjectArg } from "../../lib/arg-parsing.js";
1010
import { ContextError, ValidationError } from "../../lib/errors.js";
1111
import { resolveOrg } from "../../lib/resolve-target.js";
12+
import { setOrgProjectContext } from "../../lib/telemetry.js";
1213
import { isAllDigits } from "../../lib/utils.js";
1314
import {
1415
type DashboardWidget,
@@ -52,9 +53,11 @@ export async function resolveOrgFromTarget(
5253
switch (parsed.type) {
5354
case "explicit":
5455
case "org-all":
56+
setOrgProjectContext([parsed.org], []);
5557
return parsed.org;
5658
case "project-search":
5759
case "auto-detect": {
60+
// resolveOrg already sets telemetry context
5861
const resolved = await resolveOrg({ cwd });
5962
if (!resolved) {
6063
throw new ContextError("Organization", usageHint);

src/commands/issue/explain.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export const explainCommand = buildCommand({
7474
},
7575
async *func(this: SentryContext, flags: ExplainFlags, issueArg: string) {
7676
applyFreshFlag(flags);
77-
const { cwd, setContext } = this;
77+
const { cwd } = this;
7878

7979
// Declare org outside try block so it's accessible in catch for error messages
8080
let resolvedOrg: string | undefined;
@@ -88,9 +88,6 @@ export const explainCommand = buildCommand({
8888
});
8989
resolvedOrg = org;
9090

91-
// Set telemetry context so SeerError events carry the org tag
92-
setContext([org], []);
93-
9491
// Ensure root cause analysis exists (triggers if needed)
9592
const state = await ensureRootCauseAnalysis({
9693
org,

src/commands/issue/list.ts

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -819,7 +819,6 @@ async function fetchOrgAllIssues(
819819
type OrgAllIssuesOptions = {
820820
org: string;
821821
flags: ListFlags;
822-
setContext: (orgs: string[], projects: string[]) => void;
823822
};
824823

825824
/**
@@ -832,7 +831,7 @@ type OrgAllIssuesOptions = {
832831
async function handleOrgAllIssues(
833832
options: OrgAllIssuesOptions
834833
): Promise<IssueListResult> {
835-
const { org, flags, setContext } = options;
834+
const { org, flags } = options;
836835
// Encode sort + query in context key so cursors from different searches don't collide.
837836
const contextKey = buildPaginationContextKey("org", org, {
838837
sort: flags.sort,
@@ -841,8 +840,6 @@ async function handleOrgAllIssues(
841840
});
842841
const cursor = resolveOrgCursor(flags.cursor, PAGINATION_KEY, contextKey);
843842

844-
setContext([org], []);
845-
846843
let issuesResult: IssuesPage;
847844
try {
848845
issuesResult = await withProgress(
@@ -915,7 +912,6 @@ type ResolvedTargetsOptions = {
915912
parsed: ReturnType<typeof parseOrgProjectArg>;
916913
flags: ListFlags;
917914
cwd: string;
918-
setContext: (orgs: string[], projects: string[]) => void;
919915
};
920916

921917
/** Default --period value (used to detect user-implicit vs explicit). */
@@ -1049,15 +1045,11 @@ function build403Detail(originalDetail: string | undefined): string {
10491045
async function handleResolvedTargets(
10501046
options: ResolvedTargetsOptions
10511047
): Promise<IssueListResult> {
1052-
const { parsed, flags, cwd, setContext } = options;
1048+
const { parsed, flags, cwd } = options;
10531049

10541050
const { targets, footer, skippedSelfHosted, detectedDsns } =
10551051
await resolveTargetsFromParsedArg(parsed, cwd);
10561052

1057-
const orgs = [...new Set(targets.map((t) => t.org))];
1058-
const projects = [...new Set(targets.map((t) => t.project))];
1059-
setContext(orgs, projects);
1060-
10611053
if (targets.length === 0) {
10621054
if (skippedSelfHosted) {
10631055
throw new ContextError("Organization and project", USAGE_HINT, [
@@ -1479,7 +1471,7 @@ export const listCommand = buildListCommand("issue", {
14791471
},
14801472
},
14811473
async *func(this: SentryContext, flags: ListFlags, target?: string) {
1482-
const { cwd, setContext } = this;
1474+
const { cwd } = this;
14831475

14841476
const parsed = parseOrgProjectArg(target);
14851477

@@ -1502,7 +1494,6 @@ export const listCommand = buildListCommand("issue", {
15021494
handleResolvedTargets({
15031495
...ctx,
15041496
flags,
1505-
setContext,
15061497
});
15071498

15081499
const result = (await dispatchOrgScopedList({
@@ -1521,7 +1512,6 @@ export const listCommand = buildListCommand("issue", {
15211512
handleOrgAllIssues({
15221513
org: ctx.parsed.org,
15231514
flags,
1524-
setContext,
15251515
}),
15261516
},
15271517
})) as IssueListResult;

src/commands/issue/plan.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ export const planCommand = buildCommand({
193193
},
194194
async *func(this: SentryContext, flags: PlanFlags, issueArg: string) {
195195
applyFreshFlag(flags);
196-
const { cwd, setContext } = this;
196+
const { cwd } = this;
197197

198198
// Declare org outside try block so it's accessible in catch for error messages
199199
let resolvedOrg: string | undefined;
@@ -207,9 +207,6 @@ export const planCommand = buildCommand({
207207
});
208208
resolvedOrg = org;
209209

210-
// Set telemetry context so SeerError events carry the org tag
211-
setContext([org], []);
212-
213210
// Ensure root cause analysis exists (runs explain if needed)
214211
const state = await ensureRootCauseAnalysis({
215212
org,

src/commands/issue/view.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ export const viewCommand = buildCommand({
146146
},
147147
async *func(this: SentryContext, flags: ViewFlags, issueArg: string) {
148148
applyFreshFlag(flags);
149-
const { cwd, setContext } = this;
149+
const { cwd } = this;
150150

151151
// Resolve issue using shared resolution logic
152152
const { org: orgSlug, issue } = await resolveIssue({
@@ -155,12 +155,6 @@ export const viewCommand = buildCommand({
155155
command: "view",
156156
});
157157

158-
// Set telemetry context
159-
setContext(
160-
orgSlug ? [orgSlug] : [],
161-
issue.project?.slug ? [issue.project.slug] : []
162-
);
163-
164158
if (flags.web) {
165159
await openInBrowser(issue.permalink, "issue");
166160
return;

src/commands/log/list.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,7 @@ export const listCommand = buildListCommand(
645645
},
646646
},
647647
async *func(this: SentryContext, flags: ListFlags, ...args: string[]) {
648-
const { cwd, setContext } = this;
648+
const { cwd } = this;
649649

650650
const parsed = parseLogListArgs(args);
651651

@@ -657,8 +657,6 @@ export const listCommand = buildListCommand(
657657
cwd,
658658
TRACE_USAGE_HINT
659659
);
660-
setContext([org], []);
661-
662660
if (flags.follow) {
663661
// Banner (suppressed in JSON mode)
664662
writeFollowBanner(
@@ -725,8 +723,6 @@ export const listCommand = buildListCommand(
725723
cwd,
726724
COMMAND_NAME
727725
);
728-
setContext([org], [project]);
729-
730726
if (flags.follow) {
731727
writeFollowBanner(
732728
flags.follow ?? DEFAULT_POLL_INTERVAL,

src/commands/log/view.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ export const viewCommand = buildCommand({
349349
},
350350
async *func(this: SentryContext, flags: ViewFlags, ...args: string[]) {
351351
applyFreshFlag(flags);
352-
const { cwd, setContext } = this;
352+
const { cwd } = this;
353353
const cmdLog = logger.withTag("log.view");
354354

355355
// Parse positional args
@@ -365,9 +365,6 @@ export const viewCommand = buildCommand({
365365
throw new ContextError("Organization and project", USAGE_HINT);
366366
}
367367

368-
// Set telemetry context
369-
setContext([target.org], [target.project]);
370-
371368
if (flags.web) {
372369
await handleWebOpen(target.org, logIds);
373370
return;

src/commands/span/list.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,6 @@ function jsonTransformSpanList(data: SpanListData, fields?: string[]): unknown {
239239
type ModeContext = {
240240
cwd: string;
241241
flags: ListFlags;
242-
setContext: (orgs: string[], projects: string[]) => void;
243242
};
244243

245244
/**
@@ -259,8 +258,6 @@ async function handleTraceMode(
259258
cwd,
260259
TRACE_USAGE_HINT
261260
);
262-
ctx.setContext([org], [project]);
263-
264261
const queryParts = [`trace:${traceId}`];
265262
if (flags.query) {
266263
queryParts.push(translateSpanQuery(flags.query));
@@ -324,8 +321,6 @@ async function handleProjectMode(
324321
cwd,
325322
COMMAND_NAME
326323
);
327-
ctx.setContext([org], [project]);
328-
329324
const apiQuery = flags.query ? translateSpanQuery(flags.query) : undefined;
330325

331326
const contextKey = buildPaginationContextKey(
@@ -447,9 +442,9 @@ export const listCommand = buildListCommand("span", {
447442
},
448443
},
449444
async *func(this: SentryContext, flags: ListFlags, ...args: string[]) {
450-
const { cwd, setContext } = this;
445+
const { cwd } = this;
451446
const parsed = parseSpanListArgs(args);
452-
const modeCtx: ModeContext = { cwd, flags, setContext };
447+
const modeCtx: ModeContext = { cwd, flags };
453448

454449
const { output, hint } =
455450
parsed.mode === "trace"

src/commands/span/view.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -265,20 +265,18 @@ export const viewCommand = buildCommand({
265265
},
266266
async *func(this: SentryContext, flags: ViewFlags, ...args: string[]) {
267267
applyFreshFlag(flags);
268-
const { cwd, setContext } = this;
268+
const { cwd } = this;
269269

270270
// Parse positional args: first is trace target, rest are span IDs
271271
const { traceTarget, spanIds } = parsePositionalArgs(args);
272272
warnIfNormalized(traceTarget, "span.view");
273273

274274
// Resolve org/project
275-
const { traceId, org, project } = await resolveTraceOrgProject(
275+
const { traceId, org } = await resolveTraceOrgProject(
276276
traceTarget,
277277
cwd,
278278
USAGE_HINT
279279
);
280-
setContext([org], [project]);
281-
282280
// Fetch trace data (single fetch for all span lookups)
283281
const timestamp = Math.floor(Date.now() / 1000);
284282
const spans = await getDetailedTrace(org, traceId, timestamp);

0 commit comments

Comments
 (0)