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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ghosttrap-cli
3
- Version: 0.3.10
3
+ Version: 0.3.12
4
4
  Summary: Watch for errors streaming from ghosttrap.io
5
5
  Project-URL: Homepage, https://github.com/alex-rowley/ghosttrap-cli
6
6
  Requires-Python: >=3.10
@@ -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.0"
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 of `"owner/repo"` to `{"token": "t_xxx", "sdk_installed": bool, "sdk_version": str, "init_file": str}`
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. Look it up in the config. If the repo isn't there, tell the user to run `ghosttrap setup`.
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 _is_known_repo(config, owner, name):
107
- return f"{owner}/{name}" in config.get("repos", {})
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 = f"{r['owner']}/{r['name']}"
115
- config["repos"][key] = {"token": r["token"]}
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['owner']}/{r['name']}" == cwd_slug:
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["owner"], r["name"])]
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 and cwd_repo in config.get("repos", {}):
224
- installed = config["repos"][cwd_repo].get("sdk_version")
225
- if installed and installed != sdk_latest:
226
- print(f"ghosttrap-sdk {sdk_latest} available (you have {installed})", file=sys.stderr)
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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ghosttrap-cli
3
- Version: 0.3.10
3
+ Version: 0.3.12
4
4
  Summary: Watch for errors streaming from ghosttrap.io
5
5
  Project-URL: Homepage, https://github.com/alex-rowley/ghosttrap-cli
6
6
  Requires-Python: >=3.10
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ghosttrap-cli"
7
- version = "0.3.10"
7
+ version = "0.3.12"
8
8
  description = "Watch for errors streaming from ghosttrap.io"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
File without changes
File without changes