runspec 0.76.1__tar.gz → 0.76.2__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.
- {runspec-0.76.1 → runspec-0.76.2}/PKG-INFO +1 -1
- {runspec-0.76.1 → runspec-0.76.2}/pyproject.toml +1 -1
- {runspec-0.76.1 → runspec-0.76.2}/runspec/logs.py +32 -2
- {runspec-0.76.1 → runspec-0.76.2}/runspec.egg-info/PKG-INFO +1 -1
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_logs.py +6 -1
- {runspec-0.76.1 → runspec-0.76.2}/runspec/__init__.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/ansiblegen.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/auditlog.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/become.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/cli.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/denylist.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/discovery.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/emitutil.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/env.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/errors.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/execute.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/finder.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/inbox.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/inference.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/jump.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/loader.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/logging_setup.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/mock.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/models.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/parser.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/py.typed +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/rebrand.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/rundeckgen.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/runexec.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/runsdb.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/runspec.toml +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/schemacheck.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/serve.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/stubgen.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/testing.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/types.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec/validator.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec.egg-info/SOURCES.txt +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec.egg-info/dependency_links.txt +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec.egg-info/entry_points.txt +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec.egg-info/requires.txt +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/runspec.egg-info/top_level.txt +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/setup.cfg +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/__init__.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/conftest.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_agent.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_ansiblegen.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_audit.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_become.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_cli_discover.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_cli_emit.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_cli_init.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_cli_test.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_denylist.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_discovery.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_env.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_exec.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_finder.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_inbox.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_inference.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_jump.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_loader.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_log_resilience.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_logging_setup.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_mock.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_models.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_parser.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_password.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_rebrand.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_run_as_enforce.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_run_summary.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_rundeckgen.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_runsdb.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_runsdb_events.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_runsdb_sync.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_schemacheck.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_secret_argv.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_serve.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_serve_e2e.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_stubgen.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_testing.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_types.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_typing.py +0 -0
- {runspec-0.76.1 → runspec-0.76.2}/tests/test_validator.py +0 -0
|
@@ -342,8 +342,38 @@ def resolve_run_id(run: str, run_ids: Iterable[str], runnable: str | None = None
|
|
|
342
342
|
|
|
343
343
|
def runnables_with_logs(dirs: Iterable[Path]) -> list[str]:
|
|
344
344
|
"""Sorted names of the runnables that have log files in ``dirs`` (per-run,
|
|
345
|
-
archive or single-mode, including the inbox) — what ``runspec logs`` can show.
|
|
346
|
-
|
|
345
|
+
archive or single-mode, including the inbox) — what ``runspec logs`` can show.
|
|
346
|
+
|
|
347
|
+
``~/logs`` is shared with whatever else logs there, so a plain ``{name}.log``
|
|
348
|
+
counts only when it holds runspec records (see :func:`_is_runspec_log`);
|
|
349
|
+
per-run files and archives are known by their names."""
|
|
350
|
+
names: set[str] = set()
|
|
351
|
+
for f in _discover(dirs, None, include_inbox=True):
|
|
352
|
+
name = _runnable_of(f)
|
|
353
|
+
if name and name not in names and (_is_managed(f) or _is_runspec_log(f)):
|
|
354
|
+
names.add(name)
|
|
355
|
+
return sorted(names)
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def _is_runspec_log(path: Path, *, max_lines: int = 5) -> bool:
|
|
359
|
+
"""True when one of the first ``max_lines`` lines of ``path`` is a runspec
|
|
360
|
+
JSON record — an object with ``ts``/``level``/``message`` and an
|
|
361
|
+
``extra.run_id``, which runspec stamps on every record it writes."""
|
|
362
|
+
try:
|
|
363
|
+
with path.open("r", encoding="utf-8", errors="replace") as fh:
|
|
364
|
+
for _ in range(max_lines):
|
|
365
|
+
line = fh.readline(65536)
|
|
366
|
+
if not line:
|
|
367
|
+
return False
|
|
368
|
+
try:
|
|
369
|
+
rec = json.loads(line)
|
|
370
|
+
except ValueError:
|
|
371
|
+
continue
|
|
372
|
+
if isinstance(rec, dict) and {"ts", "level", "message"} <= rec.keys() and _line_extra(rec).get("run_id"):
|
|
373
|
+
return True
|
|
374
|
+
except OSError:
|
|
375
|
+
pass
|
|
376
|
+
return False
|
|
347
377
|
|
|
348
378
|
|
|
349
379
|
def _file_run_id(name: str) -> str | None:
|
|
@@ -134,8 +134,13 @@ class TestView:
|
|
|
134
134
|
def test_runnables_with_logs(self, tmp_path):
|
|
135
135
|
_write_run(tmp_path, "deploy", "r1", "20260601T100000Z", _summary("r1", "deploy", ts="2026-06-01T10:00:00+00:00"))
|
|
136
136
|
_write_run(tmp_path, "backup.web", "r2", "20260601T100000Z", _summary("r2", "backup.web", ts="2026-06-01T10:00:00+00:00"))
|
|
137
|
-
(tmp_path / "single.log").write_text("", encoding="utf-8")
|
|
137
|
+
(tmp_path / "single.log").write_text(_line(_id("r3"), "hi", ts="2026-06-01T10:00:00+00:00") + "\n", encoding="utf-8")
|
|
138
138
|
(tmp_path / "audit.jsonl").write_text("", encoding="utf-8")
|
|
139
|
+
# Other scripts' logs (shared ~/logs) aren't runnables: plain text, JSON
|
|
140
|
+
# without a run id, empty.
|
|
141
|
+
(tmp_path / "backup-script.log").write_text("2026-06-01 10:00 starting backup\n", encoding="utf-8")
|
|
142
|
+
(tmp_path / "other.log").write_text(json.dumps({"ts": "x", "level": "INFO", "message": "m"}) + "\n", encoding="utf-8")
|
|
143
|
+
(tmp_path / "empty.log").write_text("", encoding="utf-8")
|
|
139
144
|
assert logs.runnables_with_logs([tmp_path]) == ["backup.web", "deploy", "single"]
|
|
140
145
|
|
|
141
146
|
def test_json_passthrough(self, tmp_path):
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|