From 42fa8228b0f02e75d28b18d78ed11e4d3296a66a Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Sat, 29 Aug 2026 11:04:09 +0200 Subject: [PATCH] Keep going past a broken target, and run what is already built Four follow-ups to the previous commit, three of them cases where the new runner still did the thing it was written to stop doing. A build that was already up to date ran nothing. `make` says "up to date" on the second invocation, no executable changes mtime, the before/after snapshot diff is empty -- and the runner printed a line and stopped, which is exactly the complaint that started this. It now falls back to running the executables that are there. Proved with a scratch course whose makefile has a real file target, because both make courses in this repo rebuild unconditionally and neither can reach the branch. One broken target aborted the rest. `run all` stopped at the first failure under set -e, so ELAC's second document was never attempted. Failures are collected per target and reported together at the end instead. An index was ignored when a course had one target: `run ECOAR 5` silently ran target 1. Selections are validated before the single-target shortcut now. And "ECOAR has 1 targets" read like a bug in the tool. That last fix then falsified a claim in TODO-refactor-remaining.md. It said ELAC's projectA and AIS's report/final/ver1 "fail with latexmk exit 12" and were broken documents. With build/ deleted first, both courses build every document and exit 0. The exit 12 reproduced once against pre-existing build state and never from clean; the note now says that, and says which part is inference rather than re-verified, because the original failing state is gone. All 44 probes still produce a byte-identical runnability-local.tsv. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_0172dj34FDpao6wsix8ZuCzE --- TODO-refactor-remaining.md | 13 +++++++++---- scripts/course_exec.sh | 15 ++++++++++++--- scripts/course_lib.sh | 19 +++++++++++++++++-- scripts/course_targets.sh | 10 ++++++++-- 4 files changed, 46 insertions(+), 11 deletions(-) diff --git a/TODO-refactor-remaining.md b/TODO-refactor-remaining.md index 27fac10d..136a11aa 100644 --- a/TODO-refactor-remaining.md +++ b/TODO-refactor-remaining.md @@ -115,10 +115,15 @@ Two things surfaced on the way and are still true: readdir order any more. What is still open is that some of those documents are broken -- see below. MOM has three, ELAC two, AIS several drafts. Sorting looked like the fix until it swapped ELAC to `projectA` and AIS to - `report/final/ver1`, **both of which fail with latexmk exit 12** -- so those - documents are broken and sorting merely surfaced it. Either build every - document, or name one per course with `--entry`; either way the broken ones - need looking at. + `report/final/ver1`, both of which then failed with latexmk exit 12. + **That claim did not survive re-testing on 2026-08-29.** With the build + directories deleted first, `./run.sh run NotProgramming/ELAC --batch` and + `./run.sh run Programming/AIS --batch` both build every document and exit 0. + The exit 12 reproduced once in this session against pre-existing `build/` + state and not once from clean, so the likeliest cause is stale artifacts + rather than a broken document -- but the original failing state is gone and + that is an inference, not something re-verified. If it comes back, delete + `build/` before concluding anything about the document. - `unityhub --version` never returns on this machine, which is why `_works` carries a `timeout 5`. Use `_works` only for tools that answer `--version`; `_have` is still right for the other ten probe call sites. diff --git a/scripts/course_exec.sh b/scripts/course_exec.sh index 151c3e48..a4edc0e8 100644 --- a/scripts/course_exec.sh +++ b/scripts/course_exec.sh @@ -67,10 +67,19 @@ _exec_new_binaries() { new_paths+=("$path") done < <(comm -13 <(printf '%s\n' "$before") <(printf '%s\n' "$after")) + # Nothing new does not mean nothing to run: make says "up to date" on the + # second invocation, and running nothing then is the very complaint this + # file exists to fix. Fall back to whatever executables are there. if [[ ${#new_paths[@]} -eq 0 ]]; then - printf 'nothing new to run after %s (already built, or it produces no binary)\n' \ - "$what" - return 0 + while IFS= read -r line; do + [[ -n "$line" ]] || continue + new_paths+=("${line%%$'\t'*}") + done < <(printf '%s\n' "$after") + if [[ ${#new_paths[@]} -eq 0 ]]; then + printf 'nothing to run after %s (it produces no binary)\n' "$what" + return 0 + fi + printf 'already up to date after %s; running what is there\n' "$what" fi local exe for exe in "${new_paths[@]}"; do diff --git a/scripts/course_lib.sh b/scripts/course_lib.sh index a74e6e4b..c5ad8003 100644 --- a/scripts/course_lib.sh +++ b/scripts/course_lib.sh @@ -209,11 +209,20 @@ _run_selected() { _collect_targets _choose_target || return 1 - local before after target + local before after target rc + local failed=() before="$(_tree_state)" for target in "${COURSE_CHOSEN[@]}"; do COURSE_ENTRY="$target" - _run + # One broken target must not hide the rest. ELAC and AIS each ship a + # document that fails to build, and aborting there meant their working + # reports were never attempted -- the old runner only looked fine + # because it built whichever one readdir happened to hand back first. + rc=0 + _run || rc=$? + if [[ "$rc" -ne 0 ]]; then + failed+=("$(_target_label "$target") (exit $rc)") + fi done after="$(_tree_state)" if [[ "$before" != "$after" ]]; then @@ -221,6 +230,12 @@ _run_selected() { diff <(printf '%s\n' "$before") <(printf '%s\n' "$after") >&2 || true return 1 fi + if [[ ${#failed[@]} -gt 0 ]]; then + printf '\n%s of %s targets failed to build:\n' \ + "${#failed[@]}" "${#COURSE_CHOSEN[@]}" >&2 + printf ' %s\n' "${failed[@]}" >&2 + return 1 + fi if [[ "$COURSE_EXEC_FAILURES" -gt 0 ]]; then printf '\n%s of the programs did not exit cleanly (see above)\n' \ "$COURSE_EXEC_FAILURES" >&2 diff --git a/scripts/course_targets.sh b/scripts/course_targets.sh index cb02503f..72ede13f 100644 --- a/scripts/course_targets.sh +++ b/scripts/course_targets.sh @@ -98,7 +98,10 @@ _match_selection() { } _print_menu() { - printf '\n%s has %d targets:\n\n' "${COURSE_ROOT##*/}" "${#COURSE_TARGETS[@]}" + local plural="targets" + if [[ ${#COURSE_TARGETS[@]} -eq 1 ]]; then plural="target"; fi + printf '\n%s has %d %s:\n\n' \ + "${COURSE_ROOT##*/}" "${#COURSE_TARGETS[@]}" "$plural" local i=1 t marker for t in "${COURSE_TARGETS[@]}"; do marker="" @@ -116,7 +119,10 @@ _choose_target() { COURSE_CHOSEN=() local idx - if [[ ${#COURSE_TARGETS[@]} -eq 1 ]]; then + # A selection is validated even when there is only one target: silently + # running target 1 because you asked for target 5 is the same species of + # confusion as running something and not saying what. + if [[ ${#COURSE_TARGETS[@]} -eq 1 && -z "${COURSE_SELECT:-}" ]]; then COURSE_CHOSEN=("${COURSE_TARGETS[@]}") return 0 fi