Coverage for src / sentry_tool / commands / projects.py: 100.00%

28 statements  

« prev     ^ index     » next       coverage.py v7.13.2, created at 2026-02-17 21:46 -0500

1"""Project-related commands.""" 

2 

3import webbrowser 

4from typing import Annotated 

5 

6import typer 

7from rich.console import Console 

8 

9from sentry_tool.output import Column, OutputFormat, render 

10from sentry_tool.utils import api, get_config 

11 

12app = typer.Typer(help="Project management commands") 

13 

14 

15@app.command("list-projects") 

16def list_projects( 

17 format: Annotated[ 

18 OutputFormat, 

19 typer.Option("--format", "-f", help="Output format"), 

20 ] = OutputFormat.table, 

21) -> None: 

22 """List all projects in the configured organization. 

23 

24 Examples: 

25 sentry-tool list-projects 

26 sentry-tool list-projects --format json 

27 """ 

28 config = get_config() 

29 

30 projects = api( 

31 f"/organizations/{config['org']}/projects/", 

32 token=config["auth_token"], 

33 base_url=config["url"], 

34 ) 

35 

36 if not projects: 

37 Console().print("No projects found") 

38 return 

39 

40 rows = [ 

41 { 

42 "slug": proj.get("slug", ""), 

43 "name": proj.get("name", ""), 

44 "platform": proj.get("platform", "") or "", 

45 "status": proj.get("status", ""), 

46 } 

47 for proj in projects 

48 ] 

49 

50 columns = [ 

51 Column("Slug", "slug", style="cyan"), 

52 Column("Name", "name"), 

53 Column("Platform", "platform"), 

54 Column("Status", "status"), 

55 ] 

56 

57 render(rows, format, columns=columns, footer=f"{len(projects)} projects") 

58 

59 

60@app.command("open") 

61def open_sentry( 

62 issue_id: Annotated[ 

63 str | None, typer.Argument(help="Issue ID to open directly (optional)") 

64 ] = None, 

65) -> None: 

66 """Open Sentry web UI in browser. 

67 

68 Without arguments, opens organization dashboard. With an issue ID, opens that issue. 

69 

70 Examples: 

71 sentry-tool open 

72 sentry-tool open 24 

73 """ 

74 config = get_config() 

75 

76 base = config["url"].rstrip("/") 

77 org = config["org"] 

78 

79 if issue_id: 

80 url = f"{base}/organizations/{org}/issues/{issue_id}/" 

81 else: 

82 url = f"{base}/organizations/{org}/issues/" 

83 

84 webbrowser.open(url) 

85 console = Console() 

86 console.print(f"Opened: {url}")