Skip to content

Commit 9763dae

Browse files
sentry-junior[bot]dcramergithub-actions[bot]jared-outpost[bot]
authored
feat(auth): Make bare sentry auth run login (getsentry#1380)
Co-authored-by: David Cramer <david@sentry.io> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: jared-outpost[bot] <jared-outpost[bot]@users.noreply.github.com>
1 parent fbafeb6 commit 9763dae

17 files changed

Lines changed: 486 additions & 43 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ bunx sentry --help
5252

5353
```bash
5454
# Authenticate with Sentry
55-
sentry auth login
55+
sentry auth
5656

5757
# List issues (auto-detects project from your codebase)
5858
sentry issue list

‎plugins/sentry-cli/skills/sentry-cli/SKILL.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,8 +284,8 @@ npm install -g sentry
284284
### Authentication
285285

286286
```bash
287-
sentry auth login
288-
sentry auth login --token YOUR_SENTRY_API_TOKEN
287+
sentry auth
288+
sentry auth --token YOUR_SENTRY_API_TOKEN
289289
sentry auth status
290290
sentry auth logout
291291
```

‎plugins/sentry-cli/skills/sentry-cli/references/auth.md‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,19 @@ Authenticate with Sentry
2626
**Examples:**
2727

2828
```bash
29-
sentry auth login
29+
sentry auth
3030

31-
sentry auth login --token YOUR_SENTRY_API_TOKEN
31+
sentry auth --token YOUR_SENTRY_API_TOKEN
3232

33-
sentry auth login --read-only
33+
sentry auth --read-only
3434

35-
sentry auth login --scope project:read --scope org:read
36-
sentry auth login --scope project:read,event:read
35+
sentry auth --scope project:read --scope org:read
36+
sentry auth --scope project:read,event:read
3737

38-
sentry auth login --url https://sentry.example.com
39-
SENTRY_URL=https://sentry.example.com sentry auth login
38+
sentry auth --url https://sentry.example.com
39+
SENTRY_URL=https://sentry.example.com sentry auth
4040

41-
sentry auth login --token YOUR_TOKEN --url https://sentry.example.com
41+
sentry auth --token YOUR_TOKEN --url https://sentry.example.com
4242
```
4343

4444
### `sentry auth logout`

‎plugins/sentry-cli/skills/sentry-cli/references/issue.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,10 @@ Mark an issue as resolved
236236
**Flags:**
237237
- `-i, --in <value> - Resolve in a release, next release, or commit ('<version>' | '@next' | '@commit' | '@commit:<repo>@<sha>')`
238238

239+
### `sentry issue unresolve <issue>`
240+
241+
Reopen a resolved issue
242+
239243
**Examples:**
240244

241245
```bash
@@ -265,10 +269,6 @@ sentry issue unresolve CLI-G5
265269
sentry issue reopen CLI-G5 # alias
266270
```
267271

268-
### `sentry issue unresolve <issue>`
269-
270-
Reopen a resolved issue
271-
272272
### `sentry issue archive <issue>`
273273

274274
Archive (ignore) an issue

‎script/generate-skill-markdown.ts‎

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,20 @@ export function extractCommandPathFromHeading(
1818
export function matchExampleToCommand(
1919
code: string,
2020
commandPaths: readonly string[],
21-
groupFallback: string
21+
groupFallback: string,
22+
defaultCommandPath?: string
2223
): string | undefined {
23-
return (
24-
commandPaths.find((path) => code.includes(path)) ??
25-
(code.includes(groupFallback) ? groupFallback : undefined)
26-
);
24+
// Prefer the longest path so `sentry auth login` wins over bare `sentry auth`
25+
// when both would otherwise match via includes().
26+
const byLengthDesc = [...commandPaths].sort((a, b) => b.length - a.length);
27+
const matched = byLengthDesc.find((path) => code.includes(path));
28+
if (matched) {
29+
return matched;
30+
}
31+
if (!code.includes(groupFallback)) {
32+
return;
33+
}
34+
// Bare group examples (`sentry auth`) belong on the default subcommand when
35+
// one exists (login), not on a synthetic group-only path.
36+
return defaultCommandPath ?? groupFallback;
2737
}

‎script/generate-skill.ts‎

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,34 @@ import {
6060
isRouteMap,
6161
} from "../src/lib/introspect.js";
6262

63+
/**
64+
* Resolve the default subcommand name for a route map, if configured.
65+
*
66+
* Hidden dispatcher routes (e.g. auth's `default`) map bare group examples
67+
* onto the user-facing command they primarily document (login).
68+
*/
69+
function findDefaultCommandName(routeMap: RouteMap): string | undefined {
70+
if (!routeMap.getDefaultCommand) {
71+
return;
72+
}
73+
const defaultCmd = routeMap.getDefaultCommand();
74+
if (!defaultCmd) {
75+
return;
76+
}
77+
for (const sub of routeMap.getAllEntries()) {
78+
if (sub.target === defaultCmd) {
79+
const name = sub.name.original;
80+
// Auth uses a hidden "default" dispatcher; bare `sentry auth` examples
81+
// document the login path.
82+
if (name === "default") {
83+
return "login";
84+
}
85+
return name;
86+
}
87+
}
88+
return;
89+
}
90+
6391
const SKILL_DIR = "plugins/sentry-cli/skills/sentry-cli";
6492
const OUTPUT_PATH = `${SKILL_DIR}/SKILL.md`;
6593
const REFERENCES_DIR = `${SKILL_DIR}/references`;
@@ -290,10 +318,10 @@ npm install -g sentry
290318
291319
\`\`\`bash
292320
# OAuth login (recommended)
293-
sentry auth login
321+
sentry auth
294322
295323
# Or use an API token
296-
sentry auth login --token YOUR_SENTRY_API_TOKEN
324+
sentry auth --token YOUR_SENTRY_API_TOKEN
297325
298326
# Check auth status
299327
sentry auth status
@@ -341,10 +369,14 @@ function collectCommandPaths(
341369
*/
342370
function associateCodeBlocks(
343371
tokens: Token[],
344-
commandPaths: string[],
345-
commandGroup: string,
346-
examples: Map<string, string[]>
372+
context: {
373+
commandPaths: string[];
374+
commandGroup: string;
375+
examples: Map<string, string[]>;
376+
defaultCommandPath?: string;
377+
}
347378
): void {
379+
const { commandPaths, commandGroup, examples, defaultCommandPath } = context;
348380
const groupFallback = `sentry ${commandGroup}`;
349381
let currentCmd: string | null = null;
350382

@@ -359,7 +391,12 @@ function associateCodeBlocks(
359391
if (currentCmd && examples.has(currentCmd)) {
360392
appendExample(examples, currentCmd, code);
361393
} else {
362-
const target = matchExampleToCommand(code, commandPaths, groupFallback);
394+
const target = matchExampleToCommand(
395+
code,
396+
commandPaths,
397+
groupFallback,
398+
defaultCommandPath
399+
);
363400
if (target) {
364401
appendExample(examples, target, code);
365402
}
@@ -377,7 +414,8 @@ function associateCodeBlocks(
377414
* by matching code blocks to commands via heading context or content analysis.
378415
*/
379416
async function loadCommandExamples(
380-
commandGroup: string
417+
commandGroup: string,
418+
defaultCommandName?: string
381419
): Promise<Map<string, string[]>> {
382420
const docContent = await loadDoc(`commands/${commandGroup}.md`);
383421
if (!docContent) {
@@ -387,7 +425,15 @@ async function loadCommandExamples(
387425
const tokens = marked.lexer(docContent);
388426
const examples = new Map<string, string[]>();
389427
const commandPaths = collectCommandPaths(tokens, examples);
390-
associateCodeBlocks(tokens, commandPaths, commandGroup, examples);
428+
const defaultCommandPath = defaultCommandName
429+
? `sentry ${commandGroup} ${defaultCommandName}`
430+
: undefined;
431+
associateCodeBlocks(tokens, {
432+
commandPaths,
433+
commandGroup,
434+
examples,
435+
defaultCommandPath,
436+
});
391437
return examples;
392438
}
393439

@@ -435,7 +481,13 @@ async function extractRoutes(routeMap: RouteMap): Promise<RouteInfo[]> {
435481
}
436482
const routeName = entry.name.original;
437483
const target = entry.target;
438-
const docExamples = await loadCommandExamples(routeName);
484+
const defaultCommandName = isRouteMap(target)
485+
? findDefaultCommandName(target)
486+
: undefined;
487+
const docExamples = await loadCommandExamples(
488+
routeName,
489+
defaultCommandName
490+
);
439491
if (isRouteMap(target)) {
440492
result.push({
441493
name: routeName,

0 commit comments

Comments
 (0)