kctl-glitchtip 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ __version__ = "0.2.0"
@@ -0,0 +1,3 @@
1
+ from kctl_glitchtip.cli import _run
2
+
3
+ _run()
kctl_glitchtip/cli.py ADDED
@@ -0,0 +1,136 @@
1
+ """Main CLI entry point for kctl-glitchtip."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Annotated
6
+
7
+ import typer
8
+ from kctl_lib import KctlError, handle_cli_error
9
+ from kctl_lib.self_update import notify_if_outdated
10
+
11
+ from kctl_glitchtip import __version__
12
+ from kctl_glitchtip.commands.alerts import app as alerts_app
13
+ from kctl_glitchtip.commands.config_cmd import app as config_app
14
+ from kctl_glitchtip.commands.doctor_cmd import app as doctor_app
15
+ from kctl_glitchtip.commands.events import app as events_app
16
+ from kctl_glitchtip.commands.health import app as health_app
17
+ from kctl_glitchtip.commands.issues import app as issues_app
18
+ from kctl_glitchtip.commands.orgs import app as orgs_app
19
+ from kctl_glitchtip.commands.projects import app as projects_app
20
+ from kctl_glitchtip.commands.skill_cmd import app as skill_app
21
+ from kctl_glitchtip.commands.teams import app as teams_app
22
+ from kctl_glitchtip.commands.uptime import app as uptime_app
23
+ from kctl_glitchtip.commands.users import app as users_app
24
+ from kctl_glitchtip.core.callbacks import AppContext
25
+ from kctl_glitchtip.core.plugins import discover_and_load_plugins
26
+
27
+
28
+ def version_callback(value: bool) -> None:
29
+ if value:
30
+ typer.echo(f"kctl-glitchtip {__version__}")
31
+ raise typer.Exit()
32
+
33
+
34
+ app = typer.Typer(
35
+ name="kctl-glitchtip",
36
+ help="Kodemeio GlitchTip CLI - manage your GlitchTip error tracking platform.",
37
+ no_args_is_help=True,
38
+ rich_markup_mode="rich",
39
+ pretty_exceptions_enable=False,
40
+ )
41
+
42
+
43
+ @app.callback()
44
+ def main(
45
+ ctx: typer.Context,
46
+ json_output: Annotated[bool, typer.Option("--json", help="Output as JSON")] = False,
47
+ quiet: Annotated[bool, typer.Option("--quiet", "-q", help="Suppress info messages")] = False,
48
+ profile: Annotated[str | None, typer.Option("--profile", "-p", help="Config profile name")] = None,
49
+ format: Annotated[str, typer.Option("--format", "-f", help="Output format: pretty, json, csv, yaml")] = "pretty",
50
+ no_header: Annotated[bool, typer.Option("--no-header", help="Omit header row in CSV output")] = False,
51
+ url: Annotated[str | None, typer.Option("--url", help="API URL override")] = None,
52
+ token: Annotated[str | None, typer.Option("--token", help="API token override")] = None,
53
+ version: Annotated[
54
+ bool, typer.Option("--version", "-V", callback=version_callback, is_eager=True, help="Show version")
55
+ ] = False,
56
+ ) -> None:
57
+ """Kodemeio GlitchTip CLI."""
58
+ ctx.ensure_object(dict)
59
+ ctx.obj = AppContext(
60
+ json_mode=json_output,
61
+ quiet=quiet,
62
+ profile=profile,
63
+ format=format,
64
+ no_header=no_header,
65
+ url_override=url,
66
+ token_override=token,
67
+ )
68
+ notify_if_outdated(ctx.obj.output, "kctl-glitchtip", __version__)
69
+
70
+
71
+ # Register all command groups
72
+ app.add_typer(projects_app, name="projects")
73
+ app.add_typer(issues_app, name="issues")
74
+ app.add_typer(teams_app, name="teams")
75
+ app.add_typer(orgs_app, name="orgs")
76
+ app.add_typer(events_app, name="events")
77
+ app.add_typer(users_app, name="users")
78
+ app.add_typer(health_app, name="health")
79
+ app.add_typer(alerts_app, name="alerts")
80
+ app.add_typer(config_app, name="config")
81
+ app.add_typer(uptime_app, name="uptime")
82
+ app.add_typer(doctor_app, name="doctor")
83
+ app.add_typer(skill_app, name="skill", hidden=True)
84
+
85
+ # Load third-party plugins via entry points
86
+ discover_and_load_plugins(app)
87
+
88
+
89
+ @app.command("self-update")
90
+ def self_update_cmd(ctx: typer.Context) -> None:
91
+ """Check for updates and upgrade kctl-glitchtip."""
92
+ actx = ctx.obj
93
+ out = actx.output
94
+
95
+ from kctl_lib.self_update import check_update
96
+ from kctl_lib.self_update import update as do_update
97
+
98
+ latest = check_update("kctl-glitchtip", __version__)
99
+ if latest:
100
+ out.info(f"Updating to {latest}...")
101
+ do_update("kctl-glitchtip")
102
+ out.success(f"Updated to {latest}")
103
+ else:
104
+ out.success("Already up to date")
105
+
106
+
107
+ @app.command()
108
+ def completions(
109
+ shell: Annotated[str, typer.Argument(help="Shell type: zsh, bash, fish")] = "zsh",
110
+ install: Annotated[bool, typer.Option("--install", help="Install completions")] = False,
111
+ ) -> None:
112
+ """Generate or install shell completions."""
113
+ from kctl_lib.completions import get_completion_script, install_completions
114
+
115
+ if install:
116
+ path = install_completions("kctl-glitchtip", shell)
117
+ if path:
118
+ typer.echo(f"Completions installed to {path}")
119
+ else:
120
+ typer.echo(f"Could not install completions for {shell}", err=True)
121
+ raise typer.Exit(code=1)
122
+ else:
123
+ script = get_completion_script("kctl-glitchtip", shell)
124
+ typer.echo(script)
125
+
126
+
127
+ def _run() -> None:
128
+ """Entry point with error handling."""
129
+ try:
130
+ app()
131
+ except KctlError as e:
132
+ handle_cli_error(e)
133
+
134
+
135
+ if __name__ == "__main__":
136
+ _run()
File without changes
@@ -0,0 +1,110 @@
1
+ """Alert and notification commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import subprocess
6
+ from typing import Annotated
7
+
8
+ import httpx
9
+ import typer
10
+
11
+ from kctl_glitchtip.core.callbacks import AppContext
12
+
13
+ app = typer.Typer(help="Manage alerts and notifications.")
14
+
15
+
16
+ @app.command("list")
17
+ def list_(
18
+ ctx: typer.Context,
19
+ org_slug: Annotated[str, typer.Option("--org", help="Organization slug")],
20
+ project_slug: Annotated[str, typer.Option("--project", help="Project slug")],
21
+ ) -> None:
22
+ """List project alerts."""
23
+ actx: AppContext = ctx.obj
24
+ c, out = actx.client, actx.output
25
+
26
+ alerts = c.get_list(f"projects/{org_slug}/{project_slug}/alerts/")
27
+
28
+ rows: list[list[str]] = []
29
+ for a in alerts:
30
+ alert_id = str(a.get("id", ""))
31
+ name = a.get("name", "")
32
+ alert_type = a.get("alertType", a.get("type", ""))
33
+ quantity = str(a.get("quantity", ""))
34
+ timespan = str(a.get("timespanMinutes", a.get("timespan_minutes", "")))
35
+ rows.append([alert_id, name, alert_type, quantity, f"{timespan}m" if timespan else "-"])
36
+
37
+ out.table(
38
+ f"Alerts — {org_slug}/{project_slug}",
39
+ [("ID", "cyan"), ("Name", ""), ("Type", ""), ("Threshold", ""), ("Window", "dim")],
40
+ rows,
41
+ data_for_json=alerts,
42
+ )
43
+
44
+
45
+ @app.command("test-alert")
46
+ def test_alert(
47
+ ctx: typer.Context,
48
+ org_slug: Annotated[str, typer.Option("--org", help="Organization slug")],
49
+ project_slug: Annotated[str, typer.Option("--project", help="Project slug")],
50
+ alert_id: Annotated[int, typer.Argument(help="Alert ID to test")],
51
+ ) -> None:
52
+ """Test a project alert."""
53
+ actx: AppContext = ctx.obj
54
+ c, out = actx.client, actx.output
55
+
56
+ result = c.post(f"projects/{org_slug}/{project_slug}/alerts/{alert_id}/test/")
57
+ out.success(f"Test alert {alert_id} sent")
58
+ if out.json_mode:
59
+ out.raw_json(result)
60
+
61
+
62
+ @app.command("test-webhook")
63
+ def test_webhook(
64
+ ctx: typer.Context,
65
+ url: Annotated[str, typer.Argument(help="Webhook URL to test")],
66
+ ) -> None:
67
+ """Send test alert to a webhook URL."""
68
+ actx: AppContext = ctx.obj
69
+ out = actx.output
70
+
71
+ payload = {
72
+ "text": "GlitchTip test alert from kctl-glitchtip",
73
+ "alias": "kctl-glitchtip-test",
74
+ }
75
+
76
+ try:
77
+ resp = httpx.post(url, json=payload, timeout=10)
78
+ if resp.status_code < 300:
79
+ out.success(f"Webhook responded: {resp.status_code}")
80
+ else:
81
+ out.error(f"Webhook failed: {resp.status_code} — {resp.text[:200]}")
82
+ except Exception as e:
83
+ out.error(f"Webhook error: {e}")
84
+
85
+
86
+ @app.command("test-email")
87
+ def test_email(
88
+ ctx: typer.Context,
89
+ to: Annotated[str | None, typer.Option("--to", help="Recipient email (default: admin)")] = None,
90
+ ) -> None:
91
+ """Send test email via Django (requires Docker access)."""
92
+ actx: AppContext = ctx.obj
93
+ out = actx.output
94
+
95
+ cmd = ["docker", "exec", "kodemeio-glitchtip", "./manage.py", "sendtestemail"]
96
+ if to:
97
+ cmd.append(to)
98
+ else:
99
+ cmd.append("--admin")
100
+
101
+ try:
102
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
103
+ if result.returncode == 0:
104
+ out.success("Test email sent")
105
+ else:
106
+ out.error(f"Failed: {result.stderr.strip()}")
107
+ except FileNotFoundError:
108
+ out.error("Docker not found")
109
+ except subprocess.TimeoutExpired:
110
+ out.error("Email send timed out")