wup 0.2.11__tar.gz → 0.2.13__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.
- {wup-0.2.11/wup.egg-info → wup-0.2.13}/PKG-INFO +6 -6
- {wup-0.2.11 → wup-0.2.13}/README.md +5 -5
- {wup-0.2.11 → wup-0.2.13}/pyproject.toml +1 -1
- {wup-0.2.11 → wup-0.2.13}/wup/__init__.py +1 -1
- {wup-0.2.11 → wup-0.2.13}/wup/cli.py +101 -131
- {wup-0.2.11 → wup-0.2.13/wup.egg-info}/PKG-INFO +6 -6
- {wup-0.2.11 → wup-0.2.13}/LICENSE +0 -0
- {wup-0.2.11 → wup-0.2.13}/setup.cfg +0 -0
- {wup-0.2.11 → wup-0.2.13}/tests/test_e2e.py +0 -0
- {wup-0.2.11 → wup-0.2.13}/tests/test_testql_watcher.py +0 -0
- {wup-0.2.11 → wup-0.2.13}/tests/test_wup.py +0 -0
- {wup-0.2.11 → wup-0.2.13}/wup/config.py +0 -0
- {wup-0.2.11 → wup-0.2.13}/wup/core.py +0 -0
- {wup-0.2.11 → wup-0.2.13}/wup/dependency_mapper.py +0 -0
- {wup-0.2.11 → wup-0.2.13}/wup/models/__init__.py +0 -0
- {wup-0.2.11 → wup-0.2.13}/wup/models/config.py +0 -0
- {wup-0.2.11 → wup-0.2.13}/wup/testql_discovery.py +0 -0
- {wup-0.2.11 → wup-0.2.13}/wup/testql_watcher.py +0 -0
- {wup-0.2.11 → wup-0.2.13}/wup.egg-info/SOURCES.txt +0 -0
- {wup-0.2.11 → wup-0.2.13}/wup.egg-info/dependency_links.txt +0 -0
- {wup-0.2.11 → wup-0.2.13}/wup.egg-info/entry_points.txt +0 -0
- {wup-0.2.11 → wup-0.2.13}/wup.egg-info/requires.txt +0 -0
- {wup-0.2.11 → wup-0.2.13}/wup.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wup
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.13
|
|
4
4
|
Summary: WUP (What's Up) - Intelligent file watcher for regression testing in large projects
|
|
5
5
|
Author-email: Tom Sapletta <tom@sapletta.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -29,17 +29,17 @@ Dynamic: license-file
|
|
|
29
29
|
|
|
30
30
|
## AI Cost Tracking
|
|
31
31
|
|
|
32
|
-
    
|
|
33
|
+
  
|
|
34
34
|
|
|
35
|
-
- 🤖 **LLM usage:** $
|
|
36
|
-
- 👤 **Human dev:** ~$
|
|
35
|
+
- 🤖 **LLM usage:** $2.1000 (14 commits)
|
|
36
|
+
- 👤 **Human dev:** ~$319 (3.2h @ $100/h, 30min dedup)
|
|
37
37
|
|
|
38
38
|
Generated on 2026-04-29 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
39
39
|
|
|
40
40
|
---
|
|
41
41
|
|
|
42
|
-
    
|
|
43
43
|
|
|
44
44
|
**WUP (What's Up)** - Intelligent file watcher for regression testing in large projects.
|
|
45
45
|
|
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
|
|
4
4
|
## AI Cost Tracking
|
|
5
5
|
|
|
6
|
-
    
|
|
7
|
+
  
|
|
8
8
|
|
|
9
|
-
- 🤖 **LLM usage:** $
|
|
10
|
-
- 👤 **Human dev:** ~$
|
|
9
|
+
- 🤖 **LLM usage:** $2.1000 (14 commits)
|
|
10
|
+
- 👤 **Human dev:** ~$319 (3.2h @ $100/h, 30min dedup)
|
|
11
11
|
|
|
12
12
|
Generated on 2026-04-29 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
-
    
|
|
17
17
|
|
|
18
18
|
**WUP (What's Up)** - Intelligent file watcher for regression testing in large projects.
|
|
19
19
|
|
|
@@ -7,7 +7,7 @@ WUP monitors file changes and runs intelligent regression tests using a 3-layer
|
|
|
7
7
|
3. Detail Layer: Full tests with blame reports (only on failure)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
__version__ = "0.2.
|
|
10
|
+
__version__ = "0.2.13"
|
|
11
11
|
__author__ = "Tom Sapletta"
|
|
12
12
|
|
|
13
13
|
from .config import load_config, save_config, get_default_config
|
|
@@ -173,152 +173,122 @@ def status(
|
|
|
173
173
|
config: Optional[str] = typer.Option(None, "--config", "-C", help="Path to wup.yaml config file"),
|
|
174
174
|
delta_seconds: int = typer.Option(0, "--delta-seconds", help="Show only service health transitions from last N seconds"),
|
|
175
175
|
failed_only: bool = typer.Option(False, "--failed-only", help="Show only currently failing services"),
|
|
176
|
+
watch: bool = typer.Option(False, "--watch", "-w", help="Live mode: refresh display in real time"),
|
|
177
|
+
interval: int = typer.Option(5, "--interval", "-i", help="Refresh interval in seconds for --watch mode"),
|
|
176
178
|
):
|
|
177
179
|
"""
|
|
178
180
|
Show dependency map status and configuration.
|
|
179
181
|
"""
|
|
182
|
+
import json
|
|
183
|
+
import time
|
|
184
|
+
|
|
180
185
|
project_path = Path(".").resolve()
|
|
181
|
-
|
|
182
|
-
# Load configuration
|
|
183
186
|
config_path = Path(config) if config else None
|
|
184
187
|
wup_config = load_config(project_path, config_path)
|
|
185
|
-
|
|
186
|
-
console.print(f"[bold cyan]📊 WUP Status[/bold cyan]")
|
|
187
|
-
console.print(f"[dim]Project: {wup_config.project.name}[/dim]")
|
|
188
|
-
console.print(f"[dim]Description: {wup_config.project.description}[/dim]")
|
|
189
|
-
console.print()
|
|
190
|
-
|
|
191
|
-
# Show watch configuration
|
|
192
|
-
console.print("[bold]Watch Configuration:[/bold]")
|
|
193
|
-
console.print(f" Paths: {', '.join(wup_config.watch.paths) if wup_config.watch.paths else 'default'}")
|
|
194
|
-
console.print(f" Excludes: {', '.join(wup_config.watch.exclude_patterns)}")
|
|
195
|
-
console.print()
|
|
196
|
-
|
|
197
|
-
# Show services
|
|
198
|
-
if wup_config.services:
|
|
199
|
-
console.print(f"[bold]Services ({len(wup_config.services)}):[/bold]")
|
|
200
|
-
for svc in wup_config.services:
|
|
201
|
-
console.print(f" [cyan]{svc.name}[/cyan]")
|
|
202
|
-
console.print(f" Root: {svc.root}")
|
|
203
|
-
console.print(f" Quick: scope={svc.quick_tests.scope}, max={svc.quick_tests.max_endpoints}")
|
|
204
|
-
console.print(f" Detail: scope={svc.detail_tests.scope}, max={svc.detail_tests.max_endpoints}")
|
|
205
|
-
console.print()
|
|
206
|
-
|
|
207
|
-
# Show dependency map status
|
|
208
|
-
deps_path = Path(deps_file)
|
|
209
|
-
|
|
210
|
-
if not deps_path.exists():
|
|
211
|
-
console.print(f"[yellow]Warning: Dependency file '{deps_file}' does not exist[/yellow]")
|
|
212
|
-
console.print(f"[dim]Run 'wup map-deps' to create it[/dim]")
|
|
213
|
-
console.print()
|
|
214
|
-
return
|
|
215
|
-
|
|
216
|
-
import json
|
|
217
|
-
with open(deps_file) as f:
|
|
218
|
-
deps = json.load(f)
|
|
219
|
-
|
|
220
|
-
services = deps.get("services", {})
|
|
221
|
-
files = deps.get("files", {})
|
|
222
|
-
|
|
223
|
-
console.print(f"[bold]Dependency Map:[/bold]")
|
|
224
|
-
console.print(f" Services: {len(services)}")
|
|
225
|
-
console.print(f" Files: {len(files)}")
|
|
226
|
-
console.print()
|
|
227
|
-
|
|
228
|
-
if services:
|
|
229
|
-
console.print("[bold]Service Details:[/bold]")
|
|
230
|
-
for service, info in sorted(services.items()):
|
|
231
|
-
# Handle both dict format (new) and list format (legacy)
|
|
232
|
-
if isinstance(info, dict):
|
|
233
|
-
endpoints = info.get("endpoints", [])
|
|
234
|
-
service_files = info.get("files", [])
|
|
235
|
-
elif isinstance(info, list):
|
|
236
|
-
endpoints = info
|
|
237
|
-
service_files = []
|
|
238
|
-
else:
|
|
239
|
-
endpoints = []
|
|
240
|
-
service_files = []
|
|
241
|
-
|
|
242
|
-
console.print(f" [cyan]{service}[/cyan]")
|
|
243
|
-
console.print(f" Endpoints: {len(endpoints)}")
|
|
244
|
-
console.print(f" Files: {len(service_files)}")
|
|
245
|
-
if endpoints:
|
|
246
|
-
console.print(f" Sample endpoints: {', '.join(endpoints[:3])}")
|
|
247
|
-
|
|
248
|
-
# Show service health state and recent transitions (TestQL watcher)
|
|
249
188
|
health_state_path = project_path / ".wup" / "service-health.json"
|
|
250
189
|
health_events_path = project_path / ".wup" / "service-health-events.jsonl"
|
|
251
190
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
191
|
+
deps_path = Path(deps_file)
|
|
192
|
+
|
|
193
|
+
def _build_panel(ts: float) -> "Group":
|
|
194
|
+
from rich.console import Group
|
|
195
|
+
from rich.text import Text
|
|
196
|
+
from rich.padding import Padding
|
|
197
|
+
lines: list = []
|
|
255
198
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
199
|
+
# header
|
|
200
|
+
lines.append(Text.from_markup(
|
|
201
|
+
f"[bold cyan]📊 WUP Status[/bold cyan] "
|
|
202
|
+
f"[dim]{wup_config.project.name}[/dim] "
|
|
203
|
+
f"[dim]updated {time.strftime('%H:%M:%S', time.localtime(ts))}[/dim]"
|
|
204
|
+
))
|
|
262
205
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
206
|
+
# --- failing services ---
|
|
207
|
+
health_state: dict = {}
|
|
208
|
+
if health_state_path.exists():
|
|
209
|
+
try:
|
|
210
|
+
payload = json.loads(health_state_path.read_text(encoding="utf-8"))
|
|
211
|
+
if isinstance(payload, dict):
|
|
212
|
+
health_state = payload
|
|
213
|
+
except json.JSONDecodeError:
|
|
214
|
+
pass
|
|
215
|
+
|
|
216
|
+
if failed_only or watch:
|
|
217
|
+
failing = [
|
|
218
|
+
(svc, data)
|
|
219
|
+
for svc, data in sorted(health_state.items())
|
|
220
|
+
if isinstance(data, dict) and data.get("status") == "down"
|
|
221
|
+
]
|
|
222
|
+
lines.append(Text(""))
|
|
223
|
+
lines.append(Text.from_markup("[bold]Currently failing services:[/bold]"))
|
|
224
|
+
if not failing:
|
|
225
|
+
lines.append(Text.from_markup(" [green]✓ None[/green]"))
|
|
226
|
+
else:
|
|
227
|
+
for svc, data in failing:
|
|
228
|
+
stage = data.get("stage", "")
|
|
229
|
+
message = data.get("message", "")
|
|
230
|
+
track_file = data.get("track_file", "")
|
|
231
|
+
lines.append(Text.from_markup(f" [red]✗ {svc}[/red] [dim]{stage}[/dim]"))
|
|
232
|
+
if message:
|
|
233
|
+
lines.append(Text.from_markup(f" [dim]{message}[/dim]"))
|
|
234
|
+
if track_file:
|
|
235
|
+
lines.append(Text.from_markup(f" [dim]track: {track_file}[/dim]"))
|
|
284
236
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
237
|
+
# --- delta ---
|
|
238
|
+
effective_delta = delta_seconds if delta_seconds > 0 else (30 if watch else 0)
|
|
239
|
+
if effective_delta > 0:
|
|
240
|
+
cutoff = int(ts) - effective_delta
|
|
241
|
+
recent_events: list = []
|
|
242
|
+
if health_events_path.exists():
|
|
243
|
+
with health_events_path.open("r", encoding="utf-8") as handle:
|
|
244
|
+
for line in handle:
|
|
245
|
+
line = line.strip()
|
|
246
|
+
if not line:
|
|
247
|
+
continue
|
|
248
|
+
try:
|
|
249
|
+
event = json.loads(line)
|
|
250
|
+
except json.JSONDecodeError:
|
|
251
|
+
continue
|
|
252
|
+
if int(event.get("timestamp", 0)) >= cutoff:
|
|
253
|
+
recent_events.append(event)
|
|
288
254
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
255
|
+
lines.append(Text(""))
|
|
256
|
+
lines.append(Text.from_markup(f"[bold]Service health delta (last {effective_delta}s):[/bold]"))
|
|
257
|
+
if not recent_events:
|
|
258
|
+
lines.append(Text.from_markup(" [yellow]No health transitions in selected window[/yellow]"))
|
|
259
|
+
else:
|
|
260
|
+
recent_events.sort(key=lambda e: int(e.get("timestamp", 0)), reverse=True)
|
|
261
|
+
for event in recent_events:
|
|
262
|
+
svc = event.get("service", "unknown")
|
|
263
|
+
prev = event.get("previous_status", "unknown")
|
|
264
|
+
curr = event.get("status", "unknown")
|
|
265
|
+
stage = event.get("stage", "")
|
|
266
|
+
message = event.get("message", "")
|
|
267
|
+
track_file = event.get("track_file", "")
|
|
268
|
+
arrow_color = "green" if curr == "up" else "red"
|
|
269
|
+
lines.append(Text.from_markup(
|
|
270
|
+
f" [cyan]{svc}[/cyan]: {prev} [bold {arrow_color}]→ {curr}[/bold {arrow_color}] [dim]({stage})[/dim]"
|
|
271
|
+
))
|
|
272
|
+
if message:
|
|
273
|
+
lines.append(Text.from_markup(f" [dim]{message}[/dim]"))
|
|
274
|
+
if track_file:
|
|
275
|
+
lines.append(Text.from_markup(f" [dim]track: {track_file}[/dim]"))
|
|
276
|
+
|
|
277
|
+
return Group(*lines)
|
|
278
|
+
|
|
279
|
+
if not watch:
|
|
280
|
+
console.print(_build_panel(time.time()))
|
|
281
|
+
return
|
|
303
282
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
curr = event.get("status", "unknown")
|
|
314
|
-
stage = event.get("stage", "")
|
|
315
|
-
message = event.get("message", "")
|
|
316
|
-
track_file = event.get("track_file", "")
|
|
317
|
-
console.print(f" [cyan]{svc}[/cyan]: {prev} -> {curr} ({stage})")
|
|
318
|
-
if message:
|
|
319
|
-
console.print(f" message: {message}")
|
|
320
|
-
if track_file:
|
|
321
|
-
console.print(f" track: {track_file}")
|
|
283
|
+
# --- live / watch mode ---
|
|
284
|
+
from rich.live import Live
|
|
285
|
+
try:
|
|
286
|
+
with Live(_build_panel(time.time()), refresh_per_second=1, console=console) as live:
|
|
287
|
+
while True:
|
|
288
|
+
time.sleep(interval)
|
|
289
|
+
live.update(_build_panel(time.time()))
|
|
290
|
+
except KeyboardInterrupt:
|
|
291
|
+
pass
|
|
322
292
|
|
|
323
293
|
|
|
324
294
|
@app.command()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wup
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.13
|
|
4
4
|
Summary: WUP (What's Up) - Intelligent file watcher for regression testing in large projects
|
|
5
5
|
Author-email: Tom Sapletta <tom@sapletta.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -29,17 +29,17 @@ Dynamic: license-file
|
|
|
29
29
|
|
|
30
30
|
## AI Cost Tracking
|
|
31
31
|
|
|
32
|
-
    
|
|
33
|
+
  
|
|
34
34
|
|
|
35
|
-
- 🤖 **LLM usage:** $
|
|
36
|
-
- 👤 **Human dev:** ~$
|
|
35
|
+
- 🤖 **LLM usage:** $2.1000 (14 commits)
|
|
36
|
+
- 👤 **Human dev:** ~$319 (3.2h @ $100/h, 30min dedup)
|
|
37
37
|
|
|
38
38
|
Generated on 2026-04-29 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
39
39
|
|
|
40
40
|
---
|
|
41
41
|
|
|
42
|
-
    
|
|
43
43
|
|
|
44
44
|
**WUP (What's Up)** - Intelligent file watcher for regression testing in large projects.
|
|
45
45
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|