kctl-sentry 0.6.3__tar.gz
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.
- kctl_sentry-0.6.3/.gitignore +10 -0
- kctl_sentry-0.6.3/PKG-INFO +17 -0
- kctl_sentry-0.6.3/README.md +117 -0
- kctl_sentry-0.6.3/pyproject.toml +45 -0
- kctl_sentry-0.6.3/skills/sentry-admin/SKILL.md +166 -0
- kctl_sentry-0.6.3/src/kctl_sentry/__init__.py +3 -0
- kctl_sentry-0.6.3/src/kctl_sentry/__main__.py +5 -0
- kctl_sentry-0.6.3/src/kctl_sentry/cli.py +136 -0
- kctl_sentry-0.6.3/src/kctl_sentry/commands/__init__.py +0 -0
- kctl_sentry-0.6.3/src/kctl_sentry/commands/alerts.py +176 -0
- kctl_sentry-0.6.3/src/kctl_sentry/commands/config_cmd.py +400 -0
- kctl_sentry-0.6.3/src/kctl_sentry/commands/dashboard.py +86 -0
- kctl_sentry-0.6.3/src/kctl_sentry/commands/doctor_cmd.py +57 -0
- kctl_sentry-0.6.3/src/kctl_sentry/commands/environments.py +51 -0
- kctl_sentry-0.6.3/src/kctl_sentry/commands/health.py +72 -0
- kctl_sentry-0.6.3/src/kctl_sentry/commands/issues.py +285 -0
- kctl_sentry-0.6.3/src/kctl_sentry/commands/projects.py +173 -0
- kctl_sentry-0.6.3/src/kctl_sentry/commands/releases.py +208 -0
- kctl_sentry-0.6.3/src/kctl_sentry/commands/skill_cmd.py +76 -0
- kctl_sentry-0.6.3/src/kctl_sentry/commands/stats.py +138 -0
- kctl_sentry-0.6.3/src/kctl_sentry/commands/teams.py +120 -0
- kctl_sentry-0.6.3/src/kctl_sentry/core/__init__.py +0 -0
- kctl_sentry-0.6.3/src/kctl_sentry/core/callbacks.py +33 -0
- kctl_sentry-0.6.3/src/kctl_sentry/core/client.py +84 -0
- kctl_sentry-0.6.3/src/kctl_sentry/core/config.py +122 -0
- kctl_sentry-0.6.3/src/kctl_sentry/core/exceptions.py +10 -0
- kctl_sentry-0.6.3/src/kctl_sentry/core/plugins.py +13 -0
- kctl_sentry-0.6.3/tests/conftest.py +50 -0
- kctl_sentry-0.6.3/tests/test_alerts.py +373 -0
- kctl_sentry-0.6.3/tests/test_client.py +61 -0
- kctl_sentry-0.6.3/tests/test_config.py +64 -0
- kctl_sentry-0.6.3/tests/test_health.py +23 -0
- kctl_sentry-0.6.3/tests/test_issues.py +72 -0
- kctl_sentry-0.6.3/tests/test_projects.py +34 -0
- kctl_sentry-0.6.3/tests/test_releases.py +36 -0
- kctl_sentry-0.6.3/tests/test_smoke.py +60 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kctl-sentry
|
|
3
|
+
Version: 0.6.3
|
|
4
|
+
Summary: Kodemeio Sentry CLI — error tracking management
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Requires-Dist: httpx>=0.28.0
|
|
7
|
+
Requires-Dist: kctl-lib>=0.8.0
|
|
8
|
+
Requires-Dist: pydantic>=2.10.0
|
|
9
|
+
Requires-Dist: pyyaml>=6.0.2
|
|
10
|
+
Requires-Dist: rich>=13.9.0
|
|
11
|
+
Requires-Dist: typer>=0.15.0
|
|
12
|
+
Provides-Extra: dev
|
|
13
|
+
Requires-Dist: mypy>=1.14.0; extra == 'dev'
|
|
14
|
+
Requires-Dist: pytest-httpx>=0.35.0; extra == 'dev'
|
|
15
|
+
Requires-Dist: pytest>=8.3.0; extra == 'dev'
|
|
16
|
+
Requires-Dist: ruff>=0.9.0; extra == 'dev'
|
|
17
|
+
Requires-Dist: types-pyyaml>=6.0.0; extra == 'dev'
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# kctl-sentry
|
|
2
|
+
|
|
3
|
+
Sentry error tracking CLI — error triage, release tracking, and project management for the Kodemeio platform.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# From workspace (development)
|
|
9
|
+
uv tool install --editable packages/kctl-sentry
|
|
10
|
+
|
|
11
|
+
# From PyPI
|
|
12
|
+
uv tool install kctl-sentry
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Initialize configuration
|
|
19
|
+
kctl-sentry config init
|
|
20
|
+
|
|
21
|
+
# Check connectivity
|
|
22
|
+
kctl-sentry health check
|
|
23
|
+
|
|
24
|
+
# List all projects
|
|
25
|
+
kctl-sentry projects list
|
|
26
|
+
|
|
27
|
+
# Triage open issues
|
|
28
|
+
kctl-sentry issues list --project my-project
|
|
29
|
+
|
|
30
|
+
# View error stats
|
|
31
|
+
kctl-sentry stats errors --project my-project
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Command Groups
|
|
35
|
+
|
|
36
|
+
| Group | Commands | Description |
|
|
37
|
+
|-------|----------|-------------|
|
|
38
|
+
| `config` | init, add, use, show, set, remove, profiles, current, test, migrate | Manage profiles and credentials |
|
|
39
|
+
| `health` | check | Verify Sentry API connectivity |
|
|
40
|
+
| `dashboard` | overview | High-level summary across all projects |
|
|
41
|
+
| `issues` | list, show, resolve, ignore, bulk-resolve, assign | Error issue triage and management |
|
|
42
|
+
| `projects` | list, show, dsn, create | Project listing, details, and DSN retrieval |
|
|
43
|
+
| `releases` | list, show, create, associate | Release tracking and commit association |
|
|
44
|
+
| `alerts` | list, show, create | Alert rule management |
|
|
45
|
+
| `stats` | events, errors | Event and error volume statistics |
|
|
46
|
+
| `teams` | list, show | Team membership and details |
|
|
47
|
+
| `environments` | list | Environment listing per project |
|
|
48
|
+
|
|
49
|
+
## Global Options
|
|
50
|
+
|
|
51
|
+
All commands accept these options:
|
|
52
|
+
|
|
53
|
+
| Option | Short | Description |
|
|
54
|
+
|--------|-------|-------------|
|
|
55
|
+
| `--json` | | Output as JSON |
|
|
56
|
+
| `--format` | `-f` | Output format: `pretty`, `json`, `csv`, `yaml` |
|
|
57
|
+
| `--quiet` | `-q` | Suppress info messages |
|
|
58
|
+
| `--no-header` | | Omit header row in table/CSV output |
|
|
59
|
+
| `--profile` | `-p` | Config profile name |
|
|
60
|
+
| `--auth-token` | | Auth token override (bypasses config) |
|
|
61
|
+
| `--version` | `-V` | Show version and exit |
|
|
62
|
+
|
|
63
|
+
## Configuration
|
|
64
|
+
|
|
65
|
+
Config is stored in `~/.config/kodemeio/config.yaml` under the `sentry` key.
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# Create a new profile
|
|
69
|
+
kctl-sentry config init
|
|
70
|
+
|
|
71
|
+
# Add a named profile
|
|
72
|
+
kctl-sentry config add --profile prod
|
|
73
|
+
|
|
74
|
+
# Switch active profile
|
|
75
|
+
kctl-sentry config use prod
|
|
76
|
+
|
|
77
|
+
# Show current config (secrets masked)
|
|
78
|
+
kctl-sentry config show
|
|
79
|
+
|
|
80
|
+
# Test connectivity
|
|
81
|
+
kctl-sentry config test
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Config Keys
|
|
85
|
+
|
|
86
|
+
| Key | Description |
|
|
87
|
+
|-----|-------------|
|
|
88
|
+
| `url` | Sentry instance URL (e.g. `https://sentry.io` or self-hosted) |
|
|
89
|
+
| `auth_token` | Sentry auth token with `project:read`, `event:read` scopes |
|
|
90
|
+
| `org` | Default organization slug |
|
|
91
|
+
|
|
92
|
+
## Common Workflows
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Bulk resolve all resolved issues in a project
|
|
96
|
+
kctl-sentry issues bulk-resolve --project my-project --status resolved
|
|
97
|
+
|
|
98
|
+
# Create a release and associate commits
|
|
99
|
+
kctl-sentry releases create --project my-project --version 1.2.3
|
|
100
|
+
kctl-sentry releases associate --project my-project --version 1.2.3
|
|
101
|
+
|
|
102
|
+
# Get DSN for SDK integration
|
|
103
|
+
kctl-sentry projects dsn my-project
|
|
104
|
+
|
|
105
|
+
# View event volume over the last 24h
|
|
106
|
+
kctl-sentry stats events --project my-project --period 24h
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Development
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
cd packages/kctl-sentry
|
|
113
|
+
uv sync --all-extras
|
|
114
|
+
uv run pytest tests/ -v
|
|
115
|
+
uv run mypy src/
|
|
116
|
+
uv run ruff check src/
|
|
117
|
+
```
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "kctl-sentry"
|
|
7
|
+
version = "0.6.3"
|
|
8
|
+
description = "Kodemeio Sentry CLI — error tracking management"
|
|
9
|
+
requires-python = ">=3.12"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"kctl-lib>=0.8.0",
|
|
12
|
+
"typer>=0.15.0",
|
|
13
|
+
"rich>=13.9.0",
|
|
14
|
+
"pydantic>=2.10.0",
|
|
15
|
+
"pyyaml>=6.0.2",
|
|
16
|
+
"httpx>=0.28.0",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[project.optional-dependencies]
|
|
20
|
+
dev = [
|
|
21
|
+
"pytest>=8.3.0",
|
|
22
|
+
"pytest-httpx>=0.35.0",
|
|
23
|
+
"ruff>=0.9.0",
|
|
24
|
+
"mypy>=1.14.0",
|
|
25
|
+
"types-PyYAML>=6.0.0",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.scripts]
|
|
29
|
+
kctl-sentry = "kctl_sentry.cli:_run"
|
|
30
|
+
|
|
31
|
+
[tool.uv.sources]
|
|
32
|
+
kctl-lib = { workspace = true }
|
|
33
|
+
|
|
34
|
+
[project.entry-points."kctl_sentry.plugins"]
|
|
35
|
+
|
|
36
|
+
[tool.hatch.build.targets.wheel]
|
|
37
|
+
packages = ["src/kctl_sentry"]
|
|
38
|
+
|
|
39
|
+
[tool.ruff]
|
|
40
|
+
target-version = "py312"
|
|
41
|
+
line-length = 120
|
|
42
|
+
|
|
43
|
+
[tool.mypy]
|
|
44
|
+
python_version = "3.12"
|
|
45
|
+
strict = true
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sentry-admin
|
|
3
|
+
description: >
|
|
4
|
+
Sentry error tracking administration via kctl-sentry CLI (11 groups, ~35 commands).
|
|
5
|
+
MUST use for ANY kctl-sentry operation.
|
|
6
|
+
Triggers on: "alerts", "assign", "associate", "bulk-resolve", "check", "config", "current", "dashboard", "deploy", "environments", "errors", "events", "generate", "health", "ignore", "init", "issues", "kctl-sentry", "migrate", "overview", "profile", "profiles", "projects", "releases", "remove", "resolve", "skill", "stats", "teams", "test".
|
|
7
|
+
Auto-generated: 2026-04-05
|
|
8
|
+
registry_hash: ff99415189e2
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# sentry-admin — kctl-sentry CLI Reference
|
|
12
|
+
|
|
13
|
+
> Auto-generated from `kctl-sentry` command registry. Do not edit manually.
|
|
14
|
+
> To regenerate: `kctl-sentry skill generate`
|
|
15
|
+
> To add custom content: edit `SKILL.extra.md` in the same directory.
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
|
|
19
|
+
**CLI:** `kctl-sentry`
|
|
20
|
+
**Command groups:** 11
|
|
21
|
+
**Total commands:** ~35
|
|
22
|
+
**Install:** `cd cli && uv tool install --editable .`
|
|
23
|
+
|
|
24
|
+
## Global Options
|
|
25
|
+
|
|
26
|
+
| Flag | Description |
|
|
27
|
+
|------|-------------|
|
|
28
|
+
| `--json` | JSON output |
|
|
29
|
+
| `--quiet`, `-q` | Suppress info messages |
|
|
30
|
+
| `--format`, `-f` | Output format: pretty/json/csv/yaml |
|
|
31
|
+
| `--no-header` | Omit CSV header row |
|
|
32
|
+
| `--profile`, `-p` | Config profile name |
|
|
33
|
+
| `--version`, `-V` | Show version |
|
|
34
|
+
|
|
35
|
+
## Command Reference
|
|
36
|
+
|
|
37
|
+
### `kctl-sentry alerts`
|
|
38
|
+
|
|
39
|
+
Manage alert rules.
|
|
40
|
+
|
|
41
|
+
| Command | Description |
|
|
42
|
+
|---------|-------------|
|
|
43
|
+
| `alerts create <project> <name> [--metric] [--threshold] [--time_window]` | Create a new metric alert rule. |
|
|
44
|
+
| `alerts list [--project]` | List alert rules. |
|
|
45
|
+
| `alerts show <rule_id> <project>` | Show alert rule details and trigger history. |
|
|
46
|
+
|
|
47
|
+
### `kctl-sentry config`
|
|
48
|
+
|
|
49
|
+
Manage CLI configuration and profiles.
|
|
50
|
+
|
|
51
|
+
| Command | Description |
|
|
52
|
+
|---------|-------------|
|
|
53
|
+
| `config add <name> [--auth_token] [--organization] [--url] [--default_project] [--set_default]` | Add or update a profile's Sentry connection. |
|
|
54
|
+
| `config current` | Show the active profile and connection status. |
|
|
55
|
+
| `config init [--auth_token] [--organization] [--url] [--default_project] [--name]` | Initialize CLI configuration. |
|
|
56
|
+
| `config migrate` | Migrate config from flat format to service-scoped format. |
|
|
57
|
+
| `config profiles` | List all profiles with Sentry connection status. |
|
|
58
|
+
| `config remove <name> [--force] [--service_only]` | Remove a profile or just its Sentry config. |
|
|
59
|
+
| `config set <key> <value> [--profile_arg]` | Set a configuration value for the current service. |
|
|
60
|
+
| `config show` | Show configuration. |
|
|
61
|
+
| `config test` | Test API connection. |
|
|
62
|
+
| `config use <name>` | Switch default profile. |
|
|
63
|
+
|
|
64
|
+
### `kctl-sentry dashboard`
|
|
65
|
+
|
|
66
|
+
Quick overview of Sentry state.
|
|
67
|
+
|
|
68
|
+
| Command | Description |
|
|
69
|
+
|---------|-------------|
|
|
70
|
+
| `dashboard overview` | Show unresolved issues, recent releases, and alert status across projects. |
|
|
71
|
+
|
|
72
|
+
### `kctl-sentry environments`
|
|
73
|
+
|
|
74
|
+
Manage project environments.
|
|
75
|
+
|
|
76
|
+
| Command | Description |
|
|
77
|
+
|---------|-------------|
|
|
78
|
+
| `environments list [--project]` | List environments for a project (e.g. |
|
|
79
|
+
|
|
80
|
+
### `kctl-sentry health`
|
|
81
|
+
|
|
82
|
+
API connectivity checks.
|
|
83
|
+
|
|
84
|
+
| Command | Description |
|
|
85
|
+
|---------|-------------|
|
|
86
|
+
| `health check` | Check Sentry API connectivity, org info, and rate limits. |
|
|
87
|
+
|
|
88
|
+
### `kctl-sentry issues`
|
|
89
|
+
|
|
90
|
+
Error triage — list, inspect, resolve, ignore, assign issues.
|
|
91
|
+
|
|
92
|
+
| Command | Description |
|
|
93
|
+
|---------|-------------|
|
|
94
|
+
| `issues assign <issue_id> <to>` | Assign an issue to a team member. |
|
|
95
|
+
| `issues bulk-resolve <project> [--before] [--force]` | Bulk-resolve old unresolved issues in a project. |
|
|
96
|
+
| `issues ignore <issue_id> [--duration] [--count]` | Ignore an issue, optionally for a duration or until N more events. |
|
|
97
|
+
| `issues list [--project] [--status] [--limit] [--sort]` | List recent issues for a project. |
|
|
98
|
+
| `issues resolve <issue_id> [--release]` | Resolve an issue. |
|
|
99
|
+
| `issues show <issue_id>` | Show issue details, stack trace, and affected users. |
|
|
100
|
+
|
|
101
|
+
### `kctl-sentry projects`
|
|
102
|
+
|
|
103
|
+
Manage Sentry projects.
|
|
104
|
+
|
|
105
|
+
| Command | Description |
|
|
106
|
+
|---------|-------------|
|
|
107
|
+
| `projects create <name> <team> [--platform]` | Create a new project. |
|
|
108
|
+
| `projects dsn <slug>` | Get DSN key for SDK configuration. |
|
|
109
|
+
| `projects list` | List all projects with issue counts. |
|
|
110
|
+
| `projects show <slug>` | Show project details. |
|
|
111
|
+
|
|
112
|
+
### `kctl-sentry releases`
|
|
113
|
+
|
|
114
|
+
Manage releases and deploy tracking.
|
|
115
|
+
|
|
116
|
+
| Command | Description |
|
|
117
|
+
|---------|-------------|
|
|
118
|
+
| `releases associate <version> <commits>` | Associate commits with a release for tracking regressions. |
|
|
119
|
+
| `releases create <version> <project>` | Create a new release for a project. |
|
|
120
|
+
| `releases list [--project] [--limit]` | List recent releases. |
|
|
121
|
+
| `releases show <version>` | Show release details and associated issues. |
|
|
122
|
+
|
|
123
|
+
### `kctl-sentry skill`
|
|
124
|
+
|
|
125
|
+
Claude Code skill management.
|
|
126
|
+
|
|
127
|
+
| Command | Description |
|
|
128
|
+
|---------|-------------|
|
|
129
|
+
| `skill generate [--output] [--install] [--check]` | Auto-generate SKILL.md from CLI command registry. |
|
|
130
|
+
|
|
131
|
+
**Examples:**
|
|
132
|
+
```bash
|
|
133
|
+
kctl-sentry skill generate
|
|
134
|
+
kctl-sentry skill generate --install
|
|
135
|
+
kctl-sentry skill generate --check
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### `kctl-sentry stats`
|
|
139
|
+
|
|
140
|
+
Event and error statistics.
|
|
141
|
+
|
|
142
|
+
| Command | Description |
|
|
143
|
+
|---------|-------------|
|
|
144
|
+
| `stats errors [--project] [--period]` | Show error rate trends for a project. |
|
|
145
|
+
| `stats events [--project] [--period]` | Show event volume for a project or organization. |
|
|
146
|
+
|
|
147
|
+
### `kctl-sentry teams`
|
|
148
|
+
|
|
149
|
+
Manage teams.
|
|
150
|
+
|
|
151
|
+
| Command | Description |
|
|
152
|
+
|---------|-------------|
|
|
153
|
+
| `teams list` | List all teams in the organization. |
|
|
154
|
+
| `teams show <slug>` | Show team details, members, and assigned projects. |
|
|
155
|
+
|
|
156
|
+
## Configuration
|
|
157
|
+
|
|
158
|
+
Shared config: `~/.config/kodemeio/config.yaml`
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
kctl-sentry config init # Interactive setup
|
|
162
|
+
kctl-sentry config show # Show current config
|
|
163
|
+
kctl-sentry config profiles # List profiles
|
|
164
|
+
kctl-sentry config current # Show active profile
|
|
165
|
+
kctl-sentry config validate # Verify config
|
|
166
|
+
```
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""Main CLI entry point for kctl-sentry."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from typing import Annotated
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
from kctl_lib import KctlError, handle_cli_error
|
|
10
|
+
|
|
11
|
+
from kctl_sentry import __version__
|
|
12
|
+
from kctl_sentry.commands.alerts import app as alerts_app
|
|
13
|
+
from kctl_sentry.commands.config_cmd import app as config_app
|
|
14
|
+
from kctl_sentry.commands.dashboard import app as dashboard_app
|
|
15
|
+
from kctl_sentry.commands.environments import app as environments_app
|
|
16
|
+
from kctl_sentry.commands.health import app as health_app
|
|
17
|
+
from kctl_sentry.commands.issues import app as issues_app
|
|
18
|
+
from kctl_sentry.commands.projects import app as projects_app
|
|
19
|
+
from kctl_sentry.commands.releases import app as releases_app
|
|
20
|
+
from kctl_sentry.commands.stats import app as stats_app
|
|
21
|
+
from kctl_sentry.commands.teams import app as teams_app
|
|
22
|
+
from kctl_sentry.core.callbacks import AppContext
|
|
23
|
+
from kctl_sentry.core.plugins import discover_and_load_plugins
|
|
24
|
+
from kctl_sentry.commands.skill_cmd import app as skill_app
|
|
25
|
+
from kctl_sentry.commands.doctor_cmd import app as doctor_app
|
|
26
|
+
from kctl_lib.self_update import notify_if_outdated
|
|
27
|
+
from kctl_lib.tui import add_tui_command
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def version_callback(value: bool) -> None:
|
|
31
|
+
if value:
|
|
32
|
+
typer.echo(f"kctl-sentry {__version__}")
|
|
33
|
+
raise typer.Exit()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
app = typer.Typer(
|
|
37
|
+
name="kctl-sentry",
|
|
38
|
+
help="Kodemeio Sentry CLI — error triage, release tracking, and project management.",
|
|
39
|
+
no_args_is_help=True,
|
|
40
|
+
rich_markup_mode="rich",
|
|
41
|
+
pretty_exceptions_enable=False,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@app.callback()
|
|
46
|
+
def main(
|
|
47
|
+
ctx: typer.Context,
|
|
48
|
+
json_output: Annotated[bool, typer.Option("--json", help="Output as JSON")] = False,
|
|
49
|
+
quiet: Annotated[bool, typer.Option("--quiet", "-q", help="Suppress info messages")] = False,
|
|
50
|
+
profile: Annotated[str | None, typer.Option("--profile", "-p", help="Config profile name")] = None,
|
|
51
|
+
format: Annotated[str, typer.Option("--format", "-f", help="Output format: pretty, json, csv, yaml")] = "pretty",
|
|
52
|
+
no_header: Annotated[bool, typer.Option("--no-header", help="Omit header row in CSV output")] = False,
|
|
53
|
+
auth_token: Annotated[str | None, typer.Option("--auth-token", help="Auth token override")] = None,
|
|
54
|
+
version: Annotated[
|
|
55
|
+
bool, typer.Option("--version", "-V", callback=version_callback, is_eager=True, help="Show version")
|
|
56
|
+
] = False,
|
|
57
|
+
) -> None:
|
|
58
|
+
"""Kodemeio Sentry CLI."""
|
|
59
|
+
ctx.ensure_object(dict)
|
|
60
|
+
ctx.obj = AppContext(
|
|
61
|
+
json_mode=json_output,
|
|
62
|
+
quiet=quiet,
|
|
63
|
+
profile=profile,
|
|
64
|
+
format=format,
|
|
65
|
+
no_header=no_header,
|
|
66
|
+
auth_token_override=auth_token,
|
|
67
|
+
)
|
|
68
|
+
notify_if_outdated(ctx.obj.output, "kctl-sentry", __version__)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
app.add_typer(config_app, name="config")
|
|
72
|
+
app.add_typer(health_app, name="health")
|
|
73
|
+
app.add_typer(dashboard_app, name="dashboard")
|
|
74
|
+
app.add_typer(issues_app, name="issues")
|
|
75
|
+
app.add_typer(projects_app, name="projects")
|
|
76
|
+
app.add_typer(releases_app, name="releases")
|
|
77
|
+
app.add_typer(alerts_app, name="alerts")
|
|
78
|
+
app.add_typer(stats_app, name="stats")
|
|
79
|
+
app.add_typer(teams_app, name="teams")
|
|
80
|
+
app.add_typer(environments_app, name="environments")
|
|
81
|
+
app.add_typer(skill_app, name="skill", hidden=True)
|
|
82
|
+
app.add_typer(doctor_app, name="doctor")
|
|
83
|
+
|
|
84
|
+
# Load third-party plugins via entry points
|
|
85
|
+
discover_and_load_plugins(app)
|
|
86
|
+
add_tui_command(app, service_key="sentry", version=__version__)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@app.command("self-update")
|
|
90
|
+
def self_update_cmd(ctx: typer.Context) -> None:
|
|
91
|
+
"""Check for updates and upgrade kctl-sentry."""
|
|
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-sentry", __version__)
|
|
99
|
+
if latest:
|
|
100
|
+
out.info(f"Updating to {latest}...")
|
|
101
|
+
do_update("kctl-sentry")
|
|
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-sentry", 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-sentry", 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,176 @@
|
|
|
1
|
+
"""Alert rule management commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Annotated
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
from kctl_sentry.core.callbacks import AppContext
|
|
10
|
+
from kctl_sentry.core.exceptions import KctlError
|
|
11
|
+
|
|
12
|
+
app = typer.Typer(help="Manage alert rules.")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@app.command("list")
|
|
16
|
+
def list_(
|
|
17
|
+
ctx: typer.Context,
|
|
18
|
+
project: Annotated[str | None, typer.Option("--project", "-p", help="Filter by project slug")] = None,
|
|
19
|
+
) -> None:
|
|
20
|
+
"""List alert rules."""
|
|
21
|
+
c: AppContext = ctx.obj
|
|
22
|
+
out = c.output
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
rules = c.client.project_get(project, "/rules/") if project else c.client.org_get("/alert-rules/")
|
|
26
|
+
if not isinstance(rules, list):
|
|
27
|
+
rules = []
|
|
28
|
+
|
|
29
|
+
rows: list[list[str]] = []
|
|
30
|
+
for rule in rules:
|
|
31
|
+
rule_id = str(rule.get("id", ""))
|
|
32
|
+
name = (rule.get("name", "") or "")[:50]
|
|
33
|
+
# Project-scoped rules have 'projects', org-scoped have 'project'
|
|
34
|
+
proj_info = ""
|
|
35
|
+
if isinstance(rule.get("projects"), list):
|
|
36
|
+
proj_info = ", ".join(rule["projects"])
|
|
37
|
+
elif isinstance(rule.get("project"), dict):
|
|
38
|
+
proj_info = rule["project"].get("slug", "")
|
|
39
|
+
|
|
40
|
+
status_val = rule.get("status", "active")
|
|
41
|
+
owner = ""
|
|
42
|
+
if isinstance(rule.get("owner"), dict):
|
|
43
|
+
owner = rule["owner"].get("name", "")
|
|
44
|
+
elif isinstance(rule.get("owner"), str):
|
|
45
|
+
owner = rule["owner"]
|
|
46
|
+
|
|
47
|
+
rows.append([rule_id, name, proj_info, status_val, owner])
|
|
48
|
+
|
|
49
|
+
out.table(
|
|
50
|
+
"Alert Rules",
|
|
51
|
+
[
|
|
52
|
+
("ID", "cyan"),
|
|
53
|
+
("Name", ""),
|
|
54
|
+
("Project", ""),
|
|
55
|
+
("Status", "green"),
|
|
56
|
+
("Owner", "dim"),
|
|
57
|
+
],
|
|
58
|
+
rows,
|
|
59
|
+
data_for_json=rules,
|
|
60
|
+
)
|
|
61
|
+
except KctlError as e:
|
|
62
|
+
out.error(f"Failed to list alerts: {e}")
|
|
63
|
+
raise typer.Exit(1) from e
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@app.command("show")
|
|
67
|
+
def show(
|
|
68
|
+
ctx: typer.Context,
|
|
69
|
+
rule_id: Annotated[str, typer.Argument(help="Alert rule ID")],
|
|
70
|
+
project: Annotated[str, typer.Option("--project", "-p", help="Project slug")],
|
|
71
|
+
) -> None:
|
|
72
|
+
"""Show alert rule details and trigger history."""
|
|
73
|
+
c: AppContext = ctx.obj
|
|
74
|
+
out = c.output
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
rule = c.client.project_get(project, f"/rules/{rule_id}/")
|
|
78
|
+
if not isinstance(rule, dict):
|
|
79
|
+
rule = {}
|
|
80
|
+
|
|
81
|
+
# Parse conditions
|
|
82
|
+
conditions = rule.get("conditions", [])
|
|
83
|
+
condition_strs: list[tuple[str, str]] = []
|
|
84
|
+
for cond in conditions if isinstance(conditions, list) else []:
|
|
85
|
+
if isinstance(cond, dict):
|
|
86
|
+
condition_strs.append(("Condition", cond.get("name", str(cond.get("id", "")))))
|
|
87
|
+
|
|
88
|
+
# Parse actions
|
|
89
|
+
actions = rule.get("actions", [])
|
|
90
|
+
action_strs: list[tuple[str, str]] = []
|
|
91
|
+
for act in actions if isinstance(actions, list) else []:
|
|
92
|
+
if isinstance(act, dict):
|
|
93
|
+
action_strs.append(("Action", act.get("name", str(act.get("id", "")))))
|
|
94
|
+
|
|
95
|
+
sections = [
|
|
96
|
+
(
|
|
97
|
+
"Alert Rule",
|
|
98
|
+
[
|
|
99
|
+
("ID", str(rule.get("id", ""))),
|
|
100
|
+
("Name", rule.get("name", "")),
|
|
101
|
+
("Status", rule.get("status", "")),
|
|
102
|
+
("Frequency", f"{rule.get('frequency', '')} seconds"),
|
|
103
|
+
("Date created", (rule.get("dateCreated", "") or "")[:19]),
|
|
104
|
+
],
|
|
105
|
+
),
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
if condition_strs:
|
|
109
|
+
sections.append(("Conditions", condition_strs))
|
|
110
|
+
if action_strs:
|
|
111
|
+
sections.append(("Actions", action_strs))
|
|
112
|
+
|
|
113
|
+
out.detail(
|
|
114
|
+
f"Alert Rule: {rule.get('name', rule_id)}",
|
|
115
|
+
sections,
|
|
116
|
+
data_for_json=rule,
|
|
117
|
+
)
|
|
118
|
+
except KctlError as e:
|
|
119
|
+
out.error(f"Failed to show alert: {e}")
|
|
120
|
+
raise typer.Exit(1) from e
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@app.command("create")
|
|
124
|
+
def create(
|
|
125
|
+
ctx: typer.Context,
|
|
126
|
+
project: Annotated[str, typer.Option("--project", "-p", help="Project slug")],
|
|
127
|
+
name: Annotated[str, typer.Option("--name", "-n", help="Alert rule name")],
|
|
128
|
+
metric: Annotated[str, typer.Option("--metric", help="Metric: events, users")] = "events",
|
|
129
|
+
threshold: Annotated[int, typer.Option("--threshold", help="Threshold value")] = 100,
|
|
130
|
+
time_window: Annotated[int, typer.Option("--time-window", help="Time window in minutes")] = 60,
|
|
131
|
+
) -> None:
|
|
132
|
+
"""Create a new metric alert rule."""
|
|
133
|
+
c: AppContext = ctx.obj
|
|
134
|
+
out = c.output
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
# Map metric to Sentry's aggregate format
|
|
138
|
+
aggregate_map = {
|
|
139
|
+
"events": "count()",
|
|
140
|
+
"users": "count_unique(user)",
|
|
141
|
+
}
|
|
142
|
+
aggregate = aggregate_map.get(metric, "count()")
|
|
143
|
+
|
|
144
|
+
payload = {
|
|
145
|
+
"name": name,
|
|
146
|
+
"aggregate": aggregate,
|
|
147
|
+
"timeWindow": time_window,
|
|
148
|
+
"dataset": "events",
|
|
149
|
+
"query": "",
|
|
150
|
+
"thresholdType": 0, # Above threshold
|
|
151
|
+
"resolveThreshold": None,
|
|
152
|
+
"triggers": [
|
|
153
|
+
{
|
|
154
|
+
"label": "critical",
|
|
155
|
+
"alertThreshold": threshold,
|
|
156
|
+
"actions": [],
|
|
157
|
+
}
|
|
158
|
+
],
|
|
159
|
+
"projects": [project],
|
|
160
|
+
"owner": None,
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
result = c.client.org_post("/alert-rules/", json=payload)
|
|
164
|
+
if not isinstance(result, dict):
|
|
165
|
+
result = {}
|
|
166
|
+
|
|
167
|
+
if out.json_mode:
|
|
168
|
+
out.raw_json(result)
|
|
169
|
+
else:
|
|
170
|
+
out.success(f"Alert rule created: {result.get('name', name)}")
|
|
171
|
+
out.kv("ID", str(result.get("id", "")))
|
|
172
|
+
out.kv("Metric", aggregate)
|
|
173
|
+
out.kv("Threshold", str(threshold))
|
|
174
|
+
except KctlError as e:
|
|
175
|
+
out.error(f"Failed to create alert: {e}")
|
|
176
|
+
raise typer.Exit(1) from e
|