-
-
Notifications
You must be signed in to change notification settings - Fork 10
feat: sourcemap upload parity flags and issue archive command #883
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,126 @@ | ||
| /** | ||
| * sentry issue archive (aliased: ignore) | ||
| * | ||
| * Archive (ignore) an issue, suppressing alerts until an optional | ||
| * condition is met. This maps to the "ignored" status in the Sentry API. | ||
| */ | ||
|
|
||
| import type { SentryContext } from "../../context.js"; | ||
| import { | ||
| type IgnoreStatusDetails, | ||
| updateIssueStatus, | ||
| } from "../../lib/api-client.js"; | ||
| import { buildCommand } from "../../lib/command.js"; | ||
| import { formatIssueDetails, muted } from "../../lib/formatters/index.js"; | ||
| import { CommandOutput } from "../../lib/formatters/output.js"; | ||
| import { logger } from "../../lib/logger.js"; | ||
| import type { SentryIssue } from "../../types/index.js"; | ||
| import { issueIdPositional, resolveIssue } from "./utils.js"; | ||
|
|
||
| const log = logger.withTag("issue.archive"); | ||
|
|
||
| const COMMAND = "archive"; | ||
|
|
||
| type ArchiveFlags = { | ||
| readonly json: boolean; | ||
| readonly fields?: string[]; | ||
| readonly duration?: number; | ||
| readonly count?: number; | ||
| readonly window?: number; | ||
| readonly users?: number; | ||
| readonly "user-window"?: number; | ||
| }; | ||
|
|
||
| function formatArchived(issue: SentryIssue): string { | ||
| return `${muted("Archived")}\n\n${formatIssueDetails(issue)}`; | ||
| } | ||
|
|
||
| export const archiveCommand = buildCommand({ | ||
| docs: { | ||
| brief: "Archive (ignore) an issue", | ||
| fullDescription: | ||
| "Archive an issue, suppressing alerts until an optional condition is met.\n" + | ||
| "Without any duration/count flags, the issue is archived indefinitely.\n\n" + | ||
| "Examples:\n" + | ||
| " sentry issue archive CLI-12Z\n" + | ||
| " sentry issue archive CLI-12Z --duration 60\n" + | ||
| " sentry issue archive CLI-12Z --count 100 --window 60\n" + | ||
| " sentry issue archive CLI-12Z --users 10", | ||
| }, | ||
| output: { | ||
| human: formatArchived, | ||
| }, | ||
| parameters: { | ||
| positional: issueIdPositional, | ||
| flags: { | ||
| duration: { | ||
| kind: "parsed", | ||
| parse: Number, | ||
| brief: "Ignore for this many minutes", | ||
| optional: true, | ||
| }, | ||
| count: { | ||
| kind: "parsed", | ||
| parse: Number, | ||
| brief: "Ignore until this many more events occur", | ||
| optional: true, | ||
| }, | ||
| window: { | ||
| kind: "parsed", | ||
| parse: Number, | ||
| brief: | ||
| "Time window in minutes for --count (events must occur within this window)", | ||
| optional: true, | ||
| }, | ||
| users: { | ||
| kind: "parsed", | ||
| parse: Number, | ||
| brief: "Ignore until this many more users are affected", | ||
| optional: true, | ||
| }, | ||
| "user-window": { | ||
| kind: "parsed", | ||
| parse: Number, | ||
| brief: | ||
| "Time window in minutes for --users (users must be affected within this window)", | ||
| optional: true, | ||
| }, | ||
| }, | ||
| }, | ||
| async *func(this: SentryContext, flags: ArchiveFlags, issueArg: string) { | ||
| const { cwd } = this; | ||
|
|
||
| const { org, issue } = await resolveIssue({ | ||
| issueArg, | ||
| cwd, | ||
| command: COMMAND, | ||
| }); | ||
|
|
||
| const statusDetails: IgnoreStatusDetails = {}; | ||
| if (flags.duration !== undefined) { | ||
| statusDetails.ignoreDuration = flags.duration; | ||
| } | ||
| if (flags.count !== undefined) { | ||
| statusDetails.ignoreCount = flags.count; | ||
| } | ||
| if (flags.window !== undefined) { | ||
| statusDetails.ignoreWindow = flags.window; | ||
| } | ||
| if (flags.users !== undefined) { | ||
| statusDetails.ignoreUserCount = flags.users; | ||
| } | ||
| if (flags["user-window"] !== undefined) { | ||
| statusDetails.ignoreUserWindow = flags["user-window"]; | ||
| } | ||
|
|
||
| const hasDetails = Object.keys(statusDetails).length > 0; | ||
|
|
||
| const updated = await updateIssueStatus(issue.id, "ignored", { | ||
| ...(hasDetails ? { statusDetails } : {}), | ||
| orgSlug: org, | ||
| }); | ||
|
|
||
| log.debug(`Archived ${updated.shortId}`); | ||
| yield new CommandOutput<SentryIssue>(updated); | ||
| }, | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ import { CommandOutput } from "../../lib/formatters/output.js"; | |
| import { | ||
| assertDirectoryReadable, | ||
| buildEmptyDiscoveryError, | ||
| buildIgnoreMatcher, | ||
| diagnoseEmptyDiscovery, | ||
| discoverFilePairs, | ||
| type InjectResult, | ||
|
|
@@ -91,6 +92,18 @@ export const injectCommand = buildCommand({ | |
| "Comma-separated file extensions to process (default: .js,.cjs,.mjs)", | ||
| optional: true, | ||
| }, | ||
| ignore: { | ||
| kind: "parsed", | ||
| parse: String, | ||
| brief: "Glob pattern to exclude (gitignore-style, repeatable)", | ||
| optional: true, | ||
| }, | ||
| "ignore-file": { | ||
| kind: "parsed", | ||
| parse: String, | ||
| brief: "Path to a file with gitignore-style patterns to exclude", | ||
| optional: true, | ||
| }, | ||
| "dry-run": { | ||
| kind: "boolean", | ||
| brief: "Show what would be modified without writing", | ||
|
|
@@ -109,7 +122,13 @@ export const injectCommand = buildCommand({ | |
| }, | ||
| async *func( | ||
| this: SentryContext, | ||
| flags: { ext?: string; "dry-run"?: boolean; "allow-empty"?: boolean }, | ||
| flags: { | ||
| ext?: string; | ||
| ignore?: string; | ||
| "ignore-file"?: string; | ||
| "dry-run"?: boolean; | ||
| "allow-empty"?: boolean; | ||
| }, | ||
| dir: string | ||
| ) { | ||
| // Discover pairs read-only first so we don't error after partially | ||
|
|
@@ -123,14 +142,23 @@ export const injectCommand = buildCommand({ | |
| ? new Set(extensions.map((e) => (e.startsWith(".") ? e : `.${e}`))) | ||
| : undefined; | ||
|
|
||
| const pairs = await discoverFilePairs(dir, extSet); | ||
| const ignorePatterns = flags.ignore | ||
| ? flags.ignore.split(",").map((p) => p.trim()) | ||
| : undefined; | ||
| const ignoreMatcher = await buildIgnoreMatcher( | ||
| ignorePatterns, | ||
| flags["ignore-file"] | ||
| ); | ||
|
|
||
| const pairs = await discoverFilePairs(dir, extSet, ignoreMatcher); | ||
| if (pairs.length === 0 && !flags["allow-empty"]) { | ||
| const diag = await diagnoseEmptyDiscovery(dir, { extensions }); | ||
| throw buildEmptyDiscoveryError(dir, diag); | ||
| } | ||
|
|
||
| const results = await injectDirectory(dir, { | ||
| extensions, | ||
| ignorePatterns, | ||
| dryRun: flags["dry-run"], | ||
| }); | ||
|
Comment on lines
155
to
163
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: The Suggested FixPass the fully constructed Prompt for AI AgentDid we get this right? 👍 / 👎 to inform future reviews. |
||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inject command drops
--ignore-filepatterns during injectionHigh Severity
The inject command builds an
ignoreMatcherfrom both--ignorepatterns and--ignore-file, and correctly uses it for thediscoverFilePairspre-check. But it then passes onlyignorePatterns(notignoreMatcher) toinjectDirectory. InsideinjectDirectory, the matcher is rebuilt from justignorePatterns, losing all patterns from--ignore-file. This makes--ignore-filesilently ineffective during actual injection. The upload command correctly passesignoreMatchertoinjectDirectory; the inject command needs to do the same.Additional Locations (1)
src/commands/sourcemap/upload.ts#L275-L279Reviewed by Cursor Bugbot for commit 0970403. Configure here.