Keep going past a broken target, and run what is already built
Some checks failed
md-naming / markdown naming convention (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
runnability / runnability (push) Has been cancelled

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 <dir> 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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0172dj34FDpao6wsix8ZuCzE
This commit is contained in:
Krzysztof Rudnicki 2026-08-29 11:04:09 +02:00
parent d5b94cd8a7
commit 42fa8228b0
4 changed files with 46 additions and 11 deletions

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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