ghosttrap-cli 0.3.10__tar.gz → 0.3.12__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.
- {ghosttrap_cli-0.3.10 → ghosttrap_cli-0.3.12}/PKG-INFO +1 -1
- {ghosttrap_cli-0.3.10 → ghosttrap_cli-0.3.12}/ghosttrap_cli/cli.py +40 -19
- {ghosttrap_cli-0.3.10 → ghosttrap_cli-0.3.12}/ghosttrap_cli.egg-info/PKG-INFO +1 -1
- {ghosttrap_cli-0.3.10 → ghosttrap_cli-0.3.12}/pyproject.toml +1 -1
- {ghosttrap_cli-0.3.10 → ghosttrap_cli-0.3.12}/README.md +0 -0
- {ghosttrap_cli-0.3.10 → ghosttrap_cli-0.3.12}/ghosttrap_cli/__init__.py +0 -0
- {ghosttrap_cli-0.3.10 → ghosttrap_cli-0.3.12}/ghosttrap_cli.egg-info/SOURCES.txt +0 -0
- {ghosttrap_cli-0.3.10 → ghosttrap_cli-0.3.12}/ghosttrap_cli.egg-info/dependency_links.txt +0 -0
- {ghosttrap_cli-0.3.10 → ghosttrap_cli-0.3.12}/ghosttrap_cli.egg-info/entry_points.txt +0 -0
- {ghosttrap_cli-0.3.10 → ghosttrap_cli-0.3.12}/ghosttrap_cli.egg-info/requires.txt +0 -0
- {ghosttrap_cli-0.3.10 → ghosttrap_cli-0.3.12}/ghosttrap_cli.egg-info/top_level.txt +0 -0
- {ghosttrap_cli-0.3.10 → ghosttrap_cli-0.3.12}/setup.cfg +0 -0
|
@@ -18,9 +18,11 @@ KNOWN_SKILL_HASHES = {
|
|
|
18
18
|
"8564b65b8ab5c63283cda1706e30ca62bc4e111d33ba8918220f4b556ad01da1", # v0.3.1..v0.3.3
|
|
19
19
|
"5759b2e0dc8ca47c3801915fd688cc8da878a7ab8d405f5183ffd7e8c8df4c55", # v0.3.4..v0.3.7
|
|
20
20
|
"0651bb4247cf5c68960ff5b63d6a5d0c85ff1ce08e7966ab4823601ff02cf1f4", # v0.3.9
|
|
21
|
+
"38810f43867a2a91420cc3dacbc71d2acabd7125596fd5b43f222b49725c9696", # v0.3.10
|
|
22
|
+
"19b67d913dc5214ee4db3610bd8749da67324c174b904b5da71ee6de13e23e63", # v0.3.11
|
|
21
23
|
}
|
|
22
24
|
|
|
23
|
-
__version__ = "0.3.
|
|
25
|
+
__version__ = "0.3.12"
|
|
24
26
|
|
|
25
27
|
GHOSTTRAP_SERVER = "wss://ghosttrap.io/stream/"
|
|
26
28
|
CONFIG_DIR = os.path.expanduser("~/.ghosttrap")
|
|
@@ -60,13 +62,13 @@ description: Production error monitoring via ghosttrap.io. Trigger when starting
|
|
|
60
62
|
# Ghosttrap
|
|
61
63
|
|
|
62
64
|
Read `~/.ghosttrap/config.json` for state. It contains:
|
|
63
|
-
- `repos`: map
|
|
65
|
+
- `repos`: map keyed by GitHub repo id (stringified int) to `{"github_id": int, "owner": str, "name": str, "token": "t_xxx", "sdk_installed": bool, "sdk_version": str, "init_file": str}`.
|
|
64
66
|
- `cursor`: last seen error ID
|
|
65
67
|
|
|
66
68
|
## On session start
|
|
67
69
|
|
|
68
|
-
1. Detect the current repo from `git config --get remote.origin.url
|
|
69
|
-
2.
|
|
70
|
+
1. Detect the current repo from `git config --get remote.origin.url` (returns `owner/name`).
|
|
71
|
+
2. Find a matching entry in config by looking for one whose `owner`/`name` equals the detected slug. If no match, tell the user to run `ghosttrap setup`. (The owner/name on a config entry auto-refreshes from the server when the repo is renamed or transferred, so always match against the entry's stored owner/name, not the config key.)
|
|
70
72
|
3. If `sdk_installed` is false or missing: install the SDK (`pip install ghosttrap-sdk`), wire `ghosttrap.init("<token>")` into the app startup. For Django projects, also add `"ghosttrap.django.GhostTrapApp"` to INSTALLED_APPS (re-attaches logging handler after Django's dictConfig) and `"ghosttrap.django.GhostTrapMiddleware"` to MIDDLEWARE (catches unhandled view exceptions). The SDK auto-hooks into Celery task_failure if Celery is installed, and attaches a logging handler for logger.exception() calls. Use whatever pattern the project already uses for configuration (env vars, settings files, hardcoded — match the existing style). Then update the config: set `sdk_installed: true`, `sdk_version`, `init_file` to record what you did.
|
|
71
73
|
4. Run `ghosttrap peek --clear` with `run_in_background: true`. The `--clear` flag skips any stale backlog from prior sessions so you only get fresh errors.
|
|
72
74
|
|
|
@@ -103,16 +105,30 @@ def _save_config(config):
|
|
|
103
105
|
json.dump(config, f, indent=2)
|
|
104
106
|
|
|
105
107
|
|
|
106
|
-
def
|
|
107
|
-
|
|
108
|
+
def _repo_key(r):
|
|
109
|
+
gid = r.get("github_id")
|
|
110
|
+
if gid is not None:
|
|
111
|
+
return str(gid)
|
|
112
|
+
return f"{r.get('owner')}/{r.get('name')}"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _is_known_repo(config, repo_entry):
|
|
116
|
+
return _repo_key(repo_entry) in config.get("repos", {})
|
|
108
117
|
|
|
109
118
|
|
|
110
119
|
def _save_repos(config, repos):
|
|
111
120
|
if "repos" not in config:
|
|
112
121
|
config["repos"] = {}
|
|
113
122
|
for r in repos:
|
|
114
|
-
key =
|
|
115
|
-
config["repos"]
|
|
123
|
+
key = _repo_key(r)
|
|
124
|
+
existing = config["repos"].get(key, {})
|
|
125
|
+
existing.update({
|
|
126
|
+
"github_id": r.get("github_id"),
|
|
127
|
+
"owner": r["owner"],
|
|
128
|
+
"name": r["name"],
|
|
129
|
+
"token": r["token"],
|
|
130
|
+
})
|
|
131
|
+
config["repos"][key] = existing
|
|
116
132
|
_save_config(config)
|
|
117
133
|
|
|
118
134
|
|
|
@@ -145,7 +161,7 @@ def _find_target_repo(repos):
|
|
|
145
161
|
cwd_slug = _detect_repo_from_cwd()
|
|
146
162
|
if cwd_slug:
|
|
147
163
|
for r in repos:
|
|
148
|
-
if f"{r
|
|
164
|
+
if f"{r.get('owner')}/{r.get('name')}" == cwd_slug:
|
|
149
165
|
return r
|
|
150
166
|
return repos[0] if repos else None
|
|
151
167
|
|
|
@@ -184,11 +200,12 @@ def get_gh_token():
|
|
|
184
200
|
|
|
185
201
|
def _get_repo_token(config):
|
|
186
202
|
"""Get the repo token for the current directory from config."""
|
|
187
|
-
cwd_repo = _detect_repo_from_cwd()
|
|
188
|
-
if cwd_repo and cwd_repo in config.get("repos", {}):
|
|
189
|
-
return config["repos"][cwd_repo]["token"]
|
|
190
|
-
# Fall back to first repo in config
|
|
191
203
|
repos = config.get("repos", {})
|
|
204
|
+
cwd_repo = _detect_repo_from_cwd()
|
|
205
|
+
if cwd_repo:
|
|
206
|
+
for entry in repos.values():
|
|
207
|
+
if f"{entry.get('owner')}/{entry.get('name')}" == cwd_repo:
|
|
208
|
+
return entry["token"]
|
|
192
209
|
if repos:
|
|
193
210
|
return next(iter(repos.values()))["token"]
|
|
194
211
|
print("error: no repos configured. run 'ghosttrap setup' first.", file=sys.stderr)
|
|
@@ -210,9 +227,10 @@ async def _connect_and_handle(server_url, token, config, once=False):
|
|
|
210
227
|
repos = event.get("repos", [])
|
|
211
228
|
print(f"watching {len(repos)} repo(s)", file=sys.stderr)
|
|
212
229
|
|
|
213
|
-
new_repos = [r for r in repos if not _is_known_repo(config, r
|
|
230
|
+
new_repos = [r for r in repos if not _is_known_repo(config, r)]
|
|
231
|
+
# Always sync — picks up renamed/transferred repos by github_id.
|
|
232
|
+
_save_repos(config, repos)
|
|
214
233
|
if new_repos:
|
|
215
|
-
_save_repos(config, repos)
|
|
216
234
|
target = _find_target_repo(new_repos)
|
|
217
235
|
if target:
|
|
218
236
|
_print_setup_snippet(target)
|
|
@@ -220,10 +238,13 @@ async def _connect_and_handle(server_url, token, config, once=False):
|
|
|
220
238
|
sdk_latest = event.get("sdk_latest")
|
|
221
239
|
if sdk_latest:
|
|
222
240
|
cwd_repo = _detect_repo_from_cwd()
|
|
223
|
-
if cwd_repo
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
241
|
+
if cwd_repo:
|
|
242
|
+
for entry in config.get("repos", {}).values():
|
|
243
|
+
if f"{entry.get('owner')}/{entry.get('name')}" == cwd_repo:
|
|
244
|
+
installed = entry.get("sdk_version")
|
|
245
|
+
if installed and installed != sdk_latest:
|
|
246
|
+
print(f"ghosttrap-sdk {sdk_latest} available (you have {installed})", file=sys.stderr)
|
|
247
|
+
break
|
|
227
248
|
|
|
228
249
|
if not once:
|
|
229
250
|
print(f"waiting for errors...", file=sys.stderr)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|