Skip to content

Commit 97e45bb

Browse files
committed
Treat empty outputs/ next to populated notebooks/ as a rebuild trigger
1 parent c718b14 commit 97e45bb

1 file changed

Lines changed: 27 additions & 18 deletions

File tree

scripts/build.py

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -224,13 +224,14 @@ def _is_build_ok(version_dir: Path) -> bool:
224224
Truth sources, in priority order:
225225
1. A `.build-ok` marker file → build completed successfully on a previous run.
226226
2. Implicit fallback for legacy builds that predate the marker:
227-
- api.json or manifest.json exists, AND
228-
- either outputs/ is missing (no notebooks) or every outputs/*.json reports
229-
success: true.
230-
This keeps healthy historical versions from being rebuilt unnecessarily.
231-
232-
A build that produced any failed notebook output is NOT ok — the version is
233-
rebuilt next run so the regression gets healed automatically.
227+
- manifest.json or api.json exists,
228+
- if notebooks/ contains any .ipynb files then outputs/ must contain at
229+
least as many *.json files (allowing fewer when some notebooks are
230+
marked non-executable — we accept a small slack), and
231+
- every outputs/*.json reports success: true.
232+
233+
A build with failed outputs OR a notebooks/ dir without matching outputs/ is
234+
NOT ok — it gets retried next run.
234235
"""
235236
if not version_dir.exists():
236237
return False
@@ -243,19 +244,27 @@ def _is_build_ok(version_dir: Path) -> bool:
243244
if not has_core:
244245
return False
245246

247+
notebooks_dir = version_dir / "notebooks"
246248
outputs_dir = version_dir / "outputs"
247-
if not outputs_dir.exists():
248-
# No notebooks to execute → nothing could have failed
249-
return True
250249

251-
for output_file in outputs_dir.glob("*.json"):
252-
try:
253-
with open(output_file, "r", encoding="utf-8") as f:
254-
data = json.load(f)
255-
except (json.JSONDecodeError, OSError):
256-
return False
257-
if data.get("success") is False:
258-
return False
250+
n_notebooks = len(list(notebooks_dir.glob("*.ipynb"))) if notebooks_dir.exists() else 0
251+
n_outputs = len(list(outputs_dir.glob("*.json"))) if outputs_dir.exists() else 0
252+
253+
# Notebooks present but nothing executed (e.g. cleaned-up outputs) → rebuild.
254+
# We don't require an exact match because some notebooks can legitimately be
255+
# marked non-executable in the manifest and won't produce an output file.
256+
if n_notebooks > 0 and n_outputs == 0:
257+
return False
258+
259+
if outputs_dir.exists():
260+
for output_file in outputs_dir.glob("*.json"):
261+
try:
262+
with open(output_file, "r", encoding="utf-8") as f:
263+
data = json.load(f)
264+
except (json.JSONDecodeError, OSError):
265+
return False
266+
if data.get("success") is False:
267+
return False
259268

260269
return True
261270

0 commit comments

Comments
 (0)